@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);
    }
@ Override void scanPaths ( final LogNode log ) { if ( skipClasspathElement ) { return ; } if ( scanned . getAndSet ( true ) ) { 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 ( ) ) { 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 ) { if ( relativePath . endsWith ( "/" ) ) { continue ; } checkResourcePathWhiteBlackList ( relativePath , log ) ; if ( skipClasspathElement ) { return ; } 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 ) { if ( subLog != null ) { subLog . log ( "Skipping blacklisted path: " + relativePath ) ; } continue ; } if ( allResourcePaths . add ( relativePath ) && ( 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" ) ) ) ) { final Resource resource = newResource ( relativePath ) ; addWhitelistedResource ( resource , parentMatchStatus , subLog ) ; } } 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 ) ; }
Scan for package matches within module.

@param log
the log