KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > beans > VetoableChangeSupport


1 /*
2  * @(#)VetoableChangeSupport.java 1.44 04/03/04
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.beans;
9
10 import java.io.Serializable JavaDoc;
11 import java.io.ObjectOutputStream JavaDoc;
12 import java.io.ObjectInputStream JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19
20 /**
21  * This is a utility class that can be used by beans that support constrained
22  * properties. You can use an instance of this class as a member field
23  * of your bean and delegate various work to it.
24  *
25  * This class is serializable. When it is serialized it will save
26  * (and restore) any listeners that are themselves serializable. Any
27  * non-serializable listeners will be skipped during serialization.
28  */

29
30 public class VetoableChangeSupport implements java.io.Serializable JavaDoc {
31
32     /**
33      * Constructs a <code>VetoableChangeSupport</code> object.
34      *
35      * @param sourceBean The bean to be given as the source for any events.
36      */

37
38     public VetoableChangeSupport(Object JavaDoc sourceBean) {
39     if (sourceBean == null) {
40         throw new NullPointerException JavaDoc();
41     }
42     source = sourceBean;
43     }
44
45     /**
46      * Add a VetoableListener to the listener list.
47      * The listener is registered for all properties.
48      * The same listener object may be added more than once, and will be called
49      * as many times as it is added.
50      * If <code>listener</code> is null, no exception is thrown and no action
51      * is taken.
52      *
53      * @param listener The VetoableChangeListener to be added
54      */

55
56     public synchronized void addVetoableChangeListener(
57                     VetoableChangeListener JavaDoc listener) {
58         if (listener == null) {
59             return;
60         }
61         if (listener instanceof VetoableChangeListenerProxy JavaDoc) {
62             VetoableChangeListenerProxy JavaDoc proxy =
63                     (VetoableChangeListenerProxy JavaDoc)listener;
64             // Call two argument add method.
65
addVetoableChangeListener(proxy.getPropertyName(),
66                     (VetoableChangeListener JavaDoc)proxy.getListener());
67         } else {
68             if (listeners == null) {
69                 listeners = new java.util.Vector JavaDoc();
70             }
71         }
72         listeners.addElement(listener);
73     }
74
75     /**
76      * Remove a VetoableChangeListener from the listener list.
77      * This removes a VetoableChangeListener that was registered
78      * for all properties.
79      * If <code>listener</code> was added more than once to the same event
80      * source, it will be notified one less time after being removed.
81      * If <code>listener</code> is null, or was never added, no exception is
82      * thrown and no action is taken.
83      *
84      * @param listener The VetoableChangeListener to be removed
85      */

86     public synchronized void removeVetoableChangeListener(
87                     VetoableChangeListener JavaDoc listener) {
88         if (listener == null) {
89             return;
90         }
91         if (listener instanceof VetoableChangeListenerProxy JavaDoc) {
92             VetoableChangeListenerProxy JavaDoc proxy =
93                     (VetoableChangeListenerProxy JavaDoc)listener;
94             // Call two argument remove method.
95
removeVetoableChangeListener(proxy.getPropertyName(),
96                     (VetoableChangeListener JavaDoc)proxy.getListener());
97         } else {
98             if (listeners == null) {
99                 return;
100             }
101             listeners.removeElement(listener);
102         }
103     }
104
105     /**
106      * Returns the list of VetoableChangeListeners. If named vetoable change listeners
107      * were added, then VetoableChangeListenerProxy wrappers will returned
108      * <p>
109      * @return List of VetoableChangeListeners and VetoableChangeListenerProxys
110      * if named property change listeners were added.
111      * @since 1.4
112      */

113     public synchronized VetoableChangeListener JavaDoc[] getVetoableChangeListeners(){
114         List JavaDoc returnList = new ArrayList JavaDoc();
115
116         // Add all the VetoableChangeListeners
117
if (listeners != null) {
118             returnList.addAll(listeners);
119         }
120
121         // Add all the VetoableChangeListenerProxys
122
if (children != null) {
123             Iterator JavaDoc iterator = children.keySet().iterator();
124             while (iterator.hasNext()) {
125                 String JavaDoc key = (String JavaDoc)iterator.next();
126                 VetoableChangeSupport JavaDoc child =
127                         (VetoableChangeSupport JavaDoc)children.get(key);
128                 VetoableChangeListener JavaDoc[] childListeners =
129                     child.getVetoableChangeListeners();
130                 for (int index = childListeners.length - 1; index >= 0;
131                         index--) {
132                     returnList.add(new VetoableChangeListenerProxy JavaDoc(
133                             key, childListeners[index]));
134                 }
135             }
136         }
137     return (VetoableChangeListener JavaDoc[])(returnList.toArray(
138                 new VetoableChangeListener JavaDoc[0]));
139     }
140
141     /**
142      * Add a VetoableChangeListener for a specific property. The listener
143      * will be invoked only when a call on fireVetoableChange names that
144      * specific property.
145      * The same listener object may be added more than once. For each
146      * property, the listener will be invoked the number of times it was added
147      * for that property.
148      * If <code>propertyName</code> or <code>listener</code> is null, no
149      * exception is thrown and no action is taken.
150      *
151      * @param propertyName The name of the property to listen on.
152      * @param listener The VetoableChangeListener to be added
153      */

154
155     public synchronized void addVetoableChangeListener(
156                 String JavaDoc propertyName,
157                 VetoableChangeListener JavaDoc listener) {
158         if (listener == null || propertyName == null) {
159             return;
160         }
161         if (children == null) {
162             children = new java.util.Hashtable JavaDoc();
163         }
164         VetoableChangeSupport JavaDoc child = (VetoableChangeSupport JavaDoc)children.get(propertyName);
165         if (child == null) {
166             child = new VetoableChangeSupport JavaDoc(source);
167             children.put(propertyName, child);
168         }
169         child.addVetoableChangeListener(listener);
170     }
171
172     /**
173      * Remove a VetoableChangeListener for a specific property.
174      * If <code>listener</code> was added more than once to the same event
175      * source for the specified property, it will be notified one less time
176      * after being removed.
177      * If <code>propertyName</code> is null, no exception is thrown and no
178      * action is taken.
179      * If <code>listener</code> is null, or was never added for the specified
180      * property, no exception is thrown and no action is taken.
181      *
182      * @param propertyName The name of the property that was listened on.
183      * @param listener The VetoableChangeListener to be removed
184      */

185
186     public synchronized void removeVetoableChangeListener(
187                 String JavaDoc propertyName,
188                 VetoableChangeListener JavaDoc listener) {
189         if (listener == null || propertyName == null) {
190             return;
191         }
192         if (children == null) {
193             return;
194         }
195         VetoableChangeSupport JavaDoc child = (VetoableChangeSupport JavaDoc)children.get(propertyName);
196         if (child == null) {
197             return;
198         }
199         child.removeVetoableChangeListener(listener);
200     }
201
202     /**
203      * Returns an array of all the listeners which have been associated
204      * with the named property.
205      *
206      * @param propertyName The name of the property being listened to
207      * @return all the <code>VetoableChangeListeners</code> associated with
208      * the named property. If no such listeners have been added,
209      * or if <code>propertyName</code> is null, an empty array is
210      * returned.
211      */

212     public synchronized VetoableChangeListener JavaDoc[] getVetoableChangeListeners(
213             String JavaDoc propertyName) {
214         List JavaDoc returnList = new ArrayList JavaDoc();
215
216         if (children != null && propertyName != null) {
217             VetoableChangeSupport JavaDoc support =
218                     (VetoableChangeSupport JavaDoc)children.get(propertyName);
219             if (support != null) {
220                 returnList.addAll(
221                         Arrays.asList(support.getVetoableChangeListeners()));
222             }
223         }
224         return (VetoableChangeListener JavaDoc[])(returnList.toArray(new
225                 VetoableChangeListener JavaDoc[0]));
226     }
227
228     /**
229      * Report a vetoable property update to any registered listeners. If
230      * anyone vetos the change, then fire a new event reverting everyone to
231      * the old value and then rethrow the PropertyVetoException.
232      * <p>
233      * No event is fired if old and new are equal and non-null.
234      *
235      * @param propertyName The programmatic name of the property
236      * that is about to change..
237      * @param oldValue The old value of the property.
238      * @param newValue The new value of the property.
239      * @exception PropertyVetoException if the recipient wishes the property
240      * change to be rolled back.
241      */

242     public void fireVetoableChange(String JavaDoc propertyName,
243                     Object JavaDoc oldValue, Object JavaDoc newValue)
244                     throws PropertyVetoException JavaDoc {
245     if (listeners == null && children == null) {
246         return;
247     }
248
249         PropertyChangeEvent JavaDoc evt = new PropertyChangeEvent JavaDoc(source, propertyName,
250                                 oldValue, newValue);
251     fireVetoableChange(evt);
252     }
253
254     /**
255      * Report a int vetoable property update to any registered listeners.
256      * No event is fired if old and new are equal and non-null.
257      * <p>
258      * This is merely a convenience wrapper around the more general
259      * fireVetoableChange method that takes Object values.
260      *
261      * @param propertyName The programmatic name of the property
262      * that is about to change.
263      * @param oldValue The old value of the property.
264      * @param newValue The new value of the property.
265      */

266     public void fireVetoableChange(String JavaDoc propertyName,
267                     int oldValue, int newValue)
268                     throws PropertyVetoException JavaDoc {
269     if (oldValue == newValue) {
270         return;
271     }
272     fireVetoableChange(propertyName, new Integer JavaDoc(oldValue), new Integer JavaDoc(newValue));
273     }
274
275     /**
276      * Report a boolean vetoable property update to any registered listeners.
277      * No event is fired if old and new are equal and non-null.
278      * <p>
279      * This is merely a convenience wrapper around the more general
280      * fireVetoableChange method that takes Object values.
281      *
282      * @param propertyName The programmatic name of the property
283      * that is about to change.
284      * @param oldValue The old value of the property.
285      * @param newValue The new value of the property.
286      */

287     public void fireVetoableChange(String JavaDoc propertyName,
288                     boolean oldValue, boolean newValue)
289                     throws PropertyVetoException JavaDoc {
290     if (oldValue == newValue) {
291         return;
292     }
293     fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
294     }
295
296
297     /**
298      * Fire a vetoable property update to any registered listeners. If
299      * anyone vetos the change, then fire a new event reverting everyone to
300      * the old value and then rethrow the PropertyVetoException.
301      * <p>
302      * No event is fired if old and new are equal and non-null.
303      *
304      * @param evt The PropertyChangeEvent to be fired.
305      * @exception PropertyVetoException if the recipient wishes the property
306      * change to be rolled back.
307      */

308     public void fireVetoableChange(PropertyChangeEvent JavaDoc evt)
309                     throws PropertyVetoException JavaDoc {
310
311     Object JavaDoc oldValue = evt.getOldValue();
312     Object JavaDoc newValue = evt.getNewValue();
313     String JavaDoc propertyName = evt.getPropertyName();
314     if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
315         return;
316     }
317
318     java.util.Vector JavaDoc targets = null;
319     VetoableChangeSupport JavaDoc child = null;
320     synchronized (this) {
321         if (listeners != null) {
322             targets = (java.util.Vector JavaDoc) listeners.clone();
323         }
324         if (children != null && propertyName != null) {
325         child = (VetoableChangeSupport JavaDoc)children.get(propertyName);
326         }
327     }
328
329     if (listeners != null) {
330         try {
331             for (int i = 0; i < targets.size(); i++) {
332                 VetoableChangeListener JavaDoc target =
333                 (VetoableChangeListener JavaDoc)targets.elementAt(i);
334                 target.vetoableChange(evt);
335             }
336         } catch (PropertyVetoException JavaDoc veto) {
337             // Create an event to revert everyone to the old value.
338
evt = new PropertyChangeEvent JavaDoc(source, propertyName, newValue, oldValue);
339             for (int i = 0; i < targets.size(); i++) {
340             try {
341                     VetoableChangeListener JavaDoc target =
342                 (VetoableChangeListener JavaDoc)targets.elementAt(i);
343                     target.vetoableChange(evt);
344             } catch (PropertyVetoException JavaDoc ex) {
345                  // We just ignore exceptions that occur during reversions.
346
}
347             }
348             // And now rethrow the PropertyVetoException.
349
throw veto;
350         }
351     }
352
353     if (child != null) {
354         child.fireVetoableChange(evt);
355     }
356     }
357
358
359     /**
360      * Check if there are any listeners for a specific property, including
361      * those registered on all properties. If <code>propertyName</code>
362      * is null, only check for listeners registered on all properties.
363      *
364      * @param propertyName the property name.
365      * @return true if there are one or more listeners for the given property
366      */

367     public synchronized boolean hasListeners(String JavaDoc propertyName) {
368     if (listeners != null && !listeners.isEmpty()) {
369         // there is a generic listener
370
return true;
371     }
372     if (children != null && propertyName != null) {
373         VetoableChangeSupport JavaDoc child = (VetoableChangeSupport JavaDoc)children.get(propertyName);
374         if (child != null && child.listeners != null) {
375         return !child.listeners.isEmpty();
376         }
377     }
378     return false;
379     }
380
381     /**
382      * @serialData Null terminated list of <code>VetoableChangeListeners</code>.
383      * <p>
384      * At serialization time we skip non-serializable listeners and
385      * only serialize the serializable listeners.
386      *
387      */

388
389     private void writeObject(ObjectOutputStream JavaDoc s) throws IOException JavaDoc {
390         s.defaultWriteObject();
391
392     java.util.Vector JavaDoc v = null;
393     synchronized (this) {
394         if (listeners != null) {
395             v = (java.util.Vector JavaDoc) listeners.clone();
396             }
397     }
398
399     if (v != null) {
400         for(int i = 0; i < v.size(); i++) {
401             VetoableChangeListener JavaDoc l = (VetoableChangeListener JavaDoc)v.elementAt(i);
402             if (l instanceof Serializable JavaDoc) {
403                 s.writeObject(l);
404             }
405             }
406         }
407         s.writeObject(null);
408     }
409
410
411     private void readObject(ObjectInputStream JavaDoc s) throws ClassNotFoundException JavaDoc, IOException JavaDoc {
412         s.defaultReadObject();
413       
414         Object JavaDoc listenerOrNull;
415         while(null != (listenerOrNull = s.readObject())) {
416         addVetoableChangeListener((VetoableChangeListener JavaDoc)listenerOrNull);
417         }
418     }
419
420     /**
421      * "listeners" lists all the generic listeners.
422      *
423      * This is transient - its state is written in the writeObject method.
424      */

425     transient private java.util.Vector JavaDoc listeners;
426
427     /**
428      * Hashtable for managing listeners for specific properties.
429      * Maps property names to VetoableChangeSupport objects.
430      * @serial
431      * @since 1.2
432      */

433     private java.util.Hashtable JavaDoc children;
434
435     /**
436      * The object to be provided as the "source" for any generated events.
437      * @serial
438      */

439     private Object JavaDoc source;
440
441     /**
442      * Internal version number
443      * @serial
444      */

445     private int vetoableChangeSupportSerializedDataVersion = 2;
446
447     /**
448      * Serialization version ID, so we're compatible with JDK 1.1
449      */

450     static final long serialVersionUID = -5090210921595982017L;
451 }
452
Popular Tags