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

org.jerkar.tool.PluginDictionnary Maven / Gradle / Ivy

There is a newer version: 0.7.0.RELEASE
Show newest version
package org.jerkar.tool;

import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jerkar.api.java.JkClassLoader;
import org.jerkar.api.utils.JkUtilsString;

/**
 * Contains the Plugin description for all concrete plugin classes extending a
 * given base class.
 * 

* Jerkar offers a very simple, yet powerful, plugin mechanism.
* Basically it offers to discover every classes in the classpath that inherit * from a given class and that respect a certain naming convention.
*

* The convention naming is as follow : The class simple name should be prefixed * by the simple name of the plugin base class.
* For example, a plugin class for * or.jerkar.java.build.JkBuildPlugin class must be named * 'my.package.JkJavaBuildPluginXxxxx.class' to be discovered :Xxxxx will be its * short name, while my.package.JkJavaBuildPluginXxxxx will be its full name. * * @param * The plugin base class. * @author Jerome Angibaud * * @see {@link JkPluginDescription} */ final class PluginDictionnary { private static final Map, Set>> CACHE = new HashMap, Set>>(); /** * Creates a {@link PluginDictionnary} for the specified extension points. * That means, this instance will refer to all plugin extending the * specified extension point in the parameter templateClass. */ public static PluginDictionnary of(Class templateClass) { final PluginDictionnary result = new PluginDictionnary(templateClass); if (CACHE.containsKey(templateClass)) { final Set> pluginClasses = CACHE.get(templateClass); result.plugins = toPluginSet(templateClass, pluginClasses); } return result; } private Set> plugins; private final Class templateClass; private PluginDictionnary(Class extendingClass) { super(); this.templateClass = extendingClass; } /** * Returns all the plugins present in classpath for this template class. */ public Set> getAll() { if (plugins == null) { synchronized (this) { final Set> result = loadAllPlugins(templateClass); this.plugins = Collections.unmodifiableSet(result); } } return this.plugins; } /** * Returns the plugin having a full name equals to the specified name. If * not found, returns the plugin having a short name equals to the specified * name. Note that the short name is capitalized for you so using * "myPluging" or "MyPlugin" is equal. If not found, returns * null. */ public JkPluginDescription loadByName(String name) { if (!name.contains(".")) { final JkPluginDescription result = loadPluginHavingShortName(templateClass, JkUtilsString.capitalize(name)); if (result != null) { return result; } } return loadPluginsHavingLongName(templateClass, name); } public JkPluginDescription loadByNameOrFail(String name) { final JkPluginDescription result = loadByName(name); if (result == null) { throw new IllegalArgumentException("No class found having name " + simpleClassName(templateClass, name) + " for plugin '" + name + "'."); } return result; } private static String simpleClassName(Class templateClass, String pluginName) { return templateClass.getSimpleName() + JkUtilsString.capitalize(pluginName); } @Override public String toString() { if (this.plugins == null) { return "Not loaded (template class = " + this.templateClass + ")"; } return this.plugins.toString(); } private static Set> loadAllPlugins(Class templateClass) { final String nameSuffix = templateClass.getSimpleName(); return loadPlugins(templateClass, "**/" + nameSuffix + "*", "**/*$" + nameSuffix + "*"); } private static JkPluginDescription loadPluginHavingShortName(Class templateClass, String shortName) { final String simpleName = simpleClassName(templateClass, shortName); final Set> set = loadPlugins(templateClass, "**/" + simpleName); set.addAll(loadPlugins(templateClass, "**/*$" + simpleName)); if (set.size() > 1) { throw new JkException("Several plugin have the same short name : '" + shortName + "'. Please disambiguate with using plugin long name (full class name)." + " Following plugins have same shortName : " + set); } if (set.isEmpty()) { return null; } return set.iterator().next(); } private static JkPluginDescription loadPluginsHavingLongName(Class templateClass, String longName) { final Class pluginClass = JkClassLoader.current().loadIfExist(longName); if (pluginClass == null) { return null; } return new JkPluginDescription(templateClass, pluginClass); } private static Set> loadPlugins(Class templateClass, String... patterns) { final Set> matchingClasses = JkClassLoader.of(templateClass).loadClasses(patterns); final Set> result = new HashSet>(); for (final Class candidate : matchingClasses) { if (templateClass.isAssignableFrom(candidate) && !Modifier.isAbstract(candidate.getModifiers()) && !candidate.equals(templateClass)) { result.add(candidate); } } return toPluginSet(templateClass, result); } @SuppressWarnings("unchecked") private static Set> toPluginSet(Class extendingClass, Iterable> classes) { final Set> result = new HashSet>(); for (final Class clazz : classes) { result.add(new JkPluginDescription(extendingClass, (Class) clazz)); } return result; } /** * Give the description of a plugin class as its names, its purpose and its * base class. * * @author Jerome Angibaud * @param */ public static class JkPluginDescription { private static String shortName(Class extendingClass, Class clazz) { return JkUtilsString.substringAfterFirst(clazz.getSimpleName(), extendingClass.getSimpleName()); } private static String longName(Class extendingClass, Class clazz) { return clazz.getName(); } /** * Returns all JkPlugins instances declared as field in the * specified instance. It includes fields declared in the specified * instance class and the ones declared in its super classes. */ public static List> declaredAsField(JkBuild hostingInstance) { final List> result = new LinkedList>(); final List> templateClasses = hostingInstance.pluginTemplateClasses(); for (final Class clazz : templateClasses) { final PluginDictionnary plugins = PluginDictionnary.of(clazz); result.addAll(plugins.getAll()); } return result; } private final String shortName; private final String fullName; private final Class templateClass; private final Class clazz; public JkPluginDescription(Class templateClass, Class clazz) { super(); this.templateClass = templateClass; this.shortName = shortName(templateClass, clazz); this.fullName = longName(templateClass, clazz); this.clazz = clazz; } public String shortName() { return this.shortName; } public String fullName() { return this.fullName; } public Class templateClass() { return templateClass; } public Class pluginClass() { return clazz; } public List explanation() { if (this.clazz.getAnnotation(JkDoc.class) == null) { return Collections.emptyList(); } return Arrays.asList(this.clazz.getAnnotation(JkDoc.class).value()); } @Override public String toString() { return "name=" + this.shortName + "(" + this.fullName + ")"; } } }