Skip to content

Commit 273b00b

Browse files
committed
Refactor VerifyXML to clarify logic
- Reduces duplication in VerifyXML by handling the ID check for built-in node types up front so they can then be definitively looked up in the registered nodes. - Enhances error messaging in VerifyXML by using *either* the node name *or* the ID, depending on which is appropriate, instead of leaving users guessing "which Decorator is wrong" - Fixes custom Action and Condition nodes using shorthand syntax not being properly verified - Fixes `<Control ID="ReactiveSequence"/>` not being verified with the same logic as `<ReactiveSequence/>` - Fixes `<Action ID="MyAction"/>` not triggering a behavior lookup when `<MyAction/>` would.
1 parent 8d47d39 commit 273b00b

File tree

1 file changed

+53
-80
lines changed

1 file changed

+53
-80
lines changed

src/xml_parsing.cpp

Lines changed: 53 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -428,119 +428,76 @@ void VerifyXML(const std::string& xml_text,
428428
const std::string ID = node->Attribute("ID") ? node->Attribute("ID") : "";
429429
const int line_number = node->GetLineNum();
430430

431-
if(name == "Decorator")
431+
// Precondition: built-in XML element types must define attribute [ID]
432+
const bool is_builtin =
433+
(name == "Decorator" || name == "Action" || name == "Condition" ||
434+
name == "Control" || name == "SubTree");
435+
if(is_builtin && ID.empty())
432436
{
433-
if(children_count != 1)
434-
{
435-
ThrowError(line_number, "The tag <Decorator> must have exactly 1 "
436-
"child");
437-
}
438-
if(ID.empty())
439-
{
440-
ThrowError(line_number, "The tag <Decorator> must have the "
441-
"attribute [ID]");
442-
}
443-
}
444-
else if(name == "Action")
445-
{
446-
if(children_count != 0)
447-
{
448-
ThrowError(line_number, "The tag <Action> must not have any "
449-
"child");
450-
}
451-
if(ID.empty())
452-
{
453-
ThrowError(line_number, "The tag <Action> must have the "
454-
"attribute [ID]");
455-
}
437+
ThrowError(line_number,
438+
std::string("The tag <") + name + "> must have the attribute [ID]");
456439
}
457-
else if(name == "Condition")
440+
441+
if(name == "BehaviorTree")
458442
{
459-
if(children_count != 0)
460-
{
461-
ThrowError(line_number, "The tag <Condition> must not have any "
462-
"child");
463-
}
464-
if(ID.empty())
443+
if(ID.empty() && behavior_tree_count > 1)
465444
{
466-
ThrowError(line_number, "The tag <Condition> must have the "
467-
"attribute [ID]");
445+
ThrowError(line_number, "The tag <BehaviorTree> must have the attribute [ID]");
468446
}
469-
}
470-
else if(name == "Control")
471-
{
472-
if(children_count == 0)
447+
if(registered_nodes.count(ID) != 0)
473448
{
474-
ThrowError(line_number, "The tag <Control> must have at least 1 "
475-
"child");
449+
ThrowError(line_number, "The attribute [ID] of tag <BehaviorTree> must not use "
450+
"the name of a registered Node");
476451
}
477-
if(ID.empty())
452+
if(children_count != 1)
478453
{
479-
ThrowError(line_number, "The tag <Control> must have the "
480-
"attribute [ID]");
454+
ThrowError(line_number, "The tag <BehaviorTree> with ID '" + ID +
455+
"' must have exactly 1 child");
481456
}
482457
}
483458
else if(name == "SubTree")
484459
{
485460
if(children_count != 0)
486461
{
487-
ThrowError(line_number, "<SubTree> should not have any child");
488-
}
489-
if(ID.empty())
490-
{
491-
ThrowError(line_number, "The tag <SubTree> must have the "
492-
"attribute [ID]");
462+
ThrowError(line_number,
463+
"<SubTree> with ID '" + ID + "' should not have any child");
493464
}
494465
if(registered_nodes.count(ID) != 0)
495466
{
496-
ThrowError(line_number, "The attribute [ID] of tag <SubTree> must "
497-
"not use the name of a registered Node");
498-
}
499-
}
500-
else if(name == "BehaviorTree")
501-
{
502-
if(ID.empty() && behavior_tree_count > 1)
503-
{
504-
ThrowError(line_number, "The tag <BehaviorTree> must have the "
505-
"attribute [ID]");
506-
}
507-
if(children_count != 1)
508-
{
509-
ThrowError(line_number, "The tag <BehaviorTree> must have exactly 1 "
510-
"child");
511-
}
512-
if(registered_nodes.count(ID) != 0)
513-
{
514-
ThrowError(line_number, "The attribute [ID] of tag <BehaviorTree> "
515-
"must not use the name of a registered Node");
467+
ThrowError(line_number, "The attribute [ID] of tag <SubTree> must not use the "
468+
"name of a registered Node");
516469
}
517470
}
518471
else
519472
{
520-
// search in the factory and the list of subtrees
521-
const auto search = registered_nodes.find(name);
473+
// use ID for builtin node types, otherwise use the element name
474+
const auto lookup_name = is_builtin ? ID : name;
475+
const auto search = registered_nodes.find(lookup_name);
522476
bool found = (search != registered_nodes.end());
523477
if(!found)
524478
{
525-
ThrowError(line_number, std::string("Node not recognized: ") + name);
479+
ThrowError(line_number, std::string("Node not recognized: ") + lookup_name);
526480
}
527481

528-
if(search->second == NodeType::DECORATOR)
482+
const auto node_type = search->second;
483+
const std::string& registered_name = search->first;
484+
485+
if(node_type == NodeType::DECORATOR)
529486
{
530487
if(children_count != 1)
531488
{
532-
ThrowError(line_number,
533-
std::string("The node <") + name + "> must have exactly 1 child");
489+
ThrowError(line_number, std::string("The node '") + registered_name +
490+
"' must have exactly 1 child");
534491
}
535492
}
536-
else if(search->second == NodeType::CONTROL)
493+
else if(node_type == NodeType::CONTROL)
537494
{
538495
if(children_count == 0)
539496
{
540-
ThrowError(line_number,
541-
std::string("The node <") + name + "> must have 1 or more children");
497+
ThrowError(line_number, std::string("The node '") + registered_name +
498+
"' must have 1 or more children");
542499
}
543-
if(name == "ReactiveSequence")
500+
if(registered_name == "ReactiveSequence")
544501
{
545502
size_t async_count = 0;
546503
for(auto child = node->FirstChildElement(); child != nullptr;
@@ -562,13 +519,29 @@ void VerifyXML(const std::string& xml_text,
562519
++async_count;
563520
if(async_count > 1)
564521
{
565-
ThrowError(line_number, std::string("A ReactiveSequence cannot have more "
566-
"than one async child."));
522+
ThrowError(line_number, std::string("A ReactiveSequence cannot have "
523+
"more than one async child."));
567524
}
568525
}
569526
}
570527
}
571528
}
529+
else if(node_type == NodeType::ACTION)
530+
{
531+
if(children_count != 0)
532+
{
533+
ThrowError(line_number, std::string("The node '") + registered_name +
534+
"' must not have any child");
535+
}
536+
}
537+
else if(node_type == NodeType::CONDITION)
538+
{
539+
if(children_count != 0)
540+
{
541+
ThrowError(line_number, std::string("The node '") + registered_name +
542+
"' must not have any child");
543+
}
544+
}
572545
}
573546
//recursion
574547
for(auto child = node->FirstChildElement(); child != nullptr;

0 commit comments

Comments
 (0)