// 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;
    public static FileTime getLastModificationTime(Path path) throws IOException {
        return Files.readAttributes(path, BasicFileAttributes.class).lastModifiedTime();
    }

    public static Path getPluginImplementationDirectory(Path unpackedPluginDirectory) {
        return unpackedPluginDirectory.resolve(PLUGIN_IMPLEMENTATION_UNPACK_DIRECTORY);
    }

    public static List<Path> getMatchingFiles(Path pluginImplementationDirectory, String[] patterns) throws IOException {
        List<Path> matchingFiles = new ArrayList<>();
        List<PathMatcher> pathMatchers = Arrays.stream(patterns)
                .map(FileSystems.getDefault()::getPathMatcher)
                .collect(Collectors.toList());

        Files.walkFileTree(pluginImplementationDirectory, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
                for (PathMatcher pathMatcher : pathMatchers) {
                    if (pathMatcher.matches(file)) {
                        matchingFiles.add(file);
                    }
                }
                return FileVisitResult.CONTINUE;
            }
        });

        return matchingFiles;
    }


// 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;

    public static FileTime getLastModificationTime(Path path) throws IOException {
        return Files.readAttributes(path, BasicFileAttributes.class).lastModifiedTime();
    }

    public static Path getPluginImplementationDirectory(Path unpackedPluginDirectory) {
        return unpackedPluginDirectory.resolve(PLUGIN_IMPLEMENTATION_UNPACK_DIRECTORY);
    }

    public static List<Path> getMatchingFiles(Path pluginImplementationDirectory, String[] patterns) throws IOException {
        List<Path> matchingFiles = new ArrayList<>();
        List<PathMatcher> pathMatchers = Arrays.stream(patterns)
                .map(FileSystems.getDefault()::getPathMatcher)
                .collect(Collectors.toList());

        Files.walkFileTree(pluginImplementationDirectory, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
                for (PathMatcher pathMatcher : pathMatchers) {
                    if (pathMatcher.matches(file)) {
                        matchingFiles.add(file);
                    }
                }
                return FileVisitResult.CONTINUE;
            }
        });

        return matchingFiles;
    }


    public static PluginImplementationsAware getMatchingClasses(List<Class> extensionPoints, Path pluginImplementationDirectory, ClassLoader classLoader) throws Exception {