001: /*
002: * Copyright 2004,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.bsf.engines.jython;
018:
019: import java.io.ByteArrayInputStream;
020: import java.util.Vector;
021:
022: import org.apache.bsf.BSFDeclaredBean;
023: import org.apache.bsf.BSFException;
024: import org.apache.bsf.BSFManager;
025: import org.apache.bsf.util.BSFEngineImpl;
026: import org.apache.bsf.util.BSFFunctions;
027: import org.python.core.Py;
028: import org.python.core.PyException;
029: import org.python.core.PyJavaInstance;
030: import org.python.core.PyObject;
031: import org.python.util.InteractiveInterpreter;
032:
033: /**
034: * This is the interface to Jython (http://www.jython.org/) from BSF.
035: * It's derived from the JPython 1.x engine
036: *
037: * @author Sanjiva Weerawarana
038: * @author Finn Bock <bckfnn@worldonline.dk>
039: * @author Chuck Murcko
040: */
041:
042: public class JythonEngine extends BSFEngineImpl {
043: BSFPythonInterpreter interp;
044:
045: /**
046: * call the named method of the given object.
047: */
048: public Object call(Object object, String method, Object[] args)
049: throws BSFException {
050: try {
051: PyObject[] pyargs = Py.EmptyObjects;
052:
053: if (args != null) {
054: pyargs = new PyObject[args.length];
055: for (int i = 0; i < pyargs.length; i++)
056: pyargs[i] = Py.java2py(args[i]);
057: }
058:
059: if (object != null) {
060: PyObject o = Py.java2py(object);
061: return unwrap(o.invoke(method, pyargs));
062: }
063:
064: PyObject m = interp.get(method);
065:
066: if (m == null)
067: m = interp.eval(method);
068: if (m != null) {
069: return unwrap(m.__call__(pyargs));
070: }
071:
072: return null;
073: } catch (PyException e) {
074: throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
075: "exception from Jython:\n" + e, e);
076: }
077: }
078:
079: /**
080: * Declare a bean
081: */
082: public void declareBean(BSFDeclaredBean bean) throws BSFException {
083: interp.set(bean.name, bean.bean);
084: }
085:
086: /**
087: * Evaluate an anonymous function (differs from eval() in that apply()
088: * handles multiple lines).
089: */
090: public Object apply(String source, int lineNo, int columnNo,
091: Object funcBody, Vector paramNames, Vector arguments)
092: throws BSFException {
093: try {
094: /* We wrapper the original script in a function definition, and
095: * evaluate the function. A hack, no question, but it allows
096: * apply() to pretend to work on Jython.
097: */
098: StringBuffer script = new StringBuffer(byteify(funcBody
099: .toString()));
100: int index = 0;
101: script.insert(0, "def bsf_temp_fn():\n");
102:
103: while (index < script.length()) {
104: if (script.charAt(index) == '\n') {
105: script.insert(index + 1, '\t');
106: }
107: index++;
108: }
109:
110: interp.exec(script.toString());
111:
112: Object result = interp.eval("bsf_temp_fn()");
113:
114: if (result != null && result instanceof PyJavaInstance)
115: result = ((PyJavaInstance) result)
116: .__tojava__(Object.class);
117: return result;
118: } catch (PyException e) {
119: throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
120: "exception from Jython:\n" + e, e);
121: }
122: }
123:
124: /**
125: * Evaluate an expression.
126: */
127: public Object eval(String source, int lineNo, int columnNo,
128: Object script) throws BSFException {
129: try {
130: Object result = interp.eval(byteify(script.toString()));
131: if (result != null && result instanceof PyJavaInstance)
132: result = ((PyJavaInstance) result)
133: .__tojava__(Object.class);
134: return result;
135: } catch (PyException e) {
136: throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
137: "exception from Jython:\n" + e, e);
138: }
139: }
140:
141: /**
142: * Execute a script.
143: */
144: public void exec(String source, int lineNo, int columnNo,
145: Object script) throws BSFException {
146: try {
147: interp.exec(byteify(script.toString()));
148: } catch (PyException e) {
149: throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
150: "exception from Jython:\n" + e, e);
151: }
152: }
153:
154: /**
155: * Execute script code, emulating console interaction.
156: */
157: public void iexec(String source, int lineNo, int columnNo,
158: Object script) throws BSFException {
159: String scriptStr = byteify(script.toString());
160: int newline = scriptStr.indexOf("\n");
161:
162: if (newline > -1)
163: scriptStr = scriptStr.substring(0, newline);
164:
165: try {
166: if (interp.buffer.length() > 0)
167: interp.buffer.append("\n");
168: interp.buffer.append(scriptStr);
169: if (!(interp.runsource(interp.buffer.toString())))
170: interp.resetbuffer();
171: } catch (PyException e) {
172: interp.resetbuffer();
173: throw new BSFException(BSFException.REASON_EXECUTION_ERROR,
174: "exception from Jython:\n" + e, e);
175: }
176: }
177:
178: /**
179: * Initialize the engine.
180: */
181: public void initialize(BSFManager mgr, String lang,
182: Vector declaredBeans) throws BSFException {
183: super .initialize(mgr, lang, declaredBeans);
184:
185: // create an interpreter
186: interp = new BSFPythonInterpreter();
187:
188: // ensure that output and error streams are re-directed correctly
189: interp.setOut(System.out);
190: interp.setErr(System.err);
191:
192: // register the mgr with object name "bsf"
193: interp.set("bsf", new BSFFunctions(mgr, this ));
194:
195: // Declare all declared beans to the interpreter
196: int size = declaredBeans.size();
197: for (int i = 0; i < size; i++) {
198: declareBean((BSFDeclaredBean) declaredBeans.elementAt(i));
199: }
200: }
201:
202: /**
203: * Undeclare a previously declared bean.
204: */
205: public void undeclareBean(BSFDeclaredBean bean) throws BSFException {
206: interp.set(bean.name, null);
207: }
208:
209: public Object unwrap(PyObject result) {
210: if (result != null) {
211: Object ret = result.__tojava__(Object.class);
212: if (ret != Py.NoConversion)
213: return ret;
214: }
215: return result;
216: }
217:
218: private String byteify(String orig) {
219: // Ugh. Jython likes to be fed bytes, rather than the input string.
220: ByteArrayInputStream bais = new ByteArrayInputStream(orig
221: .getBytes());
222: StringBuffer s = new StringBuffer();
223: int c;
224:
225: while ((c = bais.read()) >= 0) {
226: s.append((char) c);
227: }
228:
229: return s.toString();
230: }
231:
232: private class BSFPythonInterpreter extends InteractiveInterpreter {
233:
234: public BSFPythonInterpreter() {
235: super ();
236: }
237:
238: // Override runcode so as not to print the stack dump
239: public void runcode(PyObject code) {
240: try {
241: this .exec(code);
242: } catch (PyException exc) {
243: throw exc;
244: }
245: }
246: }
247: }
|