diff --git a/extensions/jython/module/MOD-INF/lib/jython-2.5.1.jar b/extensions/jython/module/MOD-INF/lib/jython-2.5.1.jar new file mode 100644 index 000000000..56b9ce471 Binary files /dev/null and b/extensions/jython/module/MOD-INF/lib/jython-2.5.1.jar differ diff --git a/extensions/jython/module/MOD-INF/module.properties b/extensions/jython/module/MOD-INF/module.properties new file mode 100644 index 000000000..30937d12c --- /dev/null +++ b/extensions/jython/module/MOD-INF/module.properties @@ -0,0 +1,3 @@ +description = Gridworks Jython Extension + +templating = false diff --git a/extensions/jython/src/com/metaweb/gridworks/jython/JythonEvaluable.java b/extensions/jython/src/com/metaweb/gridworks/jython/JythonEvaluable.java new file mode 100644 index 000000000..f73e76691 --- /dev/null +++ b/extensions/jython/src/com/metaweb/gridworks/jython/JythonEvaluable.java @@ -0,0 +1,132 @@ +package com.metaweb.gridworks.jython; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import org.python.core.Py; +import org.python.core.PyException; +import org.python.core.PyFunction; +import org.python.core.PyNone; +import org.python.core.PyObject; +import org.python.core.PyString; +import org.python.util.PythonInterpreter; + +import com.metaweb.gridworks.expr.EvalError; +import com.metaweb.gridworks.expr.Evaluable; +import com.metaweb.gridworks.expr.HasFields; +import com.metaweb.gridworks.expr.LanguageSpecificParser; +import com.metaweb.gridworks.expr.ParsingException; + +public class JythonEvaluable implements Evaluable { + + static public LanguageSpecificParser createParser() { + return new LanguageSpecificParser() { + + @Override + public Evaluable parse(String s) throws ParsingException { + return new JythonEvaluable(s); + } + }; + } + + private static final String s_functionName = "___temp___"; + + private static PythonInterpreter _engine; + + // FIXME(SM): this initialization logic depends on the fact that the JVM's + // current working directory is the root of the Gridworks distributions + // or the development checkouts. While this works in practice, it would + // be preferable to have a more reliable address space, but since we + // don't have access to the servlet context from this class this is + // the best we can do for now. + static { + File libPath = new File("webapp/WEB-INF/lib/jython"); + if (!libPath.exists() && !libPath.canRead()) { + libPath = new File("main/webapp/WEB-INF/lib/jython"); + if (!libPath.exists() && !libPath.canRead()) { + libPath = null; + } + } + + if (libPath != null) { + Properties props = new Properties(); + props.setProperty("python.path", libPath.getAbsolutePath()); + PythonInterpreter.initialize(System.getProperties(), props, new String[] { "" }); + } + + _engine = new PythonInterpreter(); + } + + public JythonEvaluable(String s) { + // indent and create a function out of the code + String[] lines = s.split("\r\n|\r|\n"); + + StringBuffer sb = new StringBuffer(1024); + sb.append("def "); + sb.append(s_functionName); + sb.append("(value, cell, cells, row, rowIndex):"); + for (int i = 0; i < lines.length; i++) { + sb.append("\n "); + sb.append(lines[i]); + } + + _engine.exec(sb.toString()); + } + + public Object evaluate(Properties bindings) { + try { + // call the temporary PyFunction directly + Object result = ((PyFunction)_engine.get(s_functionName)).__call__( + new PyObject[] { + Py.java2py( bindings.get("value") ), + new JythonHasFieldsWrapper((HasFields) bindings.get("cell"), bindings), + new JythonHasFieldsWrapper((HasFields) bindings.get("cells"), bindings), + new JythonHasFieldsWrapper((HasFields) bindings.get("row"), bindings), + Py.java2py( bindings.get("rowIndex") ) + } + ); + + return unwrap(result); + } catch (PyException e) { + return new EvalError(e.getMessage()); + } + } + + protected Object unwrap(Object result) { + if (result != null) { + if (result instanceof JythonObjectWrapper) { + return ((JythonObjectWrapper) result)._obj; + } else if (result instanceof JythonHasFieldsWrapper) { + return ((JythonHasFieldsWrapper) result)._obj; + } else if (result instanceof PyString) { + return ((PyString) result).asString(); + } else if (result instanceof PyObject) { + return unwrap((PyObject) result); + } + } + + return result; + } + + protected Object unwrap(PyObject po) { + if (po instanceof PyNone) { + return null; + } else if (po.isNumberType()) { + return po.asDouble(); + } else if (po.isSequenceType()) { + Iterator i = po.asIterable().iterator(); + + List list = new ArrayList(); + while (i.hasNext()) { + list.add(unwrap((Object) i.next())); + } + + return list.toArray(); + } else { + return po; + } + } +} diff --git a/extensions/jython/src/com/metaweb/gridworks/jython/JythonHasFieldsWrapper.java b/extensions/jython/src/com/metaweb/gridworks/jython/JythonHasFieldsWrapper.java new file mode 100644 index 000000000..29d41af93 --- /dev/null +++ b/extensions/jython/src/com/metaweb/gridworks/jython/JythonHasFieldsWrapper.java @@ -0,0 +1,40 @@ +package com.metaweb.gridworks.jython; + +import java.util.Properties; + +import org.python.core.Py; +import org.python.core.PyObject; + +import com.metaweb.gridworks.expr.HasFields; + +public class JythonHasFieldsWrapper extends PyObject { + private static final long serialVersionUID = -1275353513262385099L; + + public HasFields _obj; + + private Properties _bindings; + + public JythonHasFieldsWrapper(HasFields obj, Properties bindings) { + _obj = obj; + _bindings = bindings; + } + + public PyObject __finditem__(PyObject key) { + String k = (String) key.__tojava__(String.class); + Object v = _obj.getField(k, _bindings); + if (v != null) { + if (v instanceof PyObject) { + return (PyObject) v; + } else if (v instanceof HasFields) { + return new JythonHasFieldsWrapper((HasFields) v, _bindings); + } else if (Py.getAdapter().canAdapt(v)) { + return Py.java2py(v); + } else { + return new JythonObjectWrapper(v); + } + } else { + return null; + } + } + +} diff --git a/extensions/jython/src/com/metaweb/gridworks/jython/JythonObjectWrapper.java b/extensions/jython/src/com/metaweb/gridworks/jython/JythonObjectWrapper.java new file mode 100644 index 000000000..fb10bc70c --- /dev/null +++ b/extensions/jython/src/com/metaweb/gridworks/jython/JythonObjectWrapper.java @@ -0,0 +1,17 @@ +package com.metaweb.gridworks.jython; + +import org.python.core.PyObject; + +public class JythonObjectWrapper extends PyObject { + private static final long serialVersionUID = -6608115027151667441L; + + public Object _obj; + + public JythonObjectWrapper(Object obj) { + _obj = obj; + } + + public String toString() { + return _obj.getClass().getSimpleName(); + } +}