xml_parsing.cpp
Go to the documentation of this file.
00001 /*  Copyright (C) 2018 Davide Faconti -  All Rights Reserved
00002 *
00003 *   Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
00004 *   to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
00005 *   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:
00006 *   The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
00007 *
00008 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00009 *   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,
00010 *   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.
00011 */
00012 
00013 #include <functional>
00014 #include <list>
00015 
00016 #pragma GCC diagnostic push
00017 #pragma GCC diagnostic ignored "-Wattributes"
00018 #include "behaviortree_cpp/xml_parsing.h"
00019 #include "tinyXML2/tinyxml2.h"
00020 #include "filesystem/path.h"
00021 
00022 #ifdef USING_ROS
00023 #include <ros/package.h>
00024 #endif
00025 
00026 namespace BT
00027 {
00028 
00029 using namespace tinyxml2;
00030 
00031 
00032 struct XMLParser::Pimpl
00033 {
00034     TreeNode::Ptr buildTreeRecursively(const XMLElement* root_element,
00035                                        std::vector<TreeNode::Ptr>& nodes,
00036                                        const TreeNode::Ptr& root_parent);
00037 
00038     TreeNode::Ptr buildNodeFromElement(const XMLElement* element,
00039                                        TreeNode::Ptr parent);
00040 
00041     void loadDocImpl(XMLDocument *doc);
00042 
00043     void verifyXML(const XMLDocument* doc) const;
00044 
00045     std::list< std::unique_ptr<XMLDocument>> opened_documents;
00046 
00047     std::map<std::string,const XMLElement*> tree_roots;
00048 
00049     const BehaviorTreeFactory& factory;
00050 
00051     filesystem::path current_path;
00052 
00053     int suffix_count;
00054 
00055     Blackboard::Ptr blackboard;
00056 
00057     Pimpl(const BehaviorTreeFactory &fact):
00058         factory(fact),
00059         current_path( filesystem::path::getcwd() ),
00060         suffix_count(0)
00061     {}
00062 
00063     void clear()
00064     {
00065         suffix_count = 0;
00066         current_path = filesystem::path::getcwd();
00067         opened_documents.clear();
00068         tree_roots.clear();
00069     }
00070 
00071 };
00072 #pragma GCC diagnostic pop
00073 
00074 XMLParser::XMLParser(const BehaviorTreeFactory &factory) : _p( new Pimpl(factory) )
00075 {
00076 }
00077 
00078 XMLParser::~XMLParser()
00079 {
00080     delete _p;
00081 }
00082 
00083 void XMLParser::loadFromFile(const std::string& filename)
00084 {
00085     _p->clear();
00086     _p->opened_documents.emplace_back( new XMLDocument() );
00087 
00088     XMLDocument* doc = _p->opened_documents.back().get();
00089     doc->LoadFile(filename.c_str());
00090 
00091     filesystem::path file_path( filename );
00092     _p->current_path = file_path.parent_path().make_absolute();
00093 
00094     _p->loadDocImpl( doc );
00095 }
00096 
00097 void XMLParser::loadFromText(const std::string& xml_text)
00098 {
00099     _p->clear();
00100     _p->opened_documents.emplace_back( new XMLDocument() );
00101 
00102     XMLDocument* doc = _p->opened_documents.back().get();
00103     doc->Parse(xml_text.c_str(), xml_text.size());
00104 
00105     _p->loadDocImpl( doc );
00106 }
00107 
00108 void XMLParser::Pimpl::loadDocImpl(XMLDocument* doc)
00109 {
00110     if (doc->Error())
00111     {
00112         char buffer[200];
00113         sprintf(buffer, "Error parsing the XML: %s", doc->ErrorName() );
00114         throw std::runtime_error(buffer);
00115     }
00116 
00117     const XMLElement* xml_root = doc->RootElement();
00118 
00119     // recursively include other files
00120     for (auto include_node = xml_root->FirstChildElement("include");
00121          include_node != nullptr;
00122          include_node = include_node->NextSiblingElement("include"))
00123     {
00124 
00125         filesystem::path file_path( include_node->Attribute("path") );
00126 
00127         if( include_node->Attribute("ros_pkg") )
00128         {
00129 #ifdef USING_ROS
00130             if( file_path.is_absolute() )
00131             {
00132                 std::cout << "WARNING: <include path=\"...\"> containes an absolute path.\n"
00133                           << "Attribute [ros_pkg] will be ignored."<< std::endl;
00134             }
00135             else {
00136                 auto ros_pkg_path = ros::package::getPath(  include_node->Attribute("ros_pkg") );
00137                 file_path = filesystem::path( ros_pkg_path ) / file_path;
00138             }
00139 #else
00140             throw std::runtime_error("Using attribute [ros_pkg] in <include>, but this library was compiled "
00141                                      "without ROS support. Recompile the BehaviorTree.CPP using catkin");
00142 #endif
00143         }
00144 
00145         if( !file_path.is_absolute() )
00146         {
00147             file_path = current_path / file_path;
00148         }
00149 
00150         opened_documents.emplace_back( new XMLDocument() );
00151         XMLDocument* doc = opened_documents.back().get();
00152         doc->LoadFile(file_path.str().c_str());
00153         loadDocImpl( doc );
00154     }
00155 
00156     for (auto bt_node = xml_root->FirstChildElement("BehaviorTree");
00157          bt_node != nullptr;
00158          bt_node = bt_node->NextSiblingElement("BehaviorTree"))
00159     {
00160         std::string tree_name;
00161         if (bt_node->Attribute("ID"))
00162         {
00163             tree_name = bt_node->Attribute("ID");
00164         }
00165         else{
00166             tree_name = "BehaviorTree_" + std::to_string( suffix_count++ );
00167         }
00168         tree_roots.insert( {tree_name, bt_node} );
00169     }
00170     verifyXML(doc);
00171 }
00172 
00173 void XMLParser::Pimpl::verifyXML(const XMLDocument* doc) const
00174 {
00175     //-------- Helper functions (lambdas) -----------------
00176     auto StrEqual = [](const char* str1, const char* str2) -> bool {
00177         return strcmp(str1, str2) == 0;
00178     };
00179 
00180     auto ThrowError = [&](int line_num, const std::string& text) {
00181         char buffer[256];
00182         sprintf(buffer, "Error at line %d: -> %s", line_num, text.c_str());
00183         throw std::runtime_error( buffer );
00184     };
00185 
00186     auto ChildrenCount = [](const XMLElement* parent_node) {
00187         int count = 0;
00188         for (auto node = parent_node->FirstChildElement(); node != nullptr;
00189              node = node->NextSiblingElement())
00190         {
00191             count++;
00192         }
00193         return count;
00194     };
00195     //-----------------------------
00196 
00197     const XMLElement* xml_root = doc->RootElement();
00198 
00199     if (!xml_root || !StrEqual(xml_root->Name(), "root"))
00200     {
00201         throw std::runtime_error("The XML must have a root node called <root>");
00202     }
00203     //-------------------------------------------------
00204     auto meta_root = xml_root->FirstChildElement("TreeNodesModel");
00205     auto meta_sibling = meta_root ? meta_root->NextSiblingElement("TreeNodesModel") : nullptr;
00206 
00207     if (meta_sibling)
00208     {
00209         ThrowError(meta_sibling->GetLineNum(), " Only a single node <TreeNodesModel> is "
00210                                                 "supported");
00211     }
00212     if (meta_root)
00213     {
00214         // not having a MetaModel is not an error. But consider that the
00215         // Graphical editor needs it.
00216         for (auto node = xml_root->FirstChildElement(); node != nullptr;
00217              node = node->NextSiblingElement())
00218         {
00219             const char* name = node->Name();
00220             if (StrEqual(name, "Action") || StrEqual(name, "Decorator") ||
00221                 StrEqual(name, "SubTree") || StrEqual(name, "Condition"))
00222             {
00223                 const char* ID = node->Attribute("ID");
00224                 if (!ID)
00225                 {
00226                     ThrowError(node->GetLineNum(), "Error at line %d: -> The attribute [ID] is "
00227                                                     "mandatory");
00228                 }
00229             }
00230         }
00231     }
00232     //-------------------------------------------------
00233 
00234     // function to be called recursively
00235     std::function<void(const XMLElement*)> recursiveStep;
00236 
00237     recursiveStep = [&](const XMLElement* node) {
00238         const int children_count = ChildrenCount(node);
00239         const char* name = node->Name();
00240         if (StrEqual(name, "Decorator"))
00241         {
00242             if (children_count != 1)
00243             {
00244                 ThrowError(node->GetLineNum(), "The node <Decorator> must have exactly 1 child");
00245             }
00246             if (!node->Attribute("ID"))
00247             {
00248                 ThrowError(node->GetLineNum(), "The node <Decorator> must have the attribute "
00249                                                 "[ID]");
00250             }
00251         }
00252         else if (StrEqual(name, "Action"))
00253         {
00254             if (children_count != 0)
00255             {
00256                 ThrowError(node->GetLineNum(), "The node <Action> must not have any child");
00257             }
00258             if (!node->Attribute("ID"))
00259             {
00260                 ThrowError(node->GetLineNum(), "The node <Action> must have the attribute [ID]");
00261             }
00262         }
00263         else if (StrEqual(name, "Condition"))
00264         {
00265             if (children_count != 0)
00266             {
00267                 ThrowError(node->GetLineNum(), "The node <Condition> must not have any child");
00268             }
00269             if (!node->Attribute("ID"))
00270             {
00271                 ThrowError(node->GetLineNum(), "The node <Condition> must have the attribute "
00272                                                 "[ID]");
00273             }
00274         }
00275         else if (StrEqual(name, "Sequence") || StrEqual(name, "SequenceStar") ||
00276                  StrEqual(name, "Fallback") || StrEqual(name, "FallbackStar"))
00277         {
00278             if (children_count == 0)
00279             {
00280                 ThrowError(node->GetLineNum(), "A Control node must have at least 1 child");
00281             }
00282         }
00283         else if (StrEqual(name, "SubTree"))
00284         {
00285             if (children_count > 0)
00286             {
00287                 ThrowError(node->GetLineNum(), "The <SubTree> node must have no children");
00288             }
00289             if (!node->Attribute("ID"))
00290             {
00291                 ThrowError(node->GetLineNum(), "The node <SubTree> must have the attribute [ID]");
00292             }
00293         }
00294         else
00295         {
00296             // Last resort:  MAYBE used ID as element name?
00297             bool found = false;
00298             for (const auto& model : factory.manifests())
00299             {
00300                 if (model.registration_ID == name)
00301                 {
00302                     found = true;
00303                     break;
00304                 }
00305             }
00306             for (const auto& subtrees_it : tree_roots)
00307             {
00308                 if (subtrees_it.first == name)
00309                 {
00310                     found = true;
00311                     break;
00312                 }
00313             }
00314             if (!found)
00315             {
00316                 ThrowError(node->GetLineNum(), std::string("Node not recognized: ") + name);
00317             }
00318         }
00319         //recursion
00320         for (auto child = node->FirstChildElement(); child != nullptr;
00321              child = child->NextSiblingElement())
00322         {
00323             recursiveStep(child);
00324         }
00325     };
00326 
00327     std::vector<std::string> tree_names;
00328     int tree_count = 0;
00329 
00330     for (auto bt_root = xml_root->FirstChildElement("BehaviorTree"); bt_root != nullptr;
00331          bt_root = bt_root->NextSiblingElement("BehaviorTree"))
00332     {
00333         tree_count++;
00334         if (bt_root->Attribute("ID"))
00335         {
00336             tree_names.push_back(bt_root->Attribute("ID"));
00337         }
00338         if (ChildrenCount(bt_root) != 1)
00339         {
00340             ThrowError(bt_root->GetLineNum(), "The node <BehaviorTree> must have exactly 1 child");
00341         }
00342         else
00343         {
00344             recursiveStep(bt_root->FirstChildElement());
00345         }
00346     }
00347 
00348     if (xml_root->Attribute("main_tree_to_execute"))
00349     {
00350         std::string main_tree = xml_root->Attribute("main_tree_to_execute");
00351         if (std::find(tree_names.begin(), tree_names.end(), main_tree) == tree_names.end())
00352         {
00353             throw std::runtime_error("The tree esecified in [main_tree_to_execute] can't be found");
00354         }
00355     }
00356     else
00357     {
00358         if (tree_count != 1)
00359         {
00360             throw std::runtime_error(
00361                 "If you don't specify the attribute [main_tree_to_execute], "
00362                 "Your file must contain a single BehaviorTree");
00363         }
00364     }
00365 }
00366 
00367 TreeNode::Ptr XMLParser::instantiateTree(std::vector<TreeNode::Ptr>& nodes,
00368                                          const Blackboard::Ptr& blackboard)
00369 {
00370     nodes.clear();
00371 
00372     XMLElement* xml_root = _p->opened_documents.front()->RootElement();
00373 
00374     std::string main_tree_ID;
00375     if (xml_root->Attribute("main_tree_to_execute"))
00376     {
00377         main_tree_ID = xml_root->Attribute("main_tree_to_execute");
00378     }
00379     else if( _p->tree_roots.size() == 1)
00380     {
00381         main_tree_ID = _p->tree_roots.begin()->first;
00382     }
00383     else{
00384         throw std::runtime_error("[main_tree_to_execute] was not specified correctly");
00385     }
00386 
00387     auto root_element = _p->tree_roots[main_tree_ID]->FirstChildElement();
00388 
00389     _p->blackboard = blackboard;
00390     return _p->buildTreeRecursively(root_element, nodes, TreeNode::Ptr());
00391 }
00392 
00393 TreeNode::Ptr BT::XMLParser::Pimpl::buildTreeRecursively(const XMLElement* root_element,
00394                                                          std::vector<TreeNode::Ptr>& nodes,
00395                                                          const TreeNode::Ptr& root_parent)
00396 {
00397     std::function<TreeNode::Ptr(const XMLElement*, const TreeNode::Ptr&)> recursiveStep;
00398 
00399     recursiveStep = [&](const XMLElement* element,
00400                         const TreeNode::Ptr& parent) -> TreeNode::Ptr
00401     {
00402         TreeNode::Ptr child_node = buildNodeFromElement(element, parent);
00403         nodes.push_back(child_node);
00404 
00405         DecoratorSubtreeNode* subtree_node = dynamic_cast<DecoratorSubtreeNode*>(child_node.get());
00406 
00407         if (subtree_node)
00408         {
00409             const auto& name = child_node->name();
00410             auto subtree_elem = tree_roots[name]->FirstChildElement();
00411             buildTreeRecursively(subtree_elem, nodes, child_node);
00412         }
00413 
00414         for (auto child_element = element->FirstChildElement(); child_element;
00415              child_element = child_element->NextSiblingElement())
00416         {
00417             recursiveStep(child_element, child_node);
00418         }
00419 
00420         return child_node;
00421     };
00422 
00423     // start recursion
00424     TreeNode::Ptr root = recursiveStep(root_element, root_parent );
00425     return root;
00426 }
00427 
00428 TreeNode::Ptr XMLParser::Pimpl::buildNodeFromElement(const XMLElement *element,
00429                                                      TreeNode::Ptr parent)
00430 {
00431     const std::string element_name = element->Name();
00432     std::string ID;
00433     std::string node_name;
00434     NodeParameters params;
00435 
00436     if (element_name == "Action" ||
00437         element_name == "Decorator" ||
00438         element_name == "Condition")
00439     {
00440         ID = element->Attribute("ID");
00441     }
00442     else
00443     {
00444         ID = element_name;
00445     }
00446     const char* attr_alias = element->Attribute("name");
00447     if (attr_alias)
00448     {
00449         node_name = attr_alias;
00450     }
00451     else
00452     {
00453         node_name = ID;
00454     }
00455 
00456     if (element_name == "SubTree")
00457     {
00458         node_name = element->Attribute("ID");
00459     }
00460 
00461     for (const XMLAttribute* att = element->FirstAttribute(); att; att = att->Next())
00462     {
00463         const std::string attribute_name = att->Name();
00464         if (attribute_name != "ID" && attribute_name != "name")
00465         {
00466             params[attribute_name] = att->Value();
00467         }
00468     }
00469 
00470     TreeNode::Ptr child_node;
00471 
00472     if( factory.builders().count(ID) != 0)
00473     {
00474         child_node = factory.instantiateTreeNode(ID, node_name, params, blackboard);
00475     }
00476     else if( tree_roots.count(ID) != 0) {
00477         child_node = std::unique_ptr<TreeNode>( new DecoratorSubtreeNode(node_name) );
00478     }
00479     else{
00480         throw std::runtime_error( ID + " is not a registered node, nor a Subtree");
00481     }
00482 
00483     if (parent)
00484     {
00485         ControlNode* control_parent = dynamic_cast<ControlNode*>(parent.get());
00486         if (control_parent)
00487         {
00488             control_parent->addChild(child_node.get());
00489         }
00490         DecoratorNode* decorator_parent = dynamic_cast<DecoratorNode*>(parent.get());
00491         if (decorator_parent)
00492         {
00493             decorator_parent->setChild(child_node.get());
00494         }
00495     }
00496 
00497     return child_node;
00498 }
00499 
00500 
00501 Tree buildTreeFromText(const BehaviorTreeFactory& factory, const std::string& text,
00502                        const Blackboard::Ptr& blackboard)
00503 {
00504     XMLParser parser(factory);
00505     parser.loadFromText(text);
00506 
00507     std::vector<TreeNode::Ptr> nodes;
00508     auto root = parser.instantiateTree(nodes, blackboard);
00509 
00510     return Tree(root.get(), nodes);
00511 }
00512 
00513 Tree buildTreeFromFile(const BehaviorTreeFactory& factory, const std::string& filename,
00514                        const Blackboard::Ptr& blackboard)
00515 {
00516     XMLParser parser(factory);
00517     parser.loadFromFile(filename);
00518 
00519     std::vector<TreeNode::Ptr> nodes;
00520     auto root = parser.instantiateTree(nodes, blackboard);
00521     return Tree(root.get(), nodes);
00522 }
00523 
00524 std::string writeXML(const BehaviorTreeFactory& factory,
00525                      const TreeNode* root_node,
00526                      bool compact_representation)
00527 {
00528     using namespace tinyxml2;
00529 
00530     XMLDocument doc;
00531 
00532     XMLElement* rootXML = doc.NewElement("root");
00533     doc.InsertFirstChild(rootXML);
00534 
00535     if (root_node)
00536     {
00537         XMLElement* bt_root = doc.NewElement("BehaviorTree");
00538         rootXML->InsertEndChild(bt_root);
00539 
00540         std::function<void(const TreeNode*, XMLElement* parent)> recursiveVisitor;
00541 
00542         recursiveVisitor = [&recursiveVisitor, &doc, compact_representation,
00543                 &factory](const TreeNode* node, XMLElement* parent) -> void {
00544             std::string node_type = toStr(node->type());
00545             std::string node_ID = node->registrationName();
00546             std::string node_name = node->name();
00547 
00548             if (node->type() == NodeType::CONTROL)
00549             {
00550                 node_type = node_ID;
00551             }
00552             else if (compact_representation)
00553             {
00554                 for (const auto& model : factory.manifests())
00555                 {
00556                     if (model.registration_ID == node_ID)
00557                     {
00558                         node_type = node_ID;
00559                         break;
00560                     }
00561                 }
00562             }
00563 
00564             XMLElement* element = doc.NewElement(node_type.c_str());
00565             if (node_type != node_ID && !node_ID.empty())
00566             {
00567                 element->SetAttribute("ID", node_ID.c_str());
00568             }
00569             if (node_type != node_name && !node_name.empty() && node_name != node_ID)
00570             {
00571                 element->SetAttribute("name", node_name.c_str());
00572             }
00573 
00574             for (const auto& param : node->initializationParameters())
00575             {
00576                 element->SetAttribute(param.first.c_str(), param.second.c_str());
00577             }
00578 
00579             parent->InsertEndChild(element);
00580 
00581             if (auto control = dynamic_cast<const BT::ControlNode*>(node))
00582             {
00583                 for (const auto& child : control->children())
00584                 {
00585                     recursiveVisitor(static_cast<const TreeNode*>(child), element);
00586                 }
00587             }
00588             else if (auto decorator = dynamic_cast<const BT::DecoratorNode*>(node))
00589             {
00590                 recursiveVisitor(decorator->child(), element);
00591             }
00592         };
00593 
00594         recursiveVisitor(root_node, bt_root);
00595     }
00596     //--------------------------
00597 
00598     XMLElement* model_root = doc.NewElement("TreeNodesModel");
00599     rootXML->InsertEndChild(model_root);
00600 
00601     for (auto& model : factory.manifests())
00602     {
00603         if( factory.builtinNodes().count( model.registration_ID ) != 0)
00604         {
00605             continue;
00606         }
00607 
00608         if (model.type == NodeType::CONTROL)
00609         {
00610             continue;
00611         }
00612         XMLElement* element = doc.NewElement(toStr(model.type));
00613         element->SetAttribute("ID", model.registration_ID.c_str());
00614 
00615         for (auto& param : model.required_parameters)
00616         {
00617             element->SetAttribute( param.first.c_str(),
00618                                    param.second.c_str() );
00619         }
00620 
00621         model_root->InsertEndChild(element);
00622     }
00623 
00624     XMLPrinter printer;
00625     doc.Print(&printer);
00626     return std::string(printer.CStr(), printer.CStrSize() - 1);
00627 }
00628 
00629 
00630 }


behaviortree_cpp
Author(s): Michele Colledanchise, Davide Faconti
autogenerated on Sat Feb 2 2019 03:50:10