/** 解析<code>TypeFilter</code>s。 */
  private void parseTypeFilters(
      Element element,
      ClassLoader classLoader,
      List<TypeFilter> includes,
      List<TypeFilter> excludes) {
    ElementSelector includeSelector = and(sameNs(element), name("include-filter"));
    ElementSelector excludeSelector = and(sameNs(element), name("exclude-filter"));

    for (Element subElement : subElements(element)) {
      if (includeSelector.accept(subElement)) {
        TypeFilter filter = createTypeFilter(subElement, classLoader);

        if (filter != null) {
          includes.add(filter);
        }
      } else if (excludeSelector.accept(subElement)) {
        TypeFilter filter = createTypeFilter(subElement, classLoader);

        if (filter != null) {
          excludes.add(filter);
        }
      }
    }
  }
  @Override
  protected void doParse(
      Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    Map<String, ParsingModuleInfo> classes =
        parseSpecificBeans(
            element, parserContext, builder.getRawBeanDefinition(), and(beansNs(), name("bean")));

    ElementSelector searchPackages = and(sameNs(element), name("search-packages"));
    ElementSelector searchClasses = and(sameNs(element), name("search-classes"));
    ModuleDefinitionScanner scanner = getScanner(parserContext);

    for (Element subElement : subElements(element)) {
      Pattern classNamePattern = null;
      String typeName = null;
      String moduleName = null;
      String classResourceName = null;

      if (searchPackages.accept(subElement)) {
        String packageName =
            assertNotNull(
                normalizeClassName(subElement.getAttribute("packages")),
                "no package name provided for search-packages");

        classNamePattern = compileClassName(packageName, MATCH_PREFIX);
        typeName =
            assertNotNull(trimToNull(subElement.getAttribute("type")), "no type name provided");
        classResourceName = classNameToPathName(packageName) + "/**/*.class";

        log.trace("Searching in packages: {}, moduleType={}", packageName, typeName);
      } else if (searchClasses.accept(subElement)) {
        String className =
            assertNotNull(
                normalizeClassName(subElement.getAttribute("classes")),
                "no class name provided for search-classes");

        classNamePattern = compileClassName(className, MATCH_PREFIX);
        typeName =
            assertNotNull(trimToNull(subElement.getAttribute("type")), "no type name provided");
        moduleName =
            assertNotNull(trimToNull(subElement.getAttribute("name")), "no module name provided");
        classResourceName = classNameToPathName(className);

        if (classResourceName.endsWith("**")) {
          classResourceName += "/*.class";
        } else {
          classResourceName += ".class";
        }

        log.trace(
            "Searching for classes: {}, moduleType={}, moduleName={}",
            new Object[] {className, typeName, moduleName});
      }

      boolean includeAbstractClasses =
          "true".equalsIgnoreCase(trimToNull(subElement.getAttribute("includeAbstractClasses")));

      if (classResourceName != null) {
        // 处理所有的include/exclude filters
        ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
        List<TypeFilter> includes = createLinkedList();
        List<TypeFilter> excludes = createLinkedList();

        parseTypeFilters(subElement, classLoader, includes, excludes);

        ModuleTypeFilter filter =
            new ModuleTypeFilter(classes, classNamePattern, typeName, moduleName, includes);

        // 事实上,只有一个顶级的include filter,其它的include filter被这一个moduleTypeFilter所调用。
        scanner.addIncludeFilter(filter);

        // Exclude filter比较简单,直接加到scanner中即可。
        for (TypeFilter exclude : excludes) {
          scanner.addExcludeFilter(exclude);
        }

        scanner.setBeanNameGenerator(filter);
        scanner.setResourcePattern(classResourceName.replace('?', '*'));
        scanner.setIncludeAbstractClasses(includeAbstractClasses);
        scanner.setBeanDefinitionDefaults(getBeanDefinitionDefaults(subElement, parserContext));

        int found = scanner.scan("");

        log.debug("Found {} module classes with pattern: {}", found, classResourceName);
      }
    }

    postProcessItems(element, parserContext, builder, classes, "search-packages or search-classes");
  }
  @Override
  protected void doParse(
      Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    Map<String, ParsingModuleInfo> scripts =
        parseSpecificBeans(element, parserContext, builder.getRawBeanDefinition(), ns(LANG_URI));

    ElementSelector searchFolders = and(sameNs(element), name("search-folders"));
    ElementSelector searchFiles = and(sameNs(element), name("search-files"));

    for (Element subElement : subElements(element)) {
      String prefix = null;
      String typeName = null;
      String moduleName = null;
      Pattern scriptNamePattern = null;
      String scriptResourceName = null;
      String language = null;

      if (searchFolders.accept(subElement)) {
        String folderName =
            assertNotNull(
                normalizePathName(subElement.getAttribute("folders")),
                "no folder name provided for search-folders");

        // 取prefix
        prefix = getPrefix(folderName);

        if (prefix != null) {
          folderName = folderName.substring(prefix.length() + 1);
        }

        // folderName不以/开始
        if (folderName.startsWith("/")) {
          folderName = folderName.substring(1);
        }

        scriptNamePattern = compilePathName(folderName);
        typeName =
            assertNotNull(trimToNull(subElement.getAttribute("type")), "no type name provided");
        language = trimToNull(subElement.getAttribute("language"));
        scriptResourceName = folderName + "/**/*.*";

        log.trace(
            "Searching in folders: {}, moduleType={}, language={}",
            new Object[] {folderName, typeName, language == null ? "auto" : language});
      } else if (searchFiles.accept(subElement)) {
        String fileName =
            assertNotNull(
                normalizePathName(subElement.getAttribute("files")),
                "no script file name provided for search-files");

        // fileName不以/结尾
        assertTrue(!fileName.endsWith("/"), "invalid script file name: %s", fileName);

        // 取prefix
        prefix = getPrefix(fileName);

        if (prefix != null) {
          fileName = fileName.substring(prefix.length() + 1);
        }

        // fileName不以/开始
        if (fileName.startsWith("/")) {
          fileName = fileName.substring(1);
        }

        scriptNamePattern = compilePathName(fileName);
        typeName =
            assertNotNull(trimToNull(subElement.getAttribute("type")), "no type name provided");
        moduleName =
            assertNotNull(trimToNull(subElement.getAttribute("name")), "no module name provided");
        language = trimToNull(subElement.getAttribute("language"));
        scriptResourceName = fileName;

        log.trace(
            "Searching for script files: {}, moduleType={}, moduleName={}, language={}",
            new Object[] {fileName, typeName, moduleName, language == null ? "auto" : language});
      }

      if (scriptResourceName != null) {
        scriptResourceName =
            prefix == null ? scriptResourceName : prefix + ":" + scriptResourceName;

        // 扫描scripts
        ResourcePatternResolver resolver =
            new PathMatchingResourcePatternResolver(
                parserContext.getReaderContext().getResourceLoader());
        int found = 0;

        try {
          Resource[] resources = resolver.getResources(scriptResourceName.replace('?', '*'));
          BeanDefinitionDefaults defaults = getBeanDefinitionDefaults(subElement, parserContext);
          ParsingModuleMatcher matcher =
              new ParsingModuleMatcher(scripts, scriptNamePattern, typeName, moduleName) {
                @Override
                protected String getName(String name, String itemName) {
                  String ext = getExt(itemName);

                  if (ext != null && name.endsWith("." + ext)) {
                    return name.substring(0, name.length() - ext.length() - 1);
                  }

                  return name;
                }
              };

          for (Resource resource : resources) {
            if (resource.isReadable()) {
              URI uri = resource.getURI();

              if (uri == null) {
                continue;
              }

              String resourceName = uri.normalize().toString();

              if (matcher.doMatch(resourceName)) {
                BeanDefinition scriptBean =
                    createScriptBean(subElement, parserContext, resourceName, language, defaults);
                String beanName =
                    matcher.generateBeanName(resourceName, parserContext.getRegistry());

                parserContext.getRegistry().registerBeanDefinition(beanName, scriptBean);
                found++;
              }
            }
          }
        } catch (IOException e) {
          parserContext
              .getReaderContext()
              .error("Failed to scan resources: " + scriptResourceName, subElement, e);
          return;
        }

        log.debug("Found {} module scripts with pattern: {}", found, scriptResourceName);
      }
    }

    postProcessItems(element, parserContext, builder, scripts, "search-folders or search-files");
  }