aboutsummaryrefslogtreecommitdiff
path: root/libjava/classpath/java/beans/EventSetDescriptor.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/beans/EventSetDescriptor.java')
-rw-r--r--libjava/classpath/java/beans/EventSetDescriptor.java442
1 files changed, 442 insertions, 0 deletions
diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java
new file mode 100644
index 0000000..8624e64
--- /dev/null
+++ b/libjava/classpath/java/beans/EventSetDescriptor.java
@@ -0,0 +1,442 @@
+/* java.beans.EventSetDescriptor
+ Copyright (C) 1998 Free Software Foundation, Inc.
+
+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 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;
+
+import gnu.java.lang.ClassHelper;
+
+import java.lang.reflect.Method;
+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 &lt;listenerMethodName&gt;(&lt;eventSetName&gt;Event)</CODE>
+ ** (where <CODE>&lt;eventSetName&gt;</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&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</CODE> and
+ ** <CODE>void remove&lt;eventSetName&gt;Listener(&lt;listenerType&gt;)</CODE> in
+ ** in class <CODE>eventSourceClass</CODE>, where
+ ** <CODE>&lt;eventSetName&gt;</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);
+ }
+ }
+}