< prev index next >

src/java.desktop/share/classes/java/beans/VetoableChangeSupport.java

Print this page




  66  *         this.value = newValue;
  67  *     }
  68  *
  69  *     [...]
  70  * }
  71  * }</pre>
  72  * <p>
  73  * A {@code VetoableChangeSupport} instance is thread-safe.
  74  * <p>
  75  * This class is serializable.  When it is serialized it will save
  76  * (and restore) any listeners that are themselves serializable.  Any
  77  * non-serializable listeners will be skipped during serialization.
  78  *
  79  * @see PropertyChangeSupport
  80  * @since 1.1
  81  */
  82 public class VetoableChangeSupport implements Serializable {
  83     private VetoableChangeListenerMap map = new VetoableChangeListenerMap();
  84 
  85     /**
  86      * Constructs a <code>VetoableChangeSupport</code> object.
  87      *
  88      * @param sourceBean  The bean to be given as the source for any events.
  89      */
  90     public VetoableChangeSupport(Object sourceBean) {
  91         if (sourceBean == null) {
  92             throw new NullPointerException();
  93         }
  94         source = sourceBean;
  95     }
  96 
  97     /**
  98      * Add a VetoableChangeListener to the listener list.
  99      * The listener is registered for all properties.
 100      * The same listener object may be added more than once, and will be called
 101      * as many times as it is added.
 102      * If <code>listener</code> is null, no exception is thrown and no action
 103      * is taken.
 104      *
 105      * @param listener  The VetoableChangeListener to be added
 106      */
 107     public void addVetoableChangeListener(VetoableChangeListener listener) {
 108         if (listener == null) {
 109             return;
 110         }
 111         if (listener instanceof VetoableChangeListenerProxy) {
 112             VetoableChangeListenerProxy proxy =
 113                     (VetoableChangeListenerProxy)listener;
 114             // Call two argument add method.
 115             addVetoableChangeListener(proxy.getPropertyName(),
 116                                       proxy.getListener());
 117         } else {
 118             this.map.add(null, listener);
 119         }
 120     }
 121 
 122     /**
 123      * Remove a VetoableChangeListener from the listener list.
 124      * This removes a VetoableChangeListener that was registered
 125      * for all properties.
 126      * If <code>listener</code> was added more than once to the same event
 127      * source, it will be notified one less time after being removed.
 128      * If <code>listener</code> is null, or was never added, no exception is
 129      * thrown and no action is taken.
 130      *
 131      * @param listener  The VetoableChangeListener to be removed
 132      */
 133     public void removeVetoableChangeListener(VetoableChangeListener listener) {
 134         if (listener == null) {
 135             return;
 136         }
 137         if (listener instanceof VetoableChangeListenerProxy) {
 138             VetoableChangeListenerProxy proxy =
 139                     (VetoableChangeListenerProxy)listener;
 140             // Call two argument remove method.
 141             removeVetoableChangeListener(proxy.getPropertyName(),
 142                                          proxy.getListener());
 143         } else {
 144             this.map.remove(null, listener);
 145         }
 146     }
 147 
 148     /**
 149      * Returns an array of all the listeners that were added to the
 150      * VetoableChangeSupport object with addVetoableChangeListener().
 151      * <p>
 152      * If some listeners have been added with a named property, then
 153      * the returned array will be a mixture of VetoableChangeListeners
 154      * and <code>VetoableChangeListenerProxy</code>s. If the calling
 155      * method is interested in distinguishing the listeners then it must
 156      * test each element to see if it's a
 157      * <code>VetoableChangeListenerProxy</code>, perform the cast, and examine
 158      * the parameter.
 159      *
 160      * <pre>{@code
 161      * VetoableChangeListener[] listeners = bean.getVetoableChangeListeners();
 162      * for (int i = 0; i < listeners.length; i++) {
 163      *        if (listeners[i] instanceof VetoableChangeListenerProxy) {
 164      *     VetoableChangeListenerProxy proxy =
 165      *                    (VetoableChangeListenerProxy)listeners[i];
 166      *     if (proxy.getPropertyName().equals("foo")) {
 167      *       // proxy is a VetoableChangeListener which was associated
 168      *       // with the property named "foo"
 169      *     }
 170      *   }
 171      * }
 172      * }</pre>
 173      *
 174      * @see VetoableChangeListenerProxy
 175      * @return all of the <code>VetoableChangeListeners</code> added or an
 176      *         empty array if no listeners have been added
 177      * @since 1.4
 178      */
 179     public VetoableChangeListener[] getVetoableChangeListeners(){
 180         return this.map.getListeners();
 181     }
 182 
 183     /**
 184      * Add a VetoableChangeListener for a specific property.  The listener
 185      * will be invoked only when a call on fireVetoableChange names that
 186      * specific property.
 187      * The same listener object may be added more than once.  For each
 188      * property,  the listener will be invoked the number of times it was added
 189      * for that property.
 190      * If <code>propertyName</code> or <code>listener</code> is null, no
 191      * exception is thrown and no action is taken.
 192      *
 193      * @param propertyName  The name of the property to listen on.
 194      * @param listener  The VetoableChangeListener to be added
 195      * @since 1.2
 196      */
 197     public void addVetoableChangeListener(
 198                                 String propertyName,
 199                 VetoableChangeListener listener) {
 200         if (listener == null || propertyName == null) {
 201             return;
 202         }
 203         listener = this.map.extract(listener);
 204         if (listener != null) {
 205             this.map.add(propertyName, listener);
 206         }
 207     }
 208 
 209     /**
 210      * Remove a VetoableChangeListener for a specific property.
 211      * If <code>listener</code> was added more than once to the same event
 212      * source for the specified property, it will be notified one less time
 213      * after being removed.
 214      * If <code>propertyName</code> is null, no exception is thrown and no
 215      * action is taken.
 216      * If <code>listener</code> is null, or was never added for the specified
 217      * property, no exception is thrown and no action is taken.
 218      *
 219      * @param propertyName  The name of the property that was listened on.
 220      * @param listener  The VetoableChangeListener to be removed
 221      * @since 1.2
 222      */
 223     public void removeVetoableChangeListener(
 224                                 String propertyName,
 225                 VetoableChangeListener listener) {
 226         if (listener == null || propertyName == null) {
 227             return;
 228         }
 229         listener = this.map.extract(listener);
 230         if (listener != null) {
 231             this.map.remove(propertyName, listener);
 232         }
 233     }
 234 
 235     /**
 236      * Returns an array of all the listeners which have been associated
 237      * with the named property.
 238      *
 239      * @param propertyName  The name of the property being listened to
 240      * @return all the <code>VetoableChangeListeners</code> associated with
 241      *         the named property.  If no such listeners have been added,
 242      *         or if <code>propertyName</code> is null, an empty array is
 243      *         returned.
 244      * @since 1.4
 245      */
 246     public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
 247         return this.map.getListeners(propertyName);
 248     }
 249 
 250     /**
 251      * Reports a constrained property update to listeners
 252      * that have been registered to track updates of
 253      * all properties or a property with the specified name.
 254      * <p>
 255      * Any listener can throw a {@code PropertyVetoException} to veto the update.
 256      * If one of the listeners vetoes the update, this method passes
 257      * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
 258      * to all listeners that already confirmed this update
 259      * and throws the {@code PropertyVetoException} again.
 260      * <p>
 261      * No event is fired if old and new values are equal and non-null.
 262      * <p>


 383                     }
 384                 }
 385                 catch (PropertyVetoException veto) {
 386                     event = new PropertyChangeEvent(this.source, name, newValue, oldValue);
 387                     for (int i = 0; i < current; i++) {
 388                         try {
 389                             listeners[i].vetoableChange(event);
 390                         }
 391                         catch (PropertyVetoException exception) {
 392                             // ignore exceptions that occur during rolling back
 393                         }
 394                     }
 395                     throw veto; // rethrow the veto exception
 396                 }
 397             }
 398         }
 399     }
 400 
 401     /**
 402      * Check if there are any listeners for a specific property, including
 403      * those registered on all properties.  If <code>propertyName</code>
 404      * is null, only check for listeners registered on all properties.
 405      *
 406      * @param propertyName  the property name.
 407      * @return true if there are one or more listeners for the given property
 408      * @since 1.2
 409      */
 410     public boolean hasListeners(String propertyName) {
 411         return this.map.hasListeners(propertyName);
 412     }
 413 
 414     /**
 415      * @serialData Null terminated list of <code>VetoableChangeListeners</code>.
 416      * <p>
 417      * At serialization time we skip non-serializable listeners and
 418      * only serialize the serializable listeners.
 419      */
 420     private void writeObject(ObjectOutputStream s) throws IOException {
 421         Hashtable<String, VetoableChangeSupport> children = null;
 422         VetoableChangeListener[] listeners = null;
 423         synchronized (this.map) {
 424             for (Entry<String, VetoableChangeListener[]> entry : this.map.getEntries()) {
 425                 String property = entry.getKey();
 426                 if (property == null) {
 427                     listeners = entry.getValue();
 428                 } else {
 429                     if (children == null) {
 430                         children = new Hashtable<>();
 431                     }
 432                     VetoableChangeSupport vcs = new VetoableChangeSupport(this.source);
 433                     vcs.map.set(null, entry.getValue());
 434                     children.put(property, vcs);
 435                 }




  66  *         this.value = newValue;
  67  *     }
  68  *
  69  *     [...]
  70  * }
  71  * }</pre>
  72  * <p>
  73  * A {@code VetoableChangeSupport} instance is thread-safe.
  74  * <p>
  75  * This class is serializable.  When it is serialized it will save
  76  * (and restore) any listeners that are themselves serializable.  Any
  77  * non-serializable listeners will be skipped during serialization.
  78  *
  79  * @see PropertyChangeSupport
  80  * @since 1.1
  81  */
  82 public class VetoableChangeSupport implements Serializable {
  83     private VetoableChangeListenerMap map = new VetoableChangeListenerMap();
  84 
  85     /**
  86      * Constructs a {@code VetoableChangeSupport} object.
  87      *
  88      * @param sourceBean  The bean to be given as the source for any events.
  89      */
  90     public VetoableChangeSupport(Object sourceBean) {
  91         if (sourceBean == null) {
  92             throw new NullPointerException();
  93         }
  94         source = sourceBean;
  95     }
  96 
  97     /**
  98      * Add a VetoableChangeListener to the listener list.
  99      * The listener is registered for all properties.
 100      * The same listener object may be added more than once, and will be called
 101      * as many times as it is added.
 102      * If {@code listener} is null, no exception is thrown and no action
 103      * is taken.
 104      *
 105      * @param listener  The VetoableChangeListener to be added
 106      */
 107     public void addVetoableChangeListener(VetoableChangeListener listener) {
 108         if (listener == null) {
 109             return;
 110         }
 111         if (listener instanceof VetoableChangeListenerProxy) {
 112             VetoableChangeListenerProxy proxy =
 113                     (VetoableChangeListenerProxy)listener;
 114             // Call two argument add method.
 115             addVetoableChangeListener(proxy.getPropertyName(),
 116                                       proxy.getListener());
 117         } else {
 118             this.map.add(null, listener);
 119         }
 120     }
 121 
 122     /**
 123      * Remove a VetoableChangeListener from the listener list.
 124      * This removes a VetoableChangeListener that was registered
 125      * for all properties.
 126      * If {@code listener} was added more than once to the same event
 127      * source, it will be notified one less time after being removed.
 128      * If {@code listener} is null, or was never added, no exception is
 129      * thrown and no action is taken.
 130      *
 131      * @param listener  The VetoableChangeListener to be removed
 132      */
 133     public void removeVetoableChangeListener(VetoableChangeListener listener) {
 134         if (listener == null) {
 135             return;
 136         }
 137         if (listener instanceof VetoableChangeListenerProxy) {
 138             VetoableChangeListenerProxy proxy =
 139                     (VetoableChangeListenerProxy)listener;
 140             // Call two argument remove method.
 141             removeVetoableChangeListener(proxy.getPropertyName(),
 142                                          proxy.getListener());
 143         } else {
 144             this.map.remove(null, listener);
 145         }
 146     }
 147 
 148     /**
 149      * Returns an array of all the listeners that were added to the
 150      * VetoableChangeSupport object with addVetoableChangeListener().
 151      * <p>
 152      * If some listeners have been added with a named property, then
 153      * the returned array will be a mixture of VetoableChangeListeners
 154      * and {@code VetoableChangeListenerProxy}s. If the calling
 155      * method is interested in distinguishing the listeners then it must
 156      * test each element to see if it's a
 157      * {@code VetoableChangeListenerProxy}, perform the cast, and examine
 158      * the parameter.
 159      *
 160      * <pre>{@code
 161      * VetoableChangeListener[] listeners = bean.getVetoableChangeListeners();
 162      * for (int i = 0; i < listeners.length; i++) {
 163      *        if (listeners[i] instanceof VetoableChangeListenerProxy) {
 164      *     VetoableChangeListenerProxy proxy =
 165      *                    (VetoableChangeListenerProxy)listeners[i];
 166      *     if (proxy.getPropertyName().equals("foo")) {
 167      *       // proxy is a VetoableChangeListener which was associated
 168      *       // with the property named "foo"
 169      *     }
 170      *   }
 171      * }
 172      * }</pre>
 173      *
 174      * @see VetoableChangeListenerProxy
 175      * @return all of the {@code VetoableChangeListeners} added or an
 176      *         empty array if no listeners have been added
 177      * @since 1.4
 178      */
 179     public VetoableChangeListener[] getVetoableChangeListeners(){
 180         return this.map.getListeners();
 181     }
 182 
 183     /**
 184      * Add a VetoableChangeListener for a specific property.  The listener
 185      * will be invoked only when a call on fireVetoableChange names that
 186      * specific property.
 187      * The same listener object may be added more than once.  For each
 188      * property,  the listener will be invoked the number of times it was added
 189      * for that property.
 190      * If {@code propertyName} or {@code listener} is null, no
 191      * exception is thrown and no action is taken.
 192      *
 193      * @param propertyName  The name of the property to listen on.
 194      * @param listener  The VetoableChangeListener to be added
 195      * @since 1.2
 196      */
 197     public void addVetoableChangeListener(
 198                                 String propertyName,
 199                 VetoableChangeListener listener) {
 200         if (listener == null || propertyName == null) {
 201             return;
 202         }
 203         listener = this.map.extract(listener);
 204         if (listener != null) {
 205             this.map.add(propertyName, listener);
 206         }
 207     }
 208 
 209     /**
 210      * Remove a VetoableChangeListener for a specific property.
 211      * If {@code listener} was added more than once to the same event
 212      * source for the specified property, it will be notified one less time
 213      * after being removed.
 214      * If {@code propertyName} is null, no exception is thrown and no
 215      * action is taken.
 216      * If {@code listener} is null, or was never added for the specified
 217      * property, no exception is thrown and no action is taken.
 218      *
 219      * @param propertyName  The name of the property that was listened on.
 220      * @param listener  The VetoableChangeListener to be removed
 221      * @since 1.2
 222      */
 223     public void removeVetoableChangeListener(
 224                                 String propertyName,
 225                 VetoableChangeListener listener) {
 226         if (listener == null || propertyName == null) {
 227             return;
 228         }
 229         listener = this.map.extract(listener);
 230         if (listener != null) {
 231             this.map.remove(propertyName, listener);
 232         }
 233     }
 234 
 235     /**
 236      * Returns an array of all the listeners which have been associated
 237      * with the named property.
 238      *
 239      * @param propertyName  The name of the property being listened to
 240      * @return all the {@code VetoableChangeListeners} associated with
 241      *         the named property.  If no such listeners have been added,
 242      *         or if {@code propertyName} is null, an empty array is
 243      *         returned.
 244      * @since 1.4
 245      */
 246     public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
 247         return this.map.getListeners(propertyName);
 248     }
 249 
 250     /**
 251      * Reports a constrained property update to listeners
 252      * that have been registered to track updates of
 253      * all properties or a property with the specified name.
 254      * <p>
 255      * Any listener can throw a {@code PropertyVetoException} to veto the update.
 256      * If one of the listeners vetoes the update, this method passes
 257      * a new "undo" {@code PropertyChangeEvent} that reverts to the old value
 258      * to all listeners that already confirmed this update
 259      * and throws the {@code PropertyVetoException} again.
 260      * <p>
 261      * No event is fired if old and new values are equal and non-null.
 262      * <p>


 383                     }
 384                 }
 385                 catch (PropertyVetoException veto) {
 386                     event = new PropertyChangeEvent(this.source, name, newValue, oldValue);
 387                     for (int i = 0; i < current; i++) {
 388                         try {
 389                             listeners[i].vetoableChange(event);
 390                         }
 391                         catch (PropertyVetoException exception) {
 392                             // ignore exceptions that occur during rolling back
 393                         }
 394                     }
 395                     throw veto; // rethrow the veto exception
 396                 }
 397             }
 398         }
 399     }
 400 
 401     /**
 402      * Check if there are any listeners for a specific property, including
 403      * those registered on all properties.  If {@code propertyName}
 404      * is null, only check for listeners registered on all properties.
 405      *
 406      * @param propertyName  the property name.
 407      * @return true if there are one or more listeners for the given property
 408      * @since 1.2
 409      */
 410     public boolean hasListeners(String propertyName) {
 411         return this.map.hasListeners(propertyName);
 412     }
 413 
 414     /**
 415      * @serialData Null terminated list of {@code VetoableChangeListeners}.
 416      * <p>
 417      * At serialization time we skip non-serializable listeners and
 418      * only serialize the serializable listeners.
 419      */
 420     private void writeObject(ObjectOutputStream s) throws IOException {
 421         Hashtable<String, VetoableChangeSupport> children = null;
 422         VetoableChangeListener[] listeners = null;
 423         synchronized (this.map) {
 424             for (Entry<String, VetoableChangeListener[]> entry : this.map.getEntries()) {
 425                 String property = entry.getKey();
 426                 if (property == null) {
 427                     listeners = entry.getValue();
 428                 } else {
 429                     if (children == null) {
 430                         children = new Hashtable<>();
 431                     }
 432                     VetoableChangeSupport vcs = new VetoableChangeSupport(this.source);
 433                     vcs.map.set(null, entry.getValue());
 434                     children.put(property, vcs);
 435                 }


< prev index next >