xml_parsing.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2018 Davide Faconti - All Rights Reserved
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4 * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
10 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 */
12 
13 #include <functional>
14 #include <list>
15 
16 #pragma GCC diagnostic push
17 #pragma GCC diagnostic ignored "-Wattributes"
19 #include "tinyXML2/tinyxml2.h"
20 #include "filesystem/path.h"
21 
22 #ifdef USING_ROS
23 #include <ros/package.h>
24 #endif
25 
26 namespace BT
27 {
28 
29 using namespace tinyxml2;
30 
31 
33 {
34  TreeNode::Ptr buildTreeRecursively(const XMLElement* root_element,
35  std::vector<TreeNode::Ptr>& nodes,
36  const TreeNode::Ptr& root_parent);
37 
38  TreeNode::Ptr buildNodeFromElement(const XMLElement* element,
39  TreeNode::Ptr parent);
40 
41  void loadDocImpl(XMLDocument *doc);
42 
43  void verifyXML(const XMLDocument* doc) const;
44 
45  std::list< std::unique_ptr<XMLDocument>> opened_documents;
46 
47  std::map<std::string,const XMLElement*> tree_roots;
48 
50 
51  filesystem::path current_path;
52 
54 
56 
57  Pimpl(const BehaviorTreeFactory &fact):
58  factory(fact),
59  current_path( filesystem::path::getcwd() ),
60  suffix_count(0)
61  {}
62 
63  void clear()
64  {
65  suffix_count = 0;
66  current_path = filesystem::path::getcwd();
67  opened_documents.clear();
68  tree_roots.clear();
69  }
70 
71 };
72 #pragma GCC diagnostic pop
73 
74 XMLParser::XMLParser(const BehaviorTreeFactory &factory) : _p( new Pimpl(factory) )
75 {
76 }
77 
79 {
80  delete _p;
81 }
82 
83 void XMLParser::loadFromFile(const std::string& filename)
84 {
85  _p->clear();
86  _p->opened_documents.emplace_back( new XMLDocument() );
87 
88  XMLDocument* doc = _p->opened_documents.back().get();
89  doc->LoadFile(filename.c_str());
90 
91  filesystem::path file_path( filename );
92  _p->current_path = file_path.parent_path().make_absolute();
93 
94  _p->loadDocImpl( doc );
95 }
96 
97 void XMLParser::loadFromText(const std::string& xml_text)
98 {
99  _p->clear();
100  _p->opened_documents.emplace_back( new XMLDocument() );
101 
102  XMLDocument* doc = _p->opened_documents.back().get();
103  doc->Parse(xml_text.c_str(), xml_text.size());
104 
105  _p->loadDocImpl( doc );
106 }
107 
109 {
110  if (doc->Error())
111  {
112  char buffer[200];
113  sprintf(buffer, "Error parsing the XML: %s", doc->ErrorName() );
114  throw std::runtime_error(buffer);
115  }
116 
117  const XMLElement* xml_root = doc->RootElement();
118 
119  // recursively include other files
120  for (auto include_node = xml_root->FirstChildElement("include");
121  include_node != nullptr;
122  include_node = include_node->NextSiblingElement("include"))
123  {
124 
125  filesystem::path file_path( include_node->Attribute("path") );
126 
127  if( include_node->Attribute("ros_pkg") )
128  {
129 #ifdef USING_ROS
130  if( file_path.is_absolute() )
131  {
132  std::cout << "WARNING: <include path=\"...\"> containes an absolute path.\n"
133  << "Attribute [ros_pkg] will be ignored."<< std::endl;
134  }
135  else {
136  auto ros_pkg_path = ros::package::getPath( include_node->Attribute("ros_pkg") );
137  file_path = filesystem::path( ros_pkg_path ) / file_path;
138  }
139 #else
140  throw std::runtime_error("Using attribute [ros_pkg] in <include>, but this library was compiled "
141  "without ROS support. Recompile the BehaviorTree.CPP using catkin");
142 #endif
143  }
144 
145  if( !file_path.is_absolute() )
146  {
147  file_path = current_path / file_path;
148  }
149 
150  opened_documents.emplace_back( new XMLDocument() );
151  XMLDocument* doc = opened_documents.back().get();
152  doc->LoadFile(file_path.str().c_str());
153  loadDocImpl( doc );
154  }
155 
156  for (auto bt_node = xml_root->FirstChildElement("BehaviorTree");
157  bt_node != nullptr;
158  bt_node = bt_node->NextSiblingElement("BehaviorTree"))
159  {
160  std::string tree_name;
161  if (bt_node->Attribute("ID"))
162  {
163  tree_name = bt_node->Attribute("ID");
164  }
165  else{
166  tree_name = "BehaviorTree_" + std::to_string( suffix_count++ );
167  }
168  tree_roots.insert( {tree_name, bt_node} );
169  }
170  verifyXML(doc);
171 }
172 
174 {
175  //-------- Helper functions (lambdas) -----------------
176  auto StrEqual = [](const char* str1, const char* str2) -> bool {
177  return strcmp(str1, str2) == 0;
178  };
179 
180  auto ThrowError = [&](int line_num, const std::string& text) {
181  char buffer[256];
182  sprintf(buffer, "Error at line %d: -> %s", line_num, text.c_str());
183  throw std::runtime_error( buffer );
184  };
185 
186  auto ChildrenCount = [](const XMLElement* parent_node) {
187  int count = 0;
188  for (auto node = parent_node->FirstChildElement(); node != nullptr;
189  node = node->NextSiblingElement())
190  {
191  count++;
192  }
193  return count;
194  };
195  //-----------------------------
196 
197  const XMLElement* xml_root = doc->RootElement();
198 
199  if (!xml_root || !StrEqual(xml_root->Name(), "root"))
200  {
201  throw std::runtime_error("The XML must have a root node called <root>");
202  }
203  //-------------------------------------------------
204  auto meta_root = xml_root->FirstChildElement("TreeNodesModel");
205  auto meta_sibling = meta_root ? meta_root->NextSiblingElement("TreeNodesModel") : nullptr;
206 
207  if (meta_sibling)
208  {
209  ThrowError(meta_sibling->GetLineNum(), " Only a single node <TreeNodesModel> is "
210  "supported");
211  }
212  if (meta_root)
213  {
214  // not having a MetaModel is not an error. But consider that the
215  // Graphical editor needs it.
216  for (auto node = xml_root->FirstChildElement(); node != nullptr;
217  node = node->NextSiblingElement())
218  {
219  const char* name = node->Name();
220  if (StrEqual(name, "Action") || StrEqual(name, "Decorator") ||
221  StrEqual(name, "SubTree") || StrEqual(name, "Condition"))
222  {
223  const char* ID = node->Attribute("ID");
224  if (!ID)
225  {
226  ThrowError(node->GetLineNum(), "Error at line %d: -> The attribute [ID] is "
227  "mandatory");
228  }
229  }
230  }
231  }
232  //-------------------------------------------------
233 
234  // function to be called recursively
235  std::function<void(const XMLElement*)> recursiveStep;
236 
237  recursiveStep = [&](const XMLElement* node) {
238  const int children_count = ChildrenCount(node);
239  const char* name = node->Name();
240  if (StrEqual(name, "Decorator"))
241  {
242  if (children_count != 1)
243  {
244  ThrowError(node->GetLineNum(), "The node <Decorator> must have exactly 1 child");
245  }
246  if (!node->Attribute("ID"))
247  {
248  ThrowError(node->GetLineNum(), "The node <Decorator> must have the attribute "
249  "[ID]");
250  }
251  }
252  else if (StrEqual(name, "Action"))
253  {
254  if (children_count != 0)
255  {
256  ThrowError(node->GetLineNum(), "The node <Action> must not have any child");
257  }
258  if (!node->Attribute("ID"))
259  {
260  ThrowError(node->GetLineNum(), "The node <Action> must have the attribute [ID]");
261  }
262  }
263  else if (StrEqual(name, "Condition"))
264  {
265  if (children_count != 0)
266  {
267  ThrowError(node->GetLineNum(), "The node <Condition> must not have any child");
268  }
269  if (!node->Attribute("ID"))
270  {
271  ThrowError(node->GetLineNum(), "The node <Condition> must have the attribute "
272  "[ID]");
273  }
274  }
275  else if (StrEqual(name, "Sequence") || StrEqual(name, "SequenceStar") ||
276  StrEqual(name, "Fallback") || StrEqual(name, "FallbackStar"))
277  {
278  if (children_count == 0)
279  {
280  ThrowError(node->GetLineNum(), "A Control node must have at least 1 child");
281  }
282  }
283  else if (StrEqual(name, "SubTree"))
284  {
285  if (children_count > 0)
286  {
287  ThrowError(node->GetLineNum(), "The <SubTree> node must have no children");
288  }
289  if (!node->Attribute("ID"))
290  {
291  ThrowError(node->GetLineNum(), "The node <SubTree> must have the attribute [ID]");
292  }
293  }
294  else
295  {
296  // Last resort: MAYBE used ID as element name?
297  bool found = false;
298  for (const auto& model : factory.manifests())
299  {
300  if (model.registration_ID == name)
301  {
302  found = true;
303  break;
304  }
305  }
306  for (const auto& subtrees_it : tree_roots)
307  {
308  if (subtrees_it.first == name)
309  {
310  found = true;
311  break;
312  }
313  }
314  if (!found)
315  {
316  ThrowError(node->GetLineNum(), std::string("Node not recognized: ") + name);
317  }
318  }
319  //recursion
320  for (auto child = node->FirstChildElement(); child != nullptr;
321  child = child->NextSiblingElement())
322  {
323  recursiveStep(child);
324  }
325  };
326 
327  std::vector<std::string> tree_names;
328  int tree_count = 0;
329 
330  for (auto bt_root = xml_root->FirstChildElement("BehaviorTree"); bt_root != nullptr;
331  bt_root = bt_root->NextSiblingElement("BehaviorTree"))
332  {
333  tree_count++;
334  if (bt_root->Attribute("ID"))
335  {
336  tree_names.push_back(bt_root->Attribute("ID"));
337  }
338  if (ChildrenCount(bt_root) != 1)
339  {
340  ThrowError(bt_root->GetLineNum(), "The node <BehaviorTree> must have exactly 1 child");
341  }
342  else
343  {
344  recursiveStep(bt_root->FirstChildElement());
345  }
346  }
347 
348  if (xml_root->Attribute("main_tree_to_execute"))
349  {
350  std::string main_tree = xml_root->Attribute("main_tree_to_execute");
351  if (std::find(tree_names.begin(), tree_names.end(), main_tree) == tree_names.end())
352  {
353  throw std::runtime_error("The tree esecified in [main_tree_to_execute] can't be found");
354  }
355  }
356  else
357  {
358  if (tree_count != 1)
359  {
360  throw std::runtime_error(
361  "If you don't specify the attribute [main_tree_to_execute], "
362  "Your file must contain a single BehaviorTree");
363  }
364  }
365 }
366 
367 TreeNode::Ptr XMLParser::instantiateTree(std::vector<TreeNode::Ptr>& nodes,
368  const Blackboard::Ptr& blackboard)
369 {
370  nodes.clear();
371 
372  XMLElement* xml_root = _p->opened_documents.front()->RootElement();
373 
374  std::string main_tree_ID;
375  if (xml_root->Attribute("main_tree_to_execute"))
376  {
377  main_tree_ID = xml_root->Attribute("main_tree_to_execute");
378  }
379  else if( _p->tree_roots.size() == 1)
380  {
381  main_tree_ID = _p->tree_roots.begin()->first;
382  }
383  else{
384  throw std::runtime_error("[main_tree_to_execute] was not specified correctly");
385  }
386 
387  auto root_element = _p->tree_roots[main_tree_ID]->FirstChildElement();
388 
389  _p->blackboard = blackboard;
390  return _p->buildTreeRecursively(root_element, nodes, TreeNode::Ptr());
391 }
392 
394  std::vector<TreeNode::Ptr>& nodes,
395  const TreeNode::Ptr& root_parent)
396 {
397  std::function<TreeNode::Ptr(const XMLElement*, const TreeNode::Ptr&)> recursiveStep;
398 
399  recursiveStep = [&](const XMLElement* element,
400  const TreeNode::Ptr& parent) -> TreeNode::Ptr
401  {
402  TreeNode::Ptr child_node = buildNodeFromElement(element, parent);
403  nodes.push_back(child_node);
404 
405  DecoratorSubtreeNode* subtree_node = dynamic_cast<DecoratorSubtreeNode*>(child_node.get());
406 
407  if (subtree_node)
408  {
409  const auto& name = child_node->name();
410  auto subtree_elem = tree_roots[name]->FirstChildElement();
411  buildTreeRecursively(subtree_elem, nodes, child_node);
412  }
413 
414  for (auto child_element = element->FirstChildElement(); child_element;
415  child_element = child_element->NextSiblingElement())
416  {
417  recursiveStep(child_element, child_node);
418  }
419 
420  return child_node;
421  };
422 
423  // start recursion
424  TreeNode::Ptr root = recursiveStep(root_element, root_parent );
425  return root;
426 }
427 
429  TreeNode::Ptr parent)
430 {
431  const std::string element_name = element->Name();
432  std::string ID;
433  std::string node_name;
434  NodeParameters params;
435 
436  if (element_name == "Action" ||
437  element_name == "Decorator" ||
438  element_name == "Condition")
439  {
440  ID = element->Attribute("ID");
441  }
442  else
443  {
444  ID = element_name;
445  }
446  const char* attr_alias = element->Attribute("name");
447  if (attr_alias)
448  {
449  node_name = attr_alias;
450  }
451  else
452  {
453  node_name = ID;
454  }
455 
456  if (element_name == "SubTree")
457  {
458  node_name = element->Attribute("ID");
459  }
460 
461  for (const XMLAttribute* att = element->FirstAttribute(); att; att = att->Next())
462  {
463  const std::string attribute_name = att->Name();
464  if (attribute_name != "ID" && attribute_name != "name")
465  {
466  params[attribute_name] = att->Value();
467  }
468  }
469 
470  TreeNode::Ptr child_node;
471 
472  if( factory.builders().count(ID) != 0)
473  {
474  child_node = factory.instantiateTreeNode(ID, node_name, params, blackboard);
475  }
476  else if( tree_roots.count(ID) != 0) {
477  child_node = std::unique_ptr<TreeNode>( new DecoratorSubtreeNode(node_name) );
478  }
479  else{
480  throw std::runtime_error( ID + " is not a registered node, nor a Subtree");
481  }
482 
483  if (parent)
484  {
485  ControlNode* control_parent = dynamic_cast<ControlNode*>(parent.get());
486  if (control_parent)
487  {
488  control_parent->addChild(child_node.get());
489  }
490  DecoratorNode* decorator_parent = dynamic_cast<DecoratorNode*>(parent.get());
491  if (decorator_parent)
492  {
493  decorator_parent->setChild(child_node.get());
494  }
495  }
496 
497  return child_node;
498 }
499 
500 
501 Tree buildTreeFromText(const BehaviorTreeFactory& factory, const std::string& text,
502  const Blackboard::Ptr& blackboard)
503 {
504  XMLParser parser(factory);
505  parser.loadFromText(text);
506 
507  std::vector<TreeNode::Ptr> nodes;
508  auto root = parser.instantiateTree(nodes, blackboard);
509 
510  return Tree(root.get(), nodes);
511 }
512 
513 Tree buildTreeFromFile(const BehaviorTreeFactory& factory, const std::string& filename,
514  const Blackboard::Ptr& blackboard)
515 {
516  XMLParser parser(factory);
517  parser.loadFromFile(filename);
518 
519  std::vector<TreeNode::Ptr> nodes;
520  auto root = parser.instantiateTree(nodes, blackboard);
521  return Tree(root.get(), nodes);
522 }
523 
524 std::string writeXML(const BehaviorTreeFactory& factory,
525  const TreeNode* root_node,
526  bool compact_representation)
527 {
528  using namespace tinyxml2;
529 
530  XMLDocument doc;
531 
532  XMLElement* rootXML = doc.NewElement("root");
533  doc.InsertFirstChild(rootXML);
534 
535  if (root_node)
536  {
537  XMLElement* bt_root = doc.NewElement("BehaviorTree");
538  rootXML->InsertEndChild(bt_root);
539 
540  std::function<void(const TreeNode*, XMLElement* parent)> recursiveVisitor;
541 
542  recursiveVisitor = [&recursiveVisitor, &doc, compact_representation,
543  &factory](const TreeNode* node, XMLElement* parent) -> void {
544  std::string node_type = toStr(node->type());
545  std::string node_ID = node->registrationName();
546  std::string node_name = node->name();
547 
548  if (node->type() == NodeType::CONTROL)
549  {
550  node_type = node_ID;
551  }
552  else if (compact_representation)
553  {
554  for (const auto& model : factory.manifests())
555  {
556  if (model.registration_ID == node_ID)
557  {
558  node_type = node_ID;
559  break;
560  }
561  }
562  }
563 
564  XMLElement* element = doc.NewElement(node_type.c_str());
565  if (node_type != node_ID && !node_ID.empty())
566  {
567  element->SetAttribute("ID", node_ID.c_str());
568  }
569  if (node_type != node_name && !node_name.empty() && node_name != node_ID)
570  {
571  element->SetAttribute("name", node_name.c_str());
572  }
573 
574  for (const auto& param : node->initializationParameters())
575  {
576  element->SetAttribute(param.first.c_str(), param.second.c_str());
577  }
578 
579  parent->InsertEndChild(element);
580 
581  if (auto control = dynamic_cast<const BT::ControlNode*>(node))
582  {
583  for (const auto& child : control->children())
584  {
585  recursiveVisitor(static_cast<const TreeNode*>(child), element);
586  }
587  }
588  else if (auto decorator = dynamic_cast<const BT::DecoratorNode*>(node))
589  {
590  recursiveVisitor(decorator->child(), element);
591  }
592  };
593 
594  recursiveVisitor(root_node, bt_root);
595  }
596  //--------------------------
597 
598  XMLElement* model_root = doc.NewElement("TreeNodesModel");
599  rootXML->InsertEndChild(model_root);
600 
601  for (auto& model : factory.manifests())
602  {
603  if( factory.builtinNodes().count( model.registration_ID ) != 0)
604  {
605  continue;
606  }
607 
608  if (model.type == NodeType::CONTROL)
609  {
610  continue;
611  }
612  XMLElement* element = doc.NewElement(toStr(model.type));
613  element->SetAttribute("ID", model.registration_ID.c_str());
614 
615  for (auto& param : model.required_parameters)
616  {
617  element->SetAttribute( param.first.c_str(),
618  param.second.c_str() );
619  }
620 
621  model_root->InsertEndChild(element);
622  }
623 
624  XMLPrinter printer;
625  doc.Print(&printer);
626  return std::string(printer.CStr(), printer.CStrSize() - 1);
627 }
628 
629 
630 }
Pimpl(const BehaviorTreeFactory &fact)
Definition: xml_parsing.cpp:57
bool Error() const
Return true if there was an error parsing the document.
Definition: tinyxml2.h:1816
const XMLElement * FirstChildElement(const char *name=0) const
Definition: tinyxml2.cpp:940
const std::string & name() const
Definition: tree_node.cpp:92
void verifyXML(const XMLDocument *doc) const
XMLError Parse(const char *xml, size_t nBytes=(size_t)(-1))
Definition: tinyxml2.cpp:2277
XMLError LoadFile(const char *filename)
Definition: tinyxml2.cpp:2159
static raw_event_t * buffer
Definition: minitrace.cpp:54
Pimpl * _p
Definition: xml_parsing.h:26
const XMLElement * NextSiblingElement(const char *name=0) const
Get the next (right) sibling element of this node, with an optionally supplied name.
Definition: tinyxml2.cpp:964
filesystem::path current_path
Definition: xml_parsing.cpp:51
TreeNode::Ptr buildNodeFromElement(const XMLElement *element, TreeNode::Ptr parent)
const std::string xml_text
std::shared_ptr< Blackboard > Ptr
Definition: blackboard.h:40
const std::set< std::string > & builtinNodes() const
Definition: bt_factory.cpp:158
std::string writeXML(const BehaviorTreeFactory &factory, const TreeNode *root_node, bool compact_representation=false)
void SetAttribute(const char *name, const char *value)
Sets the named attribute to value.
Definition: tinyxml2.h:1422
XMLElement * NewElement(const char *name)
Definition: tinyxml2.cpp:2086
const char * toStr(const BT::NodeStatus &status, bool colored=false)
toStr converts NodeStatus to string. Optionally colored.
Definition: basic_types.cpp:7
XMLElement * RootElement()
Definition: tinyxml2.h:1744
const XMLAttribute * Next() const
The next attribute in the list.
Definition: tinyxml2.h:1140
void loadDocImpl(XMLDocument *doc)
std::unordered_map< std::string, std::string > NodeParameters
Definition: tree_node.h:33
const char * Attribute(const char *name, const char *value=0) const
Definition: tinyxml2.cpp:1516
void setChild(TreeNode *child)
void loadFromFile(const std::string &filename)
Definition: xml_parsing.cpp:83
Tree buildTreeFromFile(const BehaviorTreeFactory &factory, const std::string &filename, const Blackboard::Ptr &blackboard=Blackboard::Ptr())
const BehaviorTreeFactory & factory
Definition: xml_parsing.cpp:49
XMLNode * InsertFirstChild(XMLNode *addThis)
Definition: tinyxml2.cpp:871
TreeNode::Ptr instantiateTree(std::vector< TreeNode::Ptr > &nodes, const Blackboard::Ptr &blackboard)
Simple class for manipulating paths on Linux/Windows/Mac OS.
Definition: path.h:42
ROSLIB_DECL std::string getPath(const std::string &package_name)
std::list< std::unique_ptr< XMLDocument > > opened_documents
Definition: xml_parsing.cpp:45
static volatile int count
Definition: minitrace.cpp:55
void loadFromText(const std::string &xml_text)
Definition: xml_parsing.cpp:97
void addChild(TreeNode *child)
std::map< std::string, const XMLElement * > tree_roots
Definition: xml_parsing.cpp:47
XMLParser(const BehaviorTreeFactory &factory)
Definition: xml_parsing.cpp:74
int CStrSize() const
Definition: tinyxml2.h:2246
const XMLAttribute * FirstAttribute() const
Return the first attribute in the list.
Definition: tinyxml2.h:1465
std::shared_ptr< TreeNode > Ptr
Definition: tree_node.h:63
TreeNode::Ptr buildTreeRecursively(const XMLElement *root_element, std::vector< TreeNode::Ptr > &nodes, const TreeNode::Ptr &root_parent)
void Print(XMLPrinter *streamer=0) const
Definition: tinyxml2.cpp:2308
Blackboard::Ptr blackboard
Definition: xml_parsing.cpp:55
std::basic_string< CharT, Traits > to_string(basic_string_view< CharT, Traits > v)
const char * ErrorName() const
Definition: tinyxml2.cpp:2366
XMLNode * InsertEndChild(XMLNode *addThis)
Definition: tinyxml2.cpp:841
const std::vector< TreeNodeManifest > & manifests() const
Manifests of all the registered TreeNodes.
Definition: bt_factory.cpp:153
Tree buildTreeFromText(const BehaviorTreeFactory &factory, const std::string &text, const Blackboard::Ptr &blackboard=Blackboard::Ptr())
const char * CStr() const
Definition: tinyxml2.h:2238
const char * Name() const
Get the name of an element (which is the Value() of the node.)
Definition: tinyxml2.h:1245


behaviortree_cpp
Author(s): Michele Colledanchise, Davide Faconti
autogenerated on Sat Feb 2 2019 04:01:53