@@ -429,153 +429,120 @@ void VerifyXML(const std::string& xml_text,
429429 const std::string ID = node->Attribute (" ID" ) ? node->Attribute (" ID" ) : " " ;
430430 const int line_number = node->GetLineNum ();
431431
432- if (name == " Decorator" )
432+ // Precondition: built-in XML element types must define attribute [ID]
433+ const bool is_builtin =
434+ (name == " Decorator" || name == " Action" || name == " Condition" ||
435+ name == " Control" || name == " SubTree" );
436+ if (is_builtin && ID.empty ())
433437 {
434- if (ID.empty ())
435- {
436- ThrowError (line_number, " The tag <Decorator> must have the "
437- " attribute [ID]" );
438- }
439- if (children_count != 1 )
440- {
441- ThrowError (line_number, " The tag <Decorator> with ID '" + ID +
442- " ' must have exactly 1 "
443- " child" );
444- }
438+ ThrowError (line_number,
439+ std::string (" The tag <" ) + name + " > must have the attribute [ID]" );
445440 }
446- else if (name == " Action" )
447- {
448- if (ID.empty ())
449- {
450- ThrowError (line_number, " The tag <Action> must have the "
451- " attribute [ID]" );
452- }
453- if (children_count != 0 )
454- {
455- ThrowError (line_number, " The tag <Action> with ID '" + ID +
456- " ' must not have any "
457- " child" );
458- }
459- }
460- else if (name == " Condition" )
441+
442+ if (name == " BehaviorTree" )
461443 {
462- if (ID.empty ())
463- {
464- ThrowError (line_number, " The tag <Condition> must have the "
465- " attribute [ID]" );
466- }
467- if (children_count != 0 )
444+ if (ID.empty () && behavior_tree_count > 1 )
468445 {
469- ThrowError (line_number, " The tag <Condition> with ID '" + ID +
470- " ' must not have any "
471- " child" );
446+ ThrowError (line_number, " The tag <BehaviorTree> must have the attribute [ID]" );
472447 }
473- }
474- else if (name == " Control" )
475- {
476- if (ID.empty ())
448+ if (registered_nodes.count (ID) != 0 )
477449 {
478- ThrowError (line_number, " The tag <Control > must have the "
479- " attribute [ID] " );
450+ ThrowError (line_number, " The attribute [ID] of tag <BehaviorTree > must not use "
451+ " the name of a registered Node " );
480452 }
481- if (children_count == 0 )
453+ if (children_count != 1 )
482454 {
483- ThrowError (line_number, " The tag <Control> with ID '" + ID +
484- " ' must have at least 1 "
485- " child" );
455+ ThrowError (line_number, " The tag <BehaviorTree> with ID '" + ID +
456+ " ' must have exactly 1 child" );
486457 }
487458 }
488459 else if (name == " SubTree" )
489460 {
490- if (ID.empty ())
491- {
492- ThrowError (line_number, " The tag <SubTree> must have the "
493- " attribute [ID]" );
494- }
495461 if (children_count != 0 )
496462 {
497463 ThrowError (line_number,
498464 " <SubTree> with ID '" + ID + " ' should not have any child" );
499465 }
500466 if (registered_nodes.count (ID) != 0 )
501467 {
502- ThrowError (line_number, " The attribute [ID] of tag <SubTree> must "
503- " not use the name of a registered Node" );
504- }
505- }
506- else if (name == " BehaviorTree" )
507- {
508- if (ID.empty () && behavior_tree_count > 1 )
509- {
510- ThrowError (line_number, " The tag <BehaviorTree> must have the "
511- " attribute [ID]" );
512- }
513- if (registered_nodes.count (ID) != 0 )
514- {
515- ThrowError (line_number, " The attribute [ID] of tag <BehaviorTree> "
516- " must not use the name of a registered Node" );
517- }
518- if (children_count != 1 )
519- {
520- ThrowError (line_number, " The tag <BehaviorTree> with ID '" + ID +
521- " ' must have exactly 1 "
522- " child" );
468+ ThrowError (line_number, " The attribute [ID] of tag <SubTree> must not use the "
469+ " name of a registered Node" );
523470 }
524471 }
525472 else
526473 {
527- // search in the factory and the list of subtrees
528- const auto search = registered_nodes.find (name);
474+ // use ID for builtin node types, otherwise use the element name
475+ const auto search = registered_nodes.find (is_builtin ? ID : name);
529476 bool found = (search != registered_nodes.end ());
530477 if (!found)
531478 {
532479 ThrowError (line_number, std::string (" Node not recognized: " ) + name);
533480 }
534-
535- if (search->second == NodeType::DECORATOR)
536- {
537- if (children_count != 1 )
538- {
539- ThrowError (line_number, std::string (" The node <" ) + name + " > with ID '" + ID +
540- " ' must have exactly 1 child" );
541- }
542- }
543- else if (search->second == NodeType::CONTROL)
481+ else
544482 {
545- if (children_count == 0 )
483+ const auto node_type = search->second ;
484+ const std::string& registered_name = search->first ;
485+
486+ if (node_type == NodeType::DECORATOR)
546487 {
547- ThrowError (line_number, std::string (" The node <" ) + name + " > with ID '" + ID +
548- " ' must have 1 or more children" );
488+ if (children_count != 1 )
489+ {
490+ ThrowError (line_number, std::string (" The node '" ) + registered_name +
491+ " ' must have exactly 1 child" );
492+ }
549493 }
550- if (name == " ReactiveSequence " )
494+ else if (node_type == NodeType::CONTROL )
551495 {
552- size_t async_count = 0 ;
553- for (auto child = node->FirstChildElement (); child != nullptr ;
554- child = child->NextSiblingElement ())
496+ if (children_count == 0 )
555497 {
556- const std::string child_name = child->Name ();
557- const auto child_search = registered_nodes.find (child_name);
558- if (child_search == registered_nodes.end ())
559- {
560- ThrowError (child->GetLineNum (),
561- std::string (" Unknown node type: " ) + child_name);
562- }
563- const auto child_type = child_search->second ;
564- if (child_type == NodeType::CONTROL &&
565- ((child_name == " ThreadedAction" ) ||
566- (child_name == " StatefulActionNode" ) ||
567- (child_name == " CoroActionNode" ) || (child_name == " AsyncSequence" )))
498+ ThrowError (line_number, std::string (" The node '" ) + registered_name +
499+ " ' must have 1 or more children" );
500+ }
501+ if (registered_name == " ReactiveSequence" )
502+ {
503+ size_t async_count = 0 ;
504+ for (auto child = node->FirstChildElement (); child != nullptr ;
505+ child = child->NextSiblingElement ())
568506 {
569- ++async_count;
570- if (async_count > 1 )
507+ const std::string child_name = child->Name ();
508+ const auto child_search = registered_nodes.find (child_name);
509+ if (child_search == registered_nodes.end ())
571510 {
572- ThrowError (line_number, std::string (" A ReactiveSequence with ID '" + ID +
573- " ' cannot have more "
574- " than one async child." ));
511+ ThrowError (child->GetLineNum (),
512+ std::string (" Unknown node type: " ) + child_name);
513+ }
514+ const auto child_type = child_search->second ;
515+ if (child_type == NodeType::CONTROL &&
516+ ((child_name == " ThreadedAction" ) ||
517+ (child_name == " StatefulActionNode" ) ||
518+ (child_name == " CoroActionNode" ) || (child_name == " AsyncSequence" )))
519+ {
520+ ++async_count;
521+ if (async_count > 1 )
522+ {
523+ ThrowError (line_number, std::string (" A ReactiveSequence cannot have "
524+ " more than one async child." ));
525+ }
575526 }
576527 }
577528 }
578529 }
530+ else if (node_type == NodeType::ACTION)
531+ {
532+ if (children_count != 0 )
533+ {
534+ ThrowError (line_number, std::string (" The node '" ) + registered_name +
535+ " ' must not have any child" );
536+ }
537+ }
538+ else if (node_type == NodeType::CONDITION)
539+ {
540+ if (children_count != 0 )
541+ {
542+ ThrowError (line_number, std::string (" The node '" ) + registered_name +
543+ " ' must not have any child" );
544+ }
545+ }
579546 }
580547 }
581548 // recursion
0 commit comments