diff --git a/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java b/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java index b36bbd63d9151a3d0922fb93e3ee9c732ac0fab7..069e56360ef08f9c757293981ee005e405418263 100644 --- a/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java +++ b/src/main/java/plugins/tprovoost/scripteditor/scriptinghandlers/ScriptEngineManager.java @@ -1,15 +1,17 @@ package plugins.tprovoost.scripteditor.scriptinghandlers; +import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; 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.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; +import javax.script.SimpleBindings; import icy.plugin.PluginDescriptor; import icy.plugin.PluginLauncher; @@ -18,20 +20,20 @@ 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); + private HashSet<ScriptEngineFactory> engineSpis = new HashSet<>(); /** Map of engine name to script engine factory. */ - private final HashMap<String, ScriptEngineFactory> nameAssociations = new HashMap<>(); + private HashMap<String, ScriptEngineFactory> nameAssociations = new HashMap<>(); /** Map of script file extension to script engine factory. */ - private final HashMap<String, ScriptEngineFactory> extensionAssociations = new HashMap<>(); + private HashMap<String, ScriptEngineFactory> extensionAssociations = new HashMap<>(); + + /** Map of script script MIME type to script engine factory. */ + private HashMap<String, ScriptEngineFactory> mimeTypeAssociations = new HashMap<>(); - /** Map of script MIME type to script engine factory. */ - private final HashMap<String, ScriptEngineFactory> mimeTypeAssociations = new HashMap<>(); + /** Global bindings associated with script engines created by this manager. */ + private Bindings globalScope = new SimpleBindings(); public ScriptEngineManager() { @@ -54,6 +56,68 @@ public class ScriptEngineManager } } + /** + * <code>setBindings</code> stores the specified <code>Bindings</code> + * in the <code>globalScope</code> field. ScriptEngineManager sets this + * <code>Bindings</code> as global bindings for <code>ScriptEngine</code> + * objects created by it. + * + * @param bindings + * The specified <code>Bindings</code> + * @throws IllegalArgumentException + * if bindings is null. + */ + public void setBindings(Bindings bindings) + { + if (bindings == null) + { + throw new IllegalArgumentException("Global scope cannot be null."); + } + + globalScope = bindings; + } + + /** + * <code>getBindings</code> returns the value of the <code>globalScope</code> field. + * ScriptEngineManager sets this <code>Bindings</code> as global bindings for + * <code>ScriptEngine</code> objects created by it. + * + * @return The globalScope field. + */ + public Bindings getBindings() + { + return globalScope; + } + + /** + * Sets the specified key/value pair in the Global Scope. + * + * @param key + * Key to set + * @param value + * Value to set. + * @throws NullPointerException + * if key is null. + * @throws IllegalArgumentException + * if key is empty string. + */ + public void put(String key, Object value) + { + globalScope.put(key, value); + } + + /** + * Gets the value for the specified key in the Global Scope + * + * @param key + * The key whose value is to be returned. + * @return The value for the specified key. + */ + public Object get(String key) + { + return globalScope.get(key); + } + /** * Looks up and creates a <code>ScriptEngine</code> for a given name. * The algorithm first searches for a <code>ScriptEngineFactory</code> that has been @@ -77,7 +141,59 @@ public class ScriptEngineManager */ public javax.script.ScriptEngine getEngineByName(String shortName) { - return getEngineBy(shortName, nameAssociations, ScriptEngineFactory::getNames); + if (shortName == null) + throw new NullPointerException(); + // look for registered name first + Object obj; + if (null != (obj = nameAssociations.get(shortName))) + { + ScriptEngineFactory spi = (ScriptEngineFactory) obj; + try + { + ScriptEngine engine = spi.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); + return engine; + } + catch (Exception exp) + { + debugPrint(exp); + } + } + + for (ScriptEngineFactory spi : engineSpis) + { + List<String> names = null; + try + { + names = spi.getNames(); + } + catch (Exception exp) + { + debugPrint(exp); + } + + if (names != null) + { + for (String name : names) + { + if (shortName.equals(name)) + { + try + { + ScriptEngine engine = spi.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); + return engine; + } + catch (Exception exp) + { + debugPrint(exp); + } + } + } + } + } + + return null; } /** @@ -93,9 +209,58 @@ public class ScriptEngineManager * @throws NullPointerException * if extension is null. */ - public javax.script.ScriptEngine getEngineByExtension(String extension) + public ScriptEngine getEngineByExtension(String extension) { - return getEngineBy(extension, extensionAssociations, ScriptEngineFactory::getExtensions); + if (extension == null) + throw new NullPointerException(); + // look for registered extension first + Object obj; + if (null != (obj = extensionAssociations.get(extension))) + { + ScriptEngineFactory spi = (ScriptEngineFactory) obj; + try + { + ScriptEngine engine = spi.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); + return engine; + } + catch (Exception exp) + { + debugPrint(exp); + } + } + + for (ScriptEngineFactory spi : engineSpis) + { + List<String> exts = null; + try + { + exts = spi.getExtensions(); + } + catch (Exception exp) + { + debugPrint(exp); + } + if (exts == null) + continue; + for (String ext : exts) + { + if (extension.equals(ext)) + { + try + { + ScriptEngine engine = spi.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); + return engine; + } + catch (Exception exp) + { + debugPrint(exp); + } + } + } + } + return null; } /** @@ -111,48 +276,58 @@ public class ScriptEngineManager * @throws NullPointerException * if mimeType is null. */ - public javax.script.ScriptEngine getEngineByMimeType(String mimeType) + public 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 -> { + if (mimeType == null) + throw new NullPointerException(); + // look for registered types first + Object obj; + if (null != (obj = mimeTypeAssociations.get(mimeType))) + { + ScriptEngineFactory spi = (ScriptEngineFactory) obj; try { - javax.script.ScriptEngine engine = spi.getScriptEngine(); + ScriptEngine engine = spi.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); return engine; } catch (Exception exp) { debugPrint(exp); - return null; } - }).filter(Objects::nonNull).findFirst().orElse(null); - } + } - private static void debugPrint(Throwable exp) - { - exp.printStackTrace(); + for (ScriptEngineFactory spi : engineSpis) + { + List<String> types = null; + try + { + types = spi.getMimeTypes(); + } + catch (Exception exp) + { + debugPrint(exp); + } + if (types == null) + continue; + for (String type : types) + { + if (mimeType.equals(type)) + { + try + { + ScriptEngine engine = spi.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); + return engine; + } + catch (Exception exp) + { + debugPrint(exp); + } + } + } + } + return null; } /** @@ -163,6 +338,72 @@ public class ScriptEngineManager */ public List<ScriptEngineFactory> getEngineFactories() { - return List.copyOf(engineSpis); + List<ScriptEngineFactory> res = new ArrayList<ScriptEngineFactory>(engineSpis.size()); + for (ScriptEngineFactory spi : engineSpis) + { + res.add(spi); + } + return Collections.unmodifiableList(res); + } + + /** + * Registers a <code>ScriptEngineFactory</code> to handle a language + * name. Overrides any such association found using the Discovery mechanism. + * + * @param name + * The name to be associated with the <code>ScriptEngineFactory</code>. + * @param factory + * The class to associate with the given name. + * @throws NullPointerException + * if any of the parameters is null. + */ + public void registerEngineName(String name, ScriptEngineFactory factory) + { + if (name == null || factory == null) + throw new NullPointerException(); + nameAssociations.put(name, factory); + } + + /** + * Registers a <code>ScriptEngineFactory</code> to handle a mime type. + * Overrides any such association found using the Discovery mechanism. + * + * @param type + * The mime type to be associated with the + * <code>ScriptEngineFactory</code>. + * @param factory + * The class to associate with the given mime type. + * @throws NullPointerException + * if any of the parameters is null. + */ + public void registerEngineMimeType(String type, ScriptEngineFactory factory) + { + if (type == null || factory == null) + throw new NullPointerException(); + mimeTypeAssociations.put(type, factory); + } + + /** + * Registers a <code>ScriptEngineFactory</code> to handle an extension. + * Overrides any such association found using the Discovery mechanism. + * + * @param extension + * The extension type to be associated with the + * <code>ScriptEngineFactory</code>. + * @param factory + * The class to associate with the given extension. + * @throws NullPointerException + * if any of the parameters is null. + */ + public void registerEngineExtension(String extension, ScriptEngineFactory factory) + { + if (extension == null || factory == null) + throw new NullPointerException(); + extensionAssociations.put(extension, factory); + } + + private static void debugPrint(Throwable exp) + { + exp.printStackTrace(); } }