All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.cucumber.core.resource.ClasspathScanner Maven / Gradle / Ivy

There is a newer version: 7.20.1
Show newest version
package io.cucumber.core.resource;

import io.cucumber.core.logging.Logger;
import io.cucumber.core.logging.LoggerFactory;

import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import static io.cucumber.core.resource.ClasspathSupport.classPathScanningExplanation;
import static io.cucumber.core.resource.ClasspathSupport.determineFullyQualifiedClassName;
import static io.cucumber.core.resource.ClasspathSupport.getUrisForPackage;
import static io.cucumber.core.resource.ClasspathSupport.requireValidPackageName;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;

public final class ClasspathScanner {

    private static final Logger log = LoggerFactory.getLogger(ClasspathScanner.class);

    private static final String CLASS_FILE_SUFFIX = ".class";
    private static final String PACKAGE_INFO_FILE_NAME = "package-info" + CLASS_FILE_SUFFIX;
    private static final String MODULE_INFO_FILE_NAME = "module-info" + CLASS_FILE_SUFFIX;
    private static final Predicate> NULL_FILTER = aClass -> true;

    private final PathScanner pathScanner = new PathScanner();

    private final Supplier classLoaderSupplier;

    public ClasspathScanner(Supplier classLoaderSupplier) {
        this.classLoaderSupplier = classLoaderSupplier;
    }

    public  List> scanForSubClassesInPackage(String packageName, Class parentClass) {
        return scanForClassesInPackage(packageName, isSubClassOf(parentClass))
                .stream()
                .map(aClass -> (Class) aClass.asSubclass(parentClass))
                .collect(toList());
    }

    private List> scanForClassesInPackage(String packageName, Predicate> classFilter) {
        requireValidPackageName(packageName);
        requireNonNull(classFilter, "classFilter must not be null");
        List rootUris = getUrisForPackage(getClassLoader(), packageName);
        return findClassesForUris(rootUris, packageName, classFilter);
    }

    private static  Predicate> isSubClassOf(Class parentClass) {
        return aClass -> !parentClass.equals(aClass) && parentClass.isAssignableFrom(aClass);
    }

    private ClassLoader getClassLoader() {
        return this.classLoaderSupplier.get();
    }

    private List> findClassesForUris(List baseUris, String packageName, Predicate> classFilter) {
        return baseUris.stream()
                .map(baseUri -> findClassesForUri(baseUri, packageName, classFilter))
                .flatMap(Collection::stream)
                .distinct()
                .collect(toList());
    }

    private List> findClassesForUri(URI baseUri, String packageName, Predicate> classFilter) {
        List> classes = new ArrayList<>();
        pathScanner.findResourcesForUri(
            baseUri,
            path -> isNotModuleInfo(path) && isNotPackageInfo(path) && isClassFile(path),
            processClassFiles(packageName, classFilter, classes::add));
        return classes;
    }

    private static boolean isNotModuleInfo(Path path) {
        return !path.endsWith(MODULE_INFO_FILE_NAME);
    }

    private static boolean isNotPackageInfo(Path path) {
        return !path.endsWith(PACKAGE_INFO_FILE_NAME);
    }

    private static boolean isClassFile(Path file) {
        return file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX);
    }

    private Function> processClassFiles(
            String basePackageName,
            Predicate> classFilter,
            Consumer> classConsumer
    ) {
        return baseDir -> classFile -> {
            String fqn = determineFullyQualifiedClassName(baseDir, basePackageName, classFile);
            safelyLoadClass(fqn)
                    .filter(classFilter)
                    .ifPresent(classConsumer);
        };
    }

    private Optional> safelyLoadClass(String fqn) {
        try {
            return Optional.ofNullable(getClassLoader().loadClass(fqn));
        } catch (ClassNotFoundException | NoClassDefFoundError e) {
            log.warn(e, () -> "Failed to load class '" + fqn + "'.\n" + classPathScanningExplanation());
        }
        return Optional.empty();
    }

    public List> scanForClassesInPackage(String packageName) {
        return scanForClassesInPackage(packageName, NULL_FILTER);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy