/* * JavaBindCmd.java -- * * Implements the java::bind command. * * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * RCS: @(#) $Id: JavaBindCmd.java,v 1.1.1.1 1998/10/14 21:09:14 cvsadmin Exp $ */ package tcl.lang; import java.lang.reflect.*; import java.beans.*; import java.util.*; /* * This class implements the built-in "java::bind" command in Tcl. */ class JavaBindCmd implements Command { /* * The Bean Event Manager associated with the interp that owns this * BindCmd instance. */ BeanEventMgr eventMgr = null; /* * Caches the BeanInfo for each Java class. The * Introspector.getBeanInfo class in JDK 1.2 returns new instances of * BeanInfo for each call. That causes a lot of problems in Jacl, * which assumes that there is the BeanInfo (and EventSetDescriptor, * etc) associated with each class is always constant (i.e., always the * same object). * * This cache allows us to always use the same BeanInfo instance for each * Java class. */ private Hashtable beanInfoCache = new Hashtable(); /* *---------------------------------------------------------------------- * * cmdProc -- * * This procedure is invoked as part of the Command interface to * process the "java::bind" Tcl command. See the user * documentation for details on what it does. * * Results: * None. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ public void cmdProc( Interp interp, // Current interpreter. TclObject argv[]) // Argument list. throws TclException // A standard Tcl exception. { if ((argv.length < 2) || (argv.length > 4)) { throw new TclNumArgsException(interp, 1, argv, "javaObj ?eventName? ?command?"); } ReflectObject robj = ReflectObject.getReflectObject(interp, argv[1]); Object obj = robj.javaObj; if (eventMgr == null) { eventMgr = BeanEventMgr.getBeanEventMgr(interp); } if (argv.length == 2) { /* * Return the list of all events handled by this widget. */ interp.setResult(eventMgr.getHandledEvents(robj)); } else { EventSetDescriptor eventDesc; Method method; Object arr[] = getEventMethod(interp, obj, argv[2].toString()); eventDesc = (EventSetDescriptor)arr[0]; if (!eventDesc.getListenerType().isInterface()) { throw new TclException(interp, "Cannot handle event listener: " + "listererType \"" + eventDesc.getListenerType() + "\" is not an interface"); } method = (Method)arr[1]; if (argv.length == 3) { /* * Return the script for the given event. */ TclObject script = eventMgr.getBinding(interp, robj, eventDesc, method); if (script != null) { interp.setResult(script); } else { interp.resetResult(); } } else { /* * Set the script for the given event. */ eventMgr.setBinding(interp, robj, eventDesc, method, argv[3]); } } } /* *---------------------------------------------------------------------- * * getEventMethod -- * * Returns the EventSet and event listener method represented by * a string name. The string name must be in one of the following * formats: * + className.listenerMethod * + listenerMethod * The first format will always work. The second and third format * may cause an error if there is an ambiguity. * * Return value: * If successful, returns an Object array of two elements. arr[0] * is the EventSetDescriptor and arr[1] is the Method, as given * by eventName. * * Side effects: * None. * *---------------------------------------------------------------------- */ Object[] getEventMethod( Interp interp, // Current interpreter. Object obj, // The object whose event listener methods // are to be queried. String eventName) // The string name of the event. throws TclException // If the method cannot be found, or if // eventName is ambiguous. { EventSetDescriptor eventDesc = null; Method method = null; int dotPos, i; search: { BeanInfo beanInfo; try { Class cls = obj.getClass(); beanInfo = (BeanInfo)beanInfoCache.get(cls); if (beanInfo == null) { beanInfo = Introspector.getBeanInfo(cls); beanInfoCache.put(cls, beanInfo); } } catch (IntrospectionException e) { break search; } EventSetDescriptor events[] = beanInfo.getEventSetDescriptors(); if (events == null) { break search; } dotPos = eventName.lastIndexOf('.'); if (dotPos == -1) { /* * the event string specifies only the event method. Must * ensure that exactly one event interface has this * method. */ for (i = 0; i < events.length; i++) { Method methods[] = events[i].getListenerType().getMethods(); for (int j=0; j < methods.length; j++) { if (methods[j].getName().equals(eventName)) { if (method == null) { method = methods[j]; eventDesc = events[i]; } else { throw new TclException(interp, "ambiguous event \"" + eventName + "\""); } } } } } else { String evtCls = eventName.substring(0, dotPos); String evtMethod = eventName.substring(dotPos + 1); for (i = 0; i < events.length; i++) { Class lsnType = events[i].getListenerType(); if (evtCls.equals(lsnType.getName())) { eventDesc = events[i]; break; } } if (eventDesc == null) { break search; } Method methods[] = eventDesc.getListenerType().getMethods(); if (methods == null) { break search; } for (int j = 0; j < methods.length; j++) { if (methods[j].getName().equals(evtMethod)) { method = methods[j]; break; } } } if (method != null) { Object arr[] = new Object[2]; arr[0] = eventDesc; arr[1] = method; return arr; } } throw new TclException(interp, "unknown event \"" + eventName + "\""); } } // end JavaBindCmd