From 9e0bb0730c8d1de8fd31a53ffeb366a2d58ac373 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 22 Jan 2011 13:34:05 -0800 Subject: [PATCH] Improved scripting support. --- build.xml | 3 +- .../sk89q/worldedit/LocalConfiguration.java | 4 +- .../sk89q/worldedit/WorldEditController.java | 70 ++++---- .../worldedit/bukkit/BukkitConfiguration.java | 2 + ...ptContext.java => CraftScriptContext.java} | 4 +- .../scripting/CraftScriptEngine.java | 30 ++++ ...nment.java => CraftScriptEnvironment.java} | 4 +- .../scripting/RhinoContextFactory.java | 61 +++++++ .../scripting/RhinoCraftScriptEngine.java | 75 +++++++++ .../scripting/SunRhinoContextFactory.java | 61 +++++++ .../scripting/SunRhinoCraftScriptEngine.java | 82 ++++++++++ .../scripting/java/RhinoScriptEngine.java | 119 ++++++++++++++ .../java/RhinoScriptEngineFactory.java | 151 ++++++++++++++++++ 13 files changed, 633 insertions(+), 33 deletions(-) rename src/com/sk89q/worldedit/scripting/{ScriptContext.java => CraftScriptContext.java} (92%) create mode 100644 src/com/sk89q/worldedit/scripting/CraftScriptEngine.java rename src/com/sk89q/worldedit/scripting/{ScriptEnvironment.java => CraftScriptEnvironment.java} (88%) create mode 100644 src/com/sk89q/worldedit/scripting/RhinoContextFactory.java create mode 100644 src/com/sk89q/worldedit/scripting/RhinoCraftScriptEngine.java create mode 100644 src/com/sk89q/worldedit/scripting/SunRhinoContextFactory.java create mode 100644 src/com/sk89q/worldedit/scripting/SunRhinoCraftScriptEngine.java create mode 100644 src/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java create mode 100644 src/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java diff --git a/build.xml b/build.xml index 73e31cf21..b43470ff4 100644 --- a/build.xml +++ b/build.xml @@ -16,6 +16,7 @@ + @@ -38,7 +39,7 @@ - + diff --git a/src/com/sk89q/worldedit/LocalConfiguration.java b/src/com/sk89q/worldedit/LocalConfiguration.java index ca65fa785..12f3be51b 100644 --- a/src/com/sk89q/worldedit/LocalConfiguration.java +++ b/src/com/sk89q/worldedit/LocalConfiguration.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit; +import java.util.HashSet; import java.util.Set; import com.sk89q.worldedit.snapshots.SnapshotRepository; @@ -36,7 +37,7 @@ public abstract class LocalConfiguration { }; public boolean profile = false; - public Set disallowedBlocks = null; + public Set disallowedBlocks = new HashSet(); public int defaultChangeLimit = -1; public int maxChangeLimit = -1; public String shellSaveType = null; @@ -54,6 +55,7 @@ public abstract class LocalConfiguration { public boolean useInventoryOverride = false; public int navigationWand = 345; public int navigationWandMaxDistance = 50; + public int scriptTimeout = 3000; /** * Loads the configuration. diff --git a/src/com/sk89q/worldedit/WorldEditController.java b/src/com/sk89q/worldedit/WorldEditController.java index 3599f99c8..84ff44bc3 100644 --- a/src/com/sk89q/worldedit/WorldEditController.java +++ b/src/com/sk89q/worldedit/WorldEditController.java @@ -22,20 +22,19 @@ package com.sk89q.worldedit; import java.util.List; import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.HashSet; import java.util.logging.Level; import java.util.logging.Logger; import java.io.*; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; import javax.script.ScriptException; import com.sk89q.util.StringUtil; import com.sk89q.worldedit.bags.BlockBag; import com.sk89q.worldedit.blocks.*; import com.sk89q.worldedit.data.*; import com.sk89q.worldedit.filters.*; -import com.sk89q.worldedit.scripting.ScriptContext; +import com.sk89q.worldedit.scripting.*; import com.sk89q.worldedit.snapshots.*; import com.sk89q.worldedit.superpickaxe.*; import com.sk89q.worldedit.regions.*; @@ -2149,44 +2148,61 @@ public class WorldEditController { String script; try { - FileInputStream file = new FileInputStream(f); - DataInputStream in = new DataInputStream (file); + InputStream file; + + if (!f.exists()) { + file = WorldEditController.class.getResourceAsStream( + "craftscripts/" + filename); + + if (file == null) { + player.printError("Script does not exist: " + filename); + return; + } + } else { + file = new FileInputStream(f); + } + + DataInputStream in = new DataInputStream(file); byte[] data = new byte[in.available()]; in.readFully(data); - in.close (); + in.close(); script = new String(data, 0, data.length, "utf-8"); - } catch (FileNotFoundException e) { - player.printError("Script does not exist: " + filename); - return; } catch (IOException e) { player.printError("Script read error: " + e.getMessage()); return; } - - ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine engine = manager.getEngineByExtension(ext); - - if (engine == null) { - player.printError("Failed to load the scripting engine."); - return; - } LocalSession session = getSession(player); - ScriptContext context = new ScriptContext(this, server, config, session, player); + CraftScriptContext scriptContext = + new CraftScriptContext(this, server, config, session, player); - engine.put("argv", args); - engine.put("ctx", context); - engine.put("player", player); - engine.put("BaseBlock", BaseBlock.class); + CraftScriptEngine engine = null; try { - engine.eval(script); + engine = new RhinoCraftScriptEngine(); + } catch (NoClassDefFoundError e) { + try { + engine = new SunRhinoCraftScriptEngine(); + } catch (NoClassDefFoundError e2) { + player.printError("Failed to find an installed script engine."); + return; + } + } + + engine.setTimeLimit(3000); + + Map vars = new HashMap(); + vars.put("argv", args); + vars.put("context", scriptContext); + vars.put("player", player); + + try { + engine.evaluate(script, filename, vars); } catch (ScriptException e) { - e.printStackTrace(); - player.printError("Script encountered an error: " + e.getMessage()); - return; + player.printError("Failed to execute:"); + player.printRaw(e.getMessage()); } finally { - for (EditSession editSession : context._getEditSessions()) { + for (EditSession editSession : scriptContext._getEditSessions()) { session.remember(editSession); } } diff --git a/src/com/sk89q/worldedit/bukkit/BukkitConfiguration.java b/src/com/sk89q/worldedit/bukkit/BukkitConfiguration.java index 4a4fa3551..d15ca4ed1 100644 --- a/src/com/sk89q/worldedit/bukkit/BukkitConfiguration.java +++ b/src/com/sk89q/worldedit/bukkit/BukkitConfiguration.java @@ -65,6 +65,8 @@ public class BukkitConfiguration extends LocalConfiguration { navigationWand = config.getInt("navigation-wand.item", navigationWand); navigationWandMaxDistance = config.getInt("navigation-wand.max-distance", navigationWandMaxDistance); + scriptTimeout = config.getInt("scripting.timeout", scriptTimeout); + disallowedBlocks = new HashSet(config.getIntList("limits.disallowed-blocks", null)); String snapshotsDir = config.getString("snapshots.directory", ""); diff --git a/src/com/sk89q/worldedit/scripting/ScriptContext.java b/src/com/sk89q/worldedit/scripting/CraftScriptContext.java similarity index 92% rename from src/com/sk89q/worldedit/scripting/ScriptContext.java rename to src/com/sk89q/worldedit/scripting/CraftScriptContext.java index bba38a21e..8d2da49ea 100644 --- a/src/com/sk89q/worldedit/scripting/ScriptContext.java +++ b/src/com/sk89q/worldedit/scripting/CraftScriptContext.java @@ -31,10 +31,10 @@ import com.sk89q.worldedit.UnknownItemException; import com.sk89q.worldedit.WorldEditController; import com.sk89q.worldedit.blocks.BaseBlock; -public class ScriptContext extends ScriptEnvironment { +public class CraftScriptContext extends CraftScriptEnvironment { private List editSessions = new ArrayList(); - public ScriptContext(WorldEditController controller, + public CraftScriptContext(WorldEditController controller, ServerInterface server, LocalConfiguration config, LocalSession session, LocalPlayer player) { super(controller, server, config, session, player); diff --git a/src/com/sk89q/worldedit/scripting/CraftScriptEngine.java b/src/com/sk89q/worldedit/scripting/CraftScriptEngine.java new file mode 100644 index 000000000..32ac9c41d --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/CraftScriptEngine.java @@ -0,0 +1,30 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.worldedit.scripting; + +import java.util.Map; +import javax.script.ScriptException; + +public interface CraftScriptEngine { + public void setTimeLimit(int milliseconds); + public int getTimeLimit(); + public Object evaluate(String script, String filename, Map args) + throws ScriptException; +} diff --git a/src/com/sk89q/worldedit/scripting/ScriptEnvironment.java b/src/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java similarity index 88% rename from src/com/sk89q/worldedit/scripting/ScriptEnvironment.java rename to src/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java index 30d7a1b27..06a83b600 100644 --- a/src/com/sk89q/worldedit/scripting/ScriptEnvironment.java +++ b/src/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java @@ -25,14 +25,14 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.ServerInterface; import com.sk89q.worldedit.WorldEditController; -public abstract class ScriptEnvironment { +public abstract class CraftScriptEnvironment { protected WorldEditController controller; protected LocalPlayer player; protected LocalConfiguration config; protected LocalSession session; protected ServerInterface server; - public ScriptEnvironment(WorldEditController controller, ServerInterface server, + public CraftScriptEnvironment(WorldEditController controller, ServerInterface server, LocalConfiguration config, LocalSession session, LocalPlayer player) { this.controller = controller; this.player = player; diff --git a/src/com/sk89q/worldedit/scripting/RhinoContextFactory.java b/src/com/sk89q/worldedit/scripting/RhinoContextFactory.java new file mode 100644 index 000000000..381b6c5f8 --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/RhinoContextFactory.java @@ -0,0 +1,61 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.scripting; + +import org.mozilla.javascript.*; + +public class RhinoContextFactory extends ContextFactory { + protected int timeLimit; + + public RhinoContextFactory(int timeLimit) { + this.timeLimit = timeLimit; + } + + protected Context makeContext() { + RhinoContext cx = new RhinoContext(this); + cx.setInstructionObserverThreshold(10000); + return cx; + } + + protected void observeInstructionCount(Context cx, int instructionCount) { + RhinoContext mcx = (RhinoContext)cx; + long currentTime = System.currentTimeMillis(); + + if (currentTime - mcx.startTime > timeLimit) { + throw new Error("Script timed out (" + timeLimit + "ms)"); + } + } + + protected Object doTopCall(Callable callable, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) { + RhinoContext mcx = (RhinoContext)cx; + mcx.startTime = System.currentTimeMillis(); + + return super.doTopCall(callable, cx, scope, thisObj, args); + } + + private static class RhinoContext extends Context { + long startTime; + + public RhinoContext(ContextFactory factory) { + super(factory); + } + } +} \ No newline at end of file diff --git a/src/com/sk89q/worldedit/scripting/RhinoCraftScriptEngine.java b/src/com/sk89q/worldedit/scripting/RhinoCraftScriptEngine.java new file mode 100644 index 000000000..c872b4874 --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/RhinoCraftScriptEngine.java @@ -0,0 +1,75 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.worldedit.scripting; + +import java.util.Map; +import javax.script.ScriptException; +import org.mozilla.javascript.*; + +public class RhinoCraftScriptEngine implements CraftScriptEngine { + private int timeLimit; + + @Override + public void setTimeLimit(int milliseconds) { + timeLimit = milliseconds; + } + + @Override + public int getTimeLimit() { + return timeLimit; + } + + @Override + public Object evaluate(String script, String filename, Map args) + throws ScriptException { + RhinoContextFactory factory = new RhinoContextFactory(timeLimit); + Context cx = factory.enterContext(); + ScriptableObject scriptable = new ImporterTopLevel(cx); + Scriptable scope = cx.initStandardObjects(scriptable); + + for (Map.Entry entry : args.entrySet()) { + ScriptableObject.putProperty(scope, entry.getKey(), + Context.javaToJS(entry.getValue(), scope)); + } + try { + return cx.evaluateString(scope, script, filename, 1, null); + } catch (Error e) { + throw new ScriptException(e.getMessage()); + } catch (RhinoException e) { + String msg; + int line = (line = e.lineNumber()) == 0 ? -1 : line; + + if (e instanceof JavaScriptException) { + msg = String.valueOf(((JavaScriptException)e).getValue()); + } else { + msg = e.getMessage(); + } + + ScriptException scriptException = + new ScriptException(msg, e.sourceName(), line); + scriptException.initCause(e); + + throw scriptException; + } finally { + Context.exit(); + } + } + +} diff --git a/src/com/sk89q/worldedit/scripting/SunRhinoContextFactory.java b/src/com/sk89q/worldedit/scripting/SunRhinoContextFactory.java new file mode 100644 index 000000000..aad0fa2ae --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/SunRhinoContextFactory.java @@ -0,0 +1,61 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.scripting; + +import sun.org.mozilla.javascript.internal.*; + +public class SunRhinoContextFactory extends ContextFactory { + protected int timeLimit; + + public SunRhinoContextFactory(int timeLimit) { + this.timeLimit = timeLimit; + } + + protected Context makeContext() { + RhinoContext cx = new RhinoContext(this); + cx.setInstructionObserverThreshold(10000); + return cx; + } + + protected void observeInstructionCount(Context cx, int instructionCount) { + RhinoContext mcx = (RhinoContext)cx; + long currentTime = System.currentTimeMillis(); + + if (currentTime - mcx.startTime > timeLimit) { + throw new Error("Script timed out (" + timeLimit + "ms)"); + } + } + + protected Object doTopCall(Callable callable, Context cx, Scriptable scope, + Scriptable thisObj, Object[] args) { + RhinoContext mcx = (RhinoContext)cx; + mcx.startTime = System.currentTimeMillis(); + + return super.doTopCall(callable, cx, scope, thisObj, args); + } + + private static class RhinoContext extends Context { + long startTime; + + public RhinoContext(ContextFactory factory) { + super(); + } + } +} \ No newline at end of file diff --git a/src/com/sk89q/worldedit/scripting/SunRhinoCraftScriptEngine.java b/src/com/sk89q/worldedit/scripting/SunRhinoCraftScriptEngine.java new file mode 100644 index 000000000..10726fcf7 --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/SunRhinoCraftScriptEngine.java @@ -0,0 +1,82 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package com.sk89q.worldedit.scripting; + +import java.util.Map; +import javax.script.ScriptException; +import com.sk89q.worldedit.WorldEditController; +import sun.org.mozilla.javascript.internal.*; + +public class SunRhinoCraftScriptEngine implements CraftScriptEngine { + private int timeLimit; + + @Override + public void setTimeLimit(int milliseconds) { + timeLimit = milliseconds; + } + + @Override + public int getTimeLimit() { + return timeLimit; + } + + @Override + public Object evaluate(final String script, final String filename, + final Map args) + throws ScriptException { + SunRhinoContextFactory factory = new SunRhinoContextFactory(timeLimit); + factory.initApplicationClassLoader(WorldEditController.class.getClassLoader()); + + try { + return factory.call(new ContextAction() { + public Object run(Context cx) { + Scriptable topLevel = new ImporterTopLevel(cx); + Scriptable scope = cx.initStandardObjects(); + topLevel.setParentScope(scope); + + for (Map.Entry entry : args.entrySet()) { + ScriptableObject.putProperty(scope, entry.getKey(), + Context.javaToJS(entry.getValue(), scope)); + } + + return cx.evaluateString(topLevel, script, filename, 1, null); + } + }); + } catch (Error e) { + throw new ScriptException(e.getMessage()); + } catch (RhinoException e) { + String msg; + int line = (line = e.lineNumber()) == 0 ? -1 : line; + + if (e instanceof JavaScriptException) { + msg = String.valueOf(((JavaScriptException)e).getValue()); + } else { + msg = e.getMessage(); + } + + ScriptException scriptException = + new ScriptException(msg, e.sourceName(), line); + scriptException.initCause(e); + + throw scriptException; + } + } + +} diff --git a/src/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java b/src/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java new file mode 100644 index 000000000..4fd93000e --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/java/RhinoScriptEngine.java @@ -0,0 +1,119 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.scripting.java; + +import java.io.*; +import javax.script.*; +import org.mozilla.javascript.*; +import com.sk89q.worldedit.scripting.RhinoContextFactory; + +public class RhinoScriptEngine extends AbstractScriptEngine { + private ScriptEngineFactory factory; + private Context cx; + + public RhinoScriptEngine() { + RhinoContextFactory factory = new RhinoContextFactory(3000); + factory.enterContext(); + } + + public Bindings createBindings() { + return new SimpleBindings(); + } + + @Override + public Object eval(String script, ScriptContext context) + throws ScriptException { + + Scriptable scope = setupScope(cx, context); + + String filename = (filename = (String)get(ScriptEngine.FILENAME)) == null + ? "" : filename; + + try { + return cx.evaluateString(scope, script, filename, 1, null); + } catch (RhinoException e) { + String msg; + int line = (line = e.lineNumber()) == 0 ? -1 : line; + + if (e instanceof JavaScriptException) { + msg = String.valueOf(((JavaScriptException)e).getValue()); + } else { + msg = e.getMessage(); + } + + ScriptException scriptException = + new ScriptException(msg, e.sourceName(), line); + scriptException.initCause(e); + + throw scriptException; + } finally { + Context.exit(); + } + } + + @Override + public Object eval(Reader reader, ScriptContext context) + throws ScriptException { + + Scriptable scope = setupScope(cx, context); + + String filename = (filename = (String)get(ScriptEngine.FILENAME)) == null + ? "" : filename; + + try { + return cx.evaluateReader(scope, reader, filename, 1, null); + } catch (RhinoException e) { + String msg; + int line = (line = e.lineNumber()) == 0 ? -1 : line; + + if (e instanceof JavaScriptException) { + msg = String.valueOf(((JavaScriptException)e).getValue()); + } else { + msg = e.getMessage(); + } + + ScriptException scriptException = + new ScriptException(msg, e.sourceName(), line); + scriptException.initCause(e); + + throw scriptException; + } catch (IOException e) { + throw new ScriptException(e); + } finally { + Context.exit(); + } + } + + @Override + public ScriptEngineFactory getFactory() { + if (factory != null) { + return factory; + } else { + return new RhinoScriptEngineFactory(); + } + } + + private Scriptable setupScope(Context cx, ScriptContext context) { + ScriptableObject scriptable = new ImporterTopLevel(cx); + Scriptable scope = cx.initStandardObjects(scriptable); + //ScriptableObject.putProperty(scope, "argv", Context.javaToJS(args, scope)); + return scope; + } +} diff --git a/src/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java b/src/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java new file mode 100644 index 000000000..0dd82acaf --- /dev/null +++ b/src/com/sk89q/worldedit/scripting/java/RhinoScriptEngineFactory.java @@ -0,0 +1,151 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.scripting.java; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; + +public class RhinoScriptEngineFactory implements ScriptEngineFactory { + private static List names; + private static List mimeTypes; + private static List extensions; + + static { + names = new ArrayList(5); + names.add("ECMAScript"); + names.add("ecmascript"); + names.add("JavaScript"); + names.add("javascript"); + names.add("js"); + names = Collections.unmodifiableList(names); + + mimeTypes = new ArrayList(4); + mimeTypes.add("application/ecmascript"); + mimeTypes.add("text/ecmascript"); + mimeTypes.add("application/javascript"); + mimeTypes.add("text/javascript"); + mimeTypes = Collections.unmodifiableList(mimeTypes); + + extensions = new ArrayList(2); + extensions.add("emcascript"); + extensions.add("js"); + extensions = Collections.unmodifiableList(extensions); + } + + @Override + public String getEngineName() { + return "Rhino JavaScript Engine (SK)"; + } + + @Override + public String getEngineVersion() { + return "unknown"; + } + + @Override + public List getExtensions() { + return extensions; + } + + @Override + public String getLanguageName() { + return "EMCAScript"; + } + + @Override + public String getLanguageVersion() { + return "1.8"; + } + + @Override + public String getMethodCallSyntax(String obj, String m, String ... args) { + StringBuilder s = new StringBuilder(); + s.append(obj); + s.append("."); + s.append(m); + s.append("("); + + for (int i = 0; i < args.length; i++) { + s.append(args[i]); + if (i < args.length - 1) { + s.append(","); + } + } + + s.append(")"); + + return s.toString(); + } + + @Override + public List getMimeTypes() { + return mimeTypes; + } + + @Override + public List getNames() { + return names; + } + + @Override + public String getOutputStatement(String str) { + return "print(" + str.replace("\\", "\\\\") + .replace("\"", "\\\\\"") + .replace(";", "\\\\;") + ")"; + } + + @Override + public Object getParameter(String key) { + if (key.equals(ScriptEngine.ENGINE)) { + return getEngineName(); + } else if (key.equals(ScriptEngine.ENGINE_VERSION)) { + return getEngineVersion(); + } else if (key.equals(ScriptEngine.NAME)) { + return getEngineName(); + } else if (key.equals(ScriptEngine.LANGUAGE)) { + return getLanguageName(); + } else if (key.equals(ScriptEngine.LANGUAGE_VERSION)) { + return getLanguageVersion(); + } else if (key.equals("THREADING")) { + return "MULTITHREADED"; + } else { + throw new IllegalArgumentException("Invalid key"); + } + } + + @Override + public String getProgram(String ... statements) { + StringBuilder s = new StringBuilder(); + for (String stmt : statements) { + s.append(stmt); + s.append(";"); + } + return s.toString(); + } + + @Override + public ScriptEngine getScriptEngine() { + return new RhinoScriptEngine(); + } + +}