class ManifestPluginDescriptorFinder implements PluginDescriptorFinder {

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

    public static final String PLUGIN_ID = "Plugin-Id";
    public static final String PLUGIN_DESCRIPTION = "Plugin-Description";
    public static final String PLUGIN_CLASS = "Plugin-Class";
    public static final String PLUGIN_VERSION = "Plugin-Version";
    public static final String PLUGIN_PROVIDER = "Plugin-Provider";
    public static final String PLUGIN_DEPENDENCIES = "Plugin-Dependencies";
    public static final String PLUGIN_REQUIRES = "Plugin-Requires";
    public static final String PLUGIN_LICENSE = "Plugin-License";

    @Override
    public boolean isApplicable(Path pluginPath) {
        return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isZipOrJarFile(pluginPath));
    }

    @Override
    public PluginDescriptor find(Path pluginPath) {
        Manifest manifest = readManifest(pluginPath);

        return createPluginDescriptor(manifest);
    }

    protected Manifest readManifest(Path pluginPath) {<FILL_FUNCTION_BODY>}

    protected PluginDescriptor createPluginDescriptor(Manifest manifest) {
        DefaultPluginDescriptor pluginDescriptor = createPluginDescriptorInstance();

        // TODO validate !!!
        Attributes attributes = manifest.getMainAttributes();
        String id = attributes.getValue(PLUGIN_ID);
        pluginDescriptor.setPluginId(id);

        String description = attributes.getValue(PLUGIN_DESCRIPTION);
        if (StringUtils.isNullOrEmpty(description)) {
            pluginDescriptor.setPluginDescription("");
        } else {
            pluginDescriptor.setPluginDescription(description);
        }

        String clazz = attributes.getValue(PLUGIN_CLASS);
        if (StringUtils.isNotNullOrEmpty(clazz)) {
            pluginDescriptor.setPluginClass(clazz);
        }

        String version = attributes.getValue(PLUGIN_VERSION);
        if (StringUtils.isNotNullOrEmpty(version)) {
            pluginDescriptor.setPluginVersion(version);
        }

        String provider = attributes.getValue(PLUGIN_PROVIDER);
        pluginDescriptor.setProvider(provider);
        String dependencies = attributes.getValue(PLUGIN_DEPENDENCIES);
        pluginDescriptor.setDependencies(dependencies);

        String requires = attributes.getValue(PLUGIN_REQUIRES);
        if (StringUtils.isNotNullOrEmpty(requires)) {
            pluginDescriptor.setRequires(requires);
        }

        pluginDescriptor.setLicense(attributes.getValue(PLUGIN_LICENSE));

        return pluginDescriptor;
    }

    protected DefaultPluginDescriptor createPluginDescriptorInstance() {
        return new DefaultPluginDescriptor();
    }

    protected Manifest readManifestFromJar(Path jarPath) {
        try (JarFile jar = new JarFile(jarPath.toFile())) {
            return jar.getManifest();
        } catch (IOException e) {
            throw new PluginRuntimeException(e, "Cannot read manifest from {}", jarPath);
        }
    }

    protected Manifest readManifestFromZip(Path zipPath) {
        try (ZipFile zip = new ZipFile(zipPath.toFile())) {
            ZipEntry manifestEntry = zip.getEntry("classes/META-INF/MANIFEST.MF");
            try (InputStream manifestInput = zip.getInputStream(manifestEntry)) {
                return new Manifest(manifestInput);
            }
        } catch (IOException e) {
            throw new PluginRuntimeException(e, "Cannot read manifest from {}", zipPath);
        }
    }

    protected Manifest readManifestFromDirectory(Path pluginPath) {
        // legacy (the path is something like "classes/META-INF/MANIFEST.MF")
        Path manifestPath = FileUtils.findFile(pluginPath,"MANIFEST.MF");
        if (manifestPath == null) {
            throw new PluginRuntimeException("Cannot find the manifest path");
        }

        log.debug("Lookup plugin descriptor in '{}'", manifestPath);
        if (Files.notExists(manifestPath)) {
            throw new PluginRuntimeException("Cannot find '{}' path", manifestPath);
        }

        try (InputStream input = Files.newInputStream(manifestPath)) {
            return new Manifest(input);
        } catch (IOException e) {
            throw new PluginRuntimeException(e, "Cannot read manifest from {}", pluginPath);
        }
    }

}

        if (FileUtils.isJarFile(pluginPath)) {
            return readManifestFromJar(pluginPath);
        }

        if (FileUtils.isZipFile(pluginPath)) {
            return readManifestFromZip(pluginPath);
        }

        return readManifestFromDirectory(pluginPath);
    
<no_super_class>