src/main/java/io/github/classgraph/ClasspathElementZip.java
@Override
    void scanPaths(final LogNode log) {
        if (logicalZipFile == null) {
            skipClasspathElement = true;
        }
        if (skipClasspathElement) {
            return;
        }
        if (scanned.getAndSet(true)) {
            // Should not happen
            throw new IllegalArgumentException("Already scanned classpath element " + getZipFilePath());
        }

        final LogNode subLog = log == null ? null
                : log.log(getZipFilePath(), "Scanning jarfile classpath element " + getZipFilePath());

        Set<String> loggedNestedClasspathRootPrefixes = null;
        String prevParentRelativePath = null;
        ScanSpecPathMatch prevParentMatchStatus = null;
        for (final FastZipEntry zipEntry : logicalZipFile.entries) {
            String relativePath = zipEntry.entryNameUnversioned;

            // Check if the relative path is within a nested classpath root
            if (nestedClasspathRootPrefixes != null) {
                // This is O(mn), which is inefficient, but the number of nested classpath roots should be small
                boolean reachedNestedRoot = false;
                for (final String nestedClasspathRoot : nestedClasspathRootPrefixes) {
                    if (relativePath.startsWith(nestedClasspathRoot)) {
                        // relativePath has a prefix of nestedClasspathRoot
                        if (subLog != null) {
                            if (loggedNestedClasspathRootPrefixes == null) {
                                loggedNestedClasspathRootPrefixes = new HashSet<>();
                            }
                            if (loggedNestedClasspathRootPrefixes.add(nestedClasspathRoot)) {
                                subLog.log("Reached nested classpath root, stopping recursion to avoid duplicate "
                                        + "scanning: " + nestedClasspathRoot);
                            }
                        }
                        reachedNestedRoot = true;
                        break;
                    }
                }
                if (reachedNestedRoot) {
                    continue;
                }
            }

            // Ignore entries without the correct classpath root prefix
            if (!packageRootPrefix.isEmpty() && !relativePath.startsWith(packageRootPrefix)) {
                continue;
            }

            // Strip the package root prefix from the relative path
            // N.B. these semantics should mirror those in getResource()
            if (!packageRootPrefix.isEmpty()) {
                relativePath = relativePath.substring(packageRootPrefix.length());
            } else {
                // Strip any package root prefix from the relative path
                for (int i = 0; i < ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES.length; i++) {
                    if (relativePath.startsWith(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i])) {
                        relativePath = relativePath
                                .substring(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i].length());
                    }
                }
            }

            // Whitelist/blacklist classpath elements based on file resource paths
            checkResourcePathWhiteBlackList(relativePath, log);
            if (skipClasspathElement) {
                return;
            }

            // Get match status of the parent directory of this ZipEntry file'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 = //
                    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;
            }

            // Add the ZipEntry path as a Resource
            final Resource resource = newResource(zipEntry, relativePath);
            if (relativePathToResource.putIfAbsent(relativePath, resource) == null
                    // 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")))) {
                // Resource is whitelisted
                addWhitelistedResource(resource, parentMatchStatus, subLog);
            }
        }

        // Save the last modified time for the zipfile
        final File zipfile = getFile();
        fileToLastModified.put(zipfile, zipfile.lastModified());

        finishScanPaths(subLog);
    }
/**
     * Scan for path matches within jarfile, and record ZipEntry objects of matching files.
     *
     * @param log
     *            the log
     */
Scan for path matches within jarfile, and record ZipEntry objects of matching files.
@param log
the log
Scan for path matches within jarfile and record ZipEntry objects of matching files . @param log the log
@Override
    void scanPaths(final LogNode log) {
        if (logicalZipFile == null) {
            skipClasspathElement = true;
        }
        if (skipClasspathElement) {
            return;
        }
        if (scanned.getAndSet(true)) {
            throw new IllegalArgumentException("Already scanned classpath element " + getZipFilePath());
        }
        final LogNode subLog = log == null ? null
                : log.log(getZipFilePath(), "Scanning jarfile classpath element " + getZipFilePath());
        Set<String> loggedNestedClasspathRootPrefixes = null;
        String prevParentRelativePath = null;
        ScanSpecPathMatch prevParentMatchStatus = null;
        for (final FastZipEntry zipEntry : logicalZipFile.entries) {
            String relativePath = zipEntry.entryNameUnversioned;
            if (nestedClasspathRootPrefixes != null) {
                boolean reachedNestedRoot = false;
                for (final String nestedClasspathRoot : nestedClasspathRootPrefixes) {
                    if (relativePath.startsWith(nestedClasspathRoot)) {
                        if (subLog != null) {
                            if (loggedNestedClasspathRootPrefixes == null) {
                                loggedNestedClasspathRootPrefixes = new HashSet<>();
                            }
                            if (loggedNestedClasspathRootPrefixes.add(nestedClasspathRoot)) {
                                subLog.log("Reached nested classpath root, stopping recursion to avoid duplicate "
                                        + "scanning: " + nestedClasspathRoot);
                            }
                        }
                        reachedNestedRoot = true;
                        break;
                    }
                }
                if (reachedNestedRoot) {
                    continue;
                }
            }
            if (!packageRootPrefix.isEmpty() && !relativePath.startsWith(packageRootPrefix)) {
                continue;
            }
            if (!packageRootPrefix.isEmpty()) {
                relativePath = relativePath.substring(packageRootPrefix.length());
            } else {
                for (int i = 0; i < ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES.length; i++) {
                    if (relativePath.startsWith(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i])) {
                        relativePath = relativePath
                                .substring(ClassLoaderHandlerRegistry.AUTOMATIC_PACKAGE_ROOT_PREFIXES[i].length());
                    }
                }
            }
            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 = 
                    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;
            }
             Add the ZipEntry path as a Resource
            final Resource resource = newResource(zipEntry, relativePath);
            if (relativePathToResource.putIfAbsent(relativePath, resource) == null
                     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")))) {
                 Resource is whitelisted
                addWhitelistedResource(resource, parentMatchStatus, subLog);
            }
        }
         Save the last modified time for the zipfile
        final File zipfile = getFile();
        fileToLastModified.put(zipfile, zipfile.lastModified());
        finishScanPaths(subLog);
    }
@ Override void scanPaths ( final LogNode log ) { if ( logicalZipFile == null ) { skipClasspathElement = true ; } if ( skipClasspathElement ) { return ; } if ( scanned . getAndSet ( true ) ) { throw new IllegalArgumentException ( "Already scanned classpath element " + getZipFilePath ( ) ) ; } final LogNode subLog = log == null ? null : log . log ( getZipFilePath ( ) , "Scanning jarfile classpath element " + getZipFilePath ( ) ) ; Set < String > loggedNestedClasspathRootPrefixes = null ; String prevParentRelativePath = null ; ScanSpecPathMatch prevParentMatchStatus = null ; for ( final FastZipEntry zipEntry : logicalZipFile . entries ) { String relativePath = zipEntry . entryNameUnversioned ; if ( nestedClasspathRootPrefixes != null ) { boolean reachedNestedRoot = false ; for ( final String nestedClasspathRoot : nestedClasspathRootPrefixes ) { if ( relativePath . startsWith ( nestedClasspathRoot ) ) { if ( subLog != null ) { if ( loggedNestedClasspathRootPrefixes == null ) { loggedNestedClasspathRootPrefixes = new HashSet < > ( ) ; } if ( loggedNestedClasspathRootPrefixes . add ( nestedClasspathRoot ) ) { subLog . log ( "Reached nested classpath root, stopping recursion to avoid duplicate " + "scanning: " + nestedClasspathRoot ) ; } } reachedNestedRoot = true ; break ; } } if ( reachedNestedRoot ) { continue ; } } if ( ! packageRootPrefix . isEmpty ( ) && ! relativePath . startsWith ( packageRootPrefix ) ) { continue ; } if ( ! packageRootPrefix . isEmpty ( ) ) { relativePath = relativePath . substring ( packageRootPrefix . length ( ) ) ; } else { for ( int i = 0 ; i < ClassLoaderHandlerRegistry . AUTOMATIC_PACKAGE_ROOT_PREFIXES . length ; i ++ ) { if ( relativePath . startsWith ( ClassLoaderHandlerRegistry . AUTOMATIC_PACKAGE_ROOT_PREFIXES [ i ] ) ) { relativePath = relativePath . substring ( ClassLoaderHandlerRegistry . AUTOMATIC_PACKAGE_ROOT_PREFIXES [ i ] . length ( ) ) ; } } } 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 = 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 ; } final Resource resource = newResource ( zipEntry , relativePath ) ; if ( relativePathToResource . putIfAbsent ( relativePath , resource ) == null && ( 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" ) ) ) ) { addWhitelistedResource ( resource , parentMatchStatus , subLog ) ; } } final File zipfile = getFile ( ) ; fileToLastModified . put ( zipfile , zipfile . lastModified ( ) ) ; finishScanPaths ( subLog ) ; }
Scan for path matches within jarfile, and record ZipEntry objects of matching files.
Scan for path matches within jarfile and record ZipEntry objects of matching files .
// Should not happen // Check if the relative path is within a nested classpath root // This is O(mn), which is inefficient, but the number of nested classpath roots should be small // relativePath has a prefix of nestedClasspathRoot // Ignore entries without the correct classpath root prefix // Strip the package root prefix from the relative path // N.B. these semantics should mirror those in getResource() // Strip any package root prefix from the relative path // Whitelist/blacklist classpath elements based on file resource paths // Get match status of the parent directory of this ZipEntry file's relative path (or reuse the last // match status for speed, if the directory name hasn't changed). // // The parent dir or one of its ancestral dirs is blacklisted // Add the ZipEntry path as a Resource // If resource is whitelisted // Resource is whitelisted // Save the last modified time for the zipfile