docs: @Override
      void scanPaths(final LogNode log) {
          if (skipClasspathElement) {
              return;
          }
          if (scanned.getAndSet(true)) {
              // Should not happen
              throw new IllegalArgumentException("Already scanned classpath element " + toString());
          }
  
          final String moduleLocationStr = moduleRef.getLocationStr();
          final LogNode subLog = log == null ? null
                  : log.log(moduleLocationStr, "Scanning module " + moduleRef.getName());
  
          try (RecycleOnClose<ModuleReaderProxy, IOException> moduleReaderProxyRecycleOnClose //
                  = moduleReaderProxyRecycler.acquireRecycleOnClose()) {
              // Look for whitelisted files in the module.
              List<String> resourceRelativePaths;
              try {
                  resourceRelativePaths = moduleReaderProxyRecycleOnClose.get().list();
              } catch (final SecurityException e) {
                  if (subLog != null) {
                      subLog.log("Could not get resource list for module " + moduleRef.getName(), e);
                  }
                  return;
              }
              CollectionUtils.sortIfNotEmpty(resourceRelativePaths);
  
              String prevParentRelativePath = null;
              ScanSpecPathMatch prevParentMatchStatus = null;
              for (final String relativePath : resourceRelativePaths) {
                  // From ModuleReader#find(): "If the module reader can determine that the name locates a
                  // directory then the resulting URI will end with a slash ('/')."  But from the documentation
                  // for ModuleReader#list(): "Whether the stream of elements includes names corresponding to
                  // directories in the module is module reader specific."  We don't have a way of checking if
                  // a resource is a directory without trying to open it, unless ModuleReader#list() also decides
                  // to put a "/" on the end of resource paths corresponding to directories. Skip directories if
                  // they are found, but if they are not able to be skipped, we will have to settle for having
                  // some IOExceptions thrown when directories are mistaken for resource files.
                  if (relativePath.endsWith("/")) {
                      continue;
                  }
  
                  // Whitelist/blacklist classpath elements based on file resource paths
                  checkResourcePathWhiteBlackList(relativePath, log);
                  if (skipClasspathElement) {
                      return;
                  }
  
                  // Get match status of the parent directory of this resource's relative path (or reuse the last
                  // match status for speed, if the directory name hasn't changed).
                  final int lastSlashIdx = relativePath.lastIndexOf('/');
                  final String parentRelativePath = lastSlashIdx < 0 ? "/"
                          : relativePath.substring(0, lastSlashIdx + 1);
                  final boolean parentRelativePathChanged = !parentRelativePath.equals(prevParentRelativePath);
                  final ScanSpecPathMatch parentMatchStatus = //
                          prevParentRelativePath == null || parentRelativePathChanged
                                  ? scanSpec.dirWhitelistMatchStatus(parentRelativePath)
                                  : prevParentMatchStatus;
                  prevParentRelativePath = parentRelativePath;
                  prevParentMatchStatus = parentMatchStatus;
  
                  if (parentMatchStatus == ScanSpecPathMatch.HAS_BLACKLISTED_PATH_PREFIX) {
                      // The parent dir or one of its ancestral dirs is blacklisted
                      if (subLog != null) {
                          subLog.log("Skipping blacklisted path: " + relativePath);
                      }
                      continue;
                  }
  
                  // Found non-blacklisted relative path
                  if (allResourcePaths.add(relativePath)
                          // If resource is whitelisted
                          && (parentMatchStatus == ScanSpecPathMatch.HAS_WHITELISTED_PATH_PREFIX
                                  || parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_PATH
                                  || (parentMatchStatus == ScanSpecPathMatch.AT_WHITELISTED_CLASS_PACKAGE
                                          && scanSpec.classfileIsSpecificallyWhitelisted(relativePath))
                                  || (scanSpec.enableClassInfo && relativePath.equals("module-info.class")))) {
                      // Add whitelisted resource
                      final Resource resource = newResource(relativePath);
                      addWhitelistedResource(resource, parentMatchStatus, subLog);
                  }
              }
  
              // Save last modified time for the module file
              final File moduleFile = moduleRef.getLocationFile();
              if (moduleFile != null && moduleFile.exists()) {
                  fileToLastModified.put(moduleFile, moduleFile.lastModified());
              }
  
          } catch (final IOException e) {
              if (subLog != null) {
                  subLog.log("Exception opening module " + moduleRef.getName(), e);
              }
              skipClasspathElement = true;
          }
  
          finishScanPaths(subLog);
      }