@Override
  public void registerExtensions(@NotNull final AntDomElement antDomElement, @NotNull DomExtensionsRegistrar registrar) {
    final XmlElement xmlElement = antDomElement.getXmlElement();
    if (xmlElement instanceof XmlTag xmlTag) {
      final String tagName = xmlTag.getName();

      final AntDomProject antProject = antDomElement.getAntProject();
      if (antProject == null) {
        return;
      }
      final ReflectedProject reflected = ReflectedProject.getProject(antProject.getClassLoader());
      if (reflected.getProject() == null) {
        return;
      }

      final DomGenericInfo genericInfo = antDomElement.getGenericInfo();
      AntIntrospector classBasedIntrospector = null;
      final Map<String, Class<?>> coreTaskDefs = reflected.getTaskDefinitions();
      final Map<String, Class<?>> coreTypeDefs = reflected.getDataTypeDefinitions();
      final boolean isCustom = antDomElement instanceof AntDomCustomElement;
      if ("project".equals(tagName)) {
        classBasedIntrospector = getIntrospector(reflected.getProject().getClass());
      }
      else if ("target".equals(tagName)) {
        classBasedIntrospector = getIntrospector(reflected.getTargetClass());
      }
      else {
        if (isCustom) {
          final AntDomCustomElement custom = (AntDomCustomElement)antDomElement;
          final Class definitionClass = custom.getDefinitionClass();
          if (definitionClass != null) {
            classBasedIntrospector = getIntrospector(definitionClass);
          }
        }
        else {
          Class elemType = antDomElement.getChildDescription().getUserData(ELEMENT_IMPL_CLASS_KEY);

          if (elemType == null) {
            if (coreTaskDefs != null) {
              elemType = coreTaskDefs.get(tagName);
            }
          }

          if (elemType == null) {
            if (coreTypeDefs != null) {
              elemType = coreTypeDefs.get(tagName);
            }
          }

          if (elemType != null) {
            classBasedIntrospector = getIntrospector(elemType);
          }
        }
      }

      AbstractIntrospector parentIntrospector = null;
      if (classBasedIntrospector != null) {
        parentIntrospector =
          new ClassIntrospectorAdapter(classBasedIntrospector, coreTaskDefs, coreTypeDefs, reflected.getRestrictedDefinitions());
      }
      else {
        if (isCustom) {
          final AntDomNamedElement declaringElement = ((AntDomCustomElement)antDomElement).getDeclaringElement();
          if (declaringElement instanceof AntDomMacroDef) {
            parentIntrospector = new MacrodefIntrospectorAdapter((AntDomMacroDef)declaringElement);
          }
          else if (declaringElement instanceof AntDomMacrodefElement) {
            parentIntrospector = new MacrodefElementOccurrenceIntrospectorAdapter(
              (AntDomMacrodefElement)declaringElement)/*ContainerElementIntrospector.INSTANCE*/;
          }
          else if (declaringElement instanceof AntDomScriptDef) {
            parentIntrospector = new ScriptdefIntrospectorAdapter((AntDomScriptDef)declaringElement);
          }
        }
      }

      if (parentIntrospector != null) {

        defineAttributes(xmlTag, registrar, genericInfo, parentIntrospector);

        if ("project".equals(tagName) || parentIntrospector.isContainer()) { // can contain any task or/and type definition
          if (coreTaskDefs != null) {
            for (Map.Entry<String, Class<?>> entry : coreTaskDefs.entrySet()) {
              final DomExtension extension = registerChild(registrar, genericInfo, entry.getKey());
              if (extension != null) {
                final Class type = entry.getValue();
                if (type != null) {
                  extension.putUserData(ELEMENT_IMPL_CLASS_KEY, type);
                }
                extension.putUserData(AntDomElement.ROLE, AntDomElement.Role.TASK);
              }
            }
          }
          if (coreTypeDefs != null) {
            for (Map.Entry<String, Class<?>> entry : coreTypeDefs.entrySet()) {
              final DomExtension extension = registerChild(registrar, genericInfo, entry.getKey());
              if (extension != null) {
                final Class type = entry.getValue();
                if (type != null) {
                  extension.putUserData(ELEMENT_IMPL_CLASS_KEY, type);
                }
                extension.putUserData(AntDomElement.ROLE, AntDomElement.Role.DATA_TYPE);
              }
            }
          }
        }
        else {
          final Iterator<String> nested = parentIntrospector.getNestedElementsIterator();
          while (nested.hasNext()) {
            final String nestedElementName = nested.next();
            final DomExtension extension = registerChild(registrar, genericInfo, nestedElementName);
            if (extension != null) {
              Class type = parentIntrospector.getNestedElementType(nestedElementName);
              if (type != null && CommonClassNames.JAVA_LANG_OBJECT.equals(type.getName())) {
                type = null; // hack to support badly written tasks
              }
              if (type == null) {
                if (coreTypeDefs != null) {
                  type = coreTypeDefs.get(nestedElementName);
                }
              }
              if (type != null) {
                extension.putUserData(ELEMENT_IMPL_CLASS_KEY, type);
              }
              AntDomElement.Role role = AntDomElement.Role.DATA_TYPE;
              if (coreTaskDefs != null && coreTaskDefs.containsKey(nestedElementName) ||
                  type != null && isAssignableFrom("org.apache.tools.ant.Task", type)) {
                role = AntDomElement.Role.TASK;
              }
              extension.putUserData(AntDomElement.ROLE, role);
            }
          }
        }
        registrar.registerCustomChildrenExtension(AntDomCustomElement.class, new AntCustomTagNameDescriptor());
      }
    }
  }
@Override
  public void registerExtensions(@NotNull final AntDomElement antDomElement, @NotNull DomExtensionsRegistrar registrar)