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