blob: dcbfc041cac092269e50c23f9c075c82f9041989 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.bsf.engines.jython;
import java.beans.PropertyChangeEvent;
import java.io.ByteArrayInputStream;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.bsf.BSFDeclaredBean;
import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
import org.apache.bsf.util.BSFEngineImpl;
import org.apache.bsf.util.BSFFunctions;
import org.python.core.Py;
import org.python.core.PyException;
import org.python.core.PyJavaInstance;
import org.python.core.PyObject;
import org.python.core.PySystemState;
import org.python.util.InteractiveInterpreter;
/**
* This is the interface to Jython (http://www.jython.org/) from BSF.
* It's derived from the JPython 1.x engine
*
* @author Sanjiva Weerawarana
* @author Finn Bock <bckfnn@worldonline.dk>
* @author Chuck Murcko
* @author Sonny To" <son.c.to@gmail.com>, 2006-10-30
*/
public class JythonEngine extends BSFEngineImpl {
BSFPythonInterpreter interp;
private final static Pattern fromRegExp = Pattern.compile("from ([.^\\S]*)");
/**
* call the named method of the given object.
*/
public Object call (Object object, String method, Object[] args)
throws BSFException {
try {
PyObject[] pyargs = Py.EmptyObjects;
if (args != null) {
pyargs = new PyObject[args.length];
for (int i = 0; i < pyargs.length; i++)
pyargs[i] = Py.java2py(args[i]);
}
if (object != null) {
PyObject o = Py.java2py(object);
return unwrap(o.invoke(method, pyargs));
}
PyObject m = interp.get(method);
if (m == null)
m = interp.eval(method);
if (m != null) {
return unwrap(m.__call__(pyargs));
}
return null;
} catch (PyException e) {
throw new BSFException (BSFException.REASON_EXECUTION_ERROR,
"exception from Jython:\n" + e, e);
}
}
/**
* Declare a bean
*/
public void declareBean (BSFDeclaredBean bean) throws BSFException {
interp.set (bean.name, bean.bean);
}
/**
* Evaluate an anonymous function (differs from eval() in that apply()
* handles multiple lines).
*/
public Object apply (String source, int lineNo, int columnNo,
Object funcBody, Vector paramNames,
Vector arguments) throws BSFException {
try {
/* We wrapper the original script in a function definition, and
* evaluate the function. A hack, no question, but it allows
* apply() to pretend to work on Jython.
*/
StringBuffer script = new StringBuffer(byteify(funcBody.toString()));
int index = 0;
script.insert(0, "def bsf_temp_fn():\n");
while (index < script.length()) {
if (script.charAt(index) == '\n') {
script.insert(index+1, '\t');
}
index++;
}
String scriptStr = script.toString ();
importPackage(scriptStr);
interp.exec (scriptStr);
Object result = interp.eval ("bsf_temp_fn()");
if (result instanceof PyJavaInstance)
result = ((PyJavaInstance)result).__tojava__(Object.class);
return result;
} catch (PyException e) {
throw new BSFException (BSFException.REASON_EXECUTION_ERROR,
"exception from Jython:\n" + e, e);
}
}
/**
* Evaluate an expression.
*/
public Object eval (String source, int lineNo, int columnNo,
Object script) throws BSFException {
try {
String scriptStr = byteify(script.toString ());
importPackage(scriptStr);
Object result = interp.eval (scriptStr);
if (result instanceof PyJavaInstance)
result = ((PyJavaInstance)result).__tojava__(Object.class);
return result;
} catch (PyException e) {
throw new BSFException (BSFException.REASON_EXECUTION_ERROR,
"exception from Jython:\n" + e, e);
}
}
/**
* Execute a script.
*/
public void exec (String source, int lineNo, int columnNo,
Object script) throws BSFException {
try {
String scriptStr = byteify(script.toString());
importPackage(scriptStr);
interp.exec (scriptStr);
} catch (PyException e) {
throw new BSFException (BSFException.REASON_EXECUTION_ERROR,
"exception from Jython:\n" + e, e);
}
}
private void importPackage(String script) {
Matcher matcher = fromRegExp.matcher(script);
while (matcher.find()) {
String packageName = matcher.group(1);
PySystemState.add_package(packageName);
}
}
/**
* Execute script code, emulating console interaction.
*/
public void iexec (String source, int lineNo, int columnNo,
Object script) throws BSFException {
String scriptStr = byteify(script.toString());
importPackage(scriptStr);
int newline = scriptStr.indexOf("\n");
if (newline > -1)
scriptStr = scriptStr.substring(0, newline);
try {
if (interp.buffer.length() > 0)
interp.buffer.append("\n");
interp.buffer.append(scriptStr);
if (!(interp.runsource(interp.buffer.toString())))
interp.resetbuffer();
} catch (PyException e) {
interp.resetbuffer();
throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
"exception from Jython:\n" + e, e);
}
}
/**
* Initialize the engine.
*/
public void initialize (BSFManager mgr, String lang,
Vector declaredBeans) throws BSFException {
super.initialize (mgr, lang, declaredBeans);
// create an interpreter
interp = new BSFPythonInterpreter ();
// ensure that output and error streams are re-directed correctly
interp.setOut(System.out);
interp.setErr(System.err);
// register the mgr with object name "bsf"
interp.set ("bsf", new BSFFunctions (mgr, this));
// Declare all declared beans to the interpreter
int size = declaredBeans.size ();
for (int i = 0; i < size; i++) {
declareBean ((BSFDeclaredBean) declaredBeans.elementAt (i));
}
}
/**
* Undeclare a previously declared bean.
*/
public void undeclareBean (BSFDeclaredBean bean) throws BSFException {
interp.set (bean.name, null);
}
public Object unwrap(PyObject result) {
if (result != null) {
Object ret = result.__tojava__(Object.class);
if (ret != Py.NoConversion)
return ret;
}
return result;
}
private String byteify (String orig) {
// Ugh. Jython likes to be fed bytes, rather than the input string.
ByteArrayInputStream bais =
new ByteArrayInputStream(orig.getBytes());
StringBuffer s = new StringBuffer();
int c;
while ((c = bais.read()) >= 0) {
s.append((char)c);
}
return s.toString();
}
private class BSFPythonInterpreter extends InteractiveInterpreter {
public BSFPythonInterpreter() {
}
// Override runcode so as not to print the stack dump
public void runcode(PyObject code) {
try {
this.exec(code);
} catch (PyException exc) {
throw exc;
}
}
}
public void propertyChange(PropertyChangeEvent e) {
super.propertyChange(e);
String name = e.getPropertyName();
Object value = e.getNewValue();
if (name.equals("classLoader")) {
Py.getSystemState().setClassLoader((ClassLoader) value);
}
}
}