001    /*
002     * $Id: AbstractBean.java 3100 2008-10-14 22:33:10Z rah003 $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.beans;
023    
024    import java.beans.PropertyChangeEvent;
025    import java.beans.PropertyChangeListener;
026    import java.beans.PropertyChangeSupport;
027    import java.beans.PropertyVetoException;
028    import java.beans.VetoableChangeListener;
029    import java.beans.VetoableChangeSupport;
030    
031    /**
032     * <p>
033     * A convenience class from which to extend all non-visual AbstractBeans. It
034     * manages the PropertyChange notification system, making it relatively trivial
035     * to add support for property change events in getters/setters.
036     * </p>
037     * 
038     * <p>
039     * A non-visual java bean is a Java class that conforms to the AbstractBean
040     * patterns to allow visual manipulation of the bean's properties and event
041     * handlers at design-time.
042     * </p>
043     * 
044     * <p>
045     * Here is a simple example bean that contains one property, foo, and the proper
046     * pattern for implementing property change notification:
047     * 
048     * <pre><code>
049     * public class ABean extends AbstractBean {
050     *     private String foo;
051     * 
052     *     public void setFoo(String newFoo) {
053     *         String old = getFoo();
054     *         this.foo = newFoo;
055     *         firePropertyChange(&quot;foo&quot;, old, getFoo());
056     *     }
057     * 
058     *     public String getFoo() {
059     *         return foo;
060     *     }
061     * }
062     * </code></pre>
063     * 
064     * </p>
065     * 
066     * <p>
067     * You will notice that "getFoo()" is used in the setFoo method rather than
068     * accessing "foo" directly for the gets. This is done intentionally so that if
069     * a subclass overrides getFoo() to return, for instance, a constant value the
070     * property change notification system will continue to work properly.
071     * </p>
072     * 
073     * <p>
074     * The firePropertyChange method takes into account the old value and the new
075     * value. Only if the two differ will it fire a property change event. So you
076     * can be assured from the above code fragment that a property change event will
077     * only occur if old is indeed different from getFoo()
078     * </p>
079     * 
080     * <p>
081     * <code>AbstractBean</code> also supports vetoable
082     * {@link PropertyChangeEvent} events. These events are similar to
083     * <code>PropertyChange</code> events, except a special exception can be used
084     * to veto changing the property. For example, perhaps the property is changing
085     * from "fred" to "red", but a listener deems that "red" is unexceptable. In
086     * this case, the listener can fire a veto exception and the property must
087     * remain "fred". For example:
088     * 
089     * <pre><code>
090     *  public class ABean extends AbstractBean {
091     *    private String foo;
092     *    
093     *    public void setFoo(String newFoo) throws PropertyVetoException {
094     *      String old = getFoo();
095     *      this.foo = newFoo;
096     *      fireVetoableChange(&quot;foo&quot;, old, getFoo());
097     *    }
098     *    public String getFoo() {
099     *      return foo;
100     *    }
101     *  }
102     * 
103     *  public class Tester {
104     *    public static void main(String... args) {
105     *      try {
106     *        ABean a = new ABean();
107     *        a.setFoo(&quot;fred&quot;);
108     *        a.addVetoableChangeListener(new VetoableChangeListener() {
109     *          public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
110     *            if (&quot;red&quot;.equals(evt.getNewValue()) {
111     *              throw new PropertyVetoException(&quot;Cannot be red!&quot;, evt);
112     *            }
113     *          }
114     *        }
115     *        a.setFoo(&quot;red&quot;);
116     *      } catch (Exception e) {
117     *        e.printStackTrace(); // this will be executed
118     *      }
119     *    }
120     *  }
121     * </code></pre>
122     * 
123     * </p>
124     * <p>
125     * {@code AbstractBean} is not {@link java.io.Serializable}. Special care must
126     * be taken when creating {@code Serializable} subclasses, as the
127     * {@code Serializable} listeners will not be saved.  Subclasses will need to 
128     * manually save the serializable listeners.  The {@link AbstractSerializableBean}
129     * is {@code Serializable} and already handles the listeners correctly.  If 
130     * possible, it is recommended that {@code Serializable} beans should extend
131     * {@code AbstractSerializableBean}.  If it is not possible, the
132     * {@code AbstractSerializableBean} bean implementation provides details on
133     * how to correctly serialize an {@code AbstractBean} subclass.
134     * </p>
135     * 
136     * @see AbstractSerializableBean
137     * @status REVIEWED
138     * @author rbair
139     */
140    public abstract class AbstractBean {
141        /**
142         * Helper class that manages all the property change notification machinery.
143         * PropertyChangeSupport cannot be extended directly because it requires
144         * a bean in the constructor, and the "this" argument is not valid until
145         * after super construction. Hence, delegation instead of extension
146         */
147        private transient PropertyChangeSupport pcs;
148        
149        /**
150         * Helper class that manages all the veto property change notification machinery.
151         */
152        private transient VetoableChangeSupport vcs;
153        
154        /** Creates a new instance of AbstractBean */
155        protected AbstractBean() {
156            pcs = new PropertyChangeSupport(this);
157            vcs = new VetoableChangeSupport(this);
158        }
159        
160        /** 
161         * Creates a new instance of AbstractBean, using the supplied PropertyChangeSupport and
162         * VetoableChangeSupport delegates. Neither of these may be null.
163         */
164        protected AbstractBean(PropertyChangeSupport pcs, VetoableChangeSupport vcs) {
165            if (pcs == null) {
166                throw new NullPointerException("PropertyChangeSupport must not be null");
167            }
168            if (vcs == null) {
169                throw new NullPointerException("VetoableChangeSupport must not be null");
170            }
171            
172            this.pcs = pcs;
173            this.vcs = vcs;
174        }
175        
176        /**
177         * Add a PropertyChangeListener to the listener list.
178         * The listener is registered for all properties.
179         * The same listener object may be added more than once, and will be called
180         * as many times as it is added.
181         * If <code>listener</code> is null, no exception is thrown and no action
182         * is taken.
183         *
184         * @param listener  The PropertyChangeListener to be added
185         */
186        public final void addPropertyChangeListener(PropertyChangeListener listener) {
187            pcs.addPropertyChangeListener(listener);
188        }
189    
190        /**
191         * Remove a PropertyChangeListener from the listener list.
192         * This removes a PropertyChangeListener that was registered
193         * for all properties.
194         * If <code>listener</code> was added more than once to the same event
195         * source, it will be notified one less time after being removed.
196         * If <code>listener</code> is null, or was never added, no exception is
197         * thrown and no action is taken.
198         *
199         * @param listener  The PropertyChangeListener to be removed
200         */
201        public final void removePropertyChangeListener(PropertyChangeListener listener) {
202            pcs.removePropertyChangeListener(listener);
203        }
204    
205        /**
206         * Returns an array of all the listeners that were added to the
207         * PropertyChangeSupport object with addPropertyChangeListener().
208         * <p>
209         * If some listeners have been added with a named property, then
210         * the returned array will be a mixture of PropertyChangeListeners
211         * and <code>PropertyChangeListenerProxy</code>s. If the calling
212         * method is interested in distinguishing the listeners then it must
213         * test each element to see if it's a
214         * <code>PropertyChangeListenerProxy</code>, perform the cast, and examine
215         * the parameter.
216         * 
217         * <pre>
218         * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
219         * for (int i = 0; i < listeners.length; i++) {
220         *     if (listeners[i] instanceof PropertyChangeListenerProxy) {
221         *     PropertyChangeListenerProxy proxy = 
222         *                    (PropertyChangeListenerProxy)listeners[i];
223         *     if (proxy.getPropertyName().equals("foo")) {
224         *       // proxy is a PropertyChangeListener which was associated
225         *       // with the property named "foo"
226         *     }
227         *   }
228         * }
229         *</pre>
230         *
231         * @see java.beans.PropertyChangeListenerProxy
232         * @return all of the <code>PropertyChangeListeners</code> added or an 
233         *         empty array if no listeners have been added
234         */
235        public final PropertyChangeListener[] getPropertyChangeListeners() {
236            return pcs.getPropertyChangeListeners();
237        }
238    
239        /**
240         * Add a PropertyChangeListener for a specific property.  The listener
241         * will be invoked only when a call on firePropertyChange names that
242         * specific property.
243         * The same listener object may be added more than once.  For each
244         * property,  the listener will be invoked the number of times it was added
245         * for that property.
246         * If <code>propertyName</code> or <code>listener</code> is null, no
247         * exception is thrown and no action is taken.
248         *
249         * @param propertyName  The name of the property to listen on.
250         * @param listener  The PropertyChangeListener to be added
251         */
252        public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
253            pcs.addPropertyChangeListener(propertyName, listener);
254        }
255    
256        /**
257         * Remove a PropertyChangeListener for a specific property.
258         * If <code>listener</code> was added more than once to the same event
259         * source for the specified property, it will be notified one less time
260         * after being removed.
261         * If <code>propertyName</code> is null,  no exception is thrown and no
262         * action is taken.
263         * If <code>listener</code> is null, or was never added for the specified
264         * property, no exception is thrown and no action is taken.
265         *
266         * @param propertyName  The name of the property that was listened on.
267         * @param listener  The PropertyChangeListener to be removed
268         */
269        public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
270            pcs.removePropertyChangeListener(propertyName, listener);
271        }
272    
273        /**
274         * Returns an array of all the listeners which have been associated 
275         * with the named property.
276         *
277         * @param propertyName  The name of the property being listened to
278         * @return all of the <code>PropertyChangeListeners</code> associated with
279         *         the named property.  If no such listeners have been added,
280         *         or if <code>propertyName</code> is null, an empty array is
281         *         returned.
282         */
283        public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
284                return pcs.getPropertyChangeListeners(propertyName);
285        }
286    
287        /**
288         * Report a bound property update to any registered listeners.
289         * No event is fired if old and new are equal and non-null.
290         *
291         * <p>
292         * This is merely a convenience wrapper around the more general
293         * firePropertyChange method that takes {@code
294         * PropertyChangeEvent} value.
295         *
296         * @param propertyName  The programmatic name of the property
297         *        that was changed.
298         * @param oldValue  The old value of the property.
299         * @param newValue  The new value of the property.
300         */
301        protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
302            pcs.firePropertyChange(propertyName, oldValue, newValue);
303        }
304    
305        /**
306         * Fire an existing PropertyChangeEvent to any registered listeners.
307         * No event is fired if the given event's old and new values are
308         * equal and non-null.
309         * @param evt  The PropertyChangeEvent object.
310         */
311        protected final void firePropertyChange(PropertyChangeEvent evt) {
312            pcs.firePropertyChange(evt);
313        }
314    
315        
316        /**
317         * Report a bound indexed property update to any registered
318         * listeners. 
319         * <p>
320         * No event is fired if old and new values are equal
321         * and non-null.
322         *
323         * <p>
324         * This is merely a convenience wrapper around the more general
325         * firePropertyChange method that takes {@code PropertyChangeEvent} value.
326         *
327         * @param propertyName The programmatic name of the property that
328         *                     was changed.
329         * @param index        index of the property element that was changed.
330         * @param oldValue     The old value of the property.
331         * @param newValue     The new value of the property.
332         */
333        protected final void fireIndexedPropertyChange(String propertyName, int index,
334                          Object oldValue, Object newValue) {
335        pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
336        }
337    
338        /**
339         * Check if there are any listeners for a specific property, including
340         * those registered on all properties.  If <code>propertyName</code>
341         * is null, only check for listeners registered on all properties.
342         *
343         * @param propertyName  the property name.
344         * @return true if there are one or more listeners for the given property
345         */
346        protected final boolean hasPropertyChangeListeners(String propertyName) {
347            return pcs.hasListeners(propertyName);
348        }
349        
350        /**
351         * Check if there are any listeners for a specific property, including
352         * those registered on all properties.  If <code>propertyName</code>
353         * is null, only check for listeners registered on all properties.
354         *
355         * @param propertyName  the property name.
356         * @return true if there are one or more listeners for the given property
357         */
358        protected final boolean hasVetoableChangeListeners(String propertyName) {
359            return vcs.hasListeners(propertyName);
360        }
361        
362        /**
363         * Add a VetoableListener to the listener list.
364         * The listener is registered for all properties.
365         * The same listener object may be added more than once, and will be called
366         * as many times as it is added.
367         * If <code>listener</code> is null, no exception is thrown and no action
368         * is taken.
369         *
370         * @param listener  The VetoableChangeListener to be added
371         */
372    
373        public final void addVetoableChangeListener(VetoableChangeListener listener) {
374            vcs.addVetoableChangeListener(listener);
375        }
376    
377        /**
378         * Remove a VetoableChangeListener from the listener list.
379         * This removes a VetoableChangeListener that was registered
380         * for all properties.
381         * If <code>listener</code> was added more than once to the same event
382         * source, it will be notified one less time after being removed.
383         * If <code>listener</code> is null, or was never added, no exception is
384         * thrown and no action is taken.
385         *
386         * @param listener  The VetoableChangeListener to be removed
387         */
388        public final void removeVetoableChangeListener(VetoableChangeListener listener) {
389            vcs.removeVetoableChangeListener(listener);
390        }
391    
392        /**
393         * Returns the list of VetoableChangeListeners. If named vetoable change listeners
394         * were added, then VetoableChangeListenerProxy wrappers will returned
395         * <p>
396         * @return List of VetoableChangeListeners and VetoableChangeListenerProxys
397         *         if named property change listeners were added.
398         */
399        public final VetoableChangeListener[] getVetoableChangeListeners(){
400            return vcs.getVetoableChangeListeners();
401        }
402    
403        /**
404         * Add a VetoableChangeListener for a specific property.  The listener
405         * will be invoked only when a call on fireVetoableChange names that
406         * specific property.
407         * The same listener object may be added more than once.  For each
408         * property,  the listener will be invoked the number of times it was added
409         * for that property.
410         * If <code>propertyName</code> or <code>listener</code> is null, no
411         * exception is thrown and no action is taken.
412         *
413         * @param propertyName  The name of the property to listen on.
414         * @param listener  The VetoableChangeListener to be added
415         */
416    
417        public final void addVetoableChangeListener(String propertyName,
418                    VetoableChangeListener listener) {
419            vcs.addVetoableChangeListener(propertyName, listener);
420        }
421    
422        /**
423         * Remove a VetoableChangeListener for a specific property.
424         * If <code>listener</code> was added more than once to the same event
425         * source for the specified property, it will be notified one less time
426         * after being removed.
427         * If <code>propertyName</code> is null, no exception is thrown and no
428         * action is taken.
429         * If <code>listener</code> is null, or was never added for the specified
430         * property, no exception is thrown and no action is taken.
431         *
432         * @param propertyName  The name of the property that was listened on.
433         * @param listener  The VetoableChangeListener to be removed
434         */
435    
436        public final void removeVetoableChangeListener(String propertyName,
437                    VetoableChangeListener listener) {
438            vcs.removeVetoableChangeListener(propertyName, listener);
439        }
440    
441        /**
442         * Returns an array of all the listeners which have been associated 
443         * with the named property.
444         *
445         * @param propertyName  The name of the property being listened to
446         * @return all the <code>VetoableChangeListeners</code> associated with
447         *         the named property.  If no such listeners have been added,
448         *         or if <code>propertyName</code> is null, an empty array is
449         *         returned.
450         */
451        public final VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
452            return vcs.getVetoableChangeListeners(propertyName);
453        }
454    
455        /**
456         * Report a vetoable property update to any registered listeners.  If
457         * anyone vetos the change, then fire a new event reverting everyone to 
458         * the old value and then rethrow the PropertyVetoException.
459         * <p>
460         * No event is fired if old and new are equal and non-null.
461         *
462         * @param propertyName  The programmatic name of the property
463         *        that is about to change..
464         * @param oldValue  The old value of the property.
465         * @param newValue  The new value of the property.
466         * @exception PropertyVetoException if the recipient wishes the property
467         *              change to be rolled back.
468         */
469        protected final void fireVetoableChange(String propertyName, 
470                        Object oldValue, Object newValue)
471                        throws PropertyVetoException {
472            vcs.fireVetoableChange(propertyName, oldValue, newValue);
473        }
474    
475        /**
476         * Fire a vetoable property update to any registered listeners.  If
477         * anyone vetos the change, then fire a new event reverting everyone to 
478         * the old value and then rethrow the PropertyVetoException.
479         * <p>
480         * No event is fired if old and new are equal and non-null.
481         *
482         * @param evt  The PropertyChangeEvent to be fired.
483         * @exception PropertyVetoException if the recipient wishes the property
484         *              change to be rolled back.
485         */
486        protected final void fireVetoableChange(PropertyChangeEvent evt)
487                        throws PropertyVetoException {
488            vcs.fireVetoableChange(evt);
489        }
490        
491        /**
492         * {@inheritDoc}
493         */
494        @Override
495        public Object clone() throws CloneNotSupportedException {
496            AbstractBean result = (AbstractBean) super.clone();
497            result.pcs = new PropertyChangeSupport(result);
498            result.vcs = new VetoableChangeSupport(result);
499            return result;
500        }
501    }