xml_utils.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2023 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #include "xml_utils.h"
11 
12 #include <mrpt/core/bits_math.h>
13 #include <mrpt/core/format.h>
14 #include <mrpt/img/TColor.h>
15 #include <mrpt/io/vector_loadsave.h> // file_get_contents()
16 #include <mrpt/math/TPolygon2D.h>
17 #include <mrpt/poses/CPose2D.h>
18 #include <mrpt/poses/CPose3D.h>
19 #include <mrpt/system/COutputLogger.h>
20 #include <mrpt/system/filesystem.h>
21 #include <mrpt/system/string_utils.h>
22 #include <mvsim/basic_types.h>
23 
24 #include <cstdio>
25 
26 #include "parse_utils.h"
27 
28 using namespace rapidxml;
29 using namespace mvsim;
30 
36  const std::string& inStr, const std::string& varName,
37  const std::map<std::string, std::string>& variableNamesValues,
38  const char* functionNameContext) const
39 {
40  const std::string str = mvsim::parse(inStr, variableNamesValues);
41 
42  // Special cases:
43  // "%s" ==> std::strings
44  if (std::string(frmt) == std::string("%s"))
45  {
46  std::string& val2 = *reinterpret_cast<std::string*>(val);
47  val2 = mrpt::system::trim(str);
48  }
49  // "%lf_deg" ==> mrpt::DEG2RAD()
50  else if (std::string(frmt) == std::string("%lf_deg"))
51  {
52  if (1 != ::sscanf(str.c_str(), frmt, val))
53  throw std::runtime_error(mrpt::format(
54  "%s Error parsing attribute '%s'='%s' (Expected "
55  "format:'%s')",
56  functionNameContext, varName.c_str(), str.c_str(), frmt));
57  double& ang = *reinterpret_cast<double*>(val);
58  ang = mrpt::DEG2RAD(ang);
59  }
60  // "%bool" ==> bool*
61  else if (std::string(frmt) == std::string("%bool"))
62  {
63  bool& bool_val = *reinterpret_cast<bool*>(val);
64 
65  const std::string sStr =
66  mrpt::system::lowerCase(mrpt::system::trim(std::string(str)));
67  if (sStr == "1" || sStr == "true")
68  bool_val = true;
69  else if (sStr == "0" || sStr == "false")
70  bool_val = false;
71  else
72  throw std::runtime_error(mrpt::format(
73  "%s Error parsing 'bool' attribute '%s'='%s' (Expected "
74  "'true' or 'false')",
75  functionNameContext, varName.c_str(), str.c_str()));
76  }
77  // "%color" ==> mrpt::img::TColor
78  else if (std::string(frmt) == std::string("%color"))
79  {
80  // HTML-like format:
81  if (!(str.size() > 1 && str[0] == '#'))
82  throw std::runtime_error(mrpt::format(
83  "%s Error parsing '%s'='%s' (Expected "
84  "format:'#RRGGBB[AA]')",
85  functionNameContext, varName.c_str(), str.c_str()));
86 
87  unsigned int r, g, b, a = 0xff;
88  int ret = ::sscanf(str.c_str() + 1, "%2x%2x%2x%2x", &r, &g, &b, &a);
89  if (ret != 3 && ret != 4)
90  throw std::runtime_error(mrpt::format(
91  "%s Error parsing '%s'='%s' (Expected "
92  "format:'#RRGGBB[AA]')",
93  functionNameContext, varName.c_str(), str.c_str()));
94  mrpt::img::TColor& col = *reinterpret_cast<mrpt::img::TColor*>(val);
95  col = mrpt::img::TColor(r, g, b, a);
96  }
97  // "%pose2d"
98  // "%pose2d_ptr3d"
99  else if (!strncmp(frmt, "%pose2d", strlen("%pose2d")))
100  {
101  double x, y, yaw;
102  int ret = ::sscanf(str.c_str(), "%lf %lf %lf", &x, &y, &yaw);
103  if (ret != 3)
104  throw std::runtime_error(mrpt::format(
105  "%s Error parsing '%s'='%s' (Expected format:'X Y "
106  "YAW_DEG')",
107  functionNameContext, varName.c_str(), str.c_str()));
108 
109  // User provides angles in deg:
110  yaw = mrpt::DEG2RAD(yaw);
111 
112  const mrpt::poses::CPose2D p(x, y, yaw);
113 
114  // Sub-cases:
115  if (!strcmp(frmt, "%pose2d"))
116  {
117  mrpt::poses::CPose2D& pp =
118  *reinterpret_cast<mrpt::poses::CPose2D*>(val);
119  pp = p;
120  }
121  else if (!strcmp(frmt, "%pose2d_ptr3d"))
122  {
123  mrpt::poses::CPose3D& pp =
124  *reinterpret_cast<mrpt::poses::CPose3D*>(val);
125  pp = mrpt::poses::CPose3D(p);
126  }
127  else
128  throw std::runtime_error(mrpt::format(
129  "%s Error: Unknown format specifier '%s'", functionNameContext,
130  frmt));
131  }
132  // %point3d
133  else if (!strncmp(frmt, "%point3d", strlen("%point3d")))
134  {
135  double x = 0, y = 0, z = 0;
136  int ret = ::sscanf(str.c_str(), "%lf %lf %lf", &x, &y, &z);
137  if (ret != 2 && ret != 3)
138  throw std::runtime_error(mrpt::format(
139  "%s Error parsing '%s'='%s' (Expected format:'X Y [Z]')",
140  functionNameContext, varName.c_str(), str.c_str()));
141 
142  mrpt::math::TPoint3D& pp =
143  *reinterpret_cast<mrpt::math::TPoint3D*>(val);
144 
145  pp.x = x;
146  pp.y = y;
147  pp.z = z;
148  }
149  // "%pose3d"
150  else if (!strncmp(frmt, "%pose3d", strlen("%pose3d")))
151  {
152  double x, y, z, yawDeg, pitchDeg, rollDeg;
153  int ret = ::sscanf(
154  str.c_str(), "%lf %lf %lf %lf %lf %lf", &x, &y, &z, &yawDeg,
155  &pitchDeg, &rollDeg);
156  if (ret != 6)
157  throw std::runtime_error(mrpt::format(
158  "%s Error parsing '%s'='%s' (Expected format:'X Y Z"
159  "YAW_DEG PITCH_DEG ROLL_DEG')",
160  functionNameContext, varName.c_str(), str.c_str()));
161 
162  // User provides angles in deg:
163  const auto yaw = mrpt::DEG2RAD(yawDeg);
164  const auto pitch = mrpt::DEG2RAD(pitchDeg);
165  const auto roll = mrpt::DEG2RAD(rollDeg);
166 
167  const mrpt::poses::CPose3D p(x, y, yaw);
168 
169  mrpt::poses::CPose3D& pp =
170  *reinterpret_cast<mrpt::poses::CPose3D*>(val);
171  pp = mrpt::poses::CPose3D(x, y, z, yaw, pitch, roll);
172  }
173  else
174  {
175  // Generic parse:
176  if (1 != ::sscanf(str.c_str(), frmt, val))
177  throw std::runtime_error(mrpt::format(
178  "%s Error parsing attribute '%s'='%s' (Expected "
179  "format:'%s')",
180  functionNameContext, varName.c_str(), str.c_str(), frmt));
181  }
182 }
183 
186  const TParameterDefinitions& params,
187  const std::map<std::string, std::string>& variableNamesValues,
188  const char* functionNameContext)
189 {
190  for (const auto& param : params)
191  {
192  if (auto attr = xml_node.first_attribute(param.first.c_str());
193  attr && attr->value())
194  {
195  param.second.parse(
196  attr->value(), attr->name(), variableNamesValues,
197  functionNameContext);
198  }
199  }
200 }
201 
204  const TParameterDefinitions& params,
205  const std::map<std::string, std::string>& variableNamesValues,
206  const char* functionNameContext)
207 {
208  if (auto it_param = params.find(xml_node.name()); it_param != params.end())
209  {
210  // parse parameter:
211  it_param->second.parse(
212  xml_node.value(), xml_node.name(), variableNamesValues,
213  functionNameContext);
214  return true;
215  }
216  else
217  {
218  return false;
219  }
220 }
221 
225  const rapidxml::xml_node<char>& root, const TParameterDefinitions& params,
226  const std::map<std::string, std::string>& variableNamesValues,
227  const char* functionNameContext, mrpt::system::COutputLogger* logger)
228 {
229  rapidxml::xml_node<>* node = root.first_node();
230  while (node)
231  {
232  bool recognized = parse_xmlnode_as_param(
233  *node, params, variableNamesValues, functionNameContext);
234  if (!recognized && logger)
235  {
236  logger->logFmt(
237  mrpt::system::LVL_WARN, "Unrecognized tag '<%s>' in %s",
238  node->name(),
239  functionNameContext ? functionNameContext : "(none)");
240  }
241  node = node->next_sibling(nullptr); // Move on to next node
242  }
243 }
244 
245 mrpt::math::TPose2D mvsim::parseXYPHI(
246  const std::string& sOrg, bool allow_missing_angle,
247  double default_angle_radians,
248  const std::map<std::string, std::string>& variableNamesValues)
249 {
250  mrpt::math::TPose2D v;
251  v.phi = mrpt::RAD2DEG(default_angle_radians); // Default ang.
252 
253  const auto s = parse(sOrg, variableNamesValues);
254  int na = ::sscanf(s.c_str(), "%lf %lf %lf", &v.x, &v.y, &v.phi);
255 
256  // User provides numbers as degrees:
257  v.phi = mrpt::DEG2RAD(v.phi);
258 
259  if ((na != 3 && !allow_missing_angle) ||
260  (na != 2 && na != 3 && allow_missing_angle))
261  throw std::runtime_error(
262  mrpt::format("Malformed pose string: '%s'", s.c_str()));
263 
264  return v;
265 }
266 
272  const rapidxml::xml_node<char>& xml_node, mrpt::math::TPolygon2D& out_poly,
273  const char* functionNameContext)
274 {
275  out_poly.clear();
276 
277  for (rapidxml::xml_node<char>* pt_node = xml_node.first_node("pt"); pt_node;
278  pt_node = pt_node->next_sibling("pt"))
279  {
280  if (!pt_node->value())
281  throw std::runtime_error(mrpt::format(
282  "%s Error: <pt> node seems empty.", functionNameContext));
283 
284  mrpt::math::TPoint2D pt;
285  const char* str_val = pt_node->value();
286  if (2 != ::sscanf(str_val, "%lf %lf", &pt.x, &pt.y))
287  throw std::runtime_error(mrpt::format(
288  "%s Error parsing <pt> node: '%s' (Expected format:'<pt>X "
289  "Y</pt>')",
290  functionNameContext, str_val));
291 
292  out_poly.push_back(pt);
293  }
294 
295  if (out_poly.size() < 3)
296  throw std::runtime_error(mrpt::format(
297  "%s Error: <shape> node requires 3 or more <pt>X Y</pt> "
298  "entries.",
299  functionNameContext));
300 }
301 
302 std::tuple<std::shared_ptr<rapidxml::xml_document<>>, rapidxml::xml_node<>*>
304  const std::string& xmlData, const std::string& pathToFile)
305 {
306  using namespace rapidxml;
307  using namespace std::string_literals;
308 
309  // Parse:
310  char* input_str = const_cast<char*>(xmlData.c_str());
311  auto xml = std::make_shared<rapidxml::xml_document<>>();
312  try
313  {
314  xml->parse<0>(input_str);
315  }
316  catch (rapidxml::parse_error& e)
317  {
318  unsigned int line =
319  static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
320  throw std::runtime_error(mrpt::format(
321  "XML parse error (Line %u): %s", static_cast<unsigned>(line),
322  e.what()));
323  }
324 
325  // Sanity check:
326  xml_node<>* root = xml->first_node();
327  ASSERTMSG_(
328  root, "XML parse error: No root node found (empty file '"s +
329  pathToFile + "'?)"s);
330  return {xml, root};
331 }
332 
333 std::tuple<XML_Doc_Data::Ptr, rapidxml::xml_node<>*> mvsim::readXmlAndGetRoot(
334  const std::string& pathToFile,
335  const std::map<std::string, std::string>& variables,
336  const std::set<std::string>& varsRetain)
337 {
338  using namespace rapidxml;
339  using namespace std::string_literals;
340 
341  ASSERT_FILE_EXISTS_(pathToFile);
342 
343  auto xmlDoc = std::make_shared<XML_Doc_Data>();
344 
345  xmlDoc->documentData = mvsim::parse_variables(
346  mrpt::io::file_get_contents(pathToFile), variables, varsRetain);
347  auto [xml, root] = readXmlTextAndGetRoot(xmlDoc->documentData, pathToFile);
348 
349  xmlDoc->doc = std::move(xml);
350 
351  return {xmlDoc, root};
352 }
353 
354 static std::string::size_type findClosing(
355  size_t pos, const std::string& s, const char searchEndChar,
356  const char otherStartChar)
357 {
358  int openEnvs = 1;
359  for (; pos < s.size(); pos++)
360  {
361  const char ch = s[pos];
362  if (ch == otherStartChar)
363  openEnvs++;
364  else if (ch == searchEndChar)
365  {
366  openEnvs--;
367  if (openEnvs == 0)
368  {
369  return pos;
370  }
371  }
372  }
373 
374  // not found:
375  return std::string::npos;
376 }
377 
378 // "foo|bar" -> {"foo","bar"}
379 static std::tuple<std::string, std::string> splitVerticalBar(
380  const std::string& s)
381 {
382  const auto posBar = s.find("|");
383  if (posBar == std::string::npos) return {s, {}};
384 
385  return {s.substr(0, posBar), s.substr(posBar + 1)};
386 }
387 
388 static std::string parseVars(
389  const std::string& text,
390  const std::map<std::string, std::string>& variables,
391  const std::set<std::string>& varsRetain, const size_t searchStartPos = 0)
392 {
393  MRPT_TRY_START
394 
395  const auto start = text.find("${", searchStartPos);
396  if (start == std::string::npos) return text;
397 
398  const std::string pre = text.substr(0, start);
399  const std::string post = text.substr(start + 2);
400 
401  const auto post_end = findClosing(0, post, '}', '{');
402  if (post_end == std::string::npos)
403  {
404  THROW_EXCEPTION_FMT(
405  "Column=%u: Cannot find matching `}` for `${` in: `%s`",
406  static_cast<unsigned int>(start), text.c_str());
407  }
408 
409  const auto varnameOrg = post.substr(0, post_end);
410 
411  const auto [varname, defaultValue] = splitVerticalBar(varnameOrg);
412 
413  if (varsRetain.count(varname) != 0)
414  {
415  // Skip replacing this one:
416  return parseVars(text, variables, varsRetain, start + 2);
417  }
418 
419  std::string varvalue;
420  if (auto itVal = variables.find(varname); itVal != variables.end())
421  {
422  varvalue = itVal->second;
423  }
424  else if (const char* v = ::getenv(varname.c_str()); v != nullptr)
425  {
426  varvalue = std::string(v);
427  }
428  else
429  {
430  if (!defaultValue.empty())
431  {
432  varvalue = defaultValue;
433  }
434  else
435  {
436  THROW_EXCEPTION_FMT(
437  "MVSIM parseVars(): Undefined variable: ${%s}",
438  varname.c_str());
439  }
440  }
441 
442  return parseVars(
443  pre + varvalue + post.substr(post_end + 1), variables, varsRetain);
444  MRPT_TRY_END
445 }
446 
448  const std::string& in, const std::map<std::string, std::string>& variables,
449  const std::set<std::string>& varsRetain)
450 {
451  return parseVars(in, variables, varsRetain);
452 }
bool param(const std::string &param_name, T &param_val, const T &default_val)
ROSCPP_DECL void start()
std::map< std::string, TParamEntry > TParameterDefinitions
virtual const char * what() const
Definition: rapidxml.hpp:85
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="", mrpt::system::COutputLogger *logger=nullptr)
Definition: xml_utils.cpp:224
XmlRpcServer s
bool parse_xmlnode_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:202
void parse_xmlnode_attribs(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:184
Ch * name() const
Definition: rapidxml.hpp:673
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
std::string parse(const std::string &input, const std::map< std::string, std::string > &variableNamesValues={})
std::tuple< XML_Doc_Data::Ptr, rapidxml::xml_node<> * > readXmlAndGetRoot(const std::string &pathToFile, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain={})
Definition: xml_utils.cpp:333
Ch * value() const
Definition: rapidxml.hpp:692
Ch * where() const
Definition: rapidxml.hpp:94
std::string parse_variables(const std::string &in, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain)
Definition: xml_utils.cpp:447
static std::string::size_type findClosing(size_t pos, const std::string &s, const char searchEndChar, const char otherStartChar)
Definition: xml_utils.cpp:354
void parse_xmlnode_shape(const rapidxml::xml_node< char > &xml_node, mrpt::math::TPolygon2D &out_poly, const char *functionNameContext="")
Definition: xml_utils.cpp:271
xml_node< Ch > * next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1004
static std::tuple< std::string, std::string > splitVerticalBar(const std::string &s)
Definition: xml_utils.cpp:379
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
mrpt::math::TPose2D parseXYPHI(const std::string &s, bool allow_missing_angle=false, double default_angle_radians=0.0, const std::map< std::string, std::string > &variableNamesValues={})
Definition: xml_utils.cpp:245
std::tuple< std::shared_ptr< rapidxml::xml_document<> >, rapidxml::xml_node<> * > readXmlTextAndGetRoot(const std::string &xmlData, const std::string &pathToFile)
Definition: xml_utils.cpp:303
document parse(const AllocationStrategy &strategy, const StringType &string)
Definition: sajson.h:2479
static std::string parseVars(const std::string &text, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain, const size_t searchStartPos=0)
Definition: xml_utils.cpp:388


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:22