001/*
002 * $Id: AbstractBean.java 4088 2011-11-17 19:53:49Z kschaefe $
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
022package org.jdesktop.beans;
023
024import java.beans.PropertyChangeEvent;
025import java.beans.PropertyChangeListener;
026import java.beans.PropertyChangeSupport;
027import java.beans.PropertyVetoException;
028import java.beans.VetoableChangeListener;
029import 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@SuppressWarnings("nls")
141public abstract class AbstractBean {
142    /**
143     * Helper class that manages all the property change notification machinery.
144     * PropertyChangeSupport cannot be extended directly because it requires
145     * a bean in the constructor, and the "this" argument is not valid until
146     * after super construction. Hence, delegation instead of extension
147     */
148    private transient PropertyChangeSupport pcs;
149    
150    /**
151     * Helper class that manages all the veto property change notification machinery.
152     */
153    private transient VetoableChangeSupport vcs;
154    
155    /** Creates a new instance of AbstractBean */
156    protected AbstractBean() {
157        pcs = new PropertyChangeSupport(this);
158        vcs = new VetoableChangeSupport(this);
159    }
160    
161    /** 
162     * Creates a new instance of AbstractBean, using the supplied PropertyChangeSupport and
163     * VetoableChangeSupport delegates. Neither of these may be null.
164     */
165    protected AbstractBean(PropertyChangeSupport pcs, VetoableChangeSupport vcs) {
166        if (pcs == null) {
167            throw new NullPointerException("PropertyChangeSupport must not be null");
168        }
169        if (vcs == null) {
170            throw new NullPointerException("VetoableChangeSupport must not be null");
171        }
172        
173        this.pcs = pcs;
174        this.vcs = vcs;
175    }
176    
177    /**
178     * Add a PropertyChangeListener to the listener list.
179     * The listener is registered for all properties.
180     * The same listener object may be added more than once, and will be called
181     * as many times as it is added.
182     * If <code>listener</code> is null, no exception is thrown and no action
183     * is taken.
184     *
185     * @param listener  The PropertyChangeListener to be added
186     */
187    public final void addPropertyChangeListener(PropertyChangeListener listener) {
188        pcs.addPropertyChangeListener(listener);
189    }
190
191    /**
192     * Remove a PropertyChangeListener from the listener list.
193     * This removes a PropertyChangeListener that was registered
194     * for all properties.
195     * If <code>listener</code> was added more than once to the same event
196     * source, it will be notified one less time after being removed.
197     * If <code>listener</code> is null, or was never added, no exception is
198     * thrown and no action is taken.
199     *
200     * @param listener  The PropertyChangeListener to be removed
201     */
202    public final void removePropertyChangeListener(PropertyChangeListener listener) {
203        pcs.removePropertyChangeListener(listener);
204    }
205
206    /**
207     * Returns an array of all the listeners that were added to the
208     * PropertyChangeSupport object with addPropertyChangeListener().
209     * <p>
210     * If some listeners have been added with a named property, then
211     * the returned array will be a mixture of PropertyChangeListeners
212     * and <code>PropertyChangeListenerProxy</code>s. If the calling
213     * method is interested in distinguishing the listeners then it must
214     * test each element to see if it's a
215     * <code>PropertyChangeListenerProxy</code>, perform the cast, and examine
216     * the parameter.
217     * 
218     * <pre>
219     * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
220     * for (int i = 0; i < listeners.length; i++) {
221     *     if (listeners[i] instanceof PropertyChangeListenerProxy) {
222     *     PropertyChangeListenerProxy proxy = 
223     *                    (PropertyChangeListenerProxy)listeners[i];
224     *     if (proxy.getPropertyName().equals("foo")) {
225     *       // proxy is a PropertyChangeListener which was associated
226     *       // with the property named "foo"
227     *     }
228     *   }
229     * }
230     *</pre>
231     *
232     * @see java.beans.PropertyChangeListenerProxy
233     * @return all of the <code>PropertyChangeListeners</code> added or an 
234     *         empty array if no listeners have been added
235     */
236    public final PropertyChangeListener[] getPropertyChangeListeners() {
237        return pcs.getPropertyChangeListeners();
238    }
239
240    /**
241     * Add a PropertyChangeListener for a specific property.  The listener
242     * will be invoked only when a call on firePropertyChange names that
243     * specific property.
244     * The same listener object may be added more than once.  For each
245     * property,  the listener will be invoked the number of times it was added
246     * for that property.
247     * If <code>propertyName</code> or <code>listener</code> is null, no
248     * exception is thrown and no action is taken.
249     *
250     * @param propertyName  The name of the property to listen on.
251     * @param listener  The PropertyChangeListener to be added
252     */
253    public final void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
254        pcs.addPropertyChangeListener(propertyName, listener);
255    }
256
257    /**
258     * Remove a PropertyChangeListener for a specific property.
259     * If <code>listener</code> was added more than once to the same event
260     * source for the specified property, it will be notified one less time
261     * after being removed.
262     * If <code>propertyName</code> is null,  no exception is thrown and no
263     * action is taken.
264     * If <code>listener</code> is null, or was never added for the specified
265     * property, no exception is thrown and no action is taken.
266     *
267     * @param propertyName  The name of the property that was listened on.
268     * @param listener  The PropertyChangeListener to be removed
269     */
270    public final void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
271        pcs.removePropertyChangeListener(propertyName, listener);
272    }
273
274    /**
275     * Returns an array of all the listeners which have been associated 
276     * with the named property.
277     *
278     * @param propertyName  The name of the property being listened to
279     * @return all of the <code>PropertyChangeListeners</code> associated with
280     *         the named property.  If no such listeners have been added,
281     *         or if <code>propertyName</code> is null, an empty array is
282     *         returned.
283     */
284    public final PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
285            return pcs.getPropertyChangeListeners(propertyName);
286    }
287
288    /**
289     * Report a bound property update to any registered listeners.
290     * No event is fired if old and new are equal and non-null.
291     *
292     * <p>
293     * This is merely a convenience wrapper around the more general
294     * firePropertyChange method that takes {@code
295     * PropertyChangeEvent} value.
296     *
297     * @param propertyName  The programmatic name of the property
298     *        that was changed.
299     * @param oldValue  The old value of the property.
300     * @param newValue  The new value of the property.
301     */
302    protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
303        pcs.firePropertyChange(propertyName, oldValue, newValue);
304    }
305
306    /**
307     * Fire an existing PropertyChangeEvent to any registered listeners.
308     * No event is fired if the given event's old and new values are
309     * equal and non-null.
310     * @param evt  The PropertyChangeEvent object.
311     */
312    protected final void firePropertyChange(PropertyChangeEvent evt) {
313        pcs.firePropertyChange(evt);
314    }
315
316    
317    /**
318     * Report a bound indexed property update to any registered
319     * listeners. 
320     * <p>
321     * No event is fired if old and new values are equal
322     * and non-null.
323     *
324     * <p>
325     * This is merely a convenience wrapper around the more general
326     * firePropertyChange method that takes {@code PropertyChangeEvent} value.
327     *
328     * @param propertyName The programmatic name of the property that
329     *                     was changed.
330     * @param index        index of the property element that was changed.
331     * @param oldValue     The old value of the property.
332     * @param newValue     The new value of the property.
333     */
334    protected final void fireIndexedPropertyChange(String propertyName, int index,
335                      Object oldValue, Object newValue) {
336    pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
337    }
338
339    /**
340     * Check if there are any listeners for a specific property, including
341     * those registered on all properties.  If <code>propertyName</code>
342     * is null, only check for listeners registered on all properties.
343     *
344     * @param propertyName  the property name.
345     * @return true if there are one or more listeners for the given property
346     */
347    protected final boolean hasPropertyChangeListeners(String propertyName) {
348        return pcs.hasListeners(propertyName);
349    }
350    
351    /**
352     * Check if there are any listeners for a specific property, including
353     * those registered on all properties.  If <code>propertyName</code>
354     * is null, only check for listeners registered on all properties.
355     *
356     * @param propertyName  the property name.
357     * @return true if there are one or more listeners for the given property
358     */
359    protected final boolean hasVetoableChangeListeners(String propertyName) {
360        return vcs.hasListeners(propertyName);
361    }
362    
363    /**
364     * Add a VetoableListener to the listener list.
365     * The listener is registered for all properties.
366     * The same listener object may be added more than once, and will be called
367     * as many times as it is added.
368     * If <code>listener</code> is null, no exception is thrown and no action
369     * is taken.
370     *
371     * @param listener  The VetoableChangeListener to be added
372     */
373
374    public final void addVetoableChangeListener(VetoableChangeListener listener) {
375        vcs.addVetoableChangeListener(listener);
376    }
377
378    /**
379     * Remove a VetoableChangeListener from the listener list.
380     * This removes a VetoableChangeListener that was registered
381     * for all properties.
382     * If <code>listener</code> was added more than once to the same event
383     * source, it will be notified one less time after being removed.
384     * If <code>listener</code> is null, or was never added, no exception is
385     * thrown and no action is taken.
386     *
387     * @param listener  The VetoableChangeListener to be removed
388     */
389    public final void removeVetoableChangeListener(VetoableChangeListener listener) {
390        vcs.removeVetoableChangeListener(listener);
391    }
392
393    /**
394     * Returns the list of VetoableChangeListeners. If named vetoable change listeners
395     * were added, then VetoableChangeListenerProxy wrappers will returned
396     * <p>
397     * @return List of VetoableChangeListeners and VetoableChangeListenerProxys
398     *         if named property change listeners were added.
399     */
400    public final VetoableChangeListener[] getVetoableChangeListeners(){
401        return vcs.getVetoableChangeListeners();
402    }
403
404    /**
405     * Add a VetoableChangeListener for a specific property.  The listener
406     * will be invoked only when a call on fireVetoableChange names that
407     * specific property.
408     * The same listener object may be added more than once.  For each
409     * property,  the listener will be invoked the number of times it was added
410     * for that property.
411     * If <code>propertyName</code> or <code>listener</code> is null, no
412     * exception is thrown and no action is taken.
413     *
414     * @param propertyName  The name of the property to listen on.
415     * @param listener  The VetoableChangeListener to be added
416     */
417
418    public final void addVetoableChangeListener(String propertyName,
419                VetoableChangeListener listener) {
420        vcs.addVetoableChangeListener(propertyName, listener);
421    }
422
423    /**
424     * Remove a VetoableChangeListener for a specific property.
425     * If <code>listener</code> was added more than once to the same event
426     * source for the specified property, it will be notified one less time
427     * after being removed.
428     * If <code>propertyName</code> is null, no exception is thrown and no
429     * action is taken.
430     * If <code>listener</code> is null, or was never added for the specified
431     * property, no exception is thrown and no action is taken.
432     *
433     * @param propertyName  The name of the property that was listened on.
434     * @param listener  The VetoableChangeListener to be removed
435     */
436
437    public final void removeVetoableChangeListener(String propertyName,
438                VetoableChangeListener listener) {
439        vcs.removeVetoableChangeListener(propertyName, listener);
440    }
441
442    /**
443     * Returns an array of all the listeners which have been associated 
444     * with the named property.
445     *
446     * @param propertyName  The name of the property being listened to
447     * @return all the <code>VetoableChangeListeners</code> associated with
448     *         the named property.  If no such listeners have been added,
449     *         or if <code>propertyName</code> is null, an empty array is
450     *         returned.
451     */
452    public final VetoableChangeListener[] getVetoableChangeListeners(String propertyName) {
453        return vcs.getVetoableChangeListeners(propertyName);
454    }
455
456    /**
457     * Report a vetoable property update to any registered listeners.  If
458     * anyone vetos the change, then fire a new event reverting everyone to 
459     * the old value and then rethrow the PropertyVetoException.
460     * <p>
461     * No event is fired if old and new are equal and non-null.
462     *
463     * @param propertyName  The programmatic name of the property
464     *        that is about to change..
465     * @param oldValue  The old value of the property.
466     * @param newValue  The new value of the property.
467     * @exception PropertyVetoException if the recipient wishes the property
468     *              change to be rolled back.
469     */
470    protected final void fireVetoableChange(String propertyName, 
471                    Object oldValue, Object newValue)
472                    throws PropertyVetoException {
473        vcs.fireVetoableChange(propertyName, oldValue, newValue);
474    }
475
476    /**
477     * Fire a vetoable property update to any registered listeners.  If
478     * anyone vetos the change, then fire a new event reverting everyone to 
479     * the old value and then rethrow the PropertyVetoException.
480     * <p>
481     * No event is fired if old and new are equal and non-null.
482     *
483     * @param evt  The PropertyChangeEvent to be fired.
484     * @exception PropertyVetoException if the recipient wishes the property
485     *              change to be rolled back.
486     */
487    protected final void fireVetoableChange(PropertyChangeEvent evt)
488                    throws PropertyVetoException {
489        vcs.fireVetoableChange(evt);
490    }
491    
492    /**
493     * {@inheritDoc}
494     */
495    @Override
496    public Object clone() throws CloneNotSupportedException {
497        AbstractBean result = (AbstractBean) super.clone();
498        result.pcs = new PropertyChangeSupport(result);
499        result.vcs = new VetoableChangeSupport(result);
500        return result;
501    }
502}