Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)
Frames | No Frames

Source for java.beans.PropertyChangeSupport

 1:  /* PropertyChangeSupport.java -- support to manage property change listeners
 2:  Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006
 3:  Free Software Foundation, Inc.
 4: 
 5: This file is part of GNU Classpath.
 6: 
 7: GNU Classpath is free software; you can redistribute it and/or modify
 8: it under the terms of the GNU General Public License as published by
 9: the Free Software Foundation; either version 2, or (at your option)
 10: any later version.
 11: 
 12: GNU Classpath is distributed in the hope that it will be useful, but
 13: WITHOUT ANY WARRANTY; without even the implied warranty of
 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15: General Public License for more details.
 16: 
 17: You should have received a copy of the GNU General Public License
 18: along with GNU Classpath; see the file COPYING. If not, write to the
 19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 20: 02110-1301 USA.
 21: 
 22: Linking this library statically or dynamically with other modules is
 23: making a combined work based on this library. Thus, the terms and
 24: conditions of the GNU General Public License cover the whole
 25: combination.
 26: 
 27: As a special exception, the copyright holders of this library give you
 28: permission to link this library with independent modules to produce an
 29: executable, regardless of the license terms of these independent
 30: modules, and to copy and distribute the resulting executable under
 31: terms of your choice, provided that you also meet, for each linked
 32: independent module, the terms and conditions of the license of that
 33: module. An independent module is a module which is not derived from
 34: or based on this library. If you modify this library, you may extend
 35: this exception to your version of the library, but you are not
 36: obligated to do so. If you do not wish to do so, delete this
 37: exception statement from your version. */
 38: 
 39: 
 40:  package java.beans;
 41: 
 42:  import java.io.IOException;
 43:  import java.io.ObjectInputStream;
 44:  import java.io.ObjectOutputStream;
 45:  import java.io.Serializable;
 46:  import java.util.ArrayList;
 47:  import java.util.Arrays;
 48:  import java.util.Hashtable;
 49:  import java.util.Iterator;
 50:  import java.util.Map.Entry;
 51:  import java.util.Vector;
 52: 
 53:  /**
 54:  * PropertyChangeSupport makes it easy to fire property change events and
 55:  * handle listeners. It allows chaining of listeners, as well as filtering
 56:  * by property name. In addition, it will serialize only those listeners
 57:  * which are serializable, ignoring the others without problem. This class
 58:  * is thread-safe.
 59:  *
 60:  * @author John Keiser
 61:  * @author Eric Blake (ebb9@email.byu.edu)
 62:  * @since 1.1
 63:  * @status updated to 1.4
 64:  */
 65:  public class PropertyChangeSupport implements Serializable
 66: {
 67:  /**
 68:  * Compatible with JDK 1.1+.
 69:  */
 70:  private static final long serialVersionUID = 6401253773779951803L;
 71: 
 72:  /**
 73:  * Maps property names (String) to named listeners (PropertyChangeSupport).
 74:  * If this is a child instance, this field will be null.
 75:  *
 76:  * @serial the map of property names to named listener managers
 77:  * @since 1.2
 78:  */
 79:  private Hashtable children;
 80: 
 81:  /**
 82:  * The non-null source object for any generated events.
 83:  *
 84:  * @serial the event source
 85:  */
 86:  private final Object source;
 87: 
 88:  /**
 89:  * A field to compare serialization versions - this class uses version 2.
 90:  *
 91:  * @serial the serialization format
 92:  */
 93:  private static final int propertyChangeSupportSerializedDataVersion = 2;
 94: 
 95:  /**
 96:  * The list of all registered property listeners. If this instance was
 97:  * created by user code, this only holds the global listeners (ie. not tied
 98:  * to a name), and may be null. If it was created by this class, as a
 99:  * helper for named properties, then this vector will be non-null, and this
 100:  * instance appears as a value in the <code>children</code> hashtable of
 101:  * another instance, so that the listeners are tied to the key of that
 102:  * hashtable entry.
 103:  */
 104:  private transient Vector listeners;
 105: 
 106:  /**
 107:  * Create a PropertyChangeSupport to work with a specific source bean.
 108:  *
 109:  * @param source the source bean to use
 110:  * @throws NullPointerException if source is null
 111:  */
 112:  public PropertyChangeSupport(Object source)
 113:  {
 114:  this.source = source;
 115:  if (source == null)
 116:  throw new NullPointerException();
 117:  }
 118: 
 119:  /**
 120:  * Adds a PropertyChangeListener to the list of global listeners. All
 121:  * property change events will be sent to this listener. The listener add
 122:  * is not unique: that is, <em>n</em> adds with the same listener will
 123:  * result in <em>n</em> events being sent to that listener for every
 124:  * property change. Adding a null listener is silently ignored.
 125:  * This method will unwrap a PropertyChangeListenerProxy,
 126:  * registering the underlying delegate to the named property list.
 127:  *
 128:  * @param l the listener to add
 129:  */
 130:  public synchronized void addPropertyChangeListener(PropertyChangeListener l)
 131:  {
 132:  if (l == null)
 133:  return;
 134: 
 135:  if (l instanceof PropertyChangeListenerProxy)
 136:  {
 137:  PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l;
 138:  addPropertyChangeListener(p.propertyName,
 139:  (PropertyChangeListener) p.getListener());
 140:  }
 141:  else
 142:  {
 143:  if (listeners == null)
 144:  listeners = new Vector();
 145:  listeners.add(l);
 146:  }
 147:  }
 148: 
 149:  /**
 150:  * Removes a PropertyChangeListener from the list of global listeners. If
 151:  * any specific properties are being listened on, they must be deregistered
 152:  * by themselves; this will only remove the general listener to all
 153:  * properties. If <code>add()</code> has been called multiple times for a
 154:  * particular listener, <code>remove()</code> will have to be called the
 155:  * same number of times to deregister it. This method will unwrap a
 156:  * PropertyChangeListenerProxy, removing the underlying delegate from the
 157:  * named property list.
 158:  *
 159:  * @param l the listener to remove
 160:  */
 161:  public synchronized void
 162:  removePropertyChangeListener(PropertyChangeListener l)
 163:  {
 164:  if (l instanceof PropertyChangeListenerProxy)
 165:  {
 166:  PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l;
 167:  removePropertyChangeListener(p.propertyName,
 168:  (PropertyChangeListener) p.getListener());
 169:  }
 170:  else if (listeners != null)
 171:  {
 172:  listeners.remove(l);
 173:  if (listeners.isEmpty())
 174:  listeners = null;
 175:  }
 176:  }
 177: 
 178:  /**
 179:  * Returns an array of all registered property change listeners. Those that
 180:  * were registered under a name will be wrapped in a
 181:  * <code>PropertyChangeListenerProxy</code>, so you must check whether the
 182:  * listener is an instance of the proxy class in order to see what name the
 183:  * real listener is registered under. If there are no registered listeners,
 184:  * this returns an empty array.
 185:  *
 186:  * @return the array of registered listeners
 187:  * @see PropertyChangeListenerProxy
 188:  * @since 1.4
 189:  */
 190:  public synchronized PropertyChangeListener[] getPropertyChangeListeners()
 191:  {
 192:  ArrayList list = new ArrayList();
 193:  if (listeners != null)
 194:  list.addAll(listeners);
 195:  if (children != null)
 196:  {
 197:  int i = children.size();
 198:  Iterator iter = children.entrySet().iterator();
 199:  while (--i >= 0)
 200:  {
 201:  Entry e = (Entry) iter.next();
 202:  String name = (String) e.getKey();
 203:  Vector v = ((PropertyChangeSupport) e.getValue()).listeners;
 204:  int j = v.size();
 205:  while (--j >= 0)
 206:  list.add(new PropertyChangeListenerProxy
 207:  (name, (PropertyChangeListener) v.get(j)));
 208:  }
 209:  }
 210:  return (PropertyChangeListener[])
 211:  list.toArray(new PropertyChangeListener[list.size()]);
 212:  }
 213: 
 214:  /**
 215:  * Adds a PropertyChangeListener listening on the specified property. Events
 216:  * will be sent to the listener only if the property name matches. The
 217:  * listener add is not unique; that is, <em>n</em> adds on a particular
 218:  * property for a particular listener will result in <em>n</em> events
 219:  * being sent to that listener when that property is changed. The effect is
 220:  * cumulative, too; if you are registered to listen to receive events on
 221:  * all property changes, and then you register on a particular property,
 222:  * you will receive change events for that property twice. Adding a null
 223:  * listener is silently ignored. This method will unwrap a
 224:  * PropertyChangeListenerProxy, registering the underlying
 225:  * delegate to the named property list if the names match, and discarding
 226:  * it otherwise.
 227:  *
 228:  * @param propertyName the name of the property to listen on
 229:  * @param l the listener to add
 230:  * @throws NullPointerException if propertyName is null
 231:  */
 232:  public synchronized void addPropertyChangeListener(String propertyName,
 233:  PropertyChangeListener l)
 234:  {
 235:  if (l == null)
 236:  return;
 237: 
 238:  while (l instanceof PropertyChangeListenerProxy)
 239:  {
 240:  PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l;
 241:  if (propertyName == null ? p.propertyName != null
 242:  : ! propertyName.equals(p.propertyName))
 243:  return;
 244:  l = (PropertyChangeListener) p.getListener();
 245:  }
 246:  PropertyChangeSupport s = null;
 247:  if (children == null)
 248:  children = new Hashtable();
 249:  else
 250:  s = (PropertyChangeSupport) children.get(propertyName);
 251:  if (s == null)
 252:  {
 253:  s = new PropertyChangeSupport(source);
 254:  s.listeners = new Vector();
 255:  children.put(propertyName, s);
 256:  }
 257:  s.listeners.add(l);
 258:  }
 259: 
 260:  /**
 261:  * Removes a PropertyChangeListener from listening to a specific property.
 262:  * If <code>add()</code> has been called multiple times for a particular
 263:  * listener on a property, <code>remove()</code> will have to be called the
 264:  * same number of times to deregister it. This method will unwrap a
 265:  * PropertyChangeListenerProxy, removing the underlying delegate from the
 266:  * named property list if the names match.
 267:  *
 268:  * @param propertyName the property to stop listening on
 269:  * @param l the listener to remove
 270:  * @throws NullPointerException if propertyName is null
 271:  */
 272:  public synchronized void
 273:  removePropertyChangeListener(String propertyName, PropertyChangeListener l)
 274:  {
 275:  if (children == null)
 276:  return;
 277:  PropertyChangeSupport s
 278:  = (PropertyChangeSupport) children.get(propertyName);
 279:  if (s == null)
 280:  return;
 281:  while (l instanceof PropertyChangeListenerProxy)
 282:  {
 283:  PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l;
 284:  if (propertyName == null ? p.propertyName != null
 285:  : ! propertyName.equals(p.propertyName))
 286:  return;
 287:  l = (PropertyChangeListener) p.getListener();
 288:  }
 289:  s.listeners.remove(l);
 290:  if (s.listeners.isEmpty())
 291:  {
 292:  children.remove(propertyName);
 293:  if (children.isEmpty())
 294:  children = null;
 295:  }
 296:  }
 297: 
 298:  /**
 299:  * Returns an array of all property change listeners registered under the
 300:  * given property name. If there are no registered listeners, or
 301:  * propertyName is null, this returns an empty array.
 302:  *
 303:  * @return the array of registered listeners
 304:  * @since 1.4
 305:  */
 306:  public synchronized PropertyChangeListener[]
 307:  getPropertyChangeListeners(String propertyName)
 308:  {
 309:  if (children == null || propertyName == null)
 310:  return new PropertyChangeListener[0];
 311:  PropertyChangeSupport s
 312:  = (PropertyChangeSupport) children.get(propertyName);
 313:  if (s == null)
 314:  return new PropertyChangeListener[0];
 315:  return (PropertyChangeListener[])
 316:  s.listeners.toArray(new PropertyChangeListener[s.listeners.size()]);
 317:  }
 318: 
 319:  /**
 320:  * Fire a PropertyChangeEvent containing the old and new values of the
 321:  * property to all the global listeners, and to all the listeners for the
 322:  * specified property name. This does nothing if old and new are non-null
 323:  * and equal.
 324:  *
 325:  * @param propertyName the name of the property that changed
 326:  * @param oldVal the old value
 327:  * @param newVal the new value
 328:  */
 329:  public void firePropertyChange(String propertyName,
 330:  Object oldVal, Object newVal)
 331:  {
 332:  firePropertyChange(new PropertyChangeEvent(source, propertyName,
 333:  oldVal, newVal));
 334:  }
 335: 
 336:  /**
 337:  * Fire a PropertyChangeEvent containing the old and new values of the
 338:  * property to all the global listeners, and to all the listeners for the
 339:  * specified property name. This does nothing if old and new are equal.
 340:  *
 341:  * @param propertyName the name of the property that changed
 342:  * @param oldVal the old value
 343:  * @param newVal the new value
 344:  */
 345:  public void firePropertyChange(String propertyName, int oldVal, int newVal)
 346:  {
 347:  if (oldVal != newVal)
 348:  firePropertyChange(new PropertyChangeEvent(source, propertyName,
 349:  new Integer(oldVal),
 350:  new Integer(newVal)));
 351:  }
 352: 
 353:  /**
 354:  * Fire a PropertyChangeEvent containing the old and new values of the
 355:  * property to all the global listeners, and to all the listeners for the
 356:  * specified property name. This does nothing if old and new are equal.
 357:  *
 358:  * @param propertyName the name of the property that changed
 359:  * @param oldVal the old value
 360:  * @param newVal the new value
 361:  */
 362:  public void firePropertyChange(String propertyName,
 363:  boolean oldVal, boolean newVal)
 364:  {
 365:  if (oldVal != newVal)
 366:  firePropertyChange(new PropertyChangeEvent(source, propertyName,
 367:  Boolean.valueOf(oldVal),
 368:  Boolean.valueOf(newVal)));
 369:  }
 370: 
 371:  /**
 372:  * Fire a PropertyChangeEvent to all the global listeners, and to all the
 373:  * listeners for the specified property name. This does nothing if old and
 374:  * new values of the event are equal.
 375:  *
 376:  * @param event the event to fire
 377:  * @throws NullPointerException if event is null
 378:  */
 379:  public void firePropertyChange(PropertyChangeEvent event)
 380:  {
 381:  if (event.oldValue != null && event.oldValue.equals(event.newValue))
 382:  return;
 383:  Vector v = listeners; // Be thread-safe.
 384:  if (v != null)
 385:  {
 386:  int i = v.size();
 387:  while (--i >= 0)
 388:  ((PropertyChangeListener) v.get(i)).propertyChange(event);
 389:  }
 390:  Hashtable h = children; // Be thread-safe.
 391:  if (h != null && event.propertyName != null)
 392:  {
 393:  PropertyChangeSupport s
 394:  = (PropertyChangeSupport) h.get(event.propertyName);
 395:  if (s != null)
 396:  {
 397:  v = s.listeners; // Be thread-safe.
 398:  int i = v == null ? 0 : v.size();
 399:  while (--i >= 0)
 400:  ((PropertyChangeListener) v.get(i)).propertyChange(event);
 401:  }
 402:  }
 403:  }
 404: 
 405:  /**
 406:  * Fire an indexed property change event. This will only fire
 407:  * an event if the old and new values are not equal and not null. 
 408:  * @param name the name of the property which changed
 409:  * @param index the index of the property which changed
 410:  * @param oldValue the old value of the property
 411:  * @param newValue the new value of the property
 412:  * @since 1.5
 413:  */
 414:  public void fireIndexedPropertyChange(String name, int index,
 415:  Object oldValue, Object newValue)
 416:  {
 417:  // Argument checking is done in firePropertyChange(PropertyChangeEvent) .
 418:  firePropertyChange(new IndexedPropertyChangeEvent(source, name,
 419:  oldValue, newValue,
 420:  index));
 421:  }
 422: 
 423:  /**
 424:  * Fire an indexed property change event. This will only fire
 425:  * an event if the old and new values are not equal.
 426:  * @param name the name of the property which changed
 427:  * @param index the index of the property which changed
 428:  * @param oldValue the old value of the property
 429:  * @param newValue the new value of the property
 430:  * @since 1.5
 431:  */
 432:  public void fireIndexedPropertyChange(String name, int index,
 433:  int oldValue, int newValue)
 434:  {
 435:  if (oldValue != newValue)
 436:  fireIndexedPropertyChange(name, index, Integer.valueOf(oldValue),
 437:  Integer.valueOf(newValue));
 438:  }
 439: 
 440:  /**
 441:  * Fire an indexed property change event. This will only fire
 442:  * an event if the old and new values are not equal.
 443:  * @param name the name of the property which changed
 444:  * @param index the index of the property which changed
 445:  * @param oldValue the old value of the property
 446:  * @param newValue the new value of the property
 447:  * @since 1.5
 448:  */
 449:  public void fireIndexedPropertyChange(String name, int index,
 450:  boolean oldValue, boolean newValue)
 451:  {
 452:  if (oldValue != newValue)
 453:  fireIndexedPropertyChange(name, index, Boolean.valueOf(oldValue),
 454:  Boolean.valueOf(newValue));
 455:  }
 456: 
 457:  /**
 458:  * Tell whether the specified property is being listened on or not. This
 459:  * will only return <code>true</code> if there are listeners on all
 460:  * properties or if there is a listener specifically on this property.
 461:  *
 462:  * @param propertyName the property that may be listened on
 463:  * @return whether the property is being listened on
 464:  */
 465:  public synchronized boolean hasListeners(String propertyName)
 466:  {
 467:  return listeners != null || (children != null
 468:  && children.get(propertyName) != null);
 469:  }
 470: 
 471:  /**
 472:  * Saves the state of the object to the stream.
 473:  *
 474:  * @param s the stream to write to
 475:  * @throws IOException if anything goes wrong
 476:  * @serialData this writes out a null-terminated list of serializable
 477:  * global property change listeners (the listeners for a named
 478:  * property are written out as the global listeners of the
 479:  * children, when the children hashtable is saved)
 480:  */
 481:  private synchronized void writeObject(ObjectOutputStream s)
 482:  throws IOException
 483:  {
 484:  s.defaultWriteObject();
 485:  if (listeners != null)
 486:  {
 487:  int i = listeners.size();
 488:  while (--i >= 0)
 489:  if (listeners.get(i) instanceof Serializable)
 490:  s.writeObject(listeners.get(i));
 491:  }
 492:  s.writeObject(null);
 493:  }
 494: 
 495:  /**
 496:  * Reads the object back from stream (deserialization).
 497:  *
 498:  * XXX Since serialization for 1.1 streams was not documented, this may
 499:  * not work if propertyChangeSupportSerializedDataVersion is 1.
 500:  *
 501:  * @param s the stream to read from
 502:  * @throws IOException if reading the stream fails
 503:  * @throws ClassNotFoundException if deserialization fails
 504:  * @serialData this reads in a null-terminated list of serializable
 505:  * global property change listeners (the listeners for a named
 506:  * property are written out as the global listeners of the
 507:  * children, when the children hashtable is saved)
 508:  */
 509:  private void readObject(ObjectInputStream s)
 510:  throws IOException, ClassNotFoundException
 511:  {
 512:  s.defaultReadObject();
 513:  PropertyChangeListener l = (PropertyChangeListener) s.readObject();
 514:  while (l != null)
 515:  {
 516:  addPropertyChangeListener(l);
 517:  l = (PropertyChangeListener) s.readObject();
 518:  }
 519:  // Sun is not as careful with children as we are, and lets some proxys
 520:  // in that can never receive events. So, we clean up anything that got
 521:  // serialized, to make sure our invariants hold.
 522:  if (children != null)
 523:  {
 524:  int i = children.size();
 525:  Iterator iter = children.entrySet().iterator();
 526:  while (--i >= 0)
 527:  {
 528:  Entry e = (Entry) iter.next();
 529:  String name = (String) e.getKey();
 530:  PropertyChangeSupport pcs = (PropertyChangeSupport) e.getValue();
 531:  if (pcs.listeners == null)
 532:  pcs.listeners = new Vector();
 533:  if (pcs.children != null)
 534:  pcs.listeners.addAll
 535:  (Arrays.asList(pcs.getPropertyChangeListeners(name)));
 536:  if (pcs.listeners.size() == 0)
 537:  iter.remove();
 538:  else
 539:  pcs.children = null;
 540:  }
 541:  if (children.size() == 0)
 542:  children = null;
 543:  }
 544:  }
 545: } // class PropertyChangeSupport
Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)

AltStyle によって変換されたページ (->オリジナル) /