@@ -429,153 +429,119 @@ void VerifyXML(const std::string& xml_text,
429429    const  std::string ID = node->Attribute (" ID" 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" " Action" " Condition" 
435+          name == " Control" " 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 '" 
442-                                     " ' must have exactly 1 " 
443-                                     " child" 
444-       }
438+       ThrowError (line_number, std::string (" The tag <" 
439+                                   " > 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 '" 
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 '" 
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 the name of a registered Node" 
480451      }
481-       if (children_count ==  0 )
452+       if (children_count !=  1 )
482453      {
483-         ThrowError (line_number, " The tag <Control> with ID '" 
484-                                     " ' must have at least 1 " 
485-                                     " child" 
454+         ThrowError (line_number, " The tag <BehaviorTree> with ID '" " ' must have exactly 1 child" 
486455      }
487456    }
488457    else  if (name == " SubTree" 
489458    {
490-       if (ID.empty ())
491-       {
492-         ThrowError (line_number, " The tag <SubTree> must have the " 
493-                                 " attribute [ID]" 
494-       }
495459      if (children_count != 0 )
496460      {
497-         ThrowError (line_number,
498-                    " <SubTree> with ID '" " ' should not have any child" 
499-       }
500-       if (registered_nodes.count (ID) != 0 )
501-       {
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]" 
461+         ThrowError (line_number, " <SubTree> with ID '" " ' should not have any child" 
512462      }
513463      if (registered_nodes.count (ID) != 0 )
514464      {
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 '" 
521-                                     " ' must have exactly 1 " 
522-                                     " child" 
465+         ThrowError (line_number, " The attribute [ID] of tag <SubTree> must not use the name of a registered Node" 
523466      }
467+       //  no further validation for SubTree
524468    }
525469    else 
526470    {
527-       //  search in the factory and the list of subtrees
528-       const  auto  search = registered_nodes.find (name);
529-       bool  found = (search != registered_nodes.end ());
530-       if (!found)
531-       {
532-         ThrowError (line_number, std::string (" Node not recognized: " 
533-       }
534- 
535-       if (search->second  == NodeType::DECORATOR)
471+       //  Unified lookup: use ID for builtin wrapper tags, otherwise use the element name
472+       const  std::string lookup_key = (is_builtin ? ID : name);
473+       const  auto  search = registered_nodes.find (lookup_key);
474+       if (search == registered_nodes.end ())
536475      {
537-         if (children_count !=  1 )
476+         if (is_builtin )
538477        {
539-           ThrowError (line_number, std::string (" The node <" " > with ID '" 
540-                                       " ' must have exactly 1 child" 
478+           ThrowError (line_number, std::string (" ID '" " ' is not a registered node" 
479+         }
480+         else 
481+         {
482+           ThrowError (line_number, std::string (" Node not recognized: " 
541483        }
542484      }
543-       else   if (search-> second  == NodeType::CONTROL) 
485+       else 
544486      {
545-         if (children_count == 0 )
487+         const  auto  node_type = search->second ;
488+         const  std::string& registered_name = search->first ;
489+ 
490+         if (node_type == NodeType::DECORATOR)
546491        {
547-           ThrowError (line_number, std::string (" The node <" " > with ID '" 
548-                                       " ' must have 1 or more children" 
492+           if (children_count != 1 )
493+           {
494+             ThrowError (line_number, std::string (" The node '" " ' must have exactly 1 child" 
495+           }
549496        }
550-         if (name  == " ReactiveSequence " 
497+         else   if (node_type  == NodeType::CONTROL )
551498        {
552-           size_t  async_count = 0 ;
553-           for (auto  child = node->FirstChildElement (); child != nullptr ;
554-               child = child->NextSiblingElement ())
499+           if (children_count == 0 )
555500          {
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: " 
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" " AsyncSequence" 
501+             ThrowError (line_number, std::string (" The node '" " ' must have 1 or more children" 
502+           }
503+           if (registered_name == " ReactiveSequence" 
504+           {
505+             size_t  async_count = 0 ;
506+             for (auto  child = node->FirstChildElement (); child != nullptr ;
507+                 child = child->NextSiblingElement ())
568508            {
569-               ++async_count;
570-               if (async_count > 1 )
509+               const  std::string child_name = child->Name ();
510+               const  auto  child_search = registered_nodes.find (child_name);
511+               if (child_search == registered_nodes.end ())
571512              {
572-                 ThrowError (line_number, std::string (" A ReactiveSequence with ID '" 
573-                                                     " ' cannot have more " 
574-                                                     " than one async child." 
513+                 ThrowError (child->GetLineNum (), std::string (" Unknown node type: " 
514+               }
515+               const  auto  child_type = child_search->second ;
516+               if (child_type == NodeType::CONTROL &&
517+                  ((child_name == " ThreadedAction" 
518+                   (child_name == " StatefulActionNode" 
519+                   (child_name == " CoroActionNode" " AsyncSequence" 
520+               {
521+                 ++async_count;
522+                 if (async_count > 1 )
523+                 {
524+                   ThrowError (line_number, std::string (" A ReactiveSequence with ID '" 
525+                                               " ' cannot have more than one async child." 
526+                 }
575527              }
576528            }
577529          }
578530        }
531+         else  if (node_type == NodeType::ACTION)
532+         {
533+           if (children_count != 0 )
534+           {
535+             ThrowError (line_number, std::string (" The node '" " ' 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 '" " ' must not have any child" 
543+           }
544+         }
579545      }
580546    }
581547    // recursion
0 commit comments