diff --git a/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineHandler.java b/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineHandler.java index a66d12531116ed55cfc7e5d29ac6501710ed3677..303fe4762ab3eafde13314147ced517ef85aba6b 100644 --- a/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineHandler.java +++ b/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineHandler.java @@ -9,7 +9,6 @@ import java.util.Collections; import java.util.HashMap; import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; import org.fife.ui.autocomplete.ParameterizedCompletion.Parameter; @@ -38,8 +37,9 @@ public class ScriptEngineHandler implements PluginInstallerListener */ private static final HashMap<String, ScriptEngine> engines = new HashMap<String, ScriptEngine>(); - /** The factory contains all the engines. */ - public static final ScriptEngineManager factory = new ScriptEngineManager(PluginLoader.getLoader()); + /** The engineManager contains all the engines. */ + public static final ScriptEngineManager engineManager = new ScriptEngineManager(); + private static HashMap<ScriptEngine, ScriptEngineHandler> engineHandlers = new HashMap<ScriptEngine, ScriptEngineHandler>(); private static ScriptEngineHandler lastEngineHandler = null; private static ArrayList<Method> bindingFunctions; @@ -152,9 +152,9 @@ public class ScriptEngineHandler implements PluginInstallerListener return engineTypesMethod; } - public static ScriptEngineManager getFactory() + public static ScriptEngineManager getEngineManager() { - return factory; + return engineManager; } private void findBindingMethodsPlugins() @@ -280,8 +280,7 @@ public class ScriptEngineHandler implements PluginInstallerListener { Class<?> returnType = method.getReturnType(); if (VariableType.isGeneric(returnType)) - engineFunctions.put(functionName, new VariableType(returnType, - VariableType.getType(method.getGenericReturnType().toString()))); + engineFunctions.put(functionName, new VariableType(returnType, VariableType.getType(method.getGenericReturnType().toString()))); else engineFunctions.put(functionName, new VariableType(returnType)); } diff --git a/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java b/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java new file mode 100644 index 0000000000000000000000000000000000000000..b36bbd63d9151a3d0922fb93e3ee9c732ac0fab7 --- /dev/null +++ b/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java @@ -0,0 +1,168 @@ +package plugins.tprovoost.scripteditor.scriptinghandlers; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Stream; + +import javax.script.ScriptEngineFactory; + +import icy.plugin.PluginDescriptor; +import icy.plugin.PluginLauncher; +import icy.plugin.PluginLoader; +import icy.plugin.interface_.PluginScriptFactory; + +public class ScriptEngineManager +{ + private static final Comparator<ScriptEngineFactory> COMPARATOR = Comparator.comparing(ScriptEngineFactory::getEngineName, + Comparator.nullsLast(Comparator.naturalOrder())); + + /** Set of script engine factories discovered. */ + private final TreeSet<ScriptEngineFactory> engineSpis = new TreeSet<>(COMPARATOR); + + /** Map of engine name to script engine factory. */ + private final HashMap<String, ScriptEngineFactory> nameAssociations = new HashMap<>(); + + /** Map of script file extension to script engine factory. */ + private final HashMap<String, ScriptEngineFactory> extensionAssociations = new HashMap<>(); + + /** Map of script MIME type to script engine factory. */ + private final HashMap<String, ScriptEngineFactory> mimeTypeAssociations = new HashMap<>(); + + public ScriptEngineManager() + { + super(); + + for (PluginDescriptor plugin : PluginLoader.getPlugins(PluginScriptFactory.class, true, false, false)) + { + try + { + // create plugin instance + PluginScriptFactory psf = (PluginScriptFactory) PluginLauncher.create(plugin); + // get the factory and add it to the list + engineSpis.add(psf.getScriptEngineFactory()); + } + catch (Throwable t) + { + System.err.println("Error while retrieving ScriptEngineFactory from " + plugin.getName() + ":"); + System.err.println(t.getMessage()); + } + } + } + + /** + * Looks up and creates a <code>ScriptEngine</code> for a given name. + * The algorithm first searches for a <code>ScriptEngineFactory</code> that has been + * registered as a handler for the specified name using the <code>registerEngineName</code> + * method. + * <br> + * <br> + * If one is not found, it searches the set of <code>ScriptEngineFactory</code> instances + * stored by the constructor for one with the specified name. If a <code>ScriptEngineFactory</code> + * is found by either method, it is used to create instance of <code>ScriptEngine</code>. + * + * @param shortName + * The short name of the <code>ScriptEngine</code> implementation. + * returned by the <code>getNames</code> method of its <code>ScriptEngineFactory</code>. + * @return A <code>ScriptEngine</code> created by the factory located in the search. Returns null + * if no such factory was found. The <code>ScriptEngineManager</code> sets its own <code>globalScope</code> + * <code>Bindings</code> as the <code>GLOBAL_SCOPE</code> <code>Bindings</code> of the newly + * created <code>ScriptEngine</code>. + * @throws NullPointerException + * if shortName is null. + */ + public javax.script.ScriptEngine getEngineByName(String shortName) + { + return getEngineBy(shortName, nameAssociations, ScriptEngineFactory::getNames); + } + + /** + * Look up and create a <code>ScriptEngine</code> for a given extension. The algorithm + * used by <code>getEngineByName</code> is used except that the search starts + * by looking for a <code>ScriptEngineFactory</code> registered to handle the + * given extension using <code>registerEngineExtension</code>. + * + * @param extension + * The given extension + * @return The engine to handle scripts with this extension. Returns <code>null</code> + * if not found. + * @throws NullPointerException + * if extension is null. + */ + public javax.script.ScriptEngine getEngineByExtension(String extension) + { + return getEngineBy(extension, extensionAssociations, ScriptEngineFactory::getExtensions); + } + + /** + * Look up and create a <code>ScriptEngine</code> for a given mime type. The algorithm + * used by <code>getEngineByName</code> is used except that the search starts + * by looking for a <code>ScriptEngineFactory</code> registered to handle the + * given mime type using <code>registerEngineMimeType</code>. + * + * @param mimeType + * The given mime type + * @return The engine to handle scripts with this mime type. Returns <code>null</code> + * if not found. + * @throws NullPointerException + * if mimeType is null. + */ + public javax.script.ScriptEngine getEngineByMimeType(String mimeType) + { + return getEngineBy(mimeType, mimeTypeAssociations, ScriptEngineFactory::getMimeTypes); + } + + private javax.script.ScriptEngine getEngineBy(String selector, Map<String, ScriptEngineFactory> associations, + Function<ScriptEngineFactory, List<String>> valuesFn) + { + Objects.requireNonNull(selector); + Stream<ScriptEngineFactory> spis = Stream.concat( + // look for registered types first + Stream.ofNullable(associations.get(selector)), + + engineSpis.stream().filter(spi -> { + try + { + List<String> matches = valuesFn.apply(spi); + return matches != null && matches.contains(selector); + } + catch (Exception exp) + { + debugPrint(exp); + return false; + } + })); + return spis.map(spi -> { + try + { + javax.script.ScriptEngine engine = spi.getScriptEngine(); + return engine; + } + catch (Exception exp) + { + debugPrint(exp); + return null; + } + }).filter(Objects::nonNull).findFirst().orElse(null); + } + + private static void debugPrint(Throwable exp) + { + exp.printStackTrace(); + } + + /** + * Returns a list whose elements are instances of all the <code>ScriptEngineFactory</code> classes + * found by the discovery mechanism. + * + * @return List of all discovered <code>ScriptEngineFactory</code>s. + */ + public List<ScriptEngineFactory> getEngineFactories() + { + return List.copyOf(engineSpis); + } +}