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: import java.lang.reflect.*;
035: import java.lang.reflect.InvocationHandler;
036: import java.util.Hashtable;
037:
038: /**
039: XThis is a dynamically loaded extension which extends This.java and adds
040: support for the generalized interface proxy mechanism introduced in
041: JDK1.3. XThis allows bsh scripted objects to implement arbitrary
042: interfaces (be arbitrary event listener types).
043:
044: Note: This module relies on new features of JDK1.3 and will not compile
045: with JDK1.2 or lower. For those environments simply do not compile this
046: class.
047:
048: Eventually XThis should become simply This, but for backward compatability
049: we will maintain This without requiring support for the proxy mechanism.
050:
051: XThis stands for "eXtended This" (I had to call it something).
052:
053: @see JThis See also JThis with explicit JFC support for compatability.
054: @see This
055: */
056: public class XThis extends This {
057: /**
058: A cache of proxy interface handlers.
059: Currently just one per interface.
060: */
061: Hashtable interfaces;
062:
063: InvocationHandler invocationHandler = new Handler();
064:
065: public XThis(NameSpace namespace, Interpreter declaringInterp) {
066: super (namespace, declaringInterp);
067: }
068:
069: public String toString() {
070: return "'this' reference (XThis) to Bsh object: " + namespace;
071: }
072:
073: /**
074: Get dynamic proxy for interface, caching those it creates.
075: */
076: public Object getInterface(Class clas) {
077: return getInterface(new Class[] { clas });
078: }
079:
080: /**
081: Get dynamic proxy for interface, caching those it creates.
082: */
083: public Object getInterface(Class[] ca) {
084: if (interfaces == null)
085: interfaces = new Hashtable();
086:
087: // Make a hash of the interface hashcodes in order to cache them
088: int hash = 21;
089: for (int i = 0; i < ca.length; i++)
090: hash *= ca[i].hashCode() + 3;
091: Object hashKey = new Integer(hash);
092:
093: Object interf = interfaces.get(hashKey);
094:
095: if (interf == null) {
096: ClassLoader classLoader = ca[0].getClassLoader(); // ?
097: interf = Proxy.newProxyInstance(classLoader, ca,
098: invocationHandler);
099: interfaces.put(hashKey, interf);
100: }
101:
102: return interf;
103: }
104:
105: /**
106: This is the invocation handler for the dynamic proxy.
107: <p>
108:
109: Notes:
110: Inner class for the invocation handler seems to shield this unavailable
111: interface from JDK1.2 VM...
112:
113: I don't understand this. JThis works just fine even if those
114: classes aren't there (doesn't it?) This class shouldn't be loaded
115: if an XThis isn't instantiated in NameSpace.java, should it?
116: */
117: class Handler implements InvocationHandler, java.io.Serializable {
118: public Object invoke(Object proxy, Method method, Object[] args)
119: throws Throwable {
120: try {
121: return invokeImpl(proxy, method, args);
122: } catch (TargetError te) {
123: // Unwrap target exception. If the interface declares that
124: // it throws the ex it will be delivered. If not it will be
125: // wrapped in an UndeclaredThrowable
126: throw te.getTarget();
127: } catch (EvalError ee) {
128: // Ease debugging...
129: // XThis.this refers to the enclosing class instance
130: if (Interpreter.DEBUG)
131: Interpreter
132: .debug("EvalError in scripted interface: "
133: + XThis.this .toString() + ": " + ee);
134: throw ee;
135: }
136: }
137:
138: public Object invokeImpl(Object proxy, Method method,
139: Object[] args) throws EvalError {
140: String methodName = method.getName();
141: CallStack callstack = new CallStack(namespace);
142:
143: /*
144: If equals() is not explicitly defined we must override the
145: default implemented by the This object protocol for scripted
146: object. To support XThis equals() must test for equality with
147: the generated proxy object, not the scripted bsh This object;
148: otherwise callers from outside in Java will not see a the
149: proxy object as equal to itself.
150: */
151: BshMethod equalsMethod = null;
152: try {
153: equalsMethod = namespace.getMethod("equals",
154: new Class[] { Object.class });
155: } catch (UtilEvalError e) {/*leave null*/
156: }
157: if (methodName.equals("equals") && equalsMethod == null) {
158: Object obj = args[0];
159: return new Boolean(proxy == obj);
160: }
161:
162: /*
163: If toString() is not explicitly defined override the default
164: to show the proxy interfaces.
165: */
166: BshMethod toStringMethod = null;
167: try {
168: toStringMethod = namespace.getMethod("toString",
169: new Class[] {});
170: } catch (UtilEvalError e) {/*leave null*/
171: }
172:
173: if (methodName.equals("toString") && toStringMethod == null) {
174: Class[] ints = proxy.getClass().getInterfaces();
175: // XThis.this refers to the enclosing class instance
176: StringBuffer sb = new StringBuffer(XThis.this
177: .toString()
178: + "\nimplements:");
179: for (int i = 0; i < ints.length; i++)
180: sb.append(" " + ints[i].getName()
181: + ((ints.length > 1) ? "," : ""));
182: return sb.toString();
183: }
184:
185: Class[] paramTypes = method.getParameterTypes();
186: return Primitive.unwrap(invokeMethod(methodName, Primitive
187: .wrap(args, paramTypes)));
188: }
189: };
190: }
|