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