001: /*****************************************************************************
002: * *
003: * This file is part of the BeanShell Java Scripting distribution. *
004: * Documentation and updates may be found at http://www.beanshell.org/ *
005: * *
006: * Sun Public License Notice: *
007: * *
008: * The contents of this file are subject to the Sun Public License Version *
009: * 1.0 (the "License"); you may not use this file except in compliance with *
010: * the License. A copy of the License is available at http://www.sun.com *
011: * *
012: * The Original Code is BeanShell. The Initial Developer of the Original *
013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
014: * (C) 2000. All Rights Reserved. *
015: * *
016: * GNU Public License Notice: *
017: * *
018: * Alternatively, the contents of this file may be used under the terms of *
019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
020: * provisions of LGPL are applicable instead of those above. If you wish to *
021: * allow use of your version of this file only under the terms of the LGPL *
022: * and not to allow others to use your version of this file under the SPL, *
023: * indicate your decision by deleting the provisions above and replace *
024: * them with the notice and other provisions required by the LGPL. If you *
025: * do not delete the provisions above, a recipient may use your version of *
026: * this file under either the SPL or the LGPL. *
027: * *
028: * Patrick Niemeyer (pat@pat.net) *
029: * Author of Learning Java, O'Reilly & Associates *
030: * http://www.pat.net/~pat/ *
031: * *
032: *****************************************************************************/package bsh;
033:
034: import java.io.IOException;
035:
036: /**
037: 'This' is the type of bsh scripted objects.
038: A 'This' object is a bsh scripted object context. It holds a namespace
039: reference and implements event listeners and various other interfaces.
040:
041: This holds a reference to the declaring interpreter for callbacks from
042: outside of bsh.
043: */
044: public class This implements java.io.Serializable, Runnable {
045: /**
046: The namespace that this This reference wraps.
047: */
048: NameSpace namespace;
049:
050: /**
051: This is the interpreter running when the This ref was created.
052: It's used as a default interpreter for callback through the This
053: where there is no current interpreter instance
054: e.g. interface proxy or event call backs from outside of bsh.
055: */
056: transient Interpreter declaringInterpreter;
057:
058: /**
059: getThis() is a factory for bsh.This type references. The capabilities
060: of ".this" references in bsh are version dependent up until jdk1.3.
061: The version dependence was to support different default interface
062: implementations. i.e. different sets of listener interfaces which
063: scripted objects were capable of implementing. In jdk1.3 the
064: reflection proxy mechanism was introduced which allowed us to
065: implement arbitrary interfaces. This is fantastic.
066:
067: A This object is a thin layer over a namespace, comprising a bsh object
068: context. We create it here only if needed for the namespace.
069:
070: Note: this method could be considered slow because of the way it
071: dynamically factories objects. However I've also done tests where
072: I hard-code the factory to return JThis and see no change in the
073: rough test suite time. This references are also cached in NameSpace.
074: */
075: static This getThis(NameSpace namespace,
076: Interpreter declaringInterpreter) {
077: try {
078: Class c;
079: if (Capabilities.canGenerateInterfaces())
080: c = Class.forName("bsh.XThis");
081: else if (Capabilities.haveSwing())
082: c = Class.forName("bsh.JThis");
083: else
084: return new This(namespace, declaringInterpreter);
085:
086: return (This) Reflect.constructObject(c, new Object[] {
087: namespace, declaringInterpreter });
088:
089: } catch (Exception e) {
090: throw new InterpreterError("internal error 1 in This: " + e);
091: }
092: }
093:
094: /**
095: Get a version of this scripted object implementing the specified
096: interface.
097: */
098: /*
099: If this type of This implements it directly return this,
100: else try complain that we don't have the proxy mechanism.
101: */
102: public Object getInterface(Class clas) throws UtilEvalError {
103: if (clas.isInstance(this ))
104: return this ;
105: else
106: throw new UtilEvalError(
107: "Dynamic proxy mechanism not available. "
108: + "Cannot construct interface type: "
109: + clas);
110: }
111:
112: /**
113: Get a version of this scripted object implementing the specified
114: interfaces.
115: */
116: public Object getInterface(Class[] ca) throws UtilEvalError {
117: for (int i = 0; i < ca.length; i++)
118: if (!(ca[i].isInstance(this )))
119: throw new UtilEvalError(
120: "Dynamic proxy mechanism not available. "
121: + "Cannot construct interface type: "
122: + ca[i]);
123:
124: return this ;
125: }
126:
127: /*
128: I wish protected access were limited to children and not also
129: package scope... I want this to be a singleton implemented by various
130: children.
131: */
132: protected This(NameSpace namespace, Interpreter declaringInterpreter) {
133: this .namespace = namespace;
134: this .declaringInterpreter = declaringInterpreter;
135: //initCallStack( namespace );
136: }
137:
138: public NameSpace getNameSpace() {
139: return namespace;
140: }
141:
142: public String toString() {
143: return "'this' reference to Bsh object: " + namespace;
144: }
145:
146: public void run() {
147: try {
148: invokeMethod("run", new Object[0]);
149: } catch (EvalError e) {
150: declaringInterpreter.error("Exception in runnable:" + e);
151: }
152: }
153:
154: /**
155: Invoke specified method as from outside java code, using the
156: declaring interpreter and current namespace.
157: The call stack will indicate that the method is being invoked from
158: outside of bsh in native java code.
159: Note: you must still wrap/unwrap args/return values using
160: Primitive/Primitive.unwrap() for use outside of BeanShell.
161: @see bsh.Primitive
162: */
163: public Object invokeMethod(String name, Object[] args)
164: throws EvalError {
165: // null callstack, one will be created for us
166: return invokeMethod(name, args, null/*declaringInterpreter*/,
167: null, null, false/*declaredOnly*/);
168: }
169:
170: /**
171: Invoke a method in this namespace with the specified args,
172: interpreter reference, callstack, and caller info.
173: <p>
174:
175: Note: If you use this method outside of the bsh package and wish to
176: use variables with primitive values you will have to wrap them using
177: bsh.Primitive. Consider using This getInterface() to make a true Java
178: interface for invoking your scripted methods.
179: <p>
180:
181: This method also implements the default object protocol of toString(),
182: hashCode() and equals() and the invoke() meta-method handling as a
183: last resort.
184: <p>
185:
186: Note: The invoke() meta-method will not catch the Object protocol
187: methods (toString(), hashCode()...). If you want to override them you
188: have to script them directly.
189: <p>
190:
191: @see bsh.This.invokeMethod(
192: String methodName, Object [] args, Interpreter interpreter,
193: CallStack callstack, SimpleNode callerInfo )
194: @param if callStack is null a new CallStack will be created and
195: initialized with this namespace.
196: @param declaredOnly if true then only methods declared directly in the
197: namespace will be visible - no inherited or imported methods will
198: be visible.
199: @see bsh.Primitive
200: */
201: /*
202: invokeMethod() here is generally used by outside code to callback
203: into the bsh interpreter. e.g. when we are acting as an interface
204: for a scripted listener, etc. In this case there is no real call stack
205: so we make a default one starting with the special JAVACODE namespace
206: and our namespace as the next.
207: */
208: public Object invokeMethod(String methodName, Object[] args,
209: Interpreter interpreter, CallStack callstack,
210: SimpleNode callerInfo, boolean declaredOnly)
211: throws EvalError {
212: /*
213: Wrap nulls.
214: This is a bit of a cludge to address a deficiency in the class
215: generator whereby it does not wrap nulls on method delegate. See
216: Class Generator.java. If we fix that then we can remove this.
217: (just have to generate the code there.)
218: */
219: if (args != null) {
220: Object[] oa = new Object[args.length];
221: for (int i = 0; i < args.length; i++)
222: oa[i] = (args[i] == null ? Primitive.NULL : args[i]);
223: args = oa;
224: }
225:
226: if (interpreter == null)
227: interpreter = declaringInterpreter;
228: if (callstack == null)
229: callstack = new CallStack(namespace);
230: if (callerInfo == null)
231: callerInfo = SimpleNode.JAVACODE;
232:
233: // Find the bsh method
234: Class[] types = Types.getTypes(args);
235: BshMethod bshMethod = null;
236: try {
237: bshMethod = namespace.getMethod(methodName, types,
238: declaredOnly);
239: } catch (UtilEvalError e) {
240: // leave null
241: }
242:
243: if (bshMethod != null)
244: return bshMethod.invoke(args, interpreter, callstack,
245: callerInfo);
246:
247: /*
248: No scripted method of that name.
249: Implement the required part of the Object protocol:
250: public int hashCode();
251: public boolean equals(java.lang.Object);
252: public java.lang.String toString();
253: if these were not handled by scripted methods we must provide
254: a default impl.
255: */
256: // a default toString() that shows the interfaces we implement
257: if (methodName.equals("toString"))
258: return toString();
259:
260: // a default hashCode()
261: if (methodName.equals("hashCode"))
262: return new Integer(this .hashCode());
263:
264: // a default equals() testing for equality with the This reference
265: if (methodName.equals("equals")) {
266: Object obj = args[0];
267: return this == obj ? Boolean.TRUE : Boolean.FALSE;
268: }
269:
270: // Look for a default invoke() handler method in the namespace
271: // Note: this code duplicates that in NameSpace getCommand()
272: // is that ok?
273: try {
274: bshMethod = namespace.getMethod("invoke", new Class[] {
275: null, null });
276: } catch (UtilEvalError e) { /*leave null*/
277: }
278:
279: // Call script "invoke( String methodName, Object [] args );
280: if (bshMethod != null)
281: return bshMethod.invoke(new Object[] { methodName, args },
282: interpreter, callstack, callerInfo);
283:
284: throw new EvalError("Method "
285: + StringUtil.methodString(methodName, types)
286: + " not found in bsh scripted object: "
287: + namespace.getName(), callerInfo, callstack);
288: }
289:
290: /**
291: Bind a This reference to a parent's namespace with the specified
292: declaring interpreter. Also re-init the callstack. It's necessary
293: to bind a This reference before it can be used after deserialization.
294: This is used by the bsh load() command.
295: <p>
296:
297: This is a static utility method because it's used by a bsh command
298: bind() and the interpreter doesn't currently allow access to direct
299: methods of This objects (small hack)
300: */
301: public static void bind(This ths, NameSpace namespace,
302: Interpreter declaringInterpreter) {
303: ths.namespace.setParent(namespace);
304: ths.declaringInterpreter = declaringInterpreter;
305: }
306:
307: /**
308: Allow invocations of these method names on This type objects.
309: Don't give bsh.This a chance to override their behavior.
310: <p>
311:
312: If the method is passed here the invocation will actually happen on
313: the bsh.This object via the regular reflective method invocation
314: mechanism. If not, then the method is evaluated by bsh.This itself
315: as a scripted method call.
316: */
317: static boolean isExposedThisMethod(String name) {
318: return name.equals("getClass") || name.equals("invokeMethod")
319: || name.equals("getInterface")
320: // These are necessary to let us test synchronization from scripts
321: || name.equals("wait") || name.equals("notify")
322: || name.equals("notifyAll");
323: }
324:
325: }
|