KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > beans > PropertyChangeSupport


1 /*
2  * @(#)PropertyChangeSupport.java 1.49 04/05/11
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 import sun.awt.EventListenerAggregate;
20
21 /**
22  * This is a utility class that can be used by beans that support bound
23  * properties. You can use an instance of this class as a member field
24  * of your bean and delegate various work to it.
25  *
26  * This class is serializable. When it is serialized it will save
27  * (and restore) any listeners that are themselves serializable. Any
28  * non-serializable listeners will be skipped during serialization.
29  *
30  */

31 public class PropertyChangeSupport implements java.io.Serializable JavaDoc {
32
33     // Manages the listener list.
34
private transient EventListenerAggregate listeners;
35
36     /**
37      * Constructs a <code>PropertyChangeSupport</code> object.
38      *
39      * @param sourceBean The bean to be given as the source for any events.
40      */

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

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

90     public synchronized void removePropertyChangeListener(
91                 PropertyChangeListener JavaDoc listener) {
92     if (listener == null) {
93         return;
94     }
95
96     if (listener instanceof PropertyChangeListenerProxy JavaDoc) {
97         PropertyChangeListenerProxy JavaDoc proxy =
98                     (PropertyChangeListenerProxy JavaDoc)listener;
99         // Call two argument remove method.
100
removePropertyChangeListener(proxy.getPropertyName(),
101                    (PropertyChangeListener JavaDoc)proxy.getListener());
102     } else {
103         if (listeners == null) {
104         return;
105         }
106         listeners.remove(listener);
107     }
108     }
109
110     /**
111      * Returns an array of all the listeners that were added to the
112      * PropertyChangeSupport object with addPropertyChangeListener().
113      * <p>
114      * If some listeners have been added with a named property, then
115      * the returned array will be a mixture of PropertyChangeListeners
116      * and <code>PropertyChangeListenerProxy</code>s. If the calling
117      * method is interested in distinguishing the listeners then it must
118      * test each element to see if it's a
119      * <code>PropertyChangeListenerProxy</code>, perform the cast, and examine
120      * the parameter.
121      *
122      * <pre>
123      * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
124      * for (int i = 0; i < listeners.length; i++) {
125      * if (listeners[i] instanceof PropertyChangeListenerProxy) {
126      * PropertyChangeListenerProxy proxy =
127      * (PropertyChangeListenerProxy)listeners[i];
128      * if (proxy.getPropertyName().equals("foo")) {
129      * // proxy is a PropertyChangeListener which was associated
130      * // with the property named "foo"
131      * }
132      * }
133      * }
134      *</pre>
135      *
136      * @see PropertyChangeListenerProxy
137      * @return all of the <code>PropertyChangeListeners</code> added or an
138      * empty array if no listeners have been added
139      * @since 1.4
140      */

141     public synchronized PropertyChangeListener JavaDoc[] getPropertyChangeListeners() {
142     List JavaDoc returnList = new ArrayList JavaDoc();
143      
144     // Add all the PropertyChangeListeners
145
if (listeners != null) {
146         returnList.addAll(Arrays.asList(listeners.getListenersInternal()));
147     }
148      
149     // Add all the PropertyChangeListenerProxys
150
if (children != null) {
151         Iterator JavaDoc iterator = children.keySet().iterator();
152         while (iterator.hasNext()) {
153         String JavaDoc key = (String JavaDoc)iterator.next();
154         PropertyChangeSupport JavaDoc child =
155                         (PropertyChangeSupport JavaDoc)children.get(key);
156         PropertyChangeListener JavaDoc[] childListeners =
157                         child.getPropertyChangeListeners();
158         for (int index = childListeners.length - 1; index >= 0;
159                         index--) {
160             returnList.add(new PropertyChangeListenerProxy JavaDoc(
161                             key, childListeners[index]));
162         }
163         }
164     }
165     return (PropertyChangeListener JavaDoc[])(returnList.toArray(
166                 new PropertyChangeListener JavaDoc[0]));
167     }
168
169     /**
170      * Add a PropertyChangeListener for a specific property. The listener
171      * will be invoked only when a call on firePropertyChange names that
172      * specific property.
173      * The same listener object may be added more than once. For each
174      * property, the listener will be invoked the number of times it was added
175      * for that property.
176      * If <code>propertyName</code> or <code>listener</code> is null, no
177      * exception is thrown and no action is taken.
178      *
179      * @param propertyName The name of the property to listen on.
180      * @param listener The PropertyChangeListener to be added
181      */

182
183     public synchronized void addPropertyChangeListener(
184                 String JavaDoc propertyName,
185                 PropertyChangeListener JavaDoc listener) {
186         if (listener == null || propertyName == null) {
187             return;
188         }
189         if (children == null) {
190             children = new java.util.Hashtable JavaDoc();
191         }
192         PropertyChangeSupport JavaDoc child = (PropertyChangeSupport JavaDoc)children.get(propertyName);
193         if (child == null) {
194             child = new PropertyChangeSupport JavaDoc(source);
195             children.put(propertyName, child);
196         }
197         child.addPropertyChangeListener(listener);
198     }
199
200     /**
201      * Remove a PropertyChangeListener for a specific property.
202      * If <code>listener</code> was added more than once to the same event
203      * source for the specified property, it will be notified one less time
204      * after being removed.
205      * If <code>propertyName</code> is null, no exception is thrown and no
206      * action is taken.
207      * If <code>listener</code> is null, or was never added for the specified
208      * property, no exception is thrown and no action is taken.
209      *
210      * @param propertyName The name of the property that was listened on.
211      * @param listener The PropertyChangeListener to be removed
212      */

213
214     public synchronized void removePropertyChangeListener(
215                 String JavaDoc propertyName,
216                 PropertyChangeListener JavaDoc listener) {
217         if (listener == null || propertyName == null) {
218             return;
219         }
220         if (children == null) {
221             return;
222         }
223         PropertyChangeSupport JavaDoc child = (PropertyChangeSupport JavaDoc)children.get(propertyName);
224         if (child == null) {
225             return;
226         }
227         child.removePropertyChangeListener(listener);
228     }
229
230     /**
231      * Returns an array of all the listeners which have been associated
232      * with the named property.
233      *
234      * @param propertyName The name of the property being listened to
235      * @return all of the <code>PropertyChangeListeners</code> associated with
236      * the named property. If no such listeners have been added,
237      * or if <code>propertyName</code> is null, an empty array is
238      * returned.
239      */

240     public synchronized PropertyChangeListener JavaDoc[] getPropertyChangeListeners(
241             String JavaDoc propertyName) {
242         ArrayList JavaDoc returnList = new ArrayList JavaDoc();
243
244         if (children != null && propertyName != null) {
245             PropertyChangeSupport JavaDoc support =
246                     (PropertyChangeSupport JavaDoc)children.get(propertyName);
247             if (support != null) {
248                 returnList.addAll(
249                         Arrays.asList(support.getPropertyChangeListeners()));
250             }
251         }
252         return (PropertyChangeListener JavaDoc[])(returnList.toArray(
253                 new PropertyChangeListener JavaDoc[0]));
254     }
255
256     /**
257      * Report a bound property update to any registered listeners.
258      * No event is fired if old and new are equal and non-null.
259      *
260      * @param propertyName The programmatic name of the property
261      * that was changed.
262      * @param oldValue The old value of the property.
263      * @param newValue The new value of the property.
264      */

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

286     public void firePropertyChange(String JavaDoc propertyName,
287                     int oldValue, int newValue) {
288     if (oldValue == newValue) {
289         return;
290     }
291     firePropertyChange(propertyName, new Integer JavaDoc(oldValue), new Integer JavaDoc(newValue));
292     }
293
294
295     /**
296      * Report a boolean bound property update to any registered listeners.
297      * No event is fired if old and new are equal and non-null.
298      * <p>
299      * This is merely a convenience wrapper around the more general
300      * firePropertyChange method that takes Object values.
301      *
302      * @param propertyName The programmatic name of the property
303      * that was changed.
304      * @param oldValue The old value of the property.
305      * @param newValue The new value of the property.
306      */

307     public void firePropertyChange(String JavaDoc propertyName,
308                     boolean oldValue, boolean newValue) {
309     if (oldValue == newValue) {
310         return;
311     }
312     firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
313     }
314
315     /**
316      * Fire an existing PropertyChangeEvent to any registered listeners.
317      * No event is fired if the given event's old and new values are
318      * equal and non-null.
319      * @param evt The PropertyChangeEvent object.
320      */

321     public void firePropertyChange(PropertyChangeEvent JavaDoc evt) {
322     Object JavaDoc oldValue = evt.getOldValue();
323     Object JavaDoc newValue = evt.getNewValue();
324         String JavaDoc propertyName = evt.getPropertyName();
325     if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
326         return;
327     }
328
329     if (listeners != null) {
330         Object JavaDoc[] list = listeners.getListenersInternal();
331         for (int i = 0; i < list.length; i++) {
332         PropertyChangeListener JavaDoc target = (PropertyChangeListener JavaDoc)list[i];
333         target.propertyChange(evt);
334         }
335     }
336
337     if (children != null && propertyName != null) {
338         PropertyChangeSupport JavaDoc child = null;
339         child = (PropertyChangeSupport JavaDoc)children.get(propertyName);
340         if (child != null) {
341         child.firePropertyChange(evt);
342         }
343     }
344     }
345
346     
347     /**
348      * Report a bound indexed property update to any registered
349      * listeners.
350      * <p>
351      * No event is fired if old and new values are equal
352      * and non-null.
353      *
354      * @param propertyName The programmatic name of the property that
355      * was changed.
356      * @param index index of the property element that was changed.
357      * @param oldValue The old value of the property.
358      * @param newValue The new value of the property.
359      * @since 1.5
360      */

361     public void fireIndexedPropertyChange(String JavaDoc propertyName, int index,
362                       Object JavaDoc oldValue, Object JavaDoc newValue) {
363     firePropertyChange(new IndexedPropertyChangeEvent JavaDoc
364         (source, propertyName, oldValue, newValue, index));
365     }
366
367     /**
368      * Report an <code>int</code> bound indexed property update to any registered
369      * listeners.
370      * <p>
371      * No event is fired if old and new values are equal
372      * and non-null.
373      * <p>
374      * This is merely a convenience wrapper around the more general
375      * fireIndexedPropertyChange method which takes Object values.
376      *
377      * @param propertyName The programmatic name of the property that
378      * was changed.
379      * @param index index of the property element that was changed.
380      * @param oldValue The old value of the property.
381      * @param newValue The new value of the property.
382      * @since 1.5
383      */

384     public void fireIndexedPropertyChange(String JavaDoc propertyName, int index,
385                       int oldValue, int newValue) {
386     if (oldValue == newValue) {
387         return;
388     }
389     fireIndexedPropertyChange(propertyName, index,
390                   new Integer JavaDoc(oldValue),
391                   new Integer JavaDoc(newValue));
392     }
393
394     /**
395      * Report a <code>boolean</code> bound indexed property update to any
396      * registered listeners.
397      * <p>
398      * No event is fired if old and new values are equal and non-null.
399      * <p>
400      * This is merely a convenience wrapper around the more general
401      * fireIndexedPropertyChange method which takes Object values.
402      *
403      * @param propertyName The programmatic name of the property that
404      * was changed.
405      * @param index index of the property element that was changed.
406      * @param oldValue The old value of the property.
407      * @param newValue The new value of the property.
408      * @since 1.5
409      */

410     public void fireIndexedPropertyChange(String JavaDoc propertyName, int index,
411                       boolean oldValue, boolean newValue) {
412     if (oldValue == newValue) {
413         return;
414     }
415     fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue),
416                   Boolean.valueOf(newValue));
417     }
418
419     /**
420      * Check if there are any listeners for a specific property, including
421      * those registered on all properties. If <code>propertyName</code>
422      * is null, only check for listeners registered on all properties.
423      *
424      * @param propertyName the property name.
425      * @return true if there are one or more listeners for the given property
426      */

427     public synchronized boolean hasListeners(String JavaDoc propertyName) {
428     if (listeners != null && !listeners.isEmpty()) {
429         // there is a generic listener
430
return true;
431     }
432     if (children != null && propertyName != null) {
433         PropertyChangeSupport JavaDoc child = (PropertyChangeSupport JavaDoc)children.get(propertyName);
434         if (child != null && child.listeners != null) {
435         return !child.listeners.isEmpty();
436         }
437     }
438     return false;
439     }
440
441     /**
442      * @serialData Null terminated list of <code>PropertyChangeListeners</code>.
443      * <p>
444      * At serialization time we skip non-serializable listeners and
445      * only serialize the serializable listeners.
446      *
447      */

448     private void writeObject(ObjectOutputStream JavaDoc s) throws IOException JavaDoc {
449         s.defaultWriteObject();
450
451     if (listeners != null) {
452         Object JavaDoc[] list = listeners.getListenersCopy();
453
454         for (int i = 0; i < list.length; i++) {
455             PropertyChangeListener JavaDoc l = (PropertyChangeListener JavaDoc)list[i];
456             if (l instanceof Serializable JavaDoc) {
457                 s.writeObject(l);
458             }
459             }
460         }
461         s.writeObject(null);
462     }
463
464
465     private void readObject(ObjectInputStream JavaDoc s) throws ClassNotFoundException JavaDoc, IOException JavaDoc {
466         s.defaultReadObject();
467       
468         Object JavaDoc listenerOrNull;
469         while (null != (listenerOrNull = s.readObject())) {
470         addPropertyChangeListener((PropertyChangeListener JavaDoc)listenerOrNull);
471         }
472     }
473
474     /**
475      * Hashtable for managing listeners for specific properties.
476      * Maps property names to PropertyChangeSupport objects.
477      * @serial
478      * @since 1.2
479      */

480     private java.util.Hashtable JavaDoc children;
481
482     /**
483      * The object to be provided as the "source" for any generated events.
484      * @serial
485      */

486     private Object JavaDoc source;
487
488     /**
489      * Internal version number
490      * @serial
491      * @since
492      */

493     private int propertyChangeSupportSerializedDataVersion = 2;
494
495     /**
496      * Serialization version ID, so we're compatible with JDK 1.1
497      */

498     static final long serialVersionUID = 6401253773779951803L;
499 }
500
Popular Tags