001    /*
002     * $Id: JavaBean.java,v 1.2 2006/05/14 15:55:55 dmouse Exp $
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.swingx;
023    
024    import java.beans.PropertyChangeEvent;
025    import java.beans.PropertyChangeListener;
026    import java.beans.PropertyChangeSupport;
027    
028    /**
029     * <p>A convenience class from which to extend all non-visual JavaBeans. It
030     * manages the PropertyChange notification system, making it relatively trivial
031     * to add support for property change events in getters/setters.</p>
032     *
033     * <p>Here is a simple example bean that contains one property, foo, and the
034     * proper pattern for implementing property change notification:
035     * <pre><code>
036     *  public class ABean extends JavaBean {
037     *    private String foo;
038     *    
039     *    public void setFoo(String newFoo) {
040     *      String old = getFoo();
041     *      this.foo = newFoo;
042     *      firePropertyChange("foo", old, getFoo());
043     *    }
044     *
045     *    public String getFoo() {
046     *      return foo;
047     *    }
048     *  }
049     * </code></pre></p>
050     *
051     * <p>You will notice that "getFoo()" is used in the setFoo method rather than
052     * accessing "foo" directly for the gets. This is done intentionally so that if
053     * a subclass overrides getFoo() to return, for instance, a constant value the
054     * property change notification system will continue to work properly.</p>
055     *
056     * <p>The firePropertyChange method takes into account the old value and the new
057     * value. Only if the two differ will it fire a property change event. So you can
058     * be assured from the above code fragment that a property change event will only
059     * occur if old is indeed different from getFoo()</p>
060     *
061     * @author rbair
062     */
063    public class JavaBean {
064        /**
065         * Helper class that manages all the property change notification machinery.
066         * PropertyChangeSupport can not be extended directly because it requires
067         * a bean in the constructor, and the "this" argument is not valid until
068         * after super construction. Hence, delegation instead of extension
069         */
070        private PropertyChangeSupport pcs;
071        
072        /** Creates a new instance of JavaBean */
073        public JavaBean() {
074            pcs = new PropertyChangeSupport(this);
075        }
076        
077        /**
078         * Add a PropertyChangeListener to the listener list.
079         * The listener is registered for all properties.
080         * The same listener object may be added more than once, and will be called
081         * as many times as it is added.
082         * If <code>listener</code> is null, no exception is thrown and no action
083         * is taken.
084         *
085         * @param listener  The PropertyChangeListener to be added
086         */
087        public void addPropertyChangeListener(PropertyChangeListener listener) {
088            pcs.addPropertyChangeListener(listener);
089        }
090    
091        /**
092         * Remove a PropertyChangeListener from the listener list.
093         * This removes a PropertyChangeListener that was registered
094         * for all properties.
095         * If <code>listener</code> was added more than once to the same event
096         * source, it will be notified one less time after being removed.
097         * If <code>listener</code> is null, or was never added, no exception is
098         * thrown and no action is taken.
099         *
100         * @param listener  The PropertyChangeListener to be removed
101         */
102        public void removePropertyChangeListener(PropertyChangeListener listener) {
103            pcs.removePropertyChangeListener(listener);
104        }
105    
106        /**
107         * Returns an array of all the listeners that were added to the
108         * PropertyChangeSupport object with addPropertyChangeListener().
109         * <p>
110         * If some listeners have been added with a named property, then
111         * the returned array will be a mixture of PropertyChangeListeners
112         * and <code>PropertyChangeListenerProxy</code>s. If the calling
113         * method is interested in distinguishing the listeners then it must
114         * test each element to see if it's a
115         * <code>PropertyChangeListenerProxy</code>, perform the cast, and examine
116         * the parameter.
117         * 
118         * <pre>
119         * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
120         * for (int i = 0; i < listeners.length; i++) {
121         *   if (listeners[i] instanceof PropertyChangeListenerProxy) {
122         *     PropertyChangeListenerProxy proxy = 
123         *                    (PropertyChangeListenerProxy)listeners[i];
124         *     if (proxy.getPropertyName().equals("foo")) {
125         *       // proxy is a PropertyChangeListener which was associated
126         *       // with the property named "foo"
127         *     }
128         *   }
129         * }
130         *</pre>
131         *
132         * @see java.beans.PropertyChangeListenerProxy
133         * @return all of the <code>PropertyChangeListeners</code> added or an 
134         *         empty array if no listeners have been added
135         */
136        public PropertyChangeListener[] getPropertyChangeListeners() {
137            return pcs.getPropertyChangeListeners();
138        }
139    
140        /**
141         * Add a PropertyChangeListener for a specific property.  The listener
142         * will be invoked only when a call on firePropertyChange names that
143         * specific property.
144         * The same listener object may be added more than once.  For each
145         * property,  the listener will be invoked the number of times it was added
146         * for that property.
147         * If <code>propertyName</code> or <code>listener</code> is null, no
148         * exception is thrown and no action is taken.
149         *
150         * @param propertyName  The name of the property to listen on.
151         * @param listener  The PropertyChangeListener to be added
152         */
153    
154        public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
155            pcs.addPropertyChangeListener(propertyName, listener);
156        }
157    
158        /**
159         * Remove a PropertyChangeListener for a specific property.
160         * If <code>listener</code> was added more than once to the same event
161         * source for the specified property, it will be notified one less time
162         * after being removed.
163         * If <code>propertyName</code> is null,  no exception is thrown and no
164         * action is taken.
165         * If <code>listener</code> is null, or was never added for the specified
166         * property, no exception is thrown and no action is taken.
167         *
168         * @param propertyName  The name of the property that was listened on.
169         * @param listener  The PropertyChangeListener to be removed
170         */
171    
172        public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
173            pcs.removePropertyChangeListener(propertyName, listener);
174        }
175    
176        /**
177         * Returns an array of all the listeners which have been associated 
178         * with the named property.
179         *
180         * @param propertyName  The name of the property being listened to
181         * @return all of the <code>PropertyChangeListeners</code> associated with
182         *         the named property.  If no such listeners have been added,
183         *         or if <code>propertyName</code> is null, an empty array is
184         *         returned.
185         */
186        public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
187                return pcs.getPropertyChangeListeners(propertyName);
188        }
189    
190        /**
191         * Report a bound property update to any registered listeners.
192         * No event is fired if old and new are equal and non-null.
193         *
194         * <p>
195         * This is merely a convenience wrapper around the more general
196         * firePropertyChange method that takes {@code
197         * PropertyChangeEvent} value.
198         *
199         * @param propertyName  The programmatic name of the property
200         *      that was changed.
201         * @param oldValue  The old value of the property.
202         * @param newValue  The new value of the property.
203         */
204        public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
205        pcs.firePropertyChange(propertyName, oldValue, newValue);
206        }
207    
208        /**
209         * Report an int bound property update to any registered listeners.
210         * No event is fired if old and new are equal.
211         * <p>
212         * This is merely a convenience wrapper around the more general
213         * firePropertyChange method that takes Object values.
214         *
215         * @param propertyName  The programmatic name of the property
216         *      that was changed.
217         * @param oldValue  The old value of the property.
218         * @param newValue  The new value of the property.
219         */
220        public void firePropertyChange(String propertyName, int oldValue, int newValue) {
221        pcs.firePropertyChange(propertyName, oldValue, newValue);
222        }
223    
224    
225        /**
226         * Report a boolean bound property update to any registered listeners.
227         * No event is fired if old and new are equal.
228         * <p>
229         * This is merely a convenience wrapper around the more general
230         * firePropertyChange method that takes Object values.
231         *
232         * @param propertyName  The programmatic name of the property
233         *      that was changed.
234         * @param oldValue  The old value of the property.
235         * @param newValue  The new value of the property.
236         */
237        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
238        pcs.firePropertyChange(propertyName, oldValue, newValue);
239        }
240    
241        /**
242         * Fire an existing PropertyChangeEvent to any registered listeners.
243         * No event is fired if the given event's old and new values are
244         * equal and non-null.
245         * @param evt  The PropertyChangeEvent object.
246         */
247        public void firePropertyChange(PropertyChangeEvent evt) {
248            pcs.firePropertyChange(evt);
249        }
250    
251        
252        /**
253         * Report a bound indexed property update to any registered
254         * listeners. 
255         * <p>
256         * No event is fired if old and new values are equal
257         * and non-null.
258         *
259         * <p>
260         * This is merely a convenience wrapper around the more general
261         * firePropertyChange method that takes {@code PropertyChangeEvent} value.
262         *
263         * @param propertyName The programmatic name of the property that
264         *                     was changed.
265         * @param index        index of the property element that was changed.
266         * @param oldValue     The old value of the property.
267         * @param newValue     The new value of the property.
268         */
269        public void fireIndexedPropertyChange(String propertyName, int index,
270                          Object oldValue, Object newValue) {
271        pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
272        }
273    
274        /**
275         * Report an <code>int</code> bound indexed property update to any registered 
276         * listeners. 
277         * <p>
278         * No event is fired if old and new values are equal.
279         * <p>
280         * This is merely a convenience wrapper around the more general
281         * fireIndexedPropertyChange method which takes Object values.
282         *
283         * @param propertyName The programmatic name of the property that
284         *                     was changed.
285         * @param index        index of the property element that was changed.
286         * @param oldValue     The old value of the property.
287         * @param newValue     The new value of the property.
288         */
289        public void fireIndexedPropertyChange(String propertyName, int index,
290                          int oldValue, int newValue) {
291        pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
292        }
293    
294        /**
295         * Report a <code>boolean</code> bound indexed property update to any 
296         * registered listeners. 
297         * <p>
298         * No event is fired if old and new values are equal.
299         * <p>
300         * This is merely a convenience wrapper around the more general
301         * fireIndexedPropertyChange method which takes Object values.
302         *
303         * @param propertyName The programmatic name of the property that
304         *                     was changed.
305         * @param index        index of the property element that was changed.
306         * @param oldValue     The old value of the property.
307         * @param newValue     The new value of the property.
308         */
309        public void fireIndexedPropertyChange(String propertyName, int index,
310                          boolean oldValue, boolean newValue) {
311            pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
312        }
313    
314        /**
315         * Check if there are any listeners for a specific property, including
316         * those registered on all properties.  If <code>propertyName</code>
317         * is null, only check for listeners registered on all properties.
318         *
319         * @param propertyName  the property name.
320         * @return true if there are one or more listeners for the given property
321         */
322        public boolean hasListeners(String propertyName) {
323            return pcs.hasListeners(propertyName);
324        }
325    }