YARP
Yet Another Robot Platform
XMLReaderFileV1.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
7 
14 
15 #include <yarp/os/LogStream.h>
16 #include <yarp/os/Network.h>
17 #include <yarp/os/Property.h>
18 
19 #include <algorithm>
20 #include <iterator>
21 #include <sstream>
22 #include <string>
23 #include <tinyxml.h>
24 #include <utility>
25 #include <vector>
26 
27 #define SYNTAX_ERROR(line) yFatal() << "Syntax error while loading" << curr_filename << "at line" << line << "."
28 #define SYNTAX_WARNING(line) yWarning() << "Invalid syntax while loading" << curr_filename << "at line" << line << "."
29 
30 // BUG in TinyXML, see
31 // https://sourceforge.net/tracker/?func=detail&aid=3567726&group_id=13559&atid=113559
32 // When this bug is fixed upstream we can enable this
33 #define TINYXML_UNSIGNED_INT_BUG 0
34 
36 {
37 public:
38  explicit Private(XMLReaderFileV1* parent);
39  virtual ~Private();
40 
41  yarp::robotinterface::XMLReaderResult readRobotFromFile(const std::string& fileName);
42  yarp::robotinterface::XMLReaderResult readRobotFromString(const std::string& xmlString);
43  yarp::robotinterface::XMLReaderResult readRobotTag(TiXmlElement* robotElem);
44 
49 
57 
62 
64  std::string filename;
65  std::string path;
66 #ifdef USE_DTD
68 #endif
69 
71  std::string curr_filename;
72  unsigned int minorVersion;
73  unsigned int majorVersion;
74 };
75 
76 
78  parent(p),
79  minorVersion(0),
80  majorVersion(0)
81 {
82  verbose_output = false;
83 }
84 
86 
88 {
89  filename = fileName;
90 #ifdef WIN32
91  std::replace(filename.begin(), filename.end(), '/', '\\');
92 #endif
93 
94  curr_filename = fileName;
95 #ifdef WIN32
96  path = filename.substr(0, filename.rfind("\\"));
97 #else // WIN32
98  path = filename.substr(0, filename.rfind('/'));
99 #endif //WIN32
100 
101  yDebug() << "Reading file" << filename.c_str();
102  auto* doc = new TiXmlDocument(filename.c_str());
103  if (!doc->LoadFile()) {
104  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
106  }
107 
108  if (!doc->RootElement()) {
109  SYNTAX_ERROR(doc->Row()) << "No root element.";
111  }
112 
113 #ifdef USE_DTD
114  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
115  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
116  if (dtd.parse(childNode->ToUnknown(), curr_filename)) {
117  break;
118  }
119  }
120  }
121 
122  if (!dtd.valid()) {
123  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
124  dtd.setDefault();
126  }
127 
128  if (dtd.type != RobotInterfaceDTD::DocTypeRobot) {
129  SYNTAX_WARNING(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeRobot)
130  << ". Found" << DocTypeToString(dtd.type);
131  }
132 
133  if (dtd.majorVersion != 1 || dtd.minorVersion != 0) {
134  SYNTAX_WARNING(doc->Row()) << "Only yarprobotinterface DTD version 1.0 is supported";
135  }
136 #endif
137 
138  yarp::robotinterface::XMLReaderResult result = readRobotTag(doc->RootElement());
139  delete doc;
140 
141  // yDebug() << robot;
142 
143  return result;
144 }
145 
147 {
148  curr_filename = " XML runtime string ";
149  auto* doc = new TiXmlDocument();
150  if (!doc->Parse(xmlString.c_str())) {
151  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
153  }
154 
155  if (!doc->RootElement()) {
156  SYNTAX_ERROR(doc->Row()) << "No root element.";
158  }
159 
160  yarp::robotinterface::XMLReaderResult result = readRobotTag(doc->RootElement());
161  delete doc;
162 
163  return result;
164 }
165 
166 
168 {
170  result.parsingIsSuccessful = true;
171 
172  if (robotElem->ValueStr() != "robot") {
173  SYNTAX_ERROR(robotElem->Row()) << "Root element should be \"robot\". Found" << robotElem->ValueStr();
175  }
176 
177  if (robotElem->QueryStringAttribute("name", &result.robot.name()) != TIXML_SUCCESS) {
178  SYNTAX_ERROR(robotElem->Row()) << R"("robot" element should contain the "name" attribute)";
180  }
181 
182 #if TINYXML_UNSIGNED_INT_BUG
183  if (robotElem->QueryUnsignedAttribute("build", &robot.build()) != TIXML_SUCCESS) {
184  // No build attribute. Assuming build="0"
185  SYNTAX_WARNING(robotElem->Row()) << "\"robot\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
186  }
187 #else
188  int tmp;
189  if (robotElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
190  // No build attribute. Assuming build="0"
191  SYNTAX_WARNING(robotElem->Row()) << R"("robot" element should contain the "build" attribute [unsigned int]. Assuming 0)";
192  tmp = 0;
193  }
194  result.robot.build() = static_cast<unsigned>(tmp);
195 #endif
196 
197  if (robotElem->QueryStringAttribute("portprefix", &result.robot.portprefix()) != TIXML_SUCCESS) {
198  SYNTAX_WARNING(robotElem->Row()) << R"("robot" element should contain the "portprefix" attribute. Using "name" attribute)";
199  result.robot.portprefix() = result.robot.name();
200  }
201 
202  // yDebug() << "Found robot [" << robot.name() << "] build [" << robot.build() << "] portprefix [" << robot.portprefix() << "]";
203 
204  for (TiXmlElement* childElem = robotElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
205  if (childElem->ValueStr() == "device" || childElem->ValueStr() == "devices") {
206  for (const auto& childDevice : readDevices(childElem, result)) {
207  result.robot.devices().push_back(childDevice);
208  }
209  } else {
210  for (const auto& childParam : readParams(childElem, result)) {
211  result.robot.params().push_back(childParam);
212  }
213  }
214  }
215 
216  return result;
217 }
218 
219 
222 {
223  const std::string& valueStr = devicesElem->ValueStr();
224 
225  if (valueStr == "device") {
226  // yDebug() << valueStr;
228  deviceList.push_back(readDeviceTag(devicesElem, result));
229  return deviceList;
230  }
231 
232  if (valueStr == "devices") {
233  // "devices"
234  return readDevicesTag(devicesElem, result);
235  }
236 
237  SYNTAX_ERROR(devicesElem->Row()) << R"(Expected "device" or "devices". Found)" << valueStr;
238  result.parsingIsSuccessful = false;
240 }
241 
244 {
245  const std::string& valueStr = deviceElem->ValueStr();
246 
247  if (valueStr != "device") {
248  SYNTAX_ERROR(deviceElem->Row()) << "Expected \"device\". Found" << valueStr;
249  result.parsingIsSuccessful = false;
250  }
251 
253 
254  if (deviceElem->QueryStringAttribute("name", &device.name()) != TIXML_SUCCESS) {
255  SYNTAX_ERROR(deviceElem->Row()) << R"("device" element should contain the "name" attribute)";
256  result.parsingIsSuccessful = false;
257  }
258 
259  // yDebug() << "Found device [" << device.name() << "]";
260 
261  if (deviceElem->QueryStringAttribute("type", &device.type()) != TIXML_SUCCESS) {
262  SYNTAX_ERROR(deviceElem->Row()) << R"("device" element should contain the "type" attribute)";
263  result.parsingIsSuccessful = false;
264  }
265 
266  device.params().push_back(yarp::robotinterface::Param("robotName", result.robot.portprefix()));
267 
268  for (TiXmlElement* childElem = deviceElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
269  if (childElem->ValueStr() == "action" || childElem->ValueStr() == "actions") {
270  for (const auto& childAction : readActions(childElem, result)) {
271  device.actions().push_back(childAction);
272  }
273  } else {
274  for (const auto& childParam : readParams(childElem, result)) {
275  device.params().push_back(childParam);
276  }
277  }
278  }
279 
280  // yDebug() << device;
281  return device;
282 }
283 
286 {
287  std::string filename;
288  if (devicesElem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) {
289  // yDebug() << "Found devices file [" << filename << "]";
290 #ifdef WIN32
291  std::replace(filename.begin(), filename.end(), '/', '\\');
292  filename = path + "\\" + filename;
293 #else // WIN32
294  filename = path + "/" + filename;
295 #endif //WIN32
296  return readDevicesFile(filename, result);
297  }
298 
300  for (TiXmlElement* childElem = devicesElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
301  for (const auto& childDevice : readDevices(childElem, result)) {
302  devices.push_back(childDevice);
303  }
304  }
305 
306  return devices;
307 }
308 
311 {
312  std::string old_filename = curr_filename;
313  curr_filename = fileName;
314 
315  yDebug() << "Reading file" << fileName.c_str();
316  auto* doc = new TiXmlDocument(fileName.c_str());
317  if (!doc->LoadFile()) {
318  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
319  }
320 
321  if (!doc->RootElement()) {
322  SYNTAX_ERROR(doc->Row()) << "No root element.";
323  }
324 
325 #ifdef USE_DTD
326  RobotInterfaceDTD devicesFileDTD;
327  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
328  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
329  if (devicesFileDTD.parse(childNode->ToUnknown(), curr_filename)) {
330  break;
331  }
332  }
333  }
334 
335  if (!devicesFileDTD.valid()) {
336  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
337  devicesFileDTD.setDefault();
338  devicesFileDTD.type = RobotInterfaceDTD::DocTypeDevices;
339  }
340 
341  if (devicesFileDTD.type != RobotInterfaceDTD::DocTypeDevices) {
342  SYNTAX_ERROR(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeDevices)
343  << ". Found" << DocTypeToString(devicesFileDTD.type);
344  }
345 
346  if (devicesFileDTD.majorVersion != dtd.majorVersion) {
347  SYNTAX_ERROR(doc->Row()) << "Trying to import a file with a different yarprobotinterface DTD version";
348  }
349 #endif
350 
351  yarp::robotinterface::DeviceList devices = readDevicesTag(doc->RootElement(), result);
352  delete doc;
353  curr_filename = old_filename;
354  return devices;
355 }
356 
357 
360 {
361  const std::string& valueStr = paramsElem->ValueStr();
362 
363  if (valueStr == "param") {
365  params.push_back(readParamTag(paramsElem, result));
366  return params;
367  }
368  if (valueStr == "group") {
370  params.push_back(readGroupTag(paramsElem, result));
371  return params;
372  }
373  if (valueStr == "paramlist") {
374  return readParamListTag(paramsElem, result);
375  }
376  if (valueStr == "subdevice") {
377  return readSubDeviceTag(paramsElem, result);
378  }
379  if (valueStr == "params") {
380  return readParamsTag(paramsElem, result);
381  }
382  SYNTAX_ERROR(paramsElem->Row()) << R"(Expected "param", "group", "paramlist", "subdevice", or "params". Found)" << valueStr;
384 }
385 
386 
389 {
390  if (paramElem->ValueStr() != "param") {
391  SYNTAX_ERROR(paramElem->Row()) << "Expected \"param\". Found" << paramElem->ValueStr();
392  result.parsingIsSuccessful = false;
393  }
394 
396 
397  if (paramElem->QueryStringAttribute("name", &param.name()) != TIXML_SUCCESS) {
398  SYNTAX_ERROR(paramElem->Row()) << R"("param" element should contain the "name" attribute)";
399  result.parsingIsSuccessful = false;
400  }
401 
402  // yDebug() << "Found param [" << param.name() << "]";
403 
404  const char* valueText = paramElem->GetText();
405  if (!valueText) {
406  SYNTAX_ERROR(paramElem->Row()) << R"("param" element should have a value [ "name" = )" << param.name() << "]";
407  } else {
408  param.value() = valueText;
409  }
410 
411  // yDebug() << param;
412  return param;
413 }
414 
417 {
418  if (groupElem->ValueStr() != "group") {
419  SYNTAX_ERROR(groupElem->Row()) << "Expected \"group\". Found" << groupElem->ValueStr();
420  result.parsingIsSuccessful = false;
421  }
422 
423  yarp::robotinterface::Param group(true);
424 
425  if (groupElem->QueryStringAttribute("name", &group.name()) != TIXML_SUCCESS) {
426  SYNTAX_ERROR(groupElem->Row()) << R"("group" element should contain the "name" attribute)";
427  result.parsingIsSuccessful = false;
428  }
429 
430  // yDebug() << "Found group [" << group.name() << "]";
431 
433  for (TiXmlElement* childElem = groupElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
434  for (const auto& childParam : readParams(childElem, result)) {
435  params.push_back(childParam);
436  }
437  }
438  if (params.empty()) {
439  SYNTAX_ERROR(groupElem->Row()) << "\"group\" cannot be empty";
440  }
441 
442  std::string groupString;
443  for (auto& param : params) {
444  if (!groupString.empty()) {
445  groupString += " ";
446  }
447  groupString += "(" + param.name() + " " + param.value() + ")";
448  }
449 
450  group.value() = groupString;
451 
452  return group;
453 }
454 
457 {
458  if (paramListElem->ValueStr() != "paramlist") {
459  SYNTAX_ERROR(paramListElem->Row()) << "Expected \"paramlist\". Found" << paramListElem->ValueStr();
460  result.parsingIsSuccessful = false;
461  }
462 
464  yarp::robotinterface::Param mainparam;
465 
466  if (paramListElem->QueryStringAttribute("name", &mainparam.name()) != TIXML_SUCCESS) {
467  SYNTAX_ERROR(paramListElem->Row()) << R"("paramlist" element should contain the "name" attribute)";
468  result.parsingIsSuccessful = false;
469  }
470 
471  params.push_back(mainparam);
472 
473  // yDebug() << "Found paramlist [" << params.at(0).name() << "]";
474 
475  for (TiXmlElement* childElem = paramListElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
476  if (childElem->ValueStr() != "elem") {
477  SYNTAX_ERROR(childElem->Row()) << "Expected \"elem\". Found" << childElem->ValueStr();
478  }
479 
480  yarp::robotinterface::Param childParam;
481 
482  if (childElem->QueryStringAttribute("name", &childParam.name()) != TIXML_SUCCESS) {
483  SYNTAX_ERROR(childElem->Row()) << R"("elem" element should contain the "name" attribute)";
484  result.parsingIsSuccessful = false;
485  }
486 
487  const char* valueText = childElem->GetText();
488  if (!valueText) {
489  SYNTAX_ERROR(childElem->Row()) << R"("elem" element should have a value [ "name" = )" << childParam.name() << "]";
490  result.parsingIsSuccessful = false;
491  } else {
492  childParam.value() = valueText;
493  }
494 
495  params.push_back(childParam);
496  }
497 
498  if (params.empty()) {
499  SYNTAX_ERROR(paramListElem->Row()) << "\"paramlist\" cannot be empty";
500  result.parsingIsSuccessful = false;
501  }
502 
503  // +1 skips the first element, that is the main param
504  for (auto it = params.begin() + 1; it != params.end(); ++it) {
505  yarp::robotinterface::Param& param = *it;
506  params.at(0).value() += (params.at(0).value().empty() ? "(" : " ") + param.name();
507  }
508  params.at(0).value() += ")";
509 
510  // yDebug() << params;
511  return params;
512 }
513 
516 {
517  if (subDeviceElem->ValueStr() != "subdevice") {
518  SYNTAX_ERROR(subDeviceElem->Row()) << "Expected \"subdevice\". Found" << subDeviceElem->ValueStr();
519  result.parsingIsSuccessful = false;
520  }
521 
523 
524  //FIXME Param featIdParam;
525  yarp::robotinterface::Param subDeviceParam;
526 
527  //FIXME featIdParam.name() = "FeatId";
528  subDeviceParam.name() = "subdevice";
529 
530  //FIXME if (subDeviceElem->QueryStringAttribute("name", &featIdParam.value()) != TIXML_SUCCESS) {
531  // SYNTAX_ERROR(subDeviceElem->Row()) << "\"subdevice\" element should contain the \"name\" attribute";
532  // }
533 
534  if (subDeviceElem->QueryStringAttribute("type", &subDeviceParam.value()) != TIXML_SUCCESS) {
535  SYNTAX_ERROR(subDeviceElem->Row()) << R"("subdevice" element should contain the "type" attribute)";
536  }
537 
538  //FIXME params.push_back(featIdParam);
539  params.push_back(subDeviceParam);
540 
541  // yDebug() << "Found subdevice [" << params.at(0).value() << "]";
542 
543  for (TiXmlElement* childElem = subDeviceElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
544  for (const auto& childParam : readParams(childElem, result)) {
545  params.push_back(yarp::robotinterface::Param(childParam.name(), childParam.value()));
546  }
547  }
548 
549  // yDebug() << params;
550  return params;
551 }
552 
555 {
556  //const std::string &valueStr = paramsElem->ValueStr();
557 
558  std::string filename;
559  if (paramsElem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) {
560  // yDebug() << "Found params file [" << filename << "]";
561 #ifdef WIN32
562  std::replace(filename.begin(), filename.end(), '/', '\\');
563  filename = path + "\\" + filename;
564 #else // WIN32
565  filename = path + "/" + filename;
566 #endif //WIN32
567  return readParamsFile(filename, result);
568  }
569 
570  /*std::string robotName;
571  if (paramsElem->QueryStringAttribute("robot", &robotName) != TIXML_SUCCESS) {
572  SYNTAX_WARNING(paramsElem->Row()) << "\"params\" element should contain the \"robot\" attribute";
573  }
574 
575  if (robotName != robot.name()) {
576  SYNTAX_WARNING(paramsElem->Row()) << "Trying to import a file for the wrong robot. Found" << robotName << "instead of" << robot.name();
577  }*/
578 
579  /*
580  unsigned int build;
581 #if TINYXML_UNSIGNED_INT_BUG
582  if (paramsElem->QueryUnsignedAttribute("build", &build()) != TIXML_SUCCESS) {
583  // No build attribute. Assuming build="0"
584  SYNTAX_WARNING(paramsElem->Row()) << "\"params\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
585  }
586 #else
587  int tmp;
588  if (paramsElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
589  // No build attribute. Assuming build="0"
590  SYNTAX_WARNING(paramsElem->Row()) << "\"params\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
591  tmp = 0;
592  }
593  build = (unsigned)tmp;
594 #endif
595 
596  if (build != robot.build()) {
597  SYNTAX_WARNING(paramsElem->Row()) << "Import a file for a different robot build. Found" << build << "instead of" << robot.build();
598  }
599  */
601  for (TiXmlElement* childElem = paramsElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
602  for (const auto & childParam : readParams(childElem, result)) {
603  params.push_back(childParam);
604  }
605  }
606 
607  return params;
608 }
609 
612 {
613  std::string old_filename = curr_filename;
614  curr_filename = fileName;
615 
616  yDebug() << "Reading file" << fileName.c_str();
617  auto* doc = new TiXmlDocument(fileName.c_str());
618  if (!doc->LoadFile()) {
619  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
620  result.parsingIsSuccessful = false;
622  }
623 
624  if (!doc->RootElement()) {
625  SYNTAX_ERROR(doc->Row()) << "No root element.";
626  result.parsingIsSuccessful = false;
628  }
629 
630 #ifdef USE_DTD
631  RobotInterfaceDTD paramsFileDTD;
632  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
633  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
634  if (paramsFileDTD.parse(childNode->ToUnknown(), curr_filename)) {
635  break;
636  }
637  }
638  }
639 
640  if (!paramsFileDTD.valid()) {
641  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
642  paramsFileDTD.setDefault();
643  paramsFileDTD.type = RobotInterfaceDTD::DocTypeParams;
644  }
645 
646  if (paramsFileDTD.type != RobotInterfaceDTD::DocTypeParams) {
647  SYNTAX_ERROR(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeParams)
648  << ". Found" << DocTypeToString(paramsFileDTD.type);
649  }
650 
651  if (paramsFileDTD.majorVersion != dtd.majorVersion) {
652  SYNTAX_ERROR(doc->Row()) << "Trying to import a file with a different yarprobotinterface DTD version";
653  }
654 
655 #endif
656  yarp::robotinterface::ParamList params = readParamsTag(doc->RootElement(), result);
657  delete doc;
658  curr_filename = old_filename;
659  return params;
660 }
661 
664 {
665  const std::string& valueStr = actionsElem->ValueStr();
666 
667  if (valueStr != "action" && valueStr != "actions") {
668  SYNTAX_ERROR(actionsElem->Row()) << R"(Expected "action" or "actions". Found)" << valueStr;
669  }
670 
671  if (valueStr == "action") {
673  actionList.push_back(readActionTag(actionsElem, result));
674  return actionList;
675  }
676  // "actions"
677  return readActionsTag(actionsElem, result);
678 }
679 
682 {
683  if (actionElem->ValueStr() != "action") {
684  SYNTAX_ERROR(actionElem->Row()) << "Expected \"action\". Found" << actionElem->ValueStr();
685  }
686 
688 
689  if (actionElem->QueryValueAttribute<yarp::robotinterface::ActionPhase>("phase", &action.phase()) != TIXML_SUCCESS || action.phase() == yarp::robotinterface::ActionPhaseUnknown) {
690  SYNTAX_ERROR(actionElem->Row()) << R"("action" element should contain the "phase" attribute [startup|interrupt{1,2,3}|shutdown])";
691  }
692 
693 
694  if (actionElem->QueryValueAttribute<yarp::robotinterface::ActionType>("type", &action.type()) != TIXML_SUCCESS || action.type() == yarp::robotinterface::ActionTypeUnknown) {
695  SYNTAX_ERROR(actionElem->Row()) << R"("action" element should contain the "type" attribute [configure|calibrate|attach|abort|detach|park|custom])";
696  }
697 
698  // yDebug() << "Found action [ ]";
699 
700 #if TINYXML_UNSIGNED_INT_BUG
701  if (actionElem->QueryUnsignedAttribute("level", &action.level()) != TIXML_SUCCESS) {
702  SYNTAX_ERROR(actionElem->Row()) << "\"action\" element should contain the \"level\" attribute [unsigned int]";
703  }
704 #else
705  int tmp;
706  if (actionElem->QueryIntAttribute("level", &tmp) != TIXML_SUCCESS || tmp < 0) {
707  SYNTAX_ERROR(actionElem->Row()) << R"("action" element should contain the "level" attribute [unsigned int])";
708  }
709  action.level() = static_cast<unsigned>(tmp);
710 #endif
711 
712  for (TiXmlElement* childElem = actionElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
713  for (const auto& childParam : readParams(childElem, result)) {
714  action.params().push_back(childParam);
715  }
716  }
717 
718  // yDebug() << action;
719  return action;
720 }
721 
724 {
725  //const std::string &valueStr = actionsElem->ValueStr();
726 
727  std::string filename;
728  if (actionsElem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) {
729  // yDebug() << "Found actions file [" << filename << "]";
730 #ifdef WIN32
731  std::replace(filename.begin(), filename.end(), '/', '\\');
732  filename = path + "\\" + filename;
733 #else // WIN32
734  filename = path + "/" + filename;
735 #endif //WIN32
736  return readActionsFile(filename, result);
737  }
738 
739  std::string robotName;
740  if (actionsElem->QueryStringAttribute("robot", &robotName) != TIXML_SUCCESS) {
741  SYNTAX_WARNING(actionsElem->Row()) << R"("actions" element should contain the "robot" attribute)";
742  }
743 
744  if (robotName != result.robot.name()) {
745  SYNTAX_WARNING(actionsElem->Row()) << "Trying to import a file for the wrong robot. Found" << robotName << "instead of" << result.robot.name();
746  }
747 
748  unsigned int build;
749 #if TINYXML_UNSIGNED_INT_BUG
750  if (actionsElem->QueryUnsignedAttribute("build", &build()) != TIXML_SUCCESS) {
751  // No build attribute. Assuming build="0"
752  SYNTAX_WARNING(actionsElem->Row()) << "\"actions\" element should contain the \"build\" attribute [unsigned int]. Assuming 0";
753  }
754 #else
755  int tmp;
756  if (actionsElem->QueryIntAttribute("build", &tmp) != TIXML_SUCCESS || tmp < 0) {
757  // No build attribute. Assuming build="0"
758  SYNTAX_WARNING(actionsElem->Row()) << R"("actions" element should contain the "build" attribute [unsigned int]. Assuming 0)";
759  tmp = 0;
760  }
761  build = static_cast<unsigned>(tmp);
762 #endif
763 
764  if (build != result.robot.build()) {
765  SYNTAX_WARNING(actionsElem->Row()) << "Import a file for a different robot build. Found" << build << "instead of" << result.robot.build();
766  }
767 
769  for (TiXmlElement* childElem = actionsElem->FirstChildElement(); childElem != nullptr; childElem = childElem->NextSiblingElement()) {
770  for (const auto& childAction : readActions(childElem, result)) {
771  actions.push_back(childAction);
772  }
773  }
774 
775  return actions;
776 }
777 
780 {
781  std::string old_filename = curr_filename;
782  curr_filename = fileName;
783 
784  yDebug() << "Reading file" << fileName.c_str();
785  auto* doc = new TiXmlDocument(fileName.c_str());
786  if (!doc->LoadFile()) {
787  SYNTAX_ERROR(doc->ErrorRow()) << doc->ErrorDesc();
788  }
789 
790  if (!doc->RootElement()) {
791  SYNTAX_ERROR(doc->Row()) << "No root element.";
792  }
793 
794 #ifdef USE_DTD
795  RobotInterfaceDTD actionsFileDTD;
796  for (TiXmlNode* childNode = doc->FirstChild(); childNode != 0; childNode = childNode->NextSibling()) {
797  if (childNode->Type() == TiXmlNode::TINYXML_UNKNOWN) {
798  if (actionsFileDTD.parse(childNode->ToUnknown(), curr_filename)) {
799  break;
800  }
801  }
802  }
803 
804  if (!actionsFileDTD.valid()) {
805  SYNTAX_WARNING(doc->Row()) << "No DTD found. Assuming version yarprobotinterfaceV1.0";
806  actionsFileDTD.setDefault();
807  actionsFileDTD.type = RobotInterfaceDTD::DocTypeActions;
808  }
809 
810  if (actionsFileDTD.type != RobotInterfaceDTD::DocTypeActions) {
811  SYNTAX_ERROR(doc->Row()) << "Expected document of type" << DocTypeToString(RobotInterfaceDTD::DocTypeActions)
812  << ". Found" << DocTypeToString(actionsFileDTD.type);
813  }
814 
815  if (actionsFileDTD.majorVersion != dtd.majorVersion) {
816  SYNTAX_ERROR(doc->Row()) << "Trying to import a file with a different yarprobotinterface DTD version";
817  }
818 
819 #endif
820  yarp::robotinterface::ActionList actions = readActionsTag(doc->RootElement(), result);
821  delete doc;
822  curr_filename = old_filename;
823  return actions;
824 }
825 
827  const yarp::os::Searchable& /*config*/,
828  bool verb)
829 {
830  mPriv->verbose_output = verb;
831  return mPriv->readRobotFromFile(filename);
832 }
833 
835  const yarp::os::Searchable& /*config*/,
836  bool verb)
837 {
838  mPriv->verbose_output = verb;
839  return mPriv->readRobotFromString(xmlString);
840 }
841 
843  mPriv(new Private(this))
844 {
845 }
846 
848 {
849  delete mPriv;
850 }
#define yDebug(...)
Definition: Log.h:234
#define SYNTAX_ERROR(line)
#define SYNTAX_WARNING(line)
A base class for nested structures that can be searched.
Definition: Searchable.h:66
unsigned int & level()
Definition: Action.cpp:102
ActionPhase & phase()
Definition: Action.cpp:92
ActionList & actions()
Definition: Device.cpp:269
std::string & type()
Definition: Device.cpp:259
std::string & name()
Definition: Device.cpp:254
std::string & name()
Definition: Param.cpp:80
std::string & value()
Definition: Param.cpp:85
bool parse(TiXmlUnknown *unknownNode, const std::string &curr_filename)
std::string & portprefix()
Definition: Robot.cpp:481
DeviceList & devices()
Definition: Robot.cpp:513
ParamList & params()
Definition: Robot.cpp:508
std::string & name()
Definition: Robot.cpp:471
unsigned int & build()
Definition: Robot.cpp:476
Result of the parsing of XMLReader.
Definition: XMLReader.h:25
bool parsingIsSuccessful
True if the parsing was successful, false otherwise.
Definition: XMLReader.h:37
Robot robot
If parsingIsSuccessful is true, contains a valid robot instance.
Definition: XMLReader.h:42
static XMLReaderResult ParsingFailed()
Definition: XMLReader.h:27
yarp::robotinterface::Param readParamTag(TiXmlElement *paramElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::Action readActionTag(TiXmlElement *actionElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::XMLReaderResult readRobotFromString(const std::string &xmlString)
yarp::robotinterface::DeviceList readDevices(TiXmlElement *devicesElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::ParamList readParamListTag(TiXmlElement *paramListElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::ActionList readActionsTag(TiXmlElement *actionsElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::ActionList readActions(TiXmlElement *actionsElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::ParamList readParams(TiXmlElement *paramsElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::Device readDeviceTag(TiXmlElement *deviceElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::DeviceList readDevicesTag(TiXmlElement *devicesElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::ParamList readParamsFile(const std::string &fileName, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::DeviceList readDevicesFile(const std::string &fileName, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::Param readGroupTag(TiXmlElement *groupElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::XMLReaderResult readRobotFromFile(const std::string &fileName)
yarp::robotinterface::ParamList readParamsTag(TiXmlElement *paramsElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::XMLReaderResult readRobotTag(TiXmlElement *robotElem)
yarp::robotinterface::ParamList readSubDeviceTag(TiXmlElement *subDeviceElem, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::ActionList readActionsFile(const std::string &fileName, yarp::robotinterface::XMLReaderResult &result)
yarp::robotinterface::XMLReaderResult getRobotFromString(const std::string &xmlString, const yarp::os::Searchable &config, bool verbose=false) override
yarp::robotinterface::XMLReaderResult getRobotFromFile(const std::string &filename, const yarp::os::Searchable &config, bool verbose=false) override
std::string DocTypeToString(RobotInterfaceDTD::DocType doctype)
std::vector< robotinterface::Action > ActionList
Definition: Types.h:31
std::vector< robotinterface::Param > ParamList
Definition: Types.h:28
std::vector< robotinterface::Device > DeviceList
Definition: Types.h:32