diff options
Diffstat (limited to 'libjava/classpath/java/beans/EventSetDescriptor.java')
-rw-r--r-- | libjava/classpath/java/beans/EventSetDescriptor.java | 1173 |
1 files changed, 747 insertions, 426 deletions
diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java index 8624e643476..381a453031e 100644 --- a/libjava/classpath/java/beans/EventSetDescriptor.java +++ b/libjava/classpath/java/beans/EventSetDescriptor.java @@ -1,39 +1,39 @@ /* java.beans.EventSetDescriptor - Copyright (C) 1998 Free Software Foundation, Inc. + Copyright (C) 1998, 2006 Free Software Foundation, Inc. -This file is part of GNU Classpath. + This file is part of GNU Classpath. -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. -GNU Classpath is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ package java.beans; @@ -45,398 +45,719 @@ import java.lang.reflect.Modifier; import java.util.Vector; /** - ** EventSetDescriptor describes the hookup between an event source - ** class and an event listener class. - ** - ** EventSets have several attributes: the listener class, the events - ** that can be fired to the listener (methods in the listener class), and - ** an add and remove listener method from the event firer's class.<P> - ** - ** The methods have these constraints on them:<P> - ** <UL> - ** <LI>event firing methods: must have <CODE>void</CODE> return value. Any - ** parameters and exceptions are allowed. May be public, protected or - ** package-protected. (Don't ask me why that is, I'm just following the spec. - ** The only place it is even mentioned is in the Java Beans white paper, and - ** there it is only implied.)</LI> - ** <LI>add listener method: must have <CODE>void</CODE> return value. Must - ** take exactly one argument, of the listener class's type. May fire either - ** zero exceptions, or one exception of type <CODE>java.util.TooManyListenersException</CODE>. - ** Must be public.</LI> - ** <LI>remove listener method: must have <CODE>void</CODE> return value. - ** Must take exactly one argument, of the listener class's type. May not - ** fire any exceptions. Must be public.</LI> - ** </UL> - ** - ** A final constraint is that event listener classes must extend from EventListener.<P> - ** - ** There are also various design patterns associated with some of the methods - ** of construction. Those are explained in more detail in the appropriate - ** constructors.<P> - ** - ** <STRONG>Documentation Convention:</STRONG> for proper - ** Internalization of Beans inside an RAD tool, sometimes there - ** are two names for a property or method: a programmatic, or - ** locale-independent name, which can be used anywhere, and a - ** localized, display name, for ease of use. In the - ** documentation I will specify different String values as - ** either <EM>programmatic</EM> or <EM>localized</EM> to - ** make this distinction clear. - ** - ** @author John Keiser - ** @since JDK1.1 - ** @version 1.1.0, 31 May 1998 - **/ - -public class EventSetDescriptor extends FeatureDescriptor { - private Method addListenerMethod; - private Method removeListenerMethod; - private Class listenerType; - private MethodDescriptor[] listenerMethodDescriptors; - private Method[] listenerMethods; - - private boolean unicast; - private boolean inDefaultEventSet = true; - - /** Create a new EventSetDescriptor. - ** This version of the constructor enforces the rules imposed on the methods - ** described at the top of this class, as well as searching for:<P> - ** <OL> - ** <LI>The event-firing method must be non-private with signature - ** <CODE>void <listenerMethodName>(<eventSetName>Event)</CODE> - ** (where <CODE><eventSetName></CODE> has its first character capitalized - ** by the constructor and the Event is a descendant of - ** <CODE>java.util.EventObject</CODE>) in class <CODE>listenerType</CODE> - ** (any exceptions may be thrown). - ** <B>Implementation note:</B> Note that there could conceivably be multiple - ** methods with this type of signature (example: java.util.MouseEvent vs. - ** my.very.own.MouseEvent). In this implementation, all methods fitting the - ** description will be put into the <CODE>EventSetDescriptor</CODE>, even - ** though the spec says only one should be chosen (they probably weren't thinking as - ** pathologically as I was). I don't like arbitrarily choosing things. - ** If your class has only one such signature, as most do, you'll have no problems.</LI> - ** <LI>The add and remove methods must be public and named - ** <CODE>void add<eventSetName>Listener(<listenerType>)</CODE> and - ** <CODE>void remove<eventSetName>Listener(<listenerType>)</CODE> in - ** in class <CODE>eventSourceClass</CODE>, where - ** <CODE><eventSetName></CODE> will have its first letter capitalized. - ** Standard exception rules (see class description) apply.</LI> - ** </OL> - ** @param eventSourceClass the class containing the add/remove listener methods. - ** @param eventSetName the programmatic name of the event set, generally starting - ** with a lowercase letter (i.e. fooManChu instead of FooManChu). This will be used - ** to generate the name of the event object as well as the names of the add and - ** remove methods. - ** @param listenerType the class containing the event firing method. - ** @param listenerMethodName the name of the event firing method. - ** @exception IntrospectionException if listenerType is not an EventListener, - ** or if methods are not found or are invalid. - **/ - public EventSetDescriptor(Class eventSourceClass, - String eventSetName, - Class listenerType, - String listenerMethodName) throws IntrospectionException { - setName(eventSetName); - if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { - throw new IntrospectionException("Listener type is not an EventListener."); - } - - String[] names = new String[1]; - names[0] = listenerMethodName; - - try { - eventSetName = Character.toUpperCase(eventSetName.charAt(0)) + eventSetName.substring(1); - } catch(StringIndexOutOfBoundsException e) { - eventSetName = ""; - } - - findMethods(eventSourceClass,listenerType,names,"add"+eventSetName+"Listener","remove"+eventSetName+"Listener",eventSetName+"Event"); - this.listenerType = listenerType; - checkAddListenerUnicast(); - if(this.removeListenerMethod.getExceptionTypes().length > 0) { - throw new IntrospectionException("Listener remove method throws exceptions."); - } - } - - /** Create a new EventSetDescriptor. - ** This form of the constructor allows you to specify the names of the methods and adds - ** no new constraints on top of the rules already described at the top of the class.<P> - ** - ** @param eventSourceClass the class containing the add and remove listener methods. - ** @param eventSetName the programmatic name of the event set, generally starting - ** with a lowercase letter (i.e. fooManChu instead of FooManChu). - ** @param listenerType the class containing the event firing methods. - ** @param listenerMethodNames the names of the even firing methods. - ** @param addListenerMethodName the name of the add listener method. - ** @param removeListenerMethodName the name of the remove listener method. - ** @exception IntrospectionException if listenerType is not an EventListener - ** or if methods are not found or are invalid. - **/ - public EventSetDescriptor(Class eventSourceClass, - String eventSetName, - Class listenerType, - String[] listenerMethodNames, - String addListenerMethodName, - String removeListenerMethodName) throws IntrospectionException { - setName(eventSetName); - if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { - throw new IntrospectionException("Listener type is not an EventListener."); - } - - findMethods(eventSourceClass,listenerType,listenerMethodNames,addListenerMethodName,removeListenerMethodName,null); - this.listenerType = listenerType; - checkAddListenerUnicast(); - if(this.removeListenerMethod.getExceptionTypes().length > 0) { - throw new IntrospectionException("Listener remove method throws exceptions."); - } - } - - /** Create a new EventSetDescriptor. - ** This form of constructor allows you to explicitly say which methods do what, and - ** no reflection is done by the EventSetDescriptor. The methods are, however, - ** checked to ensure that they follow the rules set forth at the top of the class. - ** @param eventSetName the programmatic name of the event set, generally starting - ** with a lowercase letter (i.e. fooManChu instead of FooManChu). - ** @param listenerType the class containing the listenerMethods. - ** @param listenerMethods the event firing methods. - ** @param addListenerMethod the add listener method. - ** @param removeListenerMethod the remove listener method. - ** @exception IntrospectionException if the listenerType is not an EventListener, - ** or any of the methods are invalid. - **/ - public EventSetDescriptor(String eventSetName, - Class listenerType, - Method[] listenerMethods, - Method addListenerMethod, - Method removeListenerMethod) throws IntrospectionException { - setName(eventSetName); - if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { - throw new IntrospectionException("Listener type is not an EventListener."); - } - - this.listenerMethods = listenerMethods; - this.addListenerMethod = addListenerMethod; - this.removeListenerMethod = removeListenerMethod; - this.listenerType = listenerType; - checkMethods(); - checkAddListenerUnicast(); - if(this.removeListenerMethod.getExceptionTypes().length > 0) { - throw new IntrospectionException("Listener remove method throws exceptions."); - } - } - - /** Create a new EventSetDescriptor. - ** This form of constructor allows you to explicitly say which methods do what, and - ** no reflection is done by the EventSetDescriptor. The methods are, however, - ** checked to ensure that they follow the rules set forth at the top of the class. - ** @param eventSetName the programmatic name of the event set, generally starting - ** with a lowercase letter (i.e. fooManChu instead of FooManChu). - ** @param listenerType the class containing the listenerMethods. - ** @param listenerMethodDescriptors the event firing methods. - ** @param addListenerMethod the add listener method. - ** @param removeListenerMethod the remove listener method. - ** @exception IntrospectionException if the listenerType is not an EventListener, - ** or any of the methods are invalid. - **/ - public EventSetDescriptor(String eventSetName, - Class listenerType, - MethodDescriptor[] listenerMethodDescriptors, - Method addListenerMethod, - Method removeListenerMethod) throws IntrospectionException { - setName(eventSetName); - if(!java.util.EventListener.class.isAssignableFrom(listenerType)) { - throw new IntrospectionException("Listener type is not an EventListener."); - } - - this.listenerMethodDescriptors = listenerMethodDescriptors; - this.listenerMethods = new Method[listenerMethodDescriptors.length]; - for(int i=0;i<this.listenerMethodDescriptors.length;i++) { - this.listenerMethods[i] = this.listenerMethodDescriptors[i].getMethod(); - } - - this.addListenerMethod = addListenerMethod; - this.removeListenerMethod = removeListenerMethod; - this.listenerType = listenerType; - checkMethods(); - checkAddListenerUnicast(); - if(this.removeListenerMethod.getExceptionTypes().length > 0) { - throw new IntrospectionException("Listener remove method throws exceptions."); - } - } - - /** Get the class that contains the event firing methods. **/ - public Class getListenerType() { - return listenerType; - } - - /** Get the event firing methods. **/ - public Method[] getListenerMethods() { - return listenerMethods; - } - - /** Get the event firing methods as MethodDescriptors. **/ - public MethodDescriptor[] getListenerMethodDescriptors() { - if(listenerMethodDescriptors == null) { - listenerMethodDescriptors = new MethodDescriptor[listenerMethods.length]; - for(int i=0;i<listenerMethods.length;i++) { - listenerMethodDescriptors[i] = new MethodDescriptor(listenerMethods[i]); - } - } - return listenerMethodDescriptors; - } - - /** Get the add listener method. **/ - public Method getAddListenerMethod() { - return addListenerMethod; - } - - /** Get the remove listener method. **/ - public Method getRemoveListenerMethod() { - return removeListenerMethod; - } - - /** Set whether or not multiple listeners may be added. - ** @param unicast whether or not multiple listeners may be added. - **/ - public void setUnicast(boolean unicast) { - this.unicast = unicast; - } - - /** Get whether or not multiple listeners may be added. (Defaults to false.) **/ - public boolean isUnicast() { - return unicast; - } - - /** Set whether or not this is in the default event set. - ** @param inDefaultEventSet whether this is in the default event set. - **/ - public void setInDefaultEventSet(boolean inDefaultEventSet) { - this.inDefaultEventSet = inDefaultEventSet; - } - - /** Get whether or not this is in the default event set. (Defaults to true.)**/ - public boolean isInDefaultEventSet() { - return inDefaultEventSet; - } - - private void checkAddListenerUnicast() throws IntrospectionException { - Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes(); - if(addListenerExceptions.length > 1) { - throw new IntrospectionException("Listener add method throws too many exceptions."); - } else if(addListenerExceptions.length == 1 - && !java.util.TooManyListenersException.class.isAssignableFrom(addListenerExceptions[0])) { - throw new IntrospectionException("Listener add method throws too many exceptions."); - } - } - - private void checkMethods() throws IntrospectionException { - if(!addListenerMethod.getDeclaringClass().isAssignableFrom(removeListenerMethod.getDeclaringClass()) - && !removeListenerMethod.getDeclaringClass().isAssignableFrom(addListenerMethod.getDeclaringClass())) { - throw new IntrospectionException("add and remove listener methods do not come from the same class. This is bad."); - } - if(!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE) - || addListenerMethod.getParameterTypes().length != 1 - || !listenerType.equals(addListenerMethod.getParameterTypes()[0]) - || !Modifier.isPublic(addListenerMethod.getModifiers())) { - throw new IntrospectionException("Add Listener Method invalid."); - } - if(!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE) - || removeListenerMethod.getParameterTypes().length != 1 - || !listenerType.equals(removeListenerMethod.getParameterTypes()[0]) - || removeListenerMethod.getExceptionTypes().length > 0 - || !Modifier.isPublic(removeListenerMethod.getModifiers())) { - throw new IntrospectionException("Remove Listener Method invalid."); - } - - for(int i=0;i<listenerMethods.length;i++) { - if(!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE) - || Modifier.isPrivate(listenerMethods[i].getModifiers())) { - throw new IntrospectionException("Event Method " + listenerMethods[i].getName() + " non-void or private."); - } - if(!listenerMethods[i].getDeclaringClass().isAssignableFrom(listenerType)) { - throw new IntrospectionException("Event Method " + listenerMethods[i].getName() + " not from class " + listenerType.getName()); - } - } - } - - private void findMethods(Class eventSourceClass, - Class listenerType, - String listenerMethodNames[], - String addListenerMethodName, - String removeListenerMethodName, - String absurdEventClassCheckName) throws IntrospectionException { - - /* Find add listener method and remove listener method. */ - Class[] listenerArgList = new Class[1]; - listenerArgList[0] = listenerType; - try { - this.addListenerMethod = eventSourceClass.getMethod(addListenerMethodName,listenerArgList); - } catch(SecurityException E) { - throw new IntrospectionException("SecurityException trying to access method " + addListenerMethodName + "."); - } catch(NoSuchMethodException E) { - throw new IntrospectionException("Could not find method " + addListenerMethodName + "."); - } - - if(this.addListenerMethod == null || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) { - throw new IntrospectionException("Add listener method does not exist, is not public, or is not void."); - } - - try { - this.removeListenerMethod = eventSourceClass.getMethod(removeListenerMethodName,listenerArgList); - } catch(SecurityException E) { - throw new IntrospectionException("SecurityException trying to access method " + removeListenerMethodName + "."); - } catch(NoSuchMethodException E) { - throw new IntrospectionException("Could not find method " + removeListenerMethodName + "."); - } - if(this.removeListenerMethod == null || !this.removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) { - throw new IntrospectionException("Remove listener method does not exist, is not public, or is not void."); - } - - /* Find the listener methods. */ - Method[] methods; - try { - methods = ClassHelper.getAllMethods(listenerType); - } catch(SecurityException E) { - throw new IntrospectionException("Security: You cannot access fields in this class."); - } - - Vector chosenMethods = new Vector(); - boolean[] listenerMethodFound = new boolean[listenerMethodNames.length]; - for(int i=0;i<methods.length;i++) { - if(Modifier.isPrivate(methods[i].getModifiers())) { - continue; - } - Method currentMethod = methods[i]; - Class retval = currentMethod.getReturnType(); - if(retval.equals(java.lang.Void.TYPE)) { - for(int j=0;j<listenerMethodNames.length;j++) { - if(currentMethod.getName().equals(listenerMethodNames[j]) - && (absurdEventClassCheckName == null - || (currentMethod.getParameterTypes().length == 1 - && ((currentMethod.getParameterTypes()[0]).getName().equals(absurdEventClassCheckName) - || (currentMethod.getParameterTypes()[0]).getName().endsWith("."+absurdEventClassCheckName) - ) - ) - ) - ) { - chosenMethods.addElement(currentMethod); - listenerMethodFound[j] = true; - } - } - } - } - - /* Make sure we found all the methods we were looking for. */ - for(int i=0;i<listenerMethodFound.length;i++) { - if(!listenerMethodFound[i]) { - throw new IntrospectionException("Could not find event method " + listenerMethodNames[i]); - } - } - - /* Now that we've chosen the listener methods we want, store them. */ - this.listenerMethods = new Method[chosenMethods.size()]; - for(int i=0;i<chosenMethods.size();i++) { - this.listenerMethods[i] = (Method)chosenMethods.elementAt(i); - } - } + * EventSetDescriptor describes the hookup between an event source class and + * an event listener class. + * + * <p>EventSets have several attributes: the listener class, + * the events that can be fired to the listener (methods in the listener + * class), and an add and remove listener method from the event firer's + * class. + * </p> + * + * <p> + * The methods have these constraints on them: + * <ul> + * <li>event firing methods: must have <code>void</code> return value. Any + * parameters and exceptions are allowed. May be public, protected or + * package-protected. (Don't ask me why that is, I'm just following the spec. + * The only place it is even mentioned is in the Java Beans white paper, and + * there it is only implied.)</li> + * + * <li>add listener method: must have <code>void</code> return value. Must + * take exactly one argument, of the listener class's type. May fire either + * zero exceptions, or one exception of type + * <code>java.util.TooManyListenersException</code>. + * Must be public.</li> + * + * <li>remove listener method: must have <code>void</code> return value. Must + * take exactly one argument, of the listener class's type. May not fire any + * exceptions. Must be public.</li> + * </ul> + * + * <p> + * A final constraint is that event listener classes must extend from + * EventListener. + * </p> + * + * <p> + * There are also various design patterns associated with some of the methods + * of construction. Those are explained in more detail in the appropriate + * constructors. + * </p> + * + * <p> + * <strong>Documentation Convention:</strong> for proper Internalization of + * Beans inside an RAD tool, sometimes there are two names for a property or + * method: a programmatic, or locale-independent name, which can be used + * anywhere, and a localized, display name, for ease of use. In the + * documentation I will specify different String values as either + * <em>programmatic</em> or <em>localized</em> to make this distinction clear. + * + * @author John Keiser + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.1 + */ + +public class EventSetDescriptor extends FeatureDescriptor +{ + private Method addListenerMethod; + + private Method removeListenerMethod; + + private Class listenerType; + + private MethodDescriptor[] listenerMethodDescriptors; + + private Method[] listenerMethods; + + private Method getListenerMethod; + + private boolean unicast; + + private boolean inDefaultEventSet = true; + + /** + * Creates a new <code>EventSetDescriptor</code<. + * + * <p> + * This version of the constructor enforces the rules imposed on the methods + * described at the top of this class, as well as searching for: + * </p> + * + * <ol> + * <li> + * The event-firing method must be non-private with signature <code>void + * <listenerMethodName>(<eventSetName>Event)</code> (where + * <code><eventSetName></code> has its first character capitalized + * by the constructor and the Event is a descendant of + * {@link java.util.EventObject}) in class <code>listenerType</code> + * (any exceptions may be thrown). <b>Implementation note:</b> Note that + * there could conceivably be multiple methods with this type of signature + * (example: <code>java.util.MouseEvent</code> vs. + * <code>my.very.own.MouseEvent</code>). In this implementation, all + * methods fitting the description will be put into the + * <code>EventSetDescriptor</code>, even though the spec says only one + * should be chosen (they probably weren't thinking as pathologically as I + * was). I don't like arbitrarily choosing things. If your class has only one + * such signature, as most do, you'll have no problems.</li> + * + * <li>The add and remove methods must be public and named <code>void + * add<eventSetName>Listener(<listenerType>)</code> and + * <code>void remove<eventSetName>Listener(<listenerType>)</code> + * in in class <code>eventSourceClass</code>, where + * <code><eventSetName></code> will have its first letter capitalized. + * Standard exception rules (see class description) apply.</li> + * </ol> + * + * @param eventSourceClass + * the class containing the add/remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). This will + * be used to generate the name of the event object as well as the + * names of the add and remove methods. + * @param listenerType + * the class containing the event firing method. + * @param listenerMethodName + * the name of the event firing method. + * @exception IntrospectionException + * if listenerType is not an EventListener, or if methods are not + * found or are invalid. + */ + public EventSetDescriptor(Class eventSourceClass, String eventSetName, + Class listenerType, String listenerMethodName) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + String[] names = new String[1]; + names[0] = listenerMethodName; + + try + { + eventSetName = Character.toUpperCase(eventSetName.charAt(0)) + + eventSetName.substring(1); + } + catch (StringIndexOutOfBoundsException e) + { + eventSetName = ""; + } + + findMethods(eventSourceClass, listenerType, names, + "add" + eventSetName + "Listener", + "remove" + eventSetName + "Listener", eventSetName + "Event"); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** + * Creates a new <code>EventSetDescriptor</code>. + * + * <p>This form of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + * </p> + * + * @param eventSourceClass + * the class containing the add and remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the event firing methods. + * @param listenerMethodNames + * the names of the even firing methods. + * @param addListenerMethodName + * the name of the add listener method. + * @param removeListenerMethodName + * the name of the remove listener method. + * @exception IntrospectionException + * if listenerType is not an EventListener or if methods are not + * found or are invalid. + */ + public EventSetDescriptor(Class eventSourceClass, String eventSetName, + Class listenerType, String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + findMethods(eventSourceClass, listenerType, listenerMethodNames, + addListenerMethodName, removeListenerMethodName, null); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** + * Creates a new <code>EventSetDescriptor</code>. + * + * <p> + * This variant of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + * </p> + * <p> + * A valid GetListener method is public, flags no exceptions and has one + * argument which is of type <code>Class</code> + * {@link java.awt.Component#getListeners(Class)} is such a method. + * </p> + * <p> + * Note: The validity of the return value of the GetListener method is not + * checked. + * </p> + * + * @param eventSourceClass + * the class containing the add and remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the event firing methods. + * @param listenerMethodNames + * the names of the even firing methods. + * @param addListenerMethodName + * the name of the add listener method. + * @param removeListenerMethodName + * the name of the remove listener method. + * @param getListenerMethodName + * Name of a method which returns the array of listeners. + * @exception IntrospectionException + * if listenerType is not an EventListener or if methods are not + * found or are invalid. + * @since 1.4 + */ + public EventSetDescriptor(Class eventSourceClass, String eventSetName, + Class listenerType, String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName, + String getListenerMethodName) + throws IntrospectionException + { + this(eventSourceClass, eventSetName, listenerType, listenerMethodNames, + addListenerMethodName, removeListenerMethodName); + + Method newGetListenerMethod = null; + + try + { + newGetListenerMethod + = eventSourceClass.getMethod(getListenerMethodName, + new Class[] { Class.class }); + } + catch (NoSuchMethodException nsme) + { + throw (IntrospectionException) + new IntrospectionException("No method named " + getListenerMethodName + + " in class " + listenerType + + " which can be used as" + + " getListenerMethod.").initCause(nsme); + } + + // Note: This does not check the return value (which + // should be EventListener[]) but the JDK does not either. + + getListenerMethod = newGetListenerMethod; + + } + + /** + * Creates a new <code>EventSetDescriptor.</code> + * + * <p> + * This variant of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + * </p> + * <p> + * A valid GetListener method is public, flags no exceptions and has one + * argument which is of type <code>Class</code> + * {@link java.awt.Component#getListeners(Class)} is such a method. + * </p> + * <p> + * Note: The validity of the return value of the GetListener method is not + * checked. + * </p> + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethods + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @param getListenerMethod + * The method which returns an array of the listeners. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + * @since 1.4 + */ + public EventSetDescriptor(String eventSetName, Class listenerType, + Method[] listenerMethods, Method addListenerMethod, + Method removeListenerMethod, + Method getListenerMethod) + throws IntrospectionException + { + this(eventSetName, listenerType, listenerMethods, addListenerMethod, + removeListenerMethod); + + // Do no checks if the getListenerMethod is null. + if (getListenerMethod.getParameterTypes().length != 1 + || getListenerMethod.getParameterTypes()[0] != Class.class + || getListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(getListenerMethod.getModifiers())) + throw new IntrospectionException("GetListener method is invalid."); + + // Note: This does not check the return value (which + // should be EventListener[]) but the JDK does not either. + + this.getListenerMethod = getListenerMethod; + } + + /** + * Creates a new <code>EventSetDescriptor</code>. + * + * <p>This form of constructor allows you to explicitly say which methods + * do what, and no reflection is done by the <code>EventSetDescriptor</code>. + * The methods are, however, checked to ensure that they follow the rules + * set forth at the top of the class. + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethods + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + */ + public EventSetDescriptor(String eventSetName, Class listenerType, + Method[] listenerMethods, Method addListenerMethod, + Method removeListenerMethod) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + this.listenerMethods = listenerMethods; + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** Creates a new <code>EventSetDescriptor</code>. + * + * <p>This form of constructor allows you to explicitly say which methods do + * what, and no reflection is done by the <code>EventSetDescriptor</code>. + * The methods are, however, checked to ensure that they follow the rules + * set forth at the top of the class. + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethodDescriptors + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + */ + public EventSetDescriptor(String eventSetName, Class listenerType, + MethodDescriptor[] listenerMethodDescriptors, + Method addListenerMethod, + Method removeListenerMethod) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + this.listenerMethodDescriptors = listenerMethodDescriptors; + this.listenerMethods = new Method[listenerMethodDescriptors.length]; + for (int i = 0; i < this.listenerMethodDescriptors.length; i++) + { + this.listenerMethods[i] + = this.listenerMethodDescriptors[i].getMethod(); + } + + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** Returns the class that contains the event firing methods. + */ + public Class getListenerType() + { + return listenerType; + } + + /** Returns the event firing methods. + */ + public Method[] getListenerMethods() + { + return listenerMethods; + } + + /** Returns the event firing methods as {@link MethodDescriptor}. + */ + public MethodDescriptor[] getListenerMethodDescriptors() + { + if (listenerMethodDescriptors == null) + { + listenerMethodDescriptors + = new MethodDescriptor[listenerMethods.length]; + + for (int i = 0; i < listenerMethods.length; i++) + { + listenerMethodDescriptors[i] + = new MethodDescriptor(listenerMethods[i]); + } + } + + return listenerMethodDescriptors; + } + + /** Returns the add listener method. + */ + public Method getAddListenerMethod() + { + return addListenerMethod; + } + + /* Returns the remove listener method. + */ + public Method getRemoveListenerMethod() + { + return removeListenerMethod; + } + + /** + * Returns the method that retrieves the listeners or <code>null</code> if + * it does not exist. + */ + public Method getGetListenerMethod() + { + return getListenerMethod; + } + + /** Sets whether or not multiple listeners may be added. + * + * @param unicast + * whether or not multiple listeners may be added. + */ + public void setUnicast(boolean unicast) + { + this.unicast = unicast; + } + + /** Returns whether or not multiple listeners may be added. + * (Defaults to false.) + */ + public boolean isUnicast() + { + return unicast; + } + + /** Sets whether or not this is in the default event set. + * + * @param inDefaultEventSet + * whether this is in the default event set. + */ + public void setInDefaultEventSet(boolean inDefaultEventSet) + { + this.inDefaultEventSet = inDefaultEventSet; + } + + /** Returns whether or not this is in the default event set. + * (Defaults to true.) + */ + public boolean isInDefaultEventSet() + { + return inDefaultEventSet; + } + + private void checkAddListenerUnicast() throws IntrospectionException + { + Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes(); + if (addListenerExceptions.length > 1) + { + throw new IntrospectionException( + "Listener add method throws too many exceptions."); + } + else if (addListenerExceptions.length == 1 + && !java.util.TooManyListenersException.class + .isAssignableFrom(addListenerExceptions[0])) + { + throw new IntrospectionException( + "Listener add method throws too many exceptions."); + } + } + + private void checkMethods() throws IntrospectionException + { + if (!addListenerMethod.getDeclaringClass() + .isAssignableFrom(removeListenerMethod.getDeclaringClass()) + && !removeListenerMethod.getDeclaringClass() + .isAssignableFrom(addListenerMethod.getDeclaringClass())) + { + throw new IntrospectionException( + "add and remove listener methods do not come from the" + + " same class. This is bad."); + } + if (!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || addListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(addListenerMethod.getParameterTypes()[0]) + || !Modifier.isPublic(addListenerMethod.getModifiers())) + { + throw new IntrospectionException("Add Listener Method invalid."); + } + if (!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || removeListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(removeListenerMethod.getParameterTypes()[0]) + || removeListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(removeListenerMethod.getModifiers())) + { + throw new IntrospectionException("Remove Listener Method invalid."); + } + + for (int i = 0; i < listenerMethods.length; i++) + { + if (!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE) + || Modifier.isPrivate(listenerMethods[i].getModifiers())) + { + throw new IntrospectionException("Event Method " + + listenerMethods[i].getName() + + " non-void or private."); + } + if (!listenerMethods[i].getDeclaringClass() + .isAssignableFrom(listenerType)) + { + throw new IntrospectionException("Event Method " + + listenerMethods[i].getName() + + " not from class " + + listenerType.getName()); + } + } + } + + private void findMethods(Class eventSourceClass, Class listenerType, + String listenerMethodNames[], + String addListenerMethodName, + String removeListenerMethodName, + String absurdEventClassCheckName) + throws IntrospectionException + { + + /* Find add listener method and remove listener method. */ + Class[] listenerArgList = new Class[1]; + listenerArgList[0] = listenerType; + try + { + this.addListenerMethod + = eventSourceClass.getMethod(addListenerMethodName, + listenerArgList); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "SecurityException trying to access method " + + addListenerMethodName + "."); + } + catch (NoSuchMethodException E) + { + throw new IntrospectionException("Could not find method " + + addListenerMethodName + "."); + } + + if (this.addListenerMethod == null + || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException( + "Add listener method does not exist, is not public," + + " or is not void."); + } + + try + { + this.removeListenerMethod + = eventSourceClass.getMethod(removeListenerMethodName, + listenerArgList); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "SecurityException trying to access method " + + removeListenerMethodName + "."); + } + catch (NoSuchMethodException E) + { + throw new IntrospectionException("Could not find method " + + removeListenerMethodName + "."); + } + if (this.removeListenerMethod == null + || !this.removeListenerMethod.getReturnType() + .equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException( + "Remove listener method does not exist, is not public," + + " or is not void."); + } + + /* Find the listener methods. */ + Method[] methods; + try + { + methods = ClassHelper.getAllMethods(listenerType); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "Security: You cannot access fields in this class."); + } + + Vector chosenMethods = new Vector(); + boolean[] listenerMethodFound = new boolean[listenerMethodNames.length]; + for (int i = 0; i < methods.length; i++) + { + if (Modifier.isPrivate(methods[i].getModifiers())) + { + continue; + } + Method currentMethod = methods[i]; + Class retval = currentMethod.getReturnType(); + if (retval.equals(java.lang.Void.TYPE)) + { + for (int j = 0; j < listenerMethodNames.length; j++) + { + if (currentMethod.getName().equals(listenerMethodNames[j]) + && (absurdEventClassCheckName == null + || (currentMethod.getParameterTypes().length == 1 + && ((currentMethod.getParameterTypes()[0]) + .getName().equals(absurdEventClassCheckName) + || (currentMethod.getParameterTypes()[0]) + .getName().endsWith("." + absurdEventClassCheckName))))) + { + chosenMethods.addElement(currentMethod); + listenerMethodFound[j] = true; + } + } + } + } + + /* Make sure we found all the methods we were looking for. */ + for (int i = 0; i < listenerMethodFound.length; i++) + { + if (!listenerMethodFound[i]) + { + throw new IntrospectionException("Could not find event method " + + listenerMethodNames[i]); + } + } + + /* Now that we've chosen the listener methods we want, store them. */ + this.listenerMethods = new Method[chosenMethods.size()]; + for (int i = 0; i < chosenMethods.size(); i++) + { + this.listenerMethods[i] = (Method) chosenMethods.elementAt(i); + } + } + } |