Skip to content

Commit 1e00fcd

Browse files
committed
fix issue #827 : verify <BehaviorTree> name
1 parent 17b347b commit 1e00fcd

File tree

2 files changed

+87
-59
lines changed

2 files changed

+87
-59
lines changed

src/xml_parsing.cpp

Lines changed: 57 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -408,101 +408,94 @@ void VerifyXML(const std::string& xml_text,
408408
recursiveStep = [&](const XMLElement* node) {
409409
const int children_count = ChildrenCount(node);
410410
const std::string name = node->Name();
411+
const std::string ID = node->Attribute("ID") ? node->Attribute("ID") : "";
412+
const int line_number = node->GetLineNum();
413+
411414
if(name == "Decorator")
412415
{
413416
if(children_count != 1)
414417
{
415-
ThrowError(node->GetLineNum(), "The node <Decorator> must have exactly 1 "
416-
"child");
418+
ThrowError(line_number, "The tag <Decorator> must have exactly 1 "
419+
"child");
417420
}
418-
if(!node->Attribute("ID"))
421+
if(ID.empty())
419422
{
420-
ThrowError(node->GetLineNum(), "The node <Decorator> must have the "
421-
"attribute [ID]");
423+
ThrowError(line_number, "The tag <Decorator> must have the "
424+
"attribute [ID]");
422425
}
423426
}
424427
else if(name == "Action")
425428
{
426429
if(children_count != 0)
427430
{
428-
ThrowError(node->GetLineNum(), "The node <Action> must not have any "
429-
"child");
431+
ThrowError(line_number, "The tag <Action> must not have any "
432+
"child");
430433
}
431-
if(!node->Attribute("ID"))
434+
if(ID.empty())
432435
{
433-
ThrowError(node->GetLineNum(), "The node <Action> must have the "
434-
"attribute [ID]");
436+
ThrowError(line_number, "The tag <Action> must have the "
437+
"attribute [ID]");
435438
}
436439
}
437440
else if(name == "Condition")
438441
{
439442
if(children_count != 0)
440443
{
441-
ThrowError(node->GetLineNum(), "The node <Condition> must not have any "
442-
"child");
444+
ThrowError(line_number, "The tag <Condition> must not have any "
445+
"child");
443446
}
444-
if(!node->Attribute("ID"))
447+
if(ID.empty())
445448
{
446-
ThrowError(node->GetLineNum(), "The node <Condition> must have the "
447-
"attribute [ID]");
449+
ThrowError(line_number, "The tag <Condition> must have the "
450+
"attribute [ID]");
448451
}
449452
}
450453
else if(name == "Control")
451454
{
452455
if(children_count == 0)
453456
{
454-
ThrowError(node->GetLineNum(), "The node <Control> must have at least 1 "
455-
"child");
457+
ThrowError(line_number, "The tag <Control> must have at least 1 "
458+
"child");
456459
}
457-
if(!node->Attribute("ID"))
460+
if(ID.empty())
458461
{
459-
ThrowError(node->GetLineNum(), "The node <Control> must have the "
460-
"attribute [ID]");
462+
ThrowError(line_number, "The tag <Control> must have the "
463+
"attribute [ID]");
461464
}
462465
}
463-
else if(name == "Sequence" || name == "ReactiveSequence" ||
464-
name == "SequenceWithMemory" || name == "Fallback")
466+
else if(name == "SubTree")
465467
{
466-
if(children_count == 0)
468+
if(children_count != 0)
467469
{
468-
std::string name_attr;
469-
if(node->Attribute("name"))
470-
{
471-
name_attr = "(`" + std::string(node->Attribute("name")) + "`)";
472-
}
473-
ThrowError(node->GetLineNum(), std::string("A Control node must have at least 1 "
474-
"child, error in XML node `") +
475-
node->Name() + name_attr + "`");
470+
ThrowError(line_number, "<SubTree> should not have any child");
476471
}
477-
}
478-
else if(name == "SubTree")
479-
{
480-
auto child = node->FirstChildElement();
481-
482-
if(child)
472+
if(ID.empty())
483473
{
484-
if(StrEqual(child->Name(), "remap"))
485-
{
486-
ThrowError(node->GetLineNum(), "<remap> was deprecated");
487-
}
488-
else
489-
{
490-
ThrowError(node->GetLineNum(), "<SubTree> should not have any child");
491-
}
474+
ThrowError(line_number, "The tag <SubTree> must have the "
475+
"attribute [ID]");
492476
}
493-
494-
if(!node->Attribute("ID"))
477+
if(registered_nodes.count(ID) != 0)
495478
{
496-
ThrowError(node->GetLineNum(), "The node <SubTree> must have the "
497-
"attribute [ID]");
479+
ThrowError(line_number, "The attribute [ID] of tag <SubTree> must "
480+
"not use the name of a registered Node");
498481
}
499482
}
500483
else if(name == "BehaviorTree")
501484
{
485+
if(ID.empty())
486+
{
487+
ThrowError(line_number, "The tag <BehaviorTree> must have the "
488+
"attribute [ID]");
489+
}
502490
if(children_count != 1)
503491
{
504-
ThrowError(node->GetLineNum(), "The node <BehaviorTree> must have exactly 1 "
505-
"child");
492+
ThrowError(line_number, "The tag <BehaviorTree> must have exactly 1 "
493+
"child");
494+
}
495+
if(registered_nodes.count(ID) != 0)
496+
{
497+
ThrowError(line_number, "The attribute [ID] of tag <BehaviorTree> "
498+
"must not use the name of a registered Node");
506499
}
507500
}
508501
else
@@ -512,26 +505,31 @@ void VerifyXML(const std::string& xml_text,
512505
bool found = (search != registered_nodes.end());
513506
if(!found)
514507
{
515-
ThrowError(node->GetLineNum(), std::string("Node not recognized: ") + name);
508+
ThrowError(line_number, std::string("Node not recognized: ") + name);
516509
}
517510

518511
if(search->second == NodeType::DECORATOR)
519512
{
520513
if(children_count != 1)
521514
{
522-
ThrowError(node->GetLineNum(),
515+
ThrowError(line_number,
523516
std::string("The node <") + name + "> must have exactly 1 child");
524517
}
525518
}
519+
else if(search->second == NodeType::CONTROL)
520+
{
521+
if(children_count == 0)
522+
{
523+
ThrowError(line_number,
524+
std::string("The node <") + name + "> must have 1 or more children");
525+
}
526+
}
526527
}
527528
//recursion
528-
if(name != "SubTree")
529+
for(auto child = node->FirstChildElement(); child != nullptr;
530+
child = child->NextSiblingElement())
529531
{
530-
for(auto child = node->FirstChildElement(); child != nullptr;
531-
child = child->NextSiblingElement())
532-
{
533-
recursiveStep(child);
534-
}
532+
recursiveStep(child);
535533
}
536534
};
537535

tests/gtest_subtree.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,3 +696,33 @@ TEST(SubTree, PrivateAutoRemapping)
696696
ASSERT_EQ(console.size(), 1);
697697
ASSERT_EQ(console[0], "hello");
698698
}
699+
700+
TEST(SubTree, SubtreeNameNotRegistered)
701+
{
702+
// clang-format off
703+
704+
static const char* xml_text = R"(
705+
<root BTCPP_format="4">
706+
<BehaviorTree ID="PrintToConsole">\n"
707+
<Sequence>
708+
<PrintToConsole message="world"/>
709+
</Sequence>
710+
</BehaviorTree>
711+
712+
<BehaviorTree ID="MainTree">
713+
<Sequence>
714+
<PrintToConsole message="hello"/>
715+
<SubTree ID="PrintToConsole"/>
716+
</Sequence>
717+
</BehaviorTree>
718+
</root>
719+
)";
720+
721+
// clang-format on
722+
BehaviorTreeFactory factory;
723+
std::vector<std::string> console;
724+
factory.registerNodeType<PrintToConsole>("PrintToConsole", &console);
725+
726+
ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text));
727+
ASSERT_ANY_THROW(factory.registerBehaviorTreeFromText(xml_text));
728+
}

0 commit comments

Comments
 (0)