diff options
author | Tom Tromey <tromey@redhat.com> | 2002-07-15 16:05:13 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2002-07-15 16:05:13 +0000 |
commit | 9698ee33f0214f9d85b3ec67f139a0bb605b29bf (patch) | |
tree | aed15e12fb5ec9a4f295ff0702895b39b1a9938a /libjava/java/beans/PropertyChangeSupport.java | |
parent | ed8d29205b130073bd3147cedf068446689a5386 (diff) | |
download | gcc-9698ee33f0214f9d85b3ec67f139a0bb605b29bf.zip gcc-9698ee33f0214f9d85b3ec67f139a0bb605b29bf.tar.gz gcc-9698ee33f0214f9d85b3ec67f139a0bb605b29bf.tar.bz2 |
Makefile.in: Rebuilt.
* Makefile.in: Rebuilt.
* Makefile.am (awt_java_source_files): Added new files.
* java/beans/ExceptionListener.java: Merged with Classpath.
* java/beans/PropertyChangeEvent.java: Merged with Classpath.
* java/beans/PropertyChangeListener.java: Merged with Classpath.
* java/beans/PropertyChangeListenerProxy.java: Merged with Classpath.
* java/beans/PropertyChangeSupport.java: Merged with Classpath.
* java/beans/VetoableChangeListener.java: Merged with Classpath.
* java/beans/VetoableChangeListenerProxy.java: Merged with Classpath.
* java/beans/VetoableChangeSupport.java: Merged with Classpath.
From-SVN: r55459
Diffstat (limited to 'libjava/java/beans/PropertyChangeSupport.java')
-rw-r--r-- | libjava/java/beans/PropertyChangeSupport.java | 656 |
1 files changed, 447 insertions, 209 deletions
diff --git a/libjava/java/beans/PropertyChangeSupport.java b/libjava/java/beans/PropertyChangeSupport.java index 6fbc85f..8623027 100644 --- a/libjava/java/beans/PropertyChangeSupport.java +++ b/libjava/java/beans/PropertyChangeSupport.java @@ -1,5 +1,5 @@ -/* java.beans.PropertyChangeSupport - Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. +/* PropertyChangeSupport.java -- support to manage property change listeners + Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -7,7 +7,7 @@ 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 @@ -37,214 +37,452 @@ exception statement from your version. */ package java.beans; -import java.util.Hashtable; -import java.util.Vector; -import java.util.Enumeration; + +import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.IOException; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; /** - ** PropertyChangeSupport makes it easy to fire property - ** change events and handle listeners. - ** - ** @author John Keiser - ** @since JDK1.1 - ** @version 1.2.0, 15 Mar 1999 - **/ - -public class PropertyChangeSupport implements java.io.Serializable { - transient Hashtable propertyListeners = new Hashtable(); - transient Vector listeners = new Vector(); - Hashtable children; - Object source; - int propertyChangeSupportSerializedDataVersion = 2; - private static final long serialVersionUID = 6401253773779951803L; - - /** - * Saves the state of the object to the stream. */ - private void writeObject(ObjectOutputStream stream) throws IOException { - children = propertyListeners.isEmpty() ? null : propertyListeners; - stream.defaultWriteObject(); - for (Enumeration e = listeners.elements(); e.hasMoreElements(); ) { - PropertyChangeListener l = (PropertyChangeListener)e.nextElement(); - if (l instanceof Serializable) - stream.writeObject(l); - } - stream.writeObject(null); - } - - /** - * Reads the object back from stream (deserialization). - */ - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - propertyListeners = (children == null) ? new Hashtable() : children; - PropertyChangeListener l; - while ((l = (PropertyChangeListener)stream.readObject()) != null) { - addPropertyChangeListener(l); - } - // FIXME: XXX: There is no spec for JDK 1.1 serialization - // so it is unclear what to do if the value of - // propertyChangeSupportSerializedDataVersion is 1. - } - - /** Create PropertyChangeSupport to work with a specific - ** source bean. - ** @param source the source bean to use. - **/ - public PropertyChangeSupport(Object source) { - this.source = source; - } - - /** Adds a PropertyChangeListener to the list of listeners. - ** All property change events will be sent to this listener. - ** <P> - ** - ** 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. - ** <P> - ** - ** Adding a null listener will cause undefined behavior. - ** - ** @param l the listener to add. - **/ - public void addPropertyChangeListener(PropertyChangeListener l) { - listeners.addElement(l); - } - - /** Adds a PropertyChangeListener listening on the specified property. - ** Events will be sent to the listener for that particular property. - ** <P> - ** - ** The listener add is not unique; that is, <em>n</em> adds on a - ** particular property for a particular listener will result in - ** <em>n</em> events being sent to that listener when that - ** property is changed. - ** <P> - ** - ** The effect is 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. - ** <P> - ** - ** Adding a null listener will cause undefined behavior. - ** - ** @param propertyName the name of the property to listen on. - ** @param l the listener to add. - **/ - public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) { - synchronized(propertyListeners) { - Vector v = (Vector)propertyListeners.get(propertyName); - try { - v.addElement(l); - } catch(NullPointerException e) { - /* If v is not found, create a new vector. */ - v = new Vector(); - v.addElement(l); - propertyListeners.put(propertyName, v); - } - } - } - - /** Removes a PropertyChangeListener from the list of listeners. - ** If any specific properties are being listened on, they must - ** be deregistered by themselves; this will only remove the - ** general listener to all properties. - ** <P> - ** - ** If <code>add()</code> has been called multiple times for a - ** particular listener, <code>remove()</code> will have to be - ** called the same number of times to deregister it. - ** - ** @param l the listener to remove. - **/ - public void removePropertyChangeListener(PropertyChangeListener l) { - listeners.removeElement(l); - } - - /** Removes a PropertyChangeListener from listening to a specific property. - ** <P> - ** - ** If <code>add()</code> has been called multiple times for a - ** particular listener on a property, <code>remove()</code> will - ** have to be called the same number of times to deregister it. - ** - ** @param propertyName the property to stop listening on. - ** @param l the listener to remove. - **/ - public void removePropertyChangeListener(String propertyName, PropertyChangeListener l) { - synchronized(propertyListeners) { - Vector v = (Vector)propertyListeners.get(propertyName); - try { - v.removeElement(l); - if(v.size() == 0) { - propertyListeners.remove(propertyName); - } - } catch(NullPointerException e) { - /* if v is not found, do nothing. */ - } - } - } - - /** Fire a PropertyChangeEvent to all the listeners. - ** - ** @param event the event to fire. - **/ - public void firePropertyChange(PropertyChangeEvent event) { - for(int i=0;i<listeners.size();i++) { - ((PropertyChangeListener)listeners.elementAt(i)).propertyChange(event); - } - Vector moreListeners = (Vector)propertyListeners.get(event.getPropertyName()); - if(moreListeners != null) { - for(int i=0;i<moreListeners.size();i++) { - ((PropertyChangeListener)moreListeners.elementAt(i)).propertyChange(event); - } - } - } - - /** Fire a PropertyChangeEvent containing the old and new values of the property to all the listeners. - ** - ** @param propertyName the name of the property that changed. - ** @param oldVal the old value. - ** @param newVal the new value. - **/ - public void firePropertyChange(String propertyName, Object oldVal, Object newVal) { - firePropertyChange(new PropertyChangeEvent(source,propertyName,oldVal,newVal)); - } - - /** Fire a PropertyChangeEvent containing the old and new values of the property to all the listeners. - ** - ** @param propertyName the name of the property that changed. - ** @param oldVal the old value. - ** @param newVal the new value. - **/ - public void firePropertyChange(String propertyName, boolean oldVal, boolean newVal) { - firePropertyChange(new PropertyChangeEvent(source, propertyName, new Boolean(oldVal), new Boolean(newVal))); - } - - /** Fire a PropertyChangeEvent containing the old and new values of the property to all the listeners. - ** - ** @param propertyName the name of the property that changed. - ** @param oldVal the old value. - ** @param newVal the new value. - **/ - public void firePropertyChange(String propertyName, int oldVal, int newVal) { - firePropertyChange(new PropertyChangeEvent(source, propertyName, new Integer(oldVal), new Integer(newVal))); - } - - /** Tell whether the specified property is being listened on or not. - ** This will only return <code>true</code> if there are listeners - ** on all properties or if there is a listener specifically on this - ** property. - ** - ** @param propertyName the property that may be listened on - ** @return whether the property is being listened on - **/ - public boolean hasListeners(String propertyName) { - return listeners.size() > 0 || propertyListeners.get(propertyName) != null; - } -} + * PropertyChangeSupport makes it easy to fire property change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake <ebb9@email.byu.edu> + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6401253773779951803L; + + /** + * Maps property names (String) to named listeners (PropertyChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private final int propertyChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered property listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the <code>children</code> hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a PropertyChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public PropertyChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a PropertyChangeListener to the list of global listeners. All + * 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, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener l) + { + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + addPropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a PropertyChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If <code>add()</code> has been called multiple times for a + * particular listener, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removePropertyChangeListener(PropertyChangeListener l) + { + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + removePropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered property change listeners. Those that + * were registered under a name will be wrapped in a + * <code>PropertyChangeListenerProxy</code>, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see PropertyChangeListenerProxy + * @since 1.4 + */ + public synchronized PropertyChangeListener[] getPropertyChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((PropertyChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new PropertyChangeListenerProxy + (name, (PropertyChangeListener) v.get(j))); + } + } + return (PropertyChangeListener[]) + list.toArray(new PropertyChangeListener[list.size()]); + } + + /** + * Adds a PropertyChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, <em>n</em> adds on a particular + * property for a particular listener will result in <em>n</em> events + * being sent to that listener when that property is changed. The effect is + * 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 + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + * @throws NullPointerException if propertyName is null + */ + public synchronized void addPropertyChangeListener(String propertyName, + PropertyChangeListener l) + { + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + PropertyChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + { + s = new PropertyChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a PropertyChangeListener from listening to a specific property. + * If <code>add()</code> has been called multiple times for a particular + * listener on a property, <code>remove()</code> will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removePropertyChangeListener(String propertyName, PropertyChangeListener l) + { + if (children == null) + return; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * 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. + * + * @return the array of registered listeners + * @throws NullPointerException if propertyName is null + * @since 1.4 + */ + public synchronized PropertyChangeListener[] + getPropertyChangeListeners(String propertyName) + { + if (children == null) + return new PropertyChangeListener[0]; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return new PropertyChangeListener[0]; + return (PropertyChangeListener[]) + s.listeners.toArray(new PropertyChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + Object oldVal, Object newVal) + { + firePropertyChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, int oldVal, int newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + new Integer(oldVal), + new Integer(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + boolean oldVal, boolean newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. + * + * @param event the event to fire + * @throws NullPointerException if event is null + */ + public void firePropertyChange(PropertyChangeEvent event) + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + PropertyChangeSupport s + = (PropertyChangeSupport) h.get(event.propertyName); + if (s != null) + { + v = s.listeners; // Be thread-safe. + int i = v == null ? 0 : v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + } + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return <code>true</code> if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @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) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if propertyChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + PropertyChangeListener l = (PropertyChangeListener) s.readObject(); + while (l != null) + { + addPropertyChangeListener(l); + l = (PropertyChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + PropertyChangeSupport pcs = (PropertyChangeSupport) e.getValue(); + if (pcs.listeners == null) + pcs.listeners = new Vector(); + if (pcs.children != null) + pcs.listeners.addAll + (Arrays.asList(pcs.getPropertyChangeListeners(name))); + if (pcs.listeners.size() == 0) + iter.remove(); + else + pcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class PropertyChangeSupport |