package org.python.jsr223;
import java.lang.reflect.Method;
import org.python.core.*;
import java.io.Reader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
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 org.python.util.PythonInterpreter;
public class PyScriptEngine extends AbstractScriptEngine implements Compilable, Invocable {
private final PythonInterpreter interp;
private final ScriptEngineFactory factory;
PyScriptEngine(ScriptEngineFactory factory) {
this.factory = factory;
interp = PythonInterpreter.threadLocalStateInterpreter(new PyScriptEngineScope(this, context));
}
public Object eval(String script, ScriptContext context) throws ScriptException {
return eval(compileScript(script, context), context);
}
private Object eval(PyCode code, ScriptContext context) throws ScriptException {
try {
interp.setIn(context.getReader());
interp.setOut(context.getWriter());
interp.setErr(context.getErrorWriter());
interp.setLocals(new PyScriptEngineScope(this, context));
return interp.eval(code).__tojava__(Object.class);
} catch (PyException pye) {
throw scriptException(pye);
}
}
public Object eval(Reader reader, ScriptContext context) throws ScriptException {
return eval(compileScript(reader, context), context);
}
public Bindings createBindings() {
return new SimpleBindings();
}
public ScriptEngineFactory getFactory() {
return factory;
}
public CompiledScript compile(String script) throws ScriptException {
return new PyCompiledScript(compileScript(script, context));
}
public CompiledScript compile(Reader reader) throws ScriptException {
return new PyCompiledScript(compileScript(reader, context));
}
private PyCode compileScript(String script, ScriptContext context) throws ScriptException {
try {
String filename = (String) context.getAttribute(ScriptEngine.FILENAME);
if (filename == null) {
return interp.compile(script);
} else {
return interp.compile(script, filename);
}
} catch (PyException pye) {
throw scriptException(pye);
}
}
private PyCode compileScript(Reader reader, ScriptContext context) throws ScriptException {
try {
String filename = (String) context.getAttribute(ScriptEngine.FILENAME);
if (filename == null) {
return interp.compile(reader);
} else {
return interp.compile(reader, filename);
}
} catch (PyException pye) {
throw scriptException(pye);
}
}
public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException,
NoSuchMethodException {
try {
interp.setLocals(new PyScriptEngineScope(this, context));
if (!(thiz instanceof PyObject)) {
thiz = Py.java2py(thiz);
}
PyObject method = ((PyObject) thiz).__findattr__(name);
if (method == null) {
throw new NoSuchMethodException(name);
}
//return method.__call__(Py.javas2pys(args)).__tojava__(Object.class);
PyObject result;
if(args != null) {
result = method.__call__(Py.javas2pys(args));
} else {
result = method.__call__();
}
return result.__tojava__(Object.class);
} catch (PyException pye) {
throw scriptException(pye);
}
}
public Object invokeFunction(String name, Object... args) throws ScriptException,
NoSuchMethodException {
try {
interp.setLocals(new PyScriptEngineScope(this, context));
PyObject function = interp.get(name);
if (function == null) {
throw new NoSuchMethodException(name);
}
PyObject result;
if(args != null) {
result = function.__call__(Py.javas2pys(args));
} else {
result = function.__call__();
}
return result.__tojava__(Object.class);
} catch (PyException pye) {
throw scriptException(pye);
}
}
public <T> T getInterface(Class<T> clazz) {
return getInterface(new PyModule("__jsr223__", interp.getLocals()), clazz);
}
public <T> T getInterface(Object obj, Class<T> clazz) {
if (obj == null) {
throw new IllegalArgumentException("object expected");
}
if (clazz == null || !clazz.isInterface()) {
throw new IllegalArgumentException("interface expected");
}
interp.setLocals(new PyScriptEngineScope(this, context));
final PyObject thiz = Py.java2py(obj);
@SuppressWarnings("unchecked")
T proxy = (T) Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class[] { clazz },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
interp.setLocals(new PyScriptEngineScope(PyScriptEngine.this, context));
PyObject pyMethod = thiz.__findattr__(method.getName());
if (pyMethod == null)
throw new NoSuchMethodException(method.getName());
PyObject result;
if(args != null) {
result = pyMethod.__call__(Py.javas2pys(args));
} else {
result = pyMethod.__call__();
}
return result.__tojava__(Object.class);
} catch (PyException pye) {
throw scriptException(pye);
}
}
});
return proxy;
}
private static ScriptException scriptException(PyException pye) {
ScriptException se = null;
try {
pye.normalize();
PyObject type = pye.type;
PyObject value = pye.value;
PyTraceback tb = pye.traceback;
if (__builtin__.isinstance(value, Py.SyntaxError)) {
PyObject filename = value.__findattr__("filename");
PyObject lineno = value.__findattr__("lineno");
PyObject offset = value.__findattr__("offset");
value = value.__findattr__("msg");
se = new ScriptException(
Py.formatException(type, value),
filename == null ? "<script>" : filename.toString(),
lineno == null ? 0 : lineno.asInt(),
offset == null ? 0 : offset.asInt());
} else if (tb != null) {
String filename;
if (tb.tb_frame == null || tb.tb_frame.f_code == null) {
filename = null;
} else {
filename = tb.tb_frame.f_code.co_filename;
}
se = new ScriptException(
Py.formatException(type, value),
filename,
tb.tb_lineno);
} else {
se = new ScriptException(Py.formatException(type, value));
}
se.initCause(pye);
return se;
} catch (Exception ee) {
se = new ScriptException(pye);
}
return se;
}
private class PyCompiledScript extends CompiledScript {
private PyCode code;
PyCompiledScript(PyCode code) {
this.code = code;
}
@Override
public ScriptEngine getEngine() {
return PyScriptEngine.this;
}
@Override
public Object eval(ScriptContext ctx) throws ScriptException {
return PyScriptEngine.this.eval(code, ctx);
}
}
}