/src/bsh/BshScriptEngine.java
http://beanshell2.googlecode.com/ · Java · 380 lines · 190 code · 71 blank · 119 comment · 13 complexity · 117d1511a559a3d57a92f21d7446e809 MD5 · raw file
- package bsh;
-
- import javax.script.AbstractScriptEngine;
- import javax.script.Bindings;
- import javax.script.Compilable;
- import javax.script.CompiledScript;
- import javax.script.Invocable;
- import javax.script.ScriptContext;
- import javax.script.ScriptEngine;
- import javax.script.ScriptEngineFactory;
- import javax.script.ScriptException;
- import javax.script.SimpleBindings;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.io.PrintStream;
- import java.io.Reader;
- import java.io.Writer;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- /*
- Adopted from http://ikayzo.org/svn/beanshell/BeanShell/engine/src/bsh/engine/BshScriptEngine.java
- Notes
- This engine supports open-ended pluggable scriptcontexts
- */
-
- public class BshScriptEngine extends AbstractScriptEngine implements Compilable, Invocable {
- // The BeanShell global namespace for the interpreter is stored in the
- // engine scope map under this key.
- static final String engineNameSpaceKey = "org_beanshell_engine_namespace";
-
- private BshScriptEngineFactory factory;
- private bsh.Interpreter interpreter;
-
-
- public BshScriptEngine() {
- this(null);
- }
-
-
- public BshScriptEngine(BshScriptEngineFactory factory) {
- this.factory = factory;
- getInterpreter(); // go ahead and prime the interpreter now
- }
-
-
- protected Interpreter getInterpreter() {
- if (interpreter == null) {
- this.interpreter = new bsh.Interpreter();
- interpreter.setNameSpace(null); // should always be set by context
- }
-
- return interpreter;
- }
-
-
- public Object eval(String script, ScriptContext scriptContext) throws ScriptException {
- return evalSource(script, scriptContext);
- }
-
-
- public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
- return evalSource(reader, scriptContext);
- }
-
- /*
- This is the primary implementation method.
- We respect the String/Reader difference here in BeanShell because
- BeanShell will do a few extra things in the string case... e.g.
- tack on a trailing ";" semicolon if necessary.
- */
-
-
- private Object evalSource(Object source, ScriptContext scriptContext) throws ScriptException {
- bsh.NameSpace contextNameSpace = getEngineNameSpace(scriptContext);
- Interpreter bsh = getInterpreter();
- bsh.setNameSpace(contextNameSpace);
-
- bsh.setOut(toPrintStream(scriptContext.getWriter()));
- bsh.setErr(toPrintStream(scriptContext.getErrorWriter()));
-
- try {
- if (source instanceof Reader) {
- return bsh.eval((Reader) source);
- } else {
- return bsh.eval((String) source);
- }
- } catch (ParseException e) {
- // explicit parsing error
- throw new ScriptException(e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber());
- } catch (TargetError e) {
- // The script threw an application level exception
- // set it as the cause ?
- ScriptException se = new ScriptException(e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber());
- se.initCause(e.getTarget());
- throw se;
- } catch (EvalError e) {
- // The script couldn't be evaluated properly
- throw new ScriptException(e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber());
- } catch (InterpreterError e) {
- // The interpreter had a fatal problem
- throw new ScriptException(e.toString());
- }
- }
-
-
- private PrintStream toPrintStream(final Writer writer) {
- // This is a big hack, convert writer to PrintStream
- return new PrintStream(new WriterOutputStream(writer));
- }
-
-
- /*
- Check the context for an existing global namespace embedded
- in the script context engine scope. If none exists, ininitialize the
- context with one.
- */
-
-
- private static NameSpace getEngineNameSpace(ScriptContext scriptContext) {
- NameSpace ns = (NameSpace) scriptContext.getAttribute(engineNameSpaceKey, ScriptContext.ENGINE_SCOPE);
-
- if (ns == null) {
- // Create a global namespace for the interpreter
- Map<String, Object> engineView = new ScriptContextEngineView(scriptContext);
- ns = new ExternalNameSpace(null/*parent*/, "javax_script_context", engineView);
-
- scriptContext.setAttribute(engineNameSpaceKey, ns, ScriptContext.ENGINE_SCOPE);
- }
-
- return ns;
- }
-
-
- public Bindings createBindings() {
- return new SimpleBindings();
- }
-
-
- public ScriptEngineFactory getFactory() {
- if (factory == null) {
- factory = new BshScriptEngineFactory();
- }
- return factory;
- }
-
-
- /**
- * Compiles the script (source represented as a {@code String}) for later
- * execution.
- *
- * @param script The source of the script, represented as a {@code String}.
- * @return An subclass of {@code CompiledScript} to be executed later
- * using one of the {@code eval} methods of {@code CompiledScript}.
- * @throws ScriptException if compilation fails.
- * @throws NullPointerException if the argument is null.
- */
-
- public CompiledScript compile(String script) throws ScriptException {
- try {
- final PreparsedScript preparsed = new PreparsedScript(script);
- return new CompiledScript() {
-
- @Override
- public Object eval(ScriptContext context) throws ScriptException {
- final HashMap<String, Object> map = new HashMap<String, Object>();
- final List<Integer> scopes = new ArrayList<Integer>(context.getScopes());
- Collections.sort(scopes); // lowest scope at first pos
- Collections.reverse(scopes); // highest scope at first pos
- for (final Integer scope : scopes) {
- map.putAll(context.getBindings(scope));
- }
- preparsed.setOut(toPrintStream(context.getWriter()));
- preparsed.setErr(toPrintStream(context.getErrorWriter()));
- try {
- return preparsed.invoke(map);
- } catch (final EvalError e) {
- throw constructScriptException(e);
- }
- }
-
-
- @Override
- public ScriptEngine getEngine() {
- return BshScriptEngine.this;
- }
-
- };
- } catch (final EvalError e) {
- throw constructScriptException(e);
- }
- }
-
-
- private ScriptException constructScriptException(final EvalError e) {
- return new ScriptException(e.getMessage(), e.getErrorSourceFile(), e.getErrorLineNumber());
- }
-
-
- private static String convertToString(Reader reader) throws IOException {
- final StringBuffer buffer = new StringBuffer(64);
- char[] cb = new char[64];
- int len;
- while ((len = reader.read(cb)) != -1) {
- buffer.append(cb, 0, len);
- }
- return buffer.toString();
- }
-
-
- /**
- * Compiles the script (source read from {@code Reader}) for later
- * execution. Functionality is identical to {@code compile(String)} other
- * than the way in which the source is passed.
- *
- * @param script The reader from which the script source is obtained.
- * @return An implementation of {@code CompiledScript} to be executed
- * later using one of its {@code eval} methods of
- * {@code CompiledScript}.
- * @throws ScriptException if compilation fails.
- * @throws NullPointerException if argument is null.
- */
- public CompiledScript compile(Reader script) throws ScriptException {
- try {
- return compile(convertToString(script));
- } catch (IOException e) {
- throw new ScriptException(e);
- }
- }
-
-
- /**
- * Calls a procedure compiled during a previous script execution, which is
- * retained in the state of the {@code ScriptEngine{@code .
- *
- * @param name The name of the procedure to be called.
- * @param thiz If the procedure is a member of a class defined in the script
- * and thiz is an instance of that class returned by a previous execution or
- * invocation, the named method is called through that instance. If classes are
- * not supported in the scripting language or if the procedure is not a member
- * function of any class, the argument must be {@code null}.
- * @param args Arguments to pass to the procedure. The rules for converting
- * the arguments to scripting variables are implementation-specific.
- * @return The value returned by the procedure. The rules for converting the
- * scripting variable returned by the procedure to a Java Object are
- * implementation-specific.
- * @throws javax.script.ScriptException if an error occurrs during invocation
- * of the method.
- * @throws NoSuchMethodException if method with given name or matching argument
- * types cannot be found.
- * @throws NullPointerException if method name is null.
- */
- public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException {
- if (!(thiz instanceof bsh.This)) {
- throw new ScriptException("Illegal objec type: " + thiz.getClass());
- }
-
- bsh.This bshObject = (bsh.This) thiz;
-
- try {
- return bshObject.invokeMethod(name, args);
- } catch (ParseException e) {
- // explicit parsing error
- throw new ScriptException(e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber());
- } catch (TargetError e) {
- // The script threw an application level exception
- // set it as the cause ?
- ScriptException se = new ScriptException(e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber());
- se.initCause(e.getTarget());
- throw se;
- } catch (EvalError e) {
- // The script couldn't be evaluated properly
- throw new ScriptException(e.toString(), e.getErrorSourceFile(), e.getErrorLineNumber());
- } catch (InterpreterError e) {
- // The interpreter had a fatal problem
- throw new ScriptException(e.toString());
- }
- }
-
-
- /**
- * Same as invoke(Object, String, Object...) with {@code null} as the
- * first argument. Used to call top-level procedures defined in scripts.
- *
- * @param args Arguments to pass to the procedure
- * @return The value returned by the procedure
- * @throws javax.script.ScriptException if an error occurrs during invocation
- * of the method.
- * @throws NoSuchMethodException if method with given name or matching
- * argument types cannot be found.
- * @throws NullPointerException if method name is null.
- */
- public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
- return invokeMethod(getGlobal(), name, args);
- }
-
-
- /**
- * Returns an implementation of an interface using procedures compiled in the
- * interpreter. The methods of the interface may be implemented using the
- * {@code invoke} method.
- *
- * @param clasz The {@code Class} object of the interface to return.
- * @return An instance of requested interface - null if the requested interface
- * is unavailable, i. e. if compiled methods in the
- * {@code ScriptEngine} cannot be found matching the ones in the
- * requested interface.
- * @throws IllegalArgumentException if the specified {@code Class} object
- * does not exist or is not an interface.
- */
- public <T> T getInterface(Class<T> clasz) {
- return clasz.cast(getGlobal().getInterface(clasz));
- }
-
-
- /**
- * Returns an implementation of an interface using member functions of a
- * scripting object compiled in the interpreter. The methods of the interface
- * may be implemented using invoke(Object, String, Object...) method.
- *
- * @param thiz The scripting object whose member functions are used to
- * implement the methods of the interface.
- * @param clasz The {@code Class} object of the interface to return.
- * @return An instance of requested interface - null if the requested
- * interface is unavailable, i. e. if compiled methods in the
- * {@code ScriptEngine} cannot be found matching the ones in the
- * requested interface.
- * @throws IllegalArgumentException if the specified {@code Class} object
- * does not exist or is not an interface, or if the specified Object is null
- * or does not represent a scripting object.
- */
- public <T> T getInterface(Object thiz, Class<T> clasz) {
- if (!(thiz instanceof bsh.This)) {
- throw new IllegalArgumentException("invalid object type: " + thiz.getClass());
- }
-
- bsh.This bshThis = (bsh.This) thiz;
- return clasz.cast(bshThis.getInterface(clasz));
- }
-
-
- private bsh.This getGlobal() {
- // requires 2.0b5 to make getThis() public
- return getEngineNameSpace(getContext()).getThis(getInterpreter());
- }
-
- /*
- This is a total hack. We need to introduce a writer to the
- Interpreter.
- */
-
- class WriterOutputStream extends OutputStream {
-
- Writer writer;
-
-
- WriterOutputStream(Writer writer) {
- this.writer = writer;
- }
-
-
- public void write(int b) throws IOException {
- writer.write(b);
- }
-
-
- public void flush() throws IOException {
- writer.flush();
- }
-
-
- public void close() throws IOException {
- writer.close();
- }
- }
-
- }