aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/beans/PropertyChangeSupport.java
diff options
context:
space:
mode:
authorTom Tromey <tromey@redhat.com>2002-07-15 16:05:13 +0000
committerTom Tromey <tromey@gcc.gnu.org>2002-07-15 16:05:13 +0000
commit9698ee33f0214f9d85b3ec67f139a0bb605b29bf (patch)
treeaed15e12fb5ec9a4f295ff0702895b39b1a9938a /libjava/java/beans/PropertyChangeSupport.java
parented8d29205b130073bd3147cedf068446689a5386 (diff)
downloadgcc-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.java656
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