diff options
Diffstat (limited to 'java/beans')
-rw-r--r-- | java/beans/DefaultPersistenceDelegate.java | 17 | ||||
-rw-r--r-- | java/beans/Encoder.java | 45 | ||||
-rw-r--r-- | java/beans/EventSetDescriptor.java | 1109 | ||||
-rw-r--r-- | java/beans/PersistenceDelegate.java | 5 | ||||
-rw-r--r-- | java/beans/PropertyChangeSupport.java | 25 | ||||
-rw-r--r-- | java/beans/XMLDecoder.java | 6 | ||||
-rw-r--r-- | java/beans/XMLEncoder.java | 2 |
7 files changed, 761 insertions, 448 deletions
diff --git a/java/beans/DefaultPersistenceDelegate.java b/java/beans/DefaultPersistenceDelegate.java index 9dd1ae564..ca1041fef 100644 --- a/java/beans/DefaultPersistenceDelegate.java +++ b/java/beans/DefaultPersistenceDelegate.java @@ -157,6 +157,23 @@ public class DefaultPersistenceDelegate extends PersistenceDelegate protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) { + // Calling the supertype's implementation of initialize makes it + // possible that descendants of classes like AbstractHashMap + // or Hashtable are serialized correctly. This mechanism grounds on + // two other facts: + // * Each class which has not registered a special purpose + // PersistenceDelegate is handled by a DefaultPersistenceDelegate + // instance. + // * PersistenceDelegate.initialize() is implemented in a way that it + // calls the initialize method of the superclass' persistence delegate. + super.initialize(type, oldInstance, newInstance, out); + + // Suppresses the writing of property setting statements when this delegate + // is not used for the exact instance type. By doing so the following code + // is called only once per object. + if (type != oldInstance.getClass()) + return; + try { PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo( diff --git a/java/beans/Encoder.java b/java/beans/Encoder.java index e7b53d786..cde1735f4 100644 --- a/java/beans/Encoder.java +++ b/java/beans/Encoder.java @@ -1,5 +1,5 @@ /* Encoder.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,21 +38,16 @@ package java.beans; +import gnu.java.beans.DefaultExceptionListener; import gnu.java.beans.encoder.ArrayPersistenceDelegate; import gnu.java.beans.encoder.ClassPersistenceDelegate; import gnu.java.beans.encoder.CollectionPersistenceDelegate; import gnu.java.beans.encoder.MapPersistenceDelegate; import gnu.java.beans.encoder.PrimitivePersistenceDelegate; -import java.util.ArrayList; +import java.util.AbstractCollection; import java.util.HashMap; -import java.util.HashSet; import java.util.IdentityHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.Vector; /** * @author Robert Schuster (robertschuster@fsfe.org) @@ -123,31 +118,11 @@ public class Encoder delegates.put(Object[].class, new ArrayPersistenceDelegate()); pd = new CollectionPersistenceDelegate(); - delegates.put(ArrayList.class, pd); - delegates.put(LinkedList.class, pd); - delegates.put(Vector.class, pd); - delegates.put(HashSet.class, pd); - delegates.put(LinkedHashSet.class, pd); - delegates.put(TreeSet.class, pd); + delegates.put(AbstractCollection.class, pd); pd = new MapPersistenceDelegate(); - delegates.put(HashMap.class, pd); - delegates.put(TreeMap.class, pd); + delegates.put(java.util.AbstractMap.class, pd); delegates.put(java.util.Hashtable.class, pd); - delegates.put(java.util.IdentityHashMap.class, pd); - - delegates.put(java.util.LinkedHashMap.class, pd); - delegates.put(java.util.Properties.class, pd); - - delegates.put(java.awt.RenderingHints.class, pd); - delegates.put(java.util.WeakHashMap.class, pd); - delegates.put(javax.swing.UIDefaults.class, pd); - - // TODO: These classes need to be implemented first - //delegates.put(java.security.AuthProvider.class, pd); - //delegates.put(java.util.concurrent.ConcurrentHashMap.class, pd); - //delegates.put(java.util.EnumMap.class, pd); - //delegates.put(javax.management.openmbean.TabularDataSupport.class, pd); defaultPersistenceDelegate = new DefaultPersistenceDelegate(); delegates.put(Object.class, defaultPersistenceDelegate); @@ -194,14 +169,8 @@ public class Encoder */ public void setExceptionListener(ExceptionListener listener) { - exceptionListener = (listener != null) ? listener : new ExceptionListener() - { - public void exceptionThrown(Exception e) - { - System.err.println("exception thrown: " + e); - e.printStackTrace(); - } - }; + exceptionListener = (listener != null) + ? listener : DefaultExceptionListener.INSTANCE; } /** diff --git a/java/beans/EventSetDescriptor.java b/java/beans/EventSetDescriptor.java index e687b8a30..381a45303 100644 --- a/java/beans/EventSetDescriptor.java +++ b/java/beans/EventSetDescriptor.java @@ -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); + } + } + } diff --git a/java/beans/PersistenceDelegate.java b/java/beans/PersistenceDelegate.java index 91c02d2b8..a6f715763 100644 --- a/java/beans/PersistenceDelegate.java +++ b/java/beans/PersistenceDelegate.java @@ -59,9 +59,8 @@ public abstract class PersistenceDelegate { type = type.getSuperclass(); - PersistenceDelegate pd = out.getPersistenceDelegate( - oldInstance.getClass().getSuperclass()); - + PersistenceDelegate pd = out.getPersistenceDelegate(type); + pd.initialize(type, oldInstance, newInstance, out); } } diff --git a/java/beans/PropertyChangeSupport.java b/java/beans/PropertyChangeSupport.java index 991390b56..e944e1512 100644 --- a/java/beans/PropertyChangeSupport.java +++ b/java/beans/PropertyChangeSupport.java @@ -1,5 +1,6 @@ /* PropertyChangeSupport.java -- support to manage property change listeners - Copyright (C) 1998, 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -120,14 +121,17 @@ public class PropertyChangeSupport implements Serializable * property change events will be sent to this listener. The listener add * is not unique: that is, <em>n</em> adds with the same listener will * result in <em>n</em> events being sent to that listener for every - * property change. Adding a null listener may cause a NullPointerException - * down the road. This method will unwrap a PropertyChangeListenerProxy, + * property change. Adding a null listener is silently ignored. + * This method will unwrap a PropertyChangeListenerProxy, * registering the underlying delegate to the named property list. * * @param l the listener to add */ public synchronized void addPropertyChangeListener(PropertyChangeListener l) { + if (l == null) + return; + if (l instanceof PropertyChangeListenerProxy) { PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; @@ -216,8 +220,8 @@ public class PropertyChangeSupport implements Serializable * cumulative, too; if you are registered to listen to receive events on * all property changes, and then you register on a particular property, * you will receive change events for that property twice. Adding a null - * listener may cause a NullPointerException down the road. This method - * will unwrap a PropertyChangeListenerProxy, registering the underlying + * listener is silently ignored. This method will unwrap a + * PropertyChangeListenerProxy, registering the underlying * delegate to the named property list if the names match, and discarding * it otherwise. * @@ -228,6 +232,9 @@ public class PropertyChangeSupport implements Serializable public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener l) { + if (l == null) + return; + while (l instanceof PropertyChangeListenerProxy) { PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; @@ -290,17 +297,16 @@ public class PropertyChangeSupport implements Serializable /** * Returns an array of all property change listeners registered under the - * given property name. If there are no registered listeners, this returns - * an empty array. + * given property name. If there are no registered listeners, or + * propertyName is null, this returns an empty array. * * @return the array of registered listeners - * @throws NullPointerException if propertyName is null * @since 1.4 */ public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { - if (children == null) + if (children == null || propertyName == null) return new PropertyChangeListener[0]; PropertyChangeSupport s = (PropertyChangeSupport) children.get(propertyName); @@ -455,7 +461,6 @@ public class PropertyChangeSupport implements Serializable * * @param propertyName the property that may be listened on * @return whether the property is being listened on - * @throws NullPointerException if propertyName is null */ public synchronized boolean hasListeners(String propertyName) { diff --git a/java/beans/XMLDecoder.java b/java/beans/XMLDecoder.java index 238fd6bed..7618bb8cb 100644 --- a/java/beans/XMLDecoder.java +++ b/java/beans/XMLDecoder.java @@ -1,5 +1,5 @@ /* java.beans.XMLDecoder -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,7 +38,7 @@ exception statement from your version. */ package java.beans; -import gnu.java.beans.decoder.DefaultExceptionListener; +import gnu.java.beans.DefaultExceptionListener; import gnu.java.beans.decoder.PersistenceParser; import java.io.IOException; @@ -289,7 +289,7 @@ public class XMLDecoder // uses a default implementation when null if (listener == null) { - listener = new DefaultExceptionListener(); + listener = DefaultExceptionListener.INSTANCE; } exceptionListener = listener; } diff --git a/java/beans/XMLEncoder.java b/java/beans/XMLEncoder.java index f9cbe6396..feff68bd3 100644 --- a/java/beans/XMLEncoder.java +++ b/java/beans/XMLEncoder.java @@ -168,6 +168,8 @@ public class XMLEncoder extends Encoder // an erroneous state to the ScanEngine without behaving different // to the JDK. scanEngine.revoke(); + + return; } writeObject(value); |