Source for javax.swing.event.SwingPropertyChangeSupport

   1: /* SwingPropertyChangeSupport.java --
   2:    Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package javax.swing.event;
  39: 
  40: import java.beans.PropertyChangeEvent;
  41: import java.beans.PropertyChangeListener;
  42: import java.beans.PropertyChangeListenerProxy;
  43: import java.beans.PropertyChangeSupport;
  44: import java.util.ArrayList;
  45: import java.util.EventListener;
  46: import java.util.Hashtable;
  47: import java.util.Iterator;
  48: import java.util.List;
  49: import java.util.Map;
  50: import java.util.Set;
  51: 
  52: /**
  53:  * Provides a mechanism for registering {@link PropertyChangeListener}s and
  54:  * forwarding {@link PropertyChangeEvent}s to those listeners.
  55:  * 
  56:  * @author Andrew Selkirk
  57:  */
  58: public final class SwingPropertyChangeSupport
  59:     extends PropertyChangeSupport 
  60: {
  61: 
  62:   private static final long serialVersionUID = 7162625831330845068L;
  63: 
  64:   /**
  65:    * Storage for the listeners that are not linked to a specific property.
  66:    */
  67:   private transient EventListenerList listeners;
  68: 
  69:   /**
  70:    * Storage for the listeners that are linked (by name) to a specific property.
  71:    * The hash table maps <code>String</code> objects (the property names) to 
  72:    * {@link EventListenerList} instances (which record the listener(s) for the 
  73:    * given property).
  74:    */
  75:   private Hashtable propertyListeners;
  76: 
  77:   /**
  78:    * The object that is used as the default source for the 
  79:    * {@link PropertyChangeEvent}s generated by this class.
  80:    */
  81:   private Object source;
  82: 
  83:   /**
  84:    * Creates a new instance.
  85:    * 
  86:    * @param source  the source (<code>null</code> not permitted).
  87:    * 
  88:    * @throws NullPointerException if <code>source</code> is <code>null</code>.
  89:    */
  90:   public SwingPropertyChangeSupport(Object source) 
  91:   {
  92:     super(source);
  93:     this.source = source;
  94:     this.listeners = new EventListenerList();
  95:     this.propertyListeners = new Hashtable();
  96:   }
  97: 
  98:   /**
  99:    * Registers <code>listener</code> to receive notification of any future
 100:    * {@link PropertyChangeEvent}s generated by this instance.
 101:    * 
 102:    * @param listener  the listener (<code>null</code> is ignored).
 103:    * 
 104:    * @see #removePropertyChangeListener(PropertyChangeListener)
 105:    */
 106:   public synchronized void addPropertyChangeListener(PropertyChangeListener 
 107:           listener) 
 108:   {
 109:     listeners.add(PropertyChangeListener.class, listener);
 110:   } 
 111: 
 112:   /**
 113:    * Registers <code>listener</code> to receive notification of any future
 114:    * {@link PropertyChangeEvent}s generated by this instance for the named
 115:    * property.
 116:    * 
 117:    * @param propertyName  the property name.
 118:    * @param listener  the listener.
 119:    * 
 120:    * @see #removePropertyChangeListener(String, PropertyChangeListener)
 121:    */
 122:   public synchronized void addPropertyChangeListener(String propertyName, 
 123:           PropertyChangeListener listener) 
 124:   {
 125:     EventListenerList list;
 126:     list = (EventListenerList) propertyListeners.get(propertyName);
 127:     if (list == null) 
 128:       {
 129:         list = new EventListenerList();
 130:         propertyListeners.put(propertyName, list);
 131:       }
 132:     list.add(PropertyChangeListener.class, listener);
 133:   }
 134: 
 135:   /**
 136:    * Removes <code>listener</code> from the list of registered listeners, so 
 137:    * that it will no longer receive notification of property change events.
 138:    * 
 139:    * @param listener  the listener to remove.
 140:    */
 141:   public synchronized void removePropertyChangeListener(PropertyChangeListener 
 142:           listener) 
 143:   {
 144:     listeners.remove(PropertyChangeListener.class, listener);
 145:   }
 146: 
 147:   /**
 148:    * Removes <code>listener</code> from the list of registered listeners for
 149:    * the named property, so that it will no longer receive notification of 
 150:    * property change events.
 151:    * 
 152:    * @param propertyName  the property name.
 153:    * @param listener  the listener to remove.
 154:    */
 155:   public synchronized void removePropertyChangeListener(String propertyName, 
 156:           PropertyChangeListener listener) 
 157:   {
 158:     EventListenerList list;
 159:     list = (EventListenerList) propertyListeners.get(propertyName);
 160:     if (list == null)
 161:       return;
 162:     list.remove(PropertyChangeListener.class, listener);
 163:     if (list.getListenerCount() == 0) 
 164:       {
 165:         propertyListeners.remove(propertyName);
 166:       } 
 167:   } 
 168: 
 169:   /**
 170:    * Returns an array of the {@link PropertyChangeListener}s registered with
 171:    * this <code>SwingPropertyChangeSupport</code> instance.
 172:    * 
 173:    * @return The array of listeners.
 174:    * 
 175:    * @since 1.4
 176:    */
 177:   public synchronized PropertyChangeListener[] getPropertyChangeListeners() 
 178:   {
 179:     // fetch the named listeners first so we know how many there are
 180:     List namedListeners = new ArrayList();
 181:     Set namedListenerEntries = propertyListeners.entrySet();
 182:     Iterator iterator = namedListenerEntries.iterator();
 183:     while (iterator.hasNext())
 184:       {
 185:         Map.Entry e = (Map.Entry) iterator.next();
 186:         String propertyName = (String) e.getKey();
 187:         EventListenerList ell = (EventListenerList) e.getValue();
 188:         if (ell != null)
 189:           {
 190:             Object[] list = ell.getListenerList();
 191:             for (int i = 0; i < list.length; i += 2)
 192:               {
 193:                 namedListeners.add(new PropertyChangeListenerProxy(propertyName, 
 194:                     (PropertyChangeListener) list[i + 1]));
 195:               }
 196:           }
 197:       }
 198:     
 199:     // create an array that can hold everything
 200:     int size = listeners.getListenerCount() + namedListeners.size();
 201:     PropertyChangeListener[] result = new PropertyChangeListener[size];
 202:     
 203:     // copy in the general listeners
 204:     Object[] list = listeners.getListenerList();
 205:     int index = 0;
 206:     for (int i = 0; i < list.length; i += 2)
 207:       result[index++] = (PropertyChangeListener) list[i + 1];
 208:     
 209:     // ...and the named listeners
 210:     Iterator iterator2 = namedListeners.iterator();
 211:     while (iterator2.hasNext()) 
 212:       result[index++] = (PropertyChangeListenerProxy) iterator2.next();
 213:     
 214:     return result;
 215:   }
 216:   
 217:   /**
 218:    * Returns an array of all listeners that are registered to receive 
 219:    * notification of changes to the named property.  This includes the general
 220:    * listeners as well as those registered specifically for the named 
 221:    * property.
 222:    * 
 223:    * @param propertyName  the property name.
 224:    * 
 225:    * @return An array of all listeners for the named property.
 226:    */
 227:   public synchronized PropertyChangeListener[] getPropertyChangeListeners(
 228:           String propertyName)
 229:   {
 230:     EventListenerList list 
 231:       = (EventListenerList) propertyListeners.get(propertyName);
 232:     if (list == null)
 233:       return getPropertyChangeListeners();
 234:     int size = listeners.getListenerCount() + list.getListenerCount();
 235:     PropertyChangeListener[] result = new PropertyChangeListener[size];
 236:     
 237:     // copy in the general listeners
 238:     int index = 0;
 239:     for (int i = 0; i < listeners.listenerList.length; i += 2)
 240:       {
 241:         result[index++] 
 242:             = (PropertyChangeListener) listeners.listenerList[i + 1];
 243:       }
 244:     
 245:     // copy in the specific listeners
 246:     Object[] specificListeners = list.getListenerList();
 247:     for (int i = 0; i < specificListeners.length; i += 2)
 248:       {
 249:         result[index++] = (PropertyChangeListener) specificListeners[i + 1];
 250:       }
 251:     return result;
 252:   }
 253: 
 254:   /**
 255:    * Creates a new {@link PropertyChangeEvent} using the given arguments (and
 256:    * the default <code>source</code> for this 
 257:    * <code>SwingPropertyChangeSupport</code> instance) and forwards it to all 
 258:    * registered listeners via the 
 259:    * {@link PropertyChangeListener#propertyChange(PropertyChangeEvent)} method.
 260:    * <p>
 261:    * Note that if <code>oldValue</code> and <code>newValue</code> are non-null
 262:    * and equal, no listeners will be notified.
 263:    * 
 264:    * @param propertyName  the property name.
 265:    * @param oldValue  the old value
 266:    * @param newValue  the new value.
 267:    */
 268:   public void firePropertyChange(String propertyName, Object oldValue, 
 269:           Object newValue) 
 270:   {
 271:     PropertyChangeEvent event;
 272:     event = new PropertyChangeEvent(source, propertyName, oldValue, newValue);
 273:     firePropertyChange(event);
 274:   } 
 275: 
 276:   /**
 277:    * Forwards <code>event</code> to registered listeners.
 278:    * <p>
 279:    * Note that if the event's <code>getOldValue()</code> and 
 280:    * <code>getNewValue()</code> methods return non-null and equal values, no 
 281:    * listeners will be notified.
 282:    * 
 283:    * @param event  the event.
 284:    */
 285:   public void firePropertyChange(PropertyChangeEvent event) 
 286:   {
 287:     EventListenerList list;
 288:     EventListener[] listenerList;
 289:     int index;
 290:     PropertyChangeListener listener;
 291: 
 292:     // if the old and new values are non-null and equal, don't notify listeners
 293:     if (event.getOldValue() != null && event.getNewValue() != null &&
 294:               event.getOldValue().equals(event.getNewValue())) 
 295:       return;
 296: 
 297:     // Process Main Listener List
 298:     listenerList = listeners.getListeners(PropertyChangeListener.class);
 299:     for (index = 0; index < listenerList.length; index++) 
 300:       {
 301:         listener = (PropertyChangeListener) listenerList[index];
 302:         listener.propertyChange(event);
 303:       } 
 304: 
 305:     // Process Property Listener List
 306:     list = (EventListenerList) propertyListeners.get(event.getPropertyName());
 307:     if (list != null) 
 308:       {
 309:         listenerList = list.getListeners(PropertyChangeListener.class);
 310:         for (index = 0; index < listenerList.length; index++) 
 311:           {
 312:             listener = (PropertyChangeListener) listenerList[index];
 313:             listener.propertyChange(event);
 314:           } 
 315:       } 
 316: 
 317:   } 
 318: 
 319:   /**
 320:    * Tell whether the specified property is being listened on or not. This
 321:    * will only return <code>true</code> if there are listeners on all
 322:    * properties or if there is a listener specifically on this property.
 323:    *
 324:    * @param propertyName the property that may be listened on
 325:    * @return whether the property is being listened on
 326:    * @throws NullPointerException if propertyName is null
 327:    */
 328:   public synchronized boolean hasListeners(String propertyName) 
 329:   {
 330:     if (listeners.getListenerCount() > 0)
 331:       return true;
 332:     else
 333:       return (propertyListeners.get(propertyName) != null);
 334:   } 
 335: 
 336: }