001: /*
002: * JavaBindCmd.java --
003: *
004: * Implements the java::bind command.
005: *
006: * Copyright (c) 1997 Sun Microsystems, Inc.
007: *
008: * See the file "license.terms" for information on usage and
009: * redistribution of this file, and for a DISCLAIMER OF ALL
010: * WARRANTIES.
011: *
012: * RCS: @(#) $Id: JavaBindCmd.java,v 1.3 1999/05/09 21:44:48 dejong Exp $
013: */
014:
015: package tcl.lang;
016:
017: import java.lang.reflect.*;
018: import java.beans.*;
019: import java.util.*;
020:
021: /**
022: * This class implements the built-in "java::bind" command in Tcl.
023: */
024:
025: class JavaBindCmd implements Command {
026:
027: // The Bean Event Manager associated with the interp that owns this
028: // BindCmd instance.
029:
030: BeanEventMgr eventMgr = null;
031:
032: // Caches the BeanInfo for each Java class. The
033: // Introspector.getBeanInfo class in JDK 1.2 returns new instances of
034: // BeanInfo for each call. That causes a lot of problems in Jacl,
035: // which assumes that there is the BeanInfo (and EventSetDescriptor,
036: // etc) associated with each class is always constant (i.e., always the
037: // same object).
038: //
039: // This cache allows us to always use the same BeanInfo instance for each
040: // Java class.
041:
042: private Hashtable beanInfoCache = new Hashtable();
043:
044: /*
045: *----------------------------------------------------------------------
046: *
047: * cmdProc --
048: *
049: * This procedure is invoked as part of the Command interface to
050: * process the "java::bind" Tcl command. See the user
051: * documentation for details on what it does.
052: *
053: * Results:
054: * None.
055: *
056: * Side effects:
057: * See the user documentation.
058: *
059: *----------------------------------------------------------------------
060: */
061:
062: public void cmdProc(Interp interp, // Current interpreter.
063: TclObject argv[]) // Argument list.
064: throws TclException // A standard Tcl exception.
065: {
066: if ((argv.length < 2) || (argv.length > 4)) {
067: throw new TclNumArgsException(interp, 1, argv,
068: "javaObj ?eventName? ?command?");
069: }
070:
071: ReflectObject robj = ReflectObject.getReflectObject(interp,
072: argv[1]);
073:
074: if (eventMgr == null) {
075: eventMgr = BeanEventMgr.getBeanEventMgr(interp);
076: }
077:
078: if (argv.length == 2) {
079: // Return the list of all events handled by this widget.
080:
081: interp.setResult(eventMgr.getHandledEvents(robj));
082: } else {
083: EventSetDescriptor eventDesc;
084: Method method;
085:
086: Object arr[] = getEventMethod(interp, robj.javaObj,
087: robj.javaClass, argv[2].toString());
088:
089: eventDesc = (EventSetDescriptor) arr[0];
090:
091: if (!eventDesc.getListenerType().isInterface()) {
092: throw new TclException(interp,
093: "Cannot handle event listener: "
094: + "listererType \""
095: + eventDesc.getListenerType()
096: + "\" is not an interface");
097: }
098:
099: method = (Method) arr[1];
100:
101: if (argv.length == 3) {
102: // Return the script for the given event.
103:
104: TclObject script = eventMgr.getBinding(interp, robj,
105: eventDesc, method);
106:
107: if (script != null) {
108: interp.setResult(script);
109: } else {
110: interp.resetResult();
111: }
112: } else {
113: // Set the script for the given event.
114:
115: eventMgr.setBinding(interp, robj, eventDesc, method,
116: argv[3]);
117: }
118: }
119: }
120:
121: /*
122: *----------------------------------------------------------------------
123: *
124: * getEventMethod --
125: *
126: * Returns the EventSet and event listener method represented by
127: * a string name. The string name must be in one of the following
128: * formats:
129: * + className.listenerMethod
130: * + listenerMethod
131: * The first format will always work. The second and third format
132: * may cause an error if there is an ambiguity.
133: *
134: * Return value:
135: * If successful, returns an Object array of two elements. arr[0]
136: * is the EventSetDescriptor and arr[1] is the Method, as given
137: * by eventName.
138: *
139: * Side effects:
140: * None.
141: *
142: *----------------------------------------------------------------------
143: */
144:
145: Object[] getEventMethod(Interp interp, // Current interpreter.
146: Object obj, // The object whose event listener methods
147: // are to be queried.
148: Class cls, // The class of the event object
149: String eventName) // The string name of the event.
150: throws TclException // If the method cannot be found, or if
151: // eventName is ambiguous.
152: {
153: EventSetDescriptor eventDesc = null;
154: Method method = null;
155: int dotPos, i;
156:
157: search: {
158: BeanInfo beanInfo;
159:
160: try {
161: beanInfo = (BeanInfo) beanInfoCache.get(cls);
162: if (beanInfo == null) {
163: //System.out.println("Introspecting " + cls);
164: beanInfo = Introspector.getBeanInfo(cls);
165: beanInfoCache.put(cls, beanInfo);
166:
167: }
168: } catch (IntrospectionException e) {
169: break search;
170: }
171: EventSetDescriptor[] events = beanInfo
172: .getEventSetDescriptors();
173:
174: if (events == null) {
175: break search;
176: }
177:
178: dotPos = eventName.lastIndexOf('.');
179: if (dotPos == -1) {
180: // the event string specifies only the event method. Must
181: // ensure that exactly one event interface has this
182: // method.
183:
184: for (i = 0; i < events.length; i++) {
185: Method methods[] = events[i].getListenerType()
186: .getMethods();
187: for (int j = 0; j < methods.length; j++) {
188: if (methods[j].getName().equals(eventName)) {
189: if (method == null) {
190: method = methods[j];
191: eventDesc = events[i];
192: } else {
193: throw new TclException(interp,
194: "ambiguous event \""
195: + eventName + "\"");
196: }
197: }
198: }
199: }
200: } else {
201: String evtCls = eventName.substring(0, dotPos);
202: String evtMethod = eventName.substring(dotPos + 1);
203:
204: for (i = 0; i < events.length; i++) {
205: Class lsnType = events[i].getListenerType();
206: //System.out.println("event index " + i);
207: //if (evtCls == null) {System.out.println("null 1");}
208: //if (lsnType == null) {System.out.println("null 2");}
209: //if ((lsnType != null) && (evtCls == null)) {System.out.println("null 3");}
210: if (evtCls.equals(lsnType.getName())) {
211: eventDesc = events[i];
212: break;
213: }
214: }
215:
216: if (eventDesc == null) {
217: break search;
218: }
219:
220: Method methods[] = eventDesc.getListenerType()
221: .getMethods();
222:
223: if (methods == null) {
224: break search;
225: }
226: for (int j = 0; j < methods.length; j++) {
227: if (methods[j].getName().equals(evtMethod)) {
228: method = methods[j];
229: break;
230: }
231: }
232: }
233:
234: if (method != null) {
235: Object arr[] = new Object[2];
236: arr[0] = eventDesc;
237: arr[1] = method;
238: return arr;
239: }
240: }
241:
242: throw new TclException(interp, "unknown event \"" + eventName
243: + "\"");
244: }
245:
246: } // end JavaBindCmd
|