22 #if defined(__linux) || defined(__linux__)
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wattributes"
37 #include <ament_index_cpp/get_package_share_directory.hpp>
46 std::string xsdAttributeType(
const BT::PortInfo& port_info)
50 return "blackboardType";
52 const auto& type_info = port_info.
type();
53 if((type_info ==
typeid(
int)) or (type_info ==
typeid(
unsigned int)))
55 return "integerOrBlackboardType";
57 else if(type_info ==
typeid(
double))
59 return "decimalOrBlackboardType";
61 else if(type_info ==
typeid(
bool))
63 return "booleanOrBlackboardType";
65 else if(type_info ==
typeid(std::string))
67 return "stringOrBlackboardType";
79 auto StrEqual = [](
const char* str1,
const char* str2) ->
bool {
80 return strcmp(str1, str2) == 0;
85 std::unordered_map<std::string, BT::PortInfo>
ports;
93 const std::string& prefix_path,
Tree& output_tree);
95 void recursivelyCreateSubtree(
const std::string& tree_ID,
const std::string& tree_path,
96 const std::string& prefix_path,
Tree& output_tree,
100 void getPortsRecursively(
const XMLElement* element,
101 std::vector<std::string>& output_ports);
103 void loadDocImpl(
XMLDocument* doc,
bool add_includes);
116 : factory(fact), current_path(
std::filesystem::current_path()), suffix_count(0)
122 current_path = std::filesystem::current_path();
123 opened_documents.clear();
128 void loadSubtreeModel(
const XMLElement* xml_root);
131 #if defined(__linux) || defined(__linux__)
132 #pragma GCC diagnostic pop
140 this->_p = std::move(other._p);
145 this->_p = std::move(other._p);
157 doc->
LoadFile(filepath.string().c_str());
159 _p->current_path = std::filesystem::absolute(filepath.parent_path());
161 _p->loadDocImpl(doc, add_includes);
171 _p->loadDocImpl(doc, add_includes);
176 std::vector<std::string> out;
177 for(
const auto& it :
_p->tree_roots)
179 out.push_back(it.first);
190 for(
auto sub_node = models_node->FirstChildElement(
"SubTree"); sub_node !=
nullptr;
191 sub_node = sub_node->NextSiblingElement(
"SubTree"))
193 auto subtree_id = sub_node->Attribute(
"ID");
194 auto& subtree_model = subtree_models[subtree_id];
196 std::pair<const char*, BT::PortDirection> port_types[3] = {
202 for(
const auto& [name, direction] : port_types)
204 for(
auto port_node = sub_node->FirstChildElement(name); port_node !=
nullptr;
205 port_node = port_node->NextSiblingElement(name))
208 auto name = port_node->Attribute(
"name");
211 throw RuntimeError(
"Missing attribute [name] in port (SubTree model)");
213 if(
auto default_value = port_node->Attribute(
"default"))
217 if(
auto description = port_node->Attribute(
"description"))
221 subtree_model.ports[name] = std::move(port);
239 auto format = xml_root->
Attribute(
"BTCPP_format");
242 std::cout <<
"Warnings: The first tag of the XML (<root>) should contain the "
243 "attribute [BTCPP_format=\"4\"]\n"
244 <<
"Please check if your XML is compatible with version 4.x of BT.CPP"
249 for(
auto incl_node = xml_root->
FirstChildElement(
"include"); incl_node !=
nullptr;
257 std::filesystem::path file_path(incl_node->Attribute(
"path"));
258 const char* ros_pkg_relative_path = incl_node->Attribute(
"ros_pkg");
260 if(ros_pkg_relative_path)
262 if(file_path.is_absolute())
264 std::cout <<
"WARNING: <include path=\"...\"> contains an absolute path.\n"
265 <<
"Attribute [ros_pkg] will be ignored." << std::endl;
269 std::string ros_pkg_path;
272 #elif defined USING_ROS2
274 ament_index_cpp::get_package_share_directory(ros_pkg_relative_path);
276 throw RuntimeError(
"Using attribute [ros_pkg] in <include>, but this library was "
277 "compiled without ROS support. Recompile the BehaviorTree.CPP "
280 file_path = std::filesystem::path(ros_pkg_path) / file_path;
284 if(!file_path.is_absolute())
294 current_path = std::filesystem::absolute(file_path.parent_path());
296 next_doc->
LoadFile(file_path.string().c_str());
304 std::unordered_map<std::string, BT::NodeType> registered_nodes;
307 registered_nodes.insert({ it.first, it.second.type });
311 doc->
Print(&printer);
320 for(
auto bt_node = xml_root->
FirstChildElement(
"BehaviorTree"); bt_node !=
nullptr;
323 std::string tree_name;
324 if(bt_node->Attribute(
"ID"))
326 tree_name = bt_node->Attribute(
"ID");
338 const std::unordered_map<std::string, BT::NodeType>& registered_nodes)
350 auto ThrowError = [&](
int line_num,
const std::string& text) {
352 snprintf(
buffer,
sizeof buffer,
"Error at line %d: -> %s", line_num, text.c_str());
356 auto ChildrenCount = [](
const XMLElement* parent_node) {
358 for(
auto node = parent_node->FirstChildElement(); node !=
nullptr;
359 node = node->NextSiblingElement())
371 throw RuntimeError(
"The XML must have a root node called <root>");
380 ThrowError(meta_sibling->GetLineNum(),
" Only a single node <TreeNodesModel> is "
390 const std::string name = node->Name();
391 if(name ==
"Action" || name ==
"Decorator" || name ==
"SubTree" ||
392 name ==
"Condition" || name ==
"Control")
394 const char* ID = node->Attribute(
"ID");
397 ThrowError(node->GetLineNum(),
"Error at line %d: -> The attribute "
398 "[ID] is mandatory");
405 int behavior_tree_count = 0;
409 behavior_tree_count++;
416 const int children_count = ChildrenCount(node);
417 const std::string name = node->Name();
418 const std::string ID = node->Attribute(
"ID") ? node->Attribute(
"ID") :
"";
419 const int line_number = node->GetLineNum();
421 if(name ==
"Decorator")
423 if(children_count != 1)
425 ThrowError(line_number,
"The tag <Decorator> must have exactly 1 "
430 ThrowError(line_number,
"The tag <Decorator> must have the "
434 else if(name ==
"Action")
436 if(children_count != 0)
438 ThrowError(line_number,
"The tag <Action> must not have any "
443 ThrowError(line_number,
"The tag <Action> must have the "
447 else if(name ==
"Condition")
449 if(children_count != 0)
451 ThrowError(line_number,
"The tag <Condition> must not have any "
456 ThrowError(line_number,
"The tag <Condition> must have the "
460 else if(name ==
"Control")
462 if(children_count == 0)
464 ThrowError(line_number,
"The tag <Control> must have at least 1 "
469 ThrowError(line_number,
"The tag <Control> must have the "
473 else if(name ==
"SubTree")
475 if(children_count != 0)
477 ThrowError(line_number,
"<SubTree> should not have any child");
481 ThrowError(line_number,
"The tag <SubTree> must have the "
484 if(registered_nodes.count(ID) != 0)
486 ThrowError(line_number,
"The attribute [ID] of tag <SubTree> must "
487 "not use the name of a registered Node");
490 else if(name ==
"BehaviorTree")
492 if(ID.empty() && behavior_tree_count > 1)
494 ThrowError(line_number,
"The tag <BehaviorTree> must have the "
497 if(children_count != 1)
499 ThrowError(line_number,
"The tag <BehaviorTree> must have exactly 1 "
502 if(registered_nodes.count(ID) != 0)
504 ThrowError(line_number,
"The attribute [ID] of tag <BehaviorTree> "
505 "must not use the name of a registered Node");
511 const auto search = registered_nodes.find(name);
512 bool found = (search != registered_nodes.end());
515 ThrowError(line_number, std::string(
"Node not recognized: ") + name);
520 if(children_count != 1)
522 ThrowError(line_number,
523 std::string(
"The node <") + name +
"> must have exactly 1 child");
528 if(children_count == 0)
530 ThrowError(line_number,
531 std::string(
"The node <") + name +
"> must have 1 or more children");
536 for(
auto child = node->FirstChildElement();
child !=
nullptr;
539 recursiveStep(
child);
543 for(
auto bt_root = xml_root->
FirstChildElement(
"BehaviorTree"); bt_root !=
nullptr;
546 recursiveStep(bt_root);
551 std::string main_tree_ID)
557 if(main_tree_ID.empty())
559 XMLElement* first_xml_root =
_p->opened_documents.front()->RootElement();
561 if(
auto main_tree_attribute = first_xml_root->
Attribute(
"main_tree_to_execute"))
563 main_tree_ID = main_tree_attribute;
565 else if(
_p->tree_roots.size() == 1)
568 main_tree_ID =
_p->tree_roots.begin()->first;
572 throw RuntimeError(
"[main_tree_to_execute] was not specified correctly");
579 throw RuntimeError(
"XMLParser::instantiateTree needs a non-empty "
583 _p->recursivelyCreateSubtree(main_tree_ID, {}, {}, output_tree, root_blackboard,
597 const std::string& prefix_path,
600 const auto element_name = element->
Name();
601 const auto element_ID = element->
Attribute(
"ID");
611 if(factory.builders().count(element_name) == 0)
613 throw RuntimeError(element_name,
" is not a registered node");
615 type_ID = element_name;
619 throw RuntimeError(
"Attribute [ID] is not allowed in <", type_ID,
">");
627 throw RuntimeError(
"Attribute [ID] is mandatory in <", type_ID,
">");
629 type_ID = element_ID;
634 const char* attr_name = element->
Attribute(
"name");
635 const std::string instance_name = (attr_name !=
nullptr) ? attr_name : type_ID;
639 auto manifest_it = factory.manifests().find(type_ID);
640 if(manifest_it != factory.manifests().end())
650 const std::string port_name = att->Name();
651 const std::string port_value = att->Value();
655 auto port_model_it =
manifest->ports.find(port_name);
656 if(port_model_it ==
manifest->ports.end())
659 "] is found in the XML, but not in the "
664 const auto& port_model = port_model_it->second;
665 bool is_blacbkboard = port_value.size() >= 3 && port_value.front() ==
'{' &&
666 port_value.back() ==
'}';
668 if(!is_blacbkboard && port_model.converter() && port_model.isStronglyTyped())
673 port_model.converter()(port_value);
675 catch(std::exception& ex)
677 auto msg =
StrCat(
"The port with name \"", port_name,
"\" and value \"",
678 port_value,
"\" can not be converted to ",
679 port_model.typeName());
686 port_remap[port_name] = port_value;
692 config.
path = prefix_path + instance_name;
696 if(type_ID == instance_name)
701 auto AddCondition = [&](
auto& conditions,
const char* attr_name,
auto ID) {
702 if(
auto script = element->
Attribute(attr_name))
704 conditions.insert({ ID, std::string(script) });
710 auto pre =
static_cast<PreCond>(i);
715 auto post =
static_cast<PostCond>(i);
727 auto subtree_node =
dynamic_cast<SubTreeNode*
>(new_node.get());
734 auto msg =
StrCat(
"Missing manifest for element_ID: ", element_ID,
735 ". It shouldn't happen. Please report this issue.");
740 for(
const auto& [name_in_subtree,
_] : port_remap)
742 if(
manifest->ports.count(name_in_subtree) == 0)
744 throw RuntimeError(
"Possible typo? In the XML, you tried to remap port \"",
745 name_in_subtree,
"\" in node [", config.
path,
"(type ",
747 ")], but the manifest/model of this node does not contain a "
754 for(
const auto& [port_name, port_info] :
manifest->ports)
756 auto remap_it = port_remap.find(port_name);
757 if(remap_it == port_remap.end())
766 const auto port_key =
static_cast<std::string
>(param_res.value());
769 if(
auto prev_info = blackboard->entryInfo(port_key))
772 bool const port_type_mismatch =
774 prev_info->type() != port_info.
type());
777 bool const string_input = (prev_info->type() ==
typeid(std::string));
781 blackboard->debugMessage();
783 throw RuntimeError(
"The creation of the tree failed because the port [",
784 port_key,
"] was initially created with type [",
785 demangle(prev_info->type()),
"] and, later type [",
786 demangle(port_info.
type()),
"] was used somewhere else.");
792 blackboard->createEntry(port_key, port_info);
798 for(
const auto& remap_it : port_remap)
800 const auto& port_name = remap_it.first;
801 auto port_it =
manifest->ports.find(port_name);
802 if(port_it !=
manifest->ports.end())
804 auto direction = port_it->second.direction();
817 for(
const auto& port_it :
manifest->ports)
819 const std::string& port_name = port_it.first;
820 const PortInfo& port_info = port_it.second;
822 const auto direction = port_info.
direction();
824 if(!default_string.empty())
828 config.
input_ports.insert({ port_name, default_string });
835 config.
output_ports.insert({ port_name, default_string });
840 new_node = factory.instantiateTreeNode(instance_name, type_ID, config);
844 if(node_parent !=
nullptr)
846 if(
auto control_parent =
dynamic_cast<ControlNode*
>(node_parent.get()))
848 control_parent->addChild(new_node.get());
850 else if(
auto decorator_parent =
dynamic_cast<DecoratorNode*
>(node_parent.get()))
852 decorator_parent->setChild(new_node.get());
860 const std::string& tree_path,
861 const std::string& prefix_path,
871 std::string prefix,
const XMLElement* element) {
873 auto node = createNodeFromXML(element, blackboard, parent_node, prefix, output_tree);
874 subtree->nodes.push_back(node);
879 for(
auto child_element = element->FirstChildElement(); child_element;
880 child_element = child_element->NextSiblingElement())
882 recursiveStep(node, subtree, prefix, child_element);
888 const std::string subtree_ID = element->Attribute(
"ID");
889 std::unordered_map<std::string, std::string> subtree_remapping;
890 bool do_autoremap =
false;
892 for(
auto attr = element->FirstAttribute(); attr !=
nullptr; attr = attr->Next())
894 std::string attr_name = attr->Name();
895 std::string attr_value = attr->Value();
896 if(attr_value ==
"{=}")
898 attr_value =
StrCat(
"{", attr_name,
"}");
901 if(attr_name ==
"_autoremap")
904 new_bb->enableAutoRemapping(do_autoremap);
911 subtree_remapping.insert({ attr_name, attr_value });
916 auto subtree_model_it = subtree_models.find(subtree_ID);
917 if(subtree_model_it != subtree_models.end())
919 const auto& subtree_model_ports = subtree_model_it->second.ports;
923 for(
const auto& [port_name, port_info] : subtree_model_ports)
925 auto it = subtree_remapping.find(port_name);
927 if(it == subtree_remapping.end() && !do_autoremap)
932 auto msg =
StrCat(
"In the <TreeNodesModel> the <Subtree ID=\"", subtree_ID,
933 "\"> is defining a mandatory port called [", port_name,
934 "], but you are not remapping it");
945 for(
const auto& [attr_name, attr_value] : subtree_remapping)
951 new_bb->addSubtreeRemapping(attr_name, port_name);
957 new_bb->enableAutoRemapping(
false);
958 new_bb->set(attr_name,
static_cast<std::string
>(attr_value));
959 new_bb->enableAutoRemapping(do_autoremap);
963 std::string subtree_path = subtree->instance_name;
964 if(!subtree_path.empty())
968 if(
auto name = element->Attribute(
"name"))
970 subtree_path += name;
977 recursivelyCreateSubtree(subtree_ID,
980 output_tree, new_bb, node);
984 auto it = tree_roots.find(tree_ID);
985 if(it == tree_roots.end())
987 throw std::runtime_error(std::string(
"Can't find a tree with name: ") + tree_ID);
990 auto root_element = it->second->FirstChildElement();
995 auto new_tree = std::make_shared<Tree::Subtree>();
996 new_tree->blackboard = blackboard;
997 new_tree->instance_name = tree_path;
998 new_tree->tree_ID = tree_ID;
999 output_tree.
subtrees.push_back(new_tree);
1001 recursiveStep(root_node, new_tree, prefix_path, root_element);
1005 std::vector<std::string>& output_ports)
1008 attr = attr->
Next())
1010 const char* attr_name = attr->Name();
1011 const char* attr_value = attr->Value();
1015 output_ports.push_back(
static_cast<std::string
>(port_name));
1022 getPortsRecursively(child_element, output_ports);
1032 for(
const auto& [port_name, port_info] : model.
ports)
1041 port_element = doc.
NewElement(
"output_port");
1049 if(port_info.
type() !=
typeid(
void))
1067 auto metadata_root = doc.
NewElement(
"MetadataFields");
1069 for(
const auto& [name, value] : model.
metadata)
1071 auto metadata_element = doc.
NewElement(
"Metadata");
1072 metadata_element->
SetAttribute(name.c_str(), value.c_str());
1073 metadata_root->InsertEndChild(metadata_element);
1083 bool add_metadata,
bool add_builtin_models)
1089 if(
auto subtree =
dynamic_cast<const SubTreeNode*
>(&node))
1092 elem->
SetAttribute(
"ID", subtree->subtreeID().c_str());
1095 elem->
SetAttribute(
"_fullpath", subtree->config().path.c_str());
1131 parent_elem->InsertEndChild(elem);
1137 addNode(*
child, elem);
1140 else if(
auto decorator =
dynamic_cast<const DecoratorNode*
>(&node))
1144 addNode(*decorator->child(), elem);
1149 for(
const auto& subtree : tree.
subtrees)
1152 subtree_elem->
SetAttribute(
"ID", subtree->tree_ID.c_str());
1153 subtree_elem->
SetAttribute(
"_fullpath", subtree->instance_name.c_str());
1155 addNode(*subtree->nodes.front(), subtree_elem);
1163 std::map<std::string, const TreeNodeManifest*> ordered_models;
1164 for(
const auto& [registration_ID, model] : tree.
manifests)
1166 if(add_builtin_models || !temp_factory.
builtinNodes().count(registration_ID))
1168 ordered_models.insert({ registration_ID, &model });
1172 for(
const auto& [registration_ID, model] : ordered_models)
1179 bool include_builtin)
1190 std::map<std::string, const TreeNodeManifest*> ordered_models;
1192 for(
const auto& [registration_ID, model] : factory.
manifests())
1194 if(include_builtin || factory.
builtinNodes().count(registration_ID) == 0)
1196 ordered_models.insert({ registration_ID, &model });
1200 for(
const auto& [registration_ID, model] : ordered_models)
1206 doc.
Print(&printer);
1207 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));
1218 std::map<std::string, const TreeNodeManifest*> ordered_models;
1219 for(
const auto& [registration_id, model] : factory.
manifests())
1221 ordered_models.insert({ registration_id, &model });
1228 "encoding=\"UTF-8\"");
1237 schema_element->
SetAttribute(
"xmlns:xs",
"http://www.w3.org/2001/XMLSchema");
1238 schema_element->
SetAttribute(
"elementFormDefault",
"qualified");
1241 auto parse_and_insert = [&doc](
XMLElement* parent_elem,
const char* str) {
1246 std::cerr <<
"Internal error parsing existing XML: " << tmp_doc.
ErrorStr()
1262 parse_and_insert(schema_element, R
"(
1263 <xs:simpleType name="blackboardType">
1264 <xs:restriction base="xs:string">
1265 <xs:pattern value="\{.*\}"/>
1268 <xs:simpleType name="booleanOrBlackboardType">
1269 <xs:union memberTypes="xs:boolean blackboardType"/>
1271 <xs:simpleType name="integerOrBlackboardType">
1272 <xs:union memberTypes="xs:integer blackboardType"/>
1274 <xs:simpleType name="decimalOrBlackboardType">
1275 <xs:union memberTypes="xs:decimal blackboardType"/>
1277 <xs:simpleType name="stringOrBlackboardType">
1278 <xs:union memberTypes="xs:string blackboardType"/>
1280 <xs:simpleType name="descriptionType">
1281 <xs:restriction base="xs:string">
1282 <xs:whiteSpace value="preserve"/>
1285 <xs:complexType name="inputPortType">
1287 <xs:extension base="xs:string">
1288 <xs:attribute name="name" type="xs:string" use="required"/>
1289 <xs:attribute name="type" type="xs:string" use="optional"/>
1290 <xs:attribute name="default" type="xs:string" use="optional"/>
1294 <xs:complexType name="outputPortType">
1296 <xs:extension base="xs:string">
1297 <xs:attribute name="name" type="xs:string" use="required"/>
1298 <xs:attribute name="type" type="xs:string" use="optional"/>
1302 <xs:attributeGroup name="preconditionAttributeGroup">
1303 <xs:attribute name="_failureIf" type="xs:string" use="optional"/>
1304 <xs:attribute name="_skipIf" type="xs:string" use="optional"/>
1305 <xs:attribute name="_successIf" type="xs:string" use="optional"/>
1306 <xs:attribute name="_while" type="xs:string" use="optional"/>
1307 </xs:attributeGroup>
1308 <xs:attributeGroup name="postconditionAttributeGroup">
1309 <xs:attribute name="_onSuccess" type="xs:string" use="optional"/>
1310 <xs:attribute name="_onFailure" type="xs:string" use="optional"/>
1311 <xs:attribute name="_post" type="xs:string" use="optional"/>
1312 <xs:attribute name="_onHalted" type="xs:string" use="optional"/>
1313 </xs:attributeGroup>)");
1321 parse_and_insert(schema_element, R
"(
1322 <xs:attributeGroup name="commonAttributeGroup">
1323 <xs:attribute name="name" type="xs:string" use="optional"/>
1324 <xs:attributeGroup ref="preconditionAttributeGroup"/>
1325 <xs:attributeGroup ref="postconditionAttributeGroup"/>
1326 </xs:attributeGroup>)");
1329 parse_and_insert(schema_element, R
"(
1330 <xs:complexType name="treeNodesModelNodeType">
1332 <xs:choice minOccurs="0" maxOccurs="unbounded">
1333 <xs:element name="input_port" type="inputPortType"/>
1334 <xs:element name="output_port" type="outputPortType"/>
1336 <xs:element name="description" type="descriptionType" minOccurs="0" maxOccurs="1"/>
1338 <xs:attribute name="ID" type="xs:string" use="required"/>
1340 <xs:group name="treeNodesModelNodeGroup">
1342 <xs:element name="Action" type="treeNodesModelNodeType"/>
1343 <xs:element name="Condition" type="treeNodesModelNodeType"/>
1344 <xs:element name="Control" type="treeNodesModelNodeType"/>
1345 <xs:element name="Decorator" type="treeNodesModelNodeType"/>
1351 const auto root_element_xsd = R
"(
1352 <xs:element name="root">
1355 <xs:choice minOccurs="0" maxOccurs="unbounded">
1356 <xs:element ref="include"/>
1357 <xs:element ref="BehaviorTree"/>
1359 <xs:element ref="TreeNodesModel" minOccurs="0" maxOccurs="1"/>
1361 <xs:attribute name="BTCPP_format" type="xs:string" use="required"/>
1362 <xs:attribute name="main_tree_to_execute" type="xs:string" use="optional"/>
1366 parse_and_insert(schema_element, root_element_xsd);
1372 std::ostringstream xsd;
1373 xsd <<
"<xs:choice>";
1374 for(
const auto& [registration_id, model] : ordered_models)
1376 xsd <<
"<xs:element name=\"" << registration_id <<
"\" type=\"" << registration_id
1379 xsd <<
"</xs:choice>";
1380 parse_and_insert(one_node_group, xsd.str().c_str());
1385 parse_and_insert(schema_element, R
"(
1386 <xs:element name="include">
1388 <xs:attribute name="path" type="xs:string" use="required"/>
1389 <xs:attribute name="ros_pkg" type="xs:string" use="optional"/>
1395 parse_and_insert(schema_element, R
"(
1396 <xs:element name="BehaviorTree">
1398 <xs:group ref="oneNodeGroup"/>
1399 <xs:attribute name="ID" type="xs:string" use="required"/>
1405 parse_and_insert(schema_element, R
"(
1406 <xs:element name="TreeNodesModel">
1408 <xs:group ref="treeNodesModelNodeGroup" minOccurs="0" maxOccurs="unbounded"/>
1414 for(
const auto& [registration_id, model] : ordered_models)
1417 type->
SetAttribute(
"name", (model->registration_ID +
"Type").c_str());
1445 common_attr_group->
SetAttribute(
"ref",
"commonAttributeGroup");
1447 for(
const auto& [port_name, port_info] : model->ports)
1451 const auto xsd_attribute_type = xsdAttributeType(port_info);
1452 if(not xsd_attribute_type.empty())
1454 attr->
SetAttribute(
"type", xsd_attribute_type.c_str());
1466 if(model->registration_ID ==
"SubTree")
1468 parse_and_insert(type, R
"(
1469 <xs:attribute name="ID" type="xs:string" use="required"/>
1470 <xs:anyAttribute processContents="skip"/>
1477 doc.Print(&printer);
1478 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));
1505 addTreeToXML(tree, doc, rootXML, add_metadata, add_builtin_models);
1508 doc.
Print(&printer);
1509 return std::string(printer.
CStr(),
size_t(printer.
CStrSize() - 1));