001: package bsh.util;
002:
003: /*
004: This file is associated with the BeanShell Java Scripting language
005: distribution (http://www.beanshell.org/).
006:
007: This file is hereby placed into the public domain... You may copy,
008: modify, and redistribute it without restriction.
009: */
010:
011: import java.util.Vector;
012: import org.apache.bsf.*;
013: import org.apache.bsf.util.*;
014: import bsh.Interpreter;
015: import bsh.InterpreterError;
016: import bsh.EvalError;
017: import bsh.TargetError;
018: import bsh.Primitive;
019:
020: /**
021: This is the BeanShell adapter for IBM's Bean Scripting Famework.
022: It is an implementation of the BSFEngine class, allowing BSF aware
023: applications to use BeanShell as a scripting language.
024: <p>
025:
026: I believe this implementation is complete (with some hesitation about the
027: the usefullness of the compileXXX() style methods - provided by the base
028: utility class).
029: <p/>
030:
031: @author Pat Niemeyer
032: */
033: public class BeanShellBSFEngine extends BSFEngineImpl {
034: Interpreter interpreter;
035: boolean installedApplyMethod;
036:
037: public void initialize(BSFManager mgr, String lang,
038: Vector declaredBeans) throws BSFException {
039: super .initialize(mgr, lang, declaredBeans);
040:
041: interpreter = new Interpreter();
042:
043: // declare the bsf manager for callbacks, etc.
044: try {
045: interpreter.set("bsf", mgr);
046: } catch (EvalError e) {
047: throw new BSFException("bsh internal error: "
048: + e.toString());
049: }
050:
051: for (int i = 0; i < declaredBeans.size(); i++) {
052: BSFDeclaredBean bean = (BSFDeclaredBean) declaredBeans
053: .get(i);
054: declareBean(bean);
055: }
056: }
057:
058: public void setDebug(boolean debug) {
059: interpreter.DEBUG = debug;
060: }
061:
062: /**
063: Invoke method name on the specified bsh scripted object.
064: The object may be null to indicate the global namespace of the
065: interpreter.
066: @param object may be null for the global namespace.
067: */
068: public Object call(Object object, String name, Object[] args)
069: throws BSFException {
070: /*
071: If object is null use the interpreter's global scope.
072: */
073: if (object == null)
074: try {
075: object = interpreter.get("global");
076: } catch (EvalError e) {
077: throw new BSFException("bsh internal error: "
078: + e.toString());
079: }
080:
081: if (object instanceof bsh.This)
082: try {
083: Object value = ((bsh.This) object).invokeMethod(name,
084: args);
085: return Primitive.unwrap(value);
086: } catch (InterpreterError e) {
087: throw new BSFException(
088: "BeanShell interpreter internal error: " + e);
089: } catch (TargetError e2) {
090: throw new BSFException(
091: "The application script threw an exception: "
092: + e2.getTarget());
093: } catch (EvalError e3) {
094: throw new BSFException("BeanShell script error: " + e3);
095: }
096: else
097: throw new BSFException("Cannot invoke method: " + name
098: + ". Object: " + object
099: + " is not a BeanShell scripted object.");
100: }
101:
102: /**
103: A helper BeanShell method that implements the anonymous method apply
104: proposed by BSF. Note that the script below could use the standard
105: bsh eval() method to set the variables and apply the text, however
106: then I'd have to escape quotes, etc.
107: */
108: final static String bsfApplyMethod = "_bsfApply( _bsfNames, _bsfArgs, _bsfText ) {"
109: + "for(i=0;i<_bsfNames.length;i++)"
110: + "this.namespace.setVariable(_bsfNames[i], _bsfArgs[i],false);"
111: + "return this.interpreter.eval(_bsfText, this.namespace);"
112: + "}";
113:
114: /**
115: This is an implementation of the BSF apply() method.
116: It exectutes the funcBody text in an "anonymous" method call with
117: arguments.
118: */
119: /*
120: Note: the apply() method may be supported directly in BeanShell in an
121: upcoming release and would not require special support here.
122: */
123: public Object apply(String source, int lineNo, int columnNo,
124: Object funcBody, Vector namesVec, Vector argsVec)
125: throws BSFException {
126: if (namesVec.size() != argsVec.size())
127: throw new BSFException("number of params/names mismatch");
128: if (!(funcBody instanceof String))
129: throw new BSFException(
130: "apply: functino body must be a string");
131:
132: String[] names = new String[namesVec.size()];
133: namesVec.copyInto(names);
134: Object[] args = new Object[argsVec.size()];
135: argsVec.copyInto(args);
136:
137: try {
138: if (!installedApplyMethod) {
139: interpreter.eval(bsfApplyMethod);
140: installedApplyMethod = true;
141: }
142:
143: bsh.This global = (bsh.This) interpreter.get("global");
144: Object value = global.invokeMethod("_bsfApply",
145: new Object[] { names, args, (String) funcBody });
146: return Primitive.unwrap(value);
147:
148: } catch (InterpreterError e) {
149: throw new BSFException(
150: "BeanShell interpreter internal error: " + e
151: + sourceInfo(source, lineNo, columnNo));
152: } catch (TargetError e2) {
153: throw new BSFException(
154: "The application script threw an exception: "
155: + e2.getTarget()
156: + sourceInfo(source, lineNo, columnNo));
157: } catch (EvalError e3) {
158: throw new BSFException("BeanShell script error: " + e3
159: + sourceInfo(source, lineNo, columnNo));
160: }
161: }
162:
163: public Object eval(String source, int lineNo, int columnNo,
164: Object expr) throws BSFException {
165: if (!(expr instanceof String))
166: throw new BSFException(
167: "BeanShell expression must be a string");
168:
169: try {
170: return interpreter.eval(((String) expr));
171: } catch (InterpreterError e) {
172: throw new BSFException(
173: "BeanShell interpreter internal error: " + e
174: + sourceInfo(source, lineNo, columnNo));
175: } catch (TargetError e2) {
176: throw new BSFException(
177: "The application script threw an exception: "
178: + e2.getTarget()
179: + sourceInfo(source, lineNo, columnNo));
180: } catch (EvalError e3) {
181: throw new BSFException("BeanShell script error: " + e3
182: + sourceInfo(source, lineNo, columnNo));
183: }
184: }
185:
186: public void exec(String source, int lineNo, int columnNo,
187: Object script) throws BSFException {
188: eval(source, lineNo, columnNo, script);
189: }
190:
191: /*
192: I don't quite understand these compile methods. The default impl
193: will use the CodeBuffer utility to produce an example (Test) class that
194: turns around and invokes the BSF Manager to call the script again.
195:
196: I assume a statically compiled language would return a real implementation
197: class adapter here? But in source code form? Would't it be more likely
198: to generate bytecode?
199:
200: And shouldn't a non-compiled language simply return a standard
201: precompiled adapter to itself? The indirection of building a source
202: class to call the scripting engine (possibly through the interpreter)
203: seems kind of silly.
204: */
205: /*
206: public void compileApply (String source, int lineNo, int columnNo,
207: Object funcBody, Vector paramNames, Vector arguments, CodeBuffer cb)
208: throws BSFException;
209:
210: public void compileExpr (String source, int lineNo, int columnNo,
211: Object expr, CodeBuffer cb) throws BSFException;
212:
213: public void compileScript (String source, int lineNo, int columnNo,
214: Object script, CodeBuffer cb) throws BSFException;
215: */
216:
217: public void declareBean(BSFDeclaredBean bean) throws BSFException {
218: try {
219: interpreter.set(bean.name, bean.bean);
220: } catch (EvalError e) {
221: throw new BSFException("error declaring bean: " + bean.name
222: + " : " + e.toString());
223: }
224: }
225:
226: public void undeclareBean(BSFDeclaredBean bean) throws BSFException {
227: try {
228: interpreter.unset(bean.name);
229: } catch (EvalError e) {
230: throw new BSFException("bsh internal error: "
231: + e.toString());
232: }
233: }
234:
235: public void terminate() {
236: }
237:
238: private String sourceInfo(String source, int lineNo, int columnNo) {
239: return " BSF info: " + source + " at line: " + lineNo
240: + " column: columnNo";
241: }
242:
243: }
|