1212
1313#include " behaviortree_cpp/bt_factory.h"
1414
15+ #include " tinyxml2.h"
16+
1517#include " behaviortree_cpp/utils/shared_library.h"
1618#include " behaviortree_cpp/utils/wildcards.hpp"
1719#include " behaviortree_cpp/xml_parsing.h"
1820
1921#include < filesystem>
22+ #include < functional>
2023
2124namespace BT
2225{
26+ namespace
27+ {
28+
29+ // Extract the main tree ID from an XML root element.
30+ // Checks main_tree_to_execute attribute first, then falls back to the
31+ // single BehaviorTree ID if only one is defined.
32+ std::string detectMainTreeId (const tinyxml2::XMLElement* xml_root)
33+ {
34+ if (const auto * attr = xml_root->Attribute (" main_tree_to_execute" ))
35+ {
36+ return attr;
37+ }
38+ int bt_count = 0 ;
39+ std::string single_id;
40+ for (const auto * bt_elem = xml_root->FirstChildElement (" BehaviorTree" );
41+ bt_elem != nullptr ; bt_elem = bt_elem->NextSiblingElement (" BehaviorTree" ))
42+ {
43+ bt_count++;
44+ if (const auto * tree_id = bt_elem->Attribute (" ID" ))
45+ {
46+ single_id = tree_id;
47+ }
48+ }
49+ if (bt_count == 1 && !single_id.empty ())
50+ {
51+ return single_id;
52+ }
53+ return {};
54+ }
55+
56+ // Load XML into parser and resolve which tree to instantiate.
57+ // Returns the resolved tree ID (may be empty if parser should use default).
58+ std::string loadXmlAndResolveTreeId (Parser* parser, const std::string& main_tree_ID,
59+ const std::function<void ()>& load_func)
60+ {
61+ // When the main tree couldn't be determined from the raw XML
62+ // (e.g. <BehaviorTree> without an ID), snapshot registered trees
63+ // before loading so we can diff afterwards.
64+ std::set<std::string> before_set;
65+ if (main_tree_ID.empty ())
66+ {
67+ const auto before = parser->registeredBehaviorTrees ();
68+ before_set.insert (before.begin (), before.end ());
69+ }
70+
71+ load_func ();
72+
73+ // Try to identify the newly added tree by diffing.
74+ if (main_tree_ID.empty ())
75+ {
76+ const auto after = parser->registeredBehaviorTrees ();
77+ std::string single_new_tree;
78+ int new_count = 0 ;
79+ for (const auto & name : after)
80+ {
81+ if (before_set.count (name) == 0 )
82+ {
83+ single_new_tree = name;
84+ new_count++;
85+ }
86+ }
87+ if (new_count == 1 )
88+ {
89+ return single_new_tree;
90+ }
91+ }
92+ return main_tree_ID;
93+ }
94+
95+ } // namespace
2396
2497bool WildcardMatch (std::string const & str, StringView filter)
2598{
@@ -342,17 +415,20 @@ const std::set<std::string>& BehaviorTreeFactory::builtinNodes() const
342415Tree BehaviorTreeFactory::createTreeFromText (const std::string& text,
343416 Blackboard::Ptr blackboard)
344417{
345- if (!_p->parser ->registeredBehaviorTrees ().empty ())
418+ // Determine the main tree from the XML before loading into the shared parser.
419+ tinyxml2::XMLDocument doc;
420+ doc.Parse (text.c_str (), text.size ());
421+ std::string main_tree_ID;
422+ if (const auto * root = doc.RootElement ())
346423 {
347- std::cout << " WARNING: You executed BehaviorTreeFactory::createTreeFromText "
348- " after registerBehaviorTreeFrom[File/Text].\n "
349- " This is NOT, probably, what you want to do.\n "
350- " You should probably use BehaviorTreeFactory::createTree, instead"
351- << std::endl;
424+ main_tree_ID = detectMainTreeId (root);
352425 }
353- XMLParser parser (*this );
354- parser.loadFromText (text);
355- auto tree = parser.instantiateTree (blackboard);
426+
427+ const std::string resolved_ID = loadXmlAndResolveTreeId (
428+ _p->parser .get (), main_tree_ID, [&] { _p->parser ->loadFromText (text); });
429+
430+ Tree tree = resolved_ID.empty () ? _p->parser ->instantiateTree (blackboard) :
431+ _p->parser ->instantiateTree (blackboard, resolved_ID);
356432 tree.manifests = this ->manifests ();
357433 tree.remapManifestPointers ();
358434 return tree;
@@ -361,18 +437,20 @@ Tree BehaviorTreeFactory::createTreeFromText(const std::string& text,
361437Tree BehaviorTreeFactory::createTreeFromFile (const std::filesystem::path& file_path,
362438 Blackboard::Ptr blackboard)
363439{
364- if (!_p->parser ->registeredBehaviorTrees ().empty ())
440+ // Determine the main tree from the XML before loading into the shared parser.
441+ tinyxml2::XMLDocument doc;
442+ doc.LoadFile (file_path.string ().c_str ());
443+ std::string main_tree_ID;
444+ if (const auto * root = doc.RootElement ())
365445 {
366- std::cout << " WARNING: You executed BehaviorTreeFactory::createTreeFromFile "
367- " after registerBehaviorTreeFrom[File/Text].\n "
368- " This is NOT, probably, what you want to do.\n "
369- " You should probably use BehaviorTreeFactory::createTree, instead"
370- << std::endl;
446+ main_tree_ID = detectMainTreeId (root);
371447 }
372448
373- XMLParser parser (*this );
374- parser.loadFromFile (file_path);
375- auto tree = parser.instantiateTree (blackboard);
449+ const std::string resolved_ID = loadXmlAndResolveTreeId (
450+ _p->parser .get (), main_tree_ID, [&] { _p->parser ->loadFromFile (file_path); });
451+
452+ Tree tree = resolved_ID.empty () ? _p->parser ->instantiateTree (blackboard) :
453+ _p->parser ->instantiateTree (blackboard, resolved_ID);
376454 tree.manifests = this ->manifests ();
377455 tree.remapManifestPointers ();
378456 return tree;
0 commit comments