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

Source for java.beans.VetoableChangeSupport

 1:  /* VetoableChangeSupport.java -- support to manage vetoable 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:  * VetoableChangeSupport makes it easy to fire vetoable 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 VetoableChangeSupport implements Serializable
 66: {
 67:  /**
 68:  * Compatible with JDK 1.1+.
 69:  */
 70:  private static final long serialVersionUID = -5090210921595982017L;
 71: 
 72:  /**
 73:  * Maps property names (String) to named listeners (VetoableChangeSupport).
 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 vetoableChangeSupportSerializedDataVersion = 2;
 94: 
 95:  /**
 96:  * The list of all registered vetoable 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 VetoableChangeSupport 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 VetoableChangeSupport(Object source)
 113:  {
 114:  this.source = source;
 115:  if (source == null)
 116:  throw new NullPointerException();
 117:  }
 118: 
 119:  /**
 120:  * Adds a VetoableChangeListener to the list of global listeners. All
 121:  * vetoable 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:  * vetoable change. This method will unwrap a VetoableChangeListenerProxy,
 125:  * registering the underlying delegate to the named property list.
 126:  *
 127:  * @param l the listener to add (<code>null</code> ignored).
 128:  */
 129:  public synchronized void addVetoableChangeListener(VetoableChangeListener l)
 130:  {
 131:  if (l == null)
 132:  return;
 133:  if (l instanceof VetoableChangeListenerProxy)
 134:  {
 135:  VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l;
 136:  addVetoableChangeListener(p.propertyName,
 137:  (VetoableChangeListener) p.getListener());
 138:  }
 139:  else
 140:  {
 141:  if (listeners == null)
 142:  listeners = new Vector();
 143:  listeners.add(l);
 144:  }
 145:  }
 146: 
 147:  /**
 148:  * Removes a VetoableChangeListener from the list of global listeners. If
 149:  * any specific properties are being listened on, they must be deregistered
 150:  * by themselves; this will only remove the general listener to all
 151:  * properties. If <code>add()</code> has been called multiple times for a
 152:  * particular listener, <code>remove()</code> will have to be called the
 153:  * same number of times to deregister it. This method will unwrap a
 154:  * VetoableChangeListenerProxy, removing the underlying delegate from the
 155:  * named property list.
 156:  *
 157:  * @param l the listener to remove
 158:  */
 159:  public synchronized void
 160:  removeVetoableChangeListener(VetoableChangeListener l)
 161:  {
 162:  if (l instanceof VetoableChangeListenerProxy)
 163:  {
 164:  VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l;
 165:  removeVetoableChangeListener(p.propertyName,
 166:  (VetoableChangeListener) p.getListener());
 167:  }
 168:  else if (listeners != null)
 169:  {
 170:  listeners.remove(l);
 171:  if (listeners.isEmpty())
 172:  listeners = null;
 173:  }
 174:  }
 175: 
 176:  /**
 177:  * Returns an array of all registered vetoable change listeners. Those that
 178:  * were registered under a name will be wrapped in a
 179:  * <code>VetoableChangeListenerProxy</code>, so you must check whether the
 180:  * listener is an instance of the proxy class in order to see what name the
 181:  * real listener is registered under. If there are no registered listeners,
 182:  * this returns an empty array.
 183:  *
 184:  * @return the array of registered listeners
 185:  * @see VetoableChangeListenerProxy
 186:  * @since 1.4
 187:  */
 188:  public synchronized VetoableChangeListener[] getVetoableChangeListeners()
 189:  {
 190:  ArrayList list = new ArrayList();
 191:  if (listeners != null)
 192:  list.addAll(listeners);
 193:  if (children != null)
 194:  {
 195:  int i = children.size();
 196:  Iterator iter = children.entrySet().iterator();
 197:  while (--i >= 0)
 198:  {
 199:  Entry e = (Entry) iter.next();
 200:  String name = (String) e.getKey();
 201:  Vector v = ((VetoableChangeSupport) e.getValue()).listeners;
 202:  int j = v.size();
 203:  while (--j >= 0)
 204:  list.add(new VetoableChangeListenerProxy
 205:  (name, (VetoableChangeListener) v.get(j)));
 206:  }
 207:  }
 208:  return (VetoableChangeListener[])
 209:  list.toArray(new VetoableChangeListener[list.size()]);
 210:  }
 211: 
 212:  /**
 213:  * Adds a VetoableChangeListener listening on the specified property. Events
 214:  * will be sent to the listener only if the property name matches. The
 215:  * listener add is not unique; that is, <em>n</em> adds on a particular
 216:  * property for a particular listener will result in <em>n</em> events
 217:  * being sent to that listener when that property is changed. The effect is
 218:  * cumulative, too; if you are registered to listen to receive events on
 219:  * all vetoable changes, and then you register on a particular property,
 220:  * you will receive change events for that property twice. This method
 221:  * will unwrap a VetoableChangeListenerProxy, registering the underlying
 222:  * delegate to the named property list if the names match, and discarding
 223:  * it otherwise.
 224:  *
 225:  * @param propertyName the name of the property to listen on
 226:  * @param l the listener to add
 227:  */
 228:  public synchronized void addVetoableChangeListener(String propertyName,
 229:  VetoableChangeListener l)
 230:  {
 231:  if (propertyName == null || l == null)
 232:  return;
 233:  while (l instanceof VetoableChangeListenerProxy)
 234:  {
 235:  VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l;
 236:  if (propertyName == null ? p.propertyName != null
 237:  : ! propertyName.equals(p.propertyName))
 238:  return;
 239:  l = (VetoableChangeListener) p.getListener();
 240:  }
 241:  VetoableChangeSupport s = null;
 242:  if (children == null)
 243:  children = new Hashtable();
 244:  else
 245:  s = (VetoableChangeSupport) children.get(propertyName);
 246:  if (s == null)
 247:  {
 248:  s = new VetoableChangeSupport(source);
 249:  s.listeners = new Vector();
 250:  children.put(propertyName, s);
 251:  }
 252:  s.listeners.add(l);
 253:  }
 254: 
 255:  /**
 256:  * Removes a VetoableChangeListener from listening to a specific property.
 257:  * If <code>add()</code> has been called multiple times for a particular
 258:  * listener on a property, <code>remove()</code> will have to be called the
 259:  * same number of times to deregister it. This method will unwrap a
 260:  * VetoableChangeListenerProxy, removing the underlying delegate from the
 261:  * named property list if the names match.
 262:  *
 263:  * @param propertyName the property to stop listening on
 264:  * @param l the listener to remove
 265:  * @throws NullPointerException if propertyName is null
 266:  */
 267:  public synchronized void
 268:  removeVetoableChangeListener(String propertyName, VetoableChangeListener l)
 269:  {
 270:  if (children == null)
 271:  return;
 272:  VetoableChangeSupport s
 273:  = (VetoableChangeSupport) children.get(propertyName);
 274:  if (s == null)
 275:  return;
 276:  while (l instanceof VetoableChangeListenerProxy)
 277:  {
 278:  VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l;
 279:  if (propertyName == null ? p.propertyName != null
 280:  : ! propertyName.equals(p.propertyName))
 281:  return;
 282:  l = (VetoableChangeListener) p.getListener();
 283:  }
 284:  s.listeners.remove(l);
 285:  if (s.listeners.isEmpty())
 286:  {
 287:  children.remove(propertyName);
 288:  if (children.isEmpty())
 289:  children = null;
 290:  }
 291:  }
 292: 
 293:  /**
 294:  * Returns an array of all vetoable change listeners registered under the
 295:  * given property name. If there are no registered listeners, this returns
 296:  * an empty array.
 297:  *
 298:  * @return the array of registered listeners
 299:  * @throws NullPointerException if propertyName is null
 300:  * @since 1.4
 301:  */
 302:  public synchronized VetoableChangeListener[]
 303:  getVetoableChangeListeners(String propertyName)
 304:  {
 305:  if (children == null)
 306:  return new VetoableChangeListener[0];
 307:  VetoableChangeSupport s
 308:  = (VetoableChangeSupport) children.get(propertyName);
 309:  if (s == null)
 310:  return new VetoableChangeListener[0];
 311:  return (VetoableChangeListener[])
 312:  s.listeners.toArray(new VetoableChangeListener[s.listeners.size()]);
 313:  }
 314: 
 315:  /**
 316:  * Fire a PropertyChangeEvent containing the old and new values of the
 317:  * property to all the global listeners, and to all the listeners for the
 318:  * specified property name. This does nothing if old and new are non-null
 319:  * and equal. If the change is vetoed, a new event is fired to notify
 320:  * listeners about the rollback before the exception is thrown.
 321:  *
 322:  * @param propertyName the name of the property that changed
 323:  * @param oldVal the old value
 324:  * @param newVal the new value
 325:  * @throws PropertyVetoException if the change is vetoed by a listener
 326:  */
 327:  public void fireVetoableChange(String propertyName,
 328:  Object oldVal, Object newVal)
 329:  throws PropertyVetoException
 330:  {
 331:  fireVetoableChange(new PropertyChangeEvent(source, propertyName,
 332:  oldVal, newVal));
 333:  }
 334: 
 335:  /**
 336:  * Fire a PropertyChangeEvent containing the old and new values of the
 337:  * property to all the global listeners, and to all the listeners for the
 338:  * specified property name. This does nothing if old and new are equal.
 339:  * If the change is vetoed, a new event is fired to notify listeners about
 340:  * the rollback before the exception is thrown.
 341:  *
 342:  * @param propertyName the name of the property that changed
 343:  * @param oldVal the old value
 344:  * @param newVal the new value
 345:  * @throws PropertyVetoException if the change is vetoed by a listener
 346:  */
 347:  public void fireVetoableChange(String propertyName, int oldVal, int newVal)
 348:  throws PropertyVetoException
 349:  {
 350:  if (oldVal != newVal)
 351:  fireVetoableChange(new PropertyChangeEvent(source, propertyName,
 352:  new Integer(oldVal),
 353:  new Integer(newVal)));
 354:  }
 355: 
 356:  /**
 357:  * Fire a PropertyChangeEvent containing the old and new values of the
 358:  * property to all the global listeners, and to all the listeners for the
 359:  * specified property name. This does nothing if old and new are equal.
 360:  * If the change is vetoed, a new event is fired to notify listeners about
 361:  * the rollback before the exception is thrown.
 362:  *
 363:  * @param propertyName the name of the property that changed
 364:  * @param oldVal the old value
 365:  * @param newVal the new value
 366:  * @throws PropertyVetoException if the change is vetoed by a listener
 367:  */
 368:  public void fireVetoableChange(String propertyName,
 369:  boolean oldVal, boolean newVal)
 370:  throws PropertyVetoException
 371:  {
 372:  if (oldVal != newVal)
 373:  fireVetoableChange(new PropertyChangeEvent(source, propertyName,
 374:  Boolean.valueOf(oldVal),
 375:  Boolean.valueOf(newVal)));
 376:  }
 377: 
 378:  /**
 379:  * Fire a PropertyChangeEvent to all the global listeners, and to all the
 380:  * listeners for the specified property name. This does nothing if old and
 381:  * new values of the event are equal. If the change is vetoed, a new event
 382:  * is fired to notify listeners about the rollback before the exception is
 383:  * thrown.
 384:  *
 385:  * @param event the event to fire
 386:  * @throws NullPointerException if event is null
 387:  * @throws PropertyVetoException if the change is vetoed by a listener
 388:  */
 389:  public void fireVetoableChange(PropertyChangeEvent event)
 390:  throws PropertyVetoException
 391:  {
 392:  if (event.oldValue != null && event.oldValue.equals(event.newValue))
 393:  return;
 394:  Vector v = listeners; // Be thread-safe.
 395:  if (v != null)
 396:  {
 397:  int i = v.size();
 398:  try
 399:  {
 400:  while (--i >= 0)
 401:  ((VetoableChangeListener) v.get(i)).vetoableChange(event);
 402:  }
 403:  catch (PropertyVetoException e)
 404:  {
 405:  event = event.rollback();
 406:  int limit = i;
 407:  i = v.size();
 408:  while (--i >= limit)
 409:  ((VetoableChangeListener) v.get(i)).vetoableChange(event);
 410:  throw e;
 411:  }
 412:  }
 413:  Hashtable h = children; // Be thread-safe.
 414:  if (h != null && event.propertyName != null)
 415:  {
 416:  VetoableChangeSupport s
 417:  = (VetoableChangeSupport) h.get(event.propertyName);
 418:  if (s != null)
 419:  {
 420:  Vector v1 = s.listeners; // Be thread-safe.
 421:  int i = v1 == null ? 0 : v1.size();
 422:  try
 423:  {
 424:  while (--i >= 0)
 425:  ((VetoableChangeListener) v1.get(i)).vetoableChange(event);
 426:  }
 427:  catch (PropertyVetoException e)
 428:  {
 429:  event = event.rollback();
 430:  int limit = i;
 431:  i = v.size();
 432:  while (--i >= 0)
 433:  ((VetoableChangeListener) v.get(i)).vetoableChange(event);
 434:  i = v1.size();
 435:  while (--i >= limit)
 436:  ((VetoableChangeListener) v1.get(i)).vetoableChange(event);
 437:  throw e;
 438:  }
 439:  }
 440:  }
 441:  }
 442: 
 443:  /**
 444:  * Tell whether the specified property is being listened on or not. This
 445:  * will only return <code>true</code> if there are listeners on all
 446:  * properties or if there is a listener specifically on this property.
 447:  *
 448:  * @param propertyName the property that may be listened on
 449:  * @return whether the property is being listened on
 450:  * @throws NullPointerException if propertyName is null
 451:  */
 452:  public synchronized boolean hasListeners(String propertyName)
 453:  {
 454:  return listeners != null || (children != null
 455:  && children.get(propertyName) != null);
 456:  }
 457: 
 458:  /**
 459:  * Saves the state of the object to the stream.
 460:  *
 461:  * @param s the stream to write to
 462:  * @throws IOException if anything goes wrong
 463:  * @serialData this writes out a null-terminated list of serializable
 464:  * global vetoable change listeners (the listeners for a named
 465:  * property are written out as the global listeners of the
 466:  * children, when the children hashtable is saved)
 467:  */
 468:  private synchronized void writeObject(ObjectOutputStream s)
 469:  throws IOException
 470:  {
 471:  s.defaultWriteObject();
 472:  if (listeners != null)
 473:  {
 474:  int i = listeners.size();
 475:  while (--i >= 0)
 476:  if (listeners.get(i) instanceof Serializable)
 477:  s.writeObject(listeners.get(i));
 478:  }
 479:  s.writeObject(null);
 480:  }
 481: 
 482:  /**
 483:  * Reads the object back from stream (deserialization).
 484:  *
 485:  * XXX Since serialization for 1.1 streams was not documented, this may
 486:  * not work if vetoableChangeSupportSerializedDataVersion is 1.
 487:  *
 488:  * @param s the stream to read from
 489:  * @throws IOException if reading the stream fails
 490:  * @throws ClassNotFoundException if deserialization fails
 491:  * @serialData this reads in a null-terminated list of serializable
 492:  * global vetoable change listeners (the listeners for a named
 493:  * property are written out as the global listeners of the
 494:  * children, when the children hashtable is saved)
 495:  */
 496:  private void readObject(ObjectInputStream s)
 497:  throws IOException, ClassNotFoundException
 498:  {
 499:  s.defaultReadObject();
 500:  VetoableChangeListener l = (VetoableChangeListener) s.readObject();
 501:  while (l != null)
 502:  {
 503:  addVetoableChangeListener(l);
 504:  l = (VetoableChangeListener) s.readObject();
 505:  }
 506:  // Sun is not as careful with children as we are, and lets some proxys
 507:  // in that can never receive events. So, we clean up anything that got
 508:  // serialized, to make sure our invariants hold.
 509:  if (children != null)
 510:  {
 511:  int i = children.size();
 512:  Iterator iter = children.entrySet().iterator();
 513:  while (--i >= 0)
 514:  {
 515:  Entry e = (Entry) iter.next();
 516:  String name = (String) e.getKey();
 517:  VetoableChangeSupport vcs = (VetoableChangeSupport) e.getValue();
 518:  if (vcs.listeners == null)
 519:  vcs.listeners = new Vector();
 520:  if (vcs.children != null)
 521:  vcs.listeners.addAll
 522:  (Arrays.asList(vcs.getVetoableChangeListeners(name)));
 523:  if (vcs.listeners.size() == 0)
 524:  iter.remove();
 525:  else
 526:  vcs.children = null;
 527:  }
 528:  if (children.size() == 0)
 529:  children = null;
 530:  }
 531:  }
 532: } // class VetoableChangeSupport
Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)

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