// Path: stecker-plugin-loader/src/main/java/org/meridor/stecker/PluginException.java
// public class PluginException extends Exception {
// 
//     private Optional<DependencyProblem> dependencyProblem = Optional.empty();
// 
//     private Optional<PluginMetadata> pluginMetadata = Optional.empty();
// 
//     public PluginException(Exception e) {
//         super(e);
//     }
// 
//     public PluginException(String message) {
//         super(message);
//     }
// 
//     public PluginException(String message, Exception e) {
//         super(message, e);
//     }
// 
//     public PluginException withDependencyProblem(DependencyProblem dependencyProblem) {
//         this.dependencyProblem = Optional.ofNullable(dependencyProblem);
//         return this;
//     }
// 
//     public PluginException withPlugin(PluginMetadata pluginMetadata) {
//         this.pluginMetadata = Optional.ofNullable(pluginMetadata);
//         return this;
//     }
// 
//     /**
//      * Returns dependency problem if any
//      *
//      * @return dependency problem
//      */
//     public Optional<DependencyProblem> getDependencyProblem() {
//         return dependencyProblem;
//     }
// 
//     /**
//      * Returns plugin affected
//      *
//      * @return plugin metadata
//      */
//     public Optional<PluginMetadata> getPluginMetadata() {
//         return pluginMetadata;
//     }
// 
// }
// 
// Path: stecker-plugin-loader/src/main/java/org/meridor/stecker/interfaces/PluginImplementationsAware.java
// public interface PluginImplementationsAware {
// 
//     /**
//      * Returns a list of present extension points
//      *
//      * @return a list of extension points
//      */
//     List<Class> getExtensionPoints();
// 
//     /**
//      * Returns classes implementing extension point
//      *
//      * @param extensionPoint extension point class
//      * @return a list of implementation classes
//      */
//     List<Class> getImplementations(Class extensionPoint);
// 
// }


import org.meridor.stecker.PluginException;
import org.meridor.stecker.interfaces.PluginImplementationsAware;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
                }
            }
        }

        return new ClassesRegistry(matchingClasses);
    }

    private static String getClassName(Path pluginImplementationDirectory, Path classFile) {
        String className = pluginImplementationDirectory
                .relativize(classFile)
                .toString()
                .replace(JAR_DIRECTORY_SEPARATOR, PACKAGE_SEPARATOR)
                .replace(CLASS_FILE_EXTENSION, "");
        if (className.startsWith(PACKAGE_SEPARATOR) && className.length() > 1) {
            className = className.substring(1);
        }
        return className;
    }

    private static boolean isAnnotatedWith(Class<?> currentClass, Class<?> extensionPoint) {
        return
                extensionPoint.isAnnotation() &&
                        currentClass.isAnnotationPresent(extensionPoint.asSubclass(Annotation.class));
    }


    private static boolean isSubclassOf(Class<?> currentClass, Class<?> extensionPoint) {
        return extensionPoint.isAssignableFrom(currentClass);
    }

// Path: stecker-plugin-loader/src/main/java/org/meridor/stecker/PluginException.java
// public class PluginException extends Exception {
// 
//     private Optional<DependencyProblem> dependencyProblem = Optional.empty();
// 
//     private Optional<PluginMetadata> pluginMetadata = Optional.empty();
// 
//     public PluginException(Exception e) {
//         super(e);
//     }
// 
//     public PluginException(String message) {
//         super(message);
//     }
// 
//     public PluginException(String message, Exception e) {
//         super(message, e);
//     }
// 
//     public PluginException withDependencyProblem(DependencyProblem dependencyProblem) {
//         this.dependencyProblem = Optional.ofNullable(dependencyProblem);
//         return this;
//     }
// 
//     public PluginException withPlugin(PluginMetadata pluginMetadata) {
//         this.pluginMetadata = Optional.ofNullable(pluginMetadata);
//         return this;
//     }
// 
//     /**
//      * Returns dependency problem if any
//      *
//      * @return dependency problem
//      */
//     public Optional<DependencyProblem> getDependencyProblem() {
//         return dependencyProblem;
//     }
// 
//     /**
//      * Returns plugin affected
//      *
//      * @return plugin metadata
//      */
//     public Optional<PluginMetadata> getPluginMetadata() {
//         return pluginMetadata;
//     }
// 
// }
// 
// Path: stecker-plugin-loader/src/main/java/org/meridor/stecker/interfaces/PluginImplementationsAware.java
// public interface PluginImplementationsAware {
// 
//     /**
//      * Returns a list of present extension points
//      *
//      * @return a list of extension points
//      */
//     List<Class> getExtensionPoints();
// 
//     /**
//      * Returns classes implementing extension point
//      *
//      * @param extensionPoint extension point class
//      * @return a list of implementation classes
//      */
//     List<Class> getImplementations(Class extensionPoint);
// 
// }


// Path: stecker-plugin-loader/src/main/java/org/meridor/stecker/impl/PluginUtils.java
import org.meridor.stecker.PluginException;
import org.meridor.stecker.interfaces.PluginImplementationsAware;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

                }
            }
        }

        return new ClassesRegistry(matchingClasses);
    }

    private static String getClassName(Path pluginImplementationDirectory, Path classFile) {
        String className = pluginImplementationDirectory
                .relativize(classFile)
                .toString()
                .replace(JAR_DIRECTORY_SEPARATOR, PACKAGE_SEPARATOR)
                .replace(CLASS_FILE_EXTENSION, "");
        if (className.startsWith(PACKAGE_SEPARATOR) && className.length() > 1) {
            className = className.substring(1);
        }
        return className;
    }

    private static boolean isAnnotatedWith(Class<?> currentClass, Class<?> extensionPoint) {
        return
                extensionPoint.isAnnotation() &&
                        currentClass.isAnnotationPresent(extensionPoint.asSubclass(Annotation.class));
    }


    private static boolean isSubclassOf(Class<?> currentClass, Class<?> extensionPoint) {
        return extensionPoint.isAssignableFrom(currentClass);
    }

    public static ClassLoader getClassLoader(Path classesPath, Path dependenciesPath) throws PluginException {