| ||||
|
Code - Class EDU.oswego.cs.dl.util.concurrent.VetoableChangeMulticaster1 /* 2 File: ProperyChangeMulticaster.java 3 4 Originally written by Doug Lea and released into the public domain. 5 This may be used for any purposes whatsoever without acknowledgment. 6 Thanks for the assistance and support of Sun Microsystems Labs, 7 and everyone contributing, testing, and using this code. 8 9 This class is based on Sun JDK java.beans.VetoableChangeSupport, 10 which is copyrighted by Sun. (It shares practically no code, but for 11 consistency, the documentation was lifted and adapted here.) 12 13 History: 14 Date Who What 15 14Mar1999 dl first release 16 */ 17 18 package EDU.oswego.cs.dl.util.concurrent; 19 20 import java.beans.VetoableChangeListener; 21 import java.beans.PropertyChangeEvent; 22 import java.beans.PropertyVetoException; 23 import java.util.HashMap; 24 import java.io.Serializable; 25 import java.io.ObjectOutputStream; 26 import java.io.ObjectInputStream; 27 import java.io.IOException; 28 29 /** 30 * This class is interoperable with java.beans.VetoableChangeSupport, 31 * but relies on a streamlined copy-on-write scheme similar to 32 * that used in CopyOnWriteArrayList. It also adheres to clarified 33 * semantics of add, remove, and fireVetoableChange operations. 34 * <p> 35 * <b>Sample usage.</b> 36 * 37 * <pre> 38 * class Thing { 39 * protected Color myColor = Color.red; // an example property 40 * protected boolean changePending; // track whether in midst of change 41 * 42 * // vetoable listeners: 43 * protected VetoableChangeMulticaster vetoers = 44 * new VetoableChangeMulticaster(this); 45 * 46 * // Possibly also some ordinary listeners: 47 * protected PropertyChangeMulticaster listeners = 48 * new PropertyChangeMulticaster(this); 49 * 50 * // registration methods, including: 51 * void addVetoer(VetoableChangeListener l) { 52 * // Use the `ifAbsent' version to avoid duplicate notifications 53 * vetoers.addVetoableChangeListenerIfAbsent(l); 54 * } 55 * 56 * public synchronized Color getColor() { // accessor 57 * return myColor; 58 * } 59 * 60 * // Simple transactional control for vetos 61 * 62 * public void setColor(int newColor) throws PropertyVetoException { 63 * Color oldColor = prepareSetColor(newColor); 64 * 65 * try { 66 * vetoers.fireVetoableChange("color", oldColor, newColor); 67 * commitColor(newColor); 68 * listeners.firePropertyChange("color", oldColor, newColor); 69 * } 70 * catch(PropertyVetoException ex) { 71 * abortSetColor(); 72 * throw ex; 73 * } 74 * } 75 * 76 * // Called on entry to proposed vetoable change from setColor. 77 * // Throws exception if there is already another change in progress. 78 * // Returns current color 79 * synchronized int prepareSetColor(Color c) throws PropertyVetoException { 80 * // only support one transaction at a time 81 * if (changePending) 82 * throw new PropertyVetoException("Concurrent modification"); 83 * // (Could alternatively wait out other transactions via 84 * // a wait/notify construction based on changePending.) 85 * 86 * // perhaps some other screenings, like: 87 * else if (c == null) 88 * throw new PropertyVetoException("Cannot change color to Null"); 89 * else { 90 * changePending = true; 91 * return myColor; 92 * } 93 * } 94 * 95 * synchronized void commitColor(Color newColor) { 96 * myColor = newColor; 97 * changePending = false; 98 * } 99 * 100 * synchronized void abortSetColor() { 101 * changePending = false; 102 * } 103 * 104 * } 105 * </pre> 106 * <p>[<a HREF="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] 107 **/ 108 109 public class VetoableChangeMulticaster implements Serializable { 110 111 // This code is 90% identical with PropertyChangeMulticaster, 112 // but there is no good way to unify the code while maintaining 113 // interoperability with beans versions. 114 115 /** 116 * The array of listeners. Copied on each update 117 **/ 118 119 protected transient VetoableChangeListener[] listeners = new VetoableChangeListener[0]; 120 121 122 /** 123 * The object to be provided as the "source" for any generated events. 124 * @serial 125 */ 126 protected final Object source; 127 128 /** 129 * HashMap for managing listeners for specific properties. 130 * Maps property names to VetoableChangeMulticaster objects. 131 * @serial 132 */ 133 protected HashMap children; 134 135 /** 136 * Return the child associated with property, or null if no such 137 **/ 138 139 protected synchronized VetoableChangeMulticaster getChild(String propertyName) { 140 return (children == null)? null : 141 ((VetoableChangeMulticaster)children.get(propertyName)); 142 } 143 144 145 /** 146 * Constructs a <code>VetoableChangeMulticaster</code> object. 147 * 148 * @param sourceBean The bean to be given as the source for any events. 149 * @exception NullPointerException if sourceBean is null 150 */ 151 152 public VetoableChangeMulticaster(Object sourceBean) { 153 if (sourceBean == null) { 154 throw new NullPointerException(); 155 } 156 157 source = sourceBean; 158 } 159 160 /** 161 * Add a VetoableChangeListener to the listener list. 162 * The listener is registered for all properties. 163 * If the listener is added multiple times, it will 164 * receive multiple change notifications upon any fireVetoableChange. 165 * 166 * @param listener The VetoableChangeListener to be added 167 */ 168 169 public synchronized void addVetoableChangeListener(VetoableChangeListener listener) { 170 171 if (listener == null) throw new NullPointerException(); 172 173 int len = listeners.length; 174 VetoableChangeListener[] newArray = new VetoableChangeListener[len + 1]; 175 if (len > 0) 176 System.arraycopy(listeners, 0, newArray, 0, len); 177 newArray[len] = listener; 178 listeners = newArray; 179 } 180 181 /** 182 * Add a PropertyChangeListener to the listener list if it is 183 * not already present. 184 * The listener is registered for all properties. 185 * The operation maintains Set semantics: If the listener is already 186 * registered, the operation has no effect. 187 * 188 * @param listener The PropertyChangeListener to be added 189 * @exception NullPointerException If listener is null 190 */ 191 192 public synchronized void addVetoableChangeListenerIfAbsent(VetoableChangeListener listener) { 193 194 if (listener == null) throw new NullPointerException(); 195 196 // Copy while checking if already present. 197 int len = listeners.length; 198 VetoableChangeListener[] newArray = new VetoableChangeListener[len + 1]; 199 for (int i = 0; i < len; ++i) { 200 newArray[i] = listeners[i]; 201 if (listener.equals(listeners[i])) 202 return; // already present -- throw away copy 203 } 204 newArray[len] = listener; 205 listeners = newArray; 206 } 207 208 209 /** 210 * Remove an occurrence of a VetoableChangeListener from the listener list. 211 * It removes at most one occurrence of the given listener. 212 * If the listener was added multiple times it must be removed 213 * mulitple times. 214 * This removes a VetoableChangeListener that was registered 215 * for all properties, and has no effect if registered for only 216 * one or more specified properties. 217 * 218 * @param listener The VetoableChangeListener to be removed 219 */ 220 221 public synchronized void removeVetoableChangeListener(VetoableChangeListener listener) { 222 223 int newlen = listeners.length-1; 224 if (newlen < 0 || listener == null) return; 225 226 // Copy while searching for element to remove 227 228 VetoableChangeListener[] newArray = new VetoableChangeListener[newlen]; 229 230 for (int i = 0; i < newlen; ++i) { 231 if (listener.equals(listeners[i])) { 232 // copy remaining and exit 233 for (int k = i + 1; k <= newlen; ++k) newArray[k-1] = listeners[k]; 234 listeners = newArray; 235 return; 236 } 237 else 238 newArray[i] = listeners[i]; 239 } 240 241 // special-case last cell 242 if (listener.equals(listeners[newlen])) 243 listeners = newArray; 244 245 } 246 247 /** 248 * Add a VetoableChangeListener for a specific property. The listener 249 * will be invoked only when a call on fireVetoableChange names that 250 * specific property. However, if a listener is registered both for all 251 * properties and a specific property, it will receive multiple 252 * notifications upon changes to that property. 253 * 254 * @param propertyName The name of the property to listen on. 255 * @param listener The VetoableChangeListener to be added 256 * @exception NullPointerException If listener is null 257 */ 258 259 public void addVetoableChangeListener(String propertyName, 260 VetoableChangeListener listener) { 261 262 if (listener == null) throw new NullPointerException(); 263 264 VetoableChangeMulticaster child = null; 265 266 synchronized(this) { 267 if (children == null) 268 children = new HashMap(); 269 else 270 child = (VetoableChangeMulticaster)children.get(propertyName); 271 272 if (child == null) { 273 child = new VetoableChangeMulticaster(source); 274 children.put(propertyName, child); 275 } 276 } 277 278 child.addVetoableChangeListener(listener); 279 } 280 281 /** 282 * Add a VetoableChangeListener for a specific property, if it is not 283 * already registered. The listener 284 * will be invoked only when a call on fireVetoableChange names that 285 * specific property. 286 * 287 * @param propertyName The name of the property to listen on. 288 * @param listener The VetoableChangeListener to be added 289 * @exception NullPointerException If listener is null 290 */ 291 292 public void addVetoableChangeListenerIfAbsent(String propertyName, 293 VetoableChangeListener listener) { 294 295 if (listener == null) throw new NullPointerException(); 296 297 VetoableChangeMulticaster child = null; 298 299 synchronized(this) { 300 if (children == null) 301 children = new HashMap(); 302 else 303 child = (VetoableChangeMulticaster)children.get(propertyName); 304 305 if (child == null) { 306 child = new VetoableChangeMulticaster(source); 307 children.put(propertyName, child); 308 } 309 } 310 311 child.addVetoableChangeListenerIfAbsent(listener); 312 } 313 314 315 /** 316 * Remove a VetoableChangeListener for a specific property. 317 * Affects only the given property. 318 * If the listener is also registered for all properties, 319 * then it will continue to be registered for them. 320 * 321 * @param propertyName The name of the property that was listened on. 322 * @param listener The VetoableChangeListener to be removed 323 */ 324 325 public void removeVetoableChangeListener(String propertyName, 326 VetoableChangeListener listener) { 327 328 VetoableChangeMulticaster child = getChild(propertyName); 329 if (child != null) 330 child.removeVetoableChangeListener(listener); 331 } 332 333 334 /** 335 * Helper method to relay evt to all listeners. 336 * Called by all public fireVetoableChange methods. 337 **/ 338 339 protected void multicast(PropertyChangeEvent evt) throws PropertyVetoException { 340 341 VetoableChangeListener[] array; // bind in synch block below 342 VetoableChangeMulticaster child = null; 343 344 synchronized (this) { 345 array = listeners; 346 347 if (children != null && evt.getPropertyName() != null) 348 child = (VetoableChangeMulticaster)children.get(evt.getPropertyName()); 349 } 350 351 // Loop through array, and then cascade to child. 352 353 int i = 0; // make visible to catch clause 354 355 try { 356 for (i = 0; i < array.length; ++i) 357 array[i].vetoableChange(evt); 358 359 if (child != null) 360 child.multicast(evt); 361 } 362 363 catch (PropertyVetoException veto) { 364 365 // Revert all that have been notified 366 367 PropertyChangeEvent revert = 368 new PropertyChangeEvent(evt.getSource(), 369 evt.getPropertyName(), 370 evt.getNewValue(), 371 evt.getOldValue()); 372 373 int lastNotified = (i < array.length)? i : (array.length-1); 374 375 for (int k = 0; k <= lastNotified; ++k) { 376 try { 377 array[k].vetoableChange(revert); 378 } 379 catch (PropertyVetoException ignore) { 380 // Cannot veto a reversion 381 } 382 } 383 384 // Rethrow the PropertyVetoException. 385 throw veto; 386 } 387 } 388 389 390 /** 391 * Report a vetoable property update to any registered listeners. 392 * Notifications are sent serially (although in no particular order) 393 * to the list of listeners, 394 * aborting if one throws PropertyVetoException. Upon this exception, 395 * fire a new event reverting this 396 * change to all listeners that have already been notified 397 * (ignoring any further vetos), 398 * suppress notifications to all other listeners, and 399 * then rethrow the PropertyVetoException. 400 * <p> 401 * No event is fired if old and new are equal non-null. 402 * 403 * @param propertyName The programmatic name of the property 404 * that was changed. 405 * @param oldValue The old value of the property. 406 * @param newValue The new value of the property. 407 * @exception PropertyVetoException if a recipient wishes the property 408 * change to be rolled back. 409 */ 410 public void fireVetoableChange(String propertyName, 411 Object oldValue, Object newValue) throws PropertyVetoException { 412 413 if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { 414 multicast(new PropertyChangeEvent(source, 415 propertyName, 416 oldValue, 417 newValue)); 418 } 419 420 } 421 422 /** 423 * Report a vetoable property update to any registered listeners. 424 * Notifications are sent serially (although in no particular order) 425 * to the list of listeners, 426 * aborting if one throws PropertyVetoException. Upon this exception, 427 * fire a new event reverting this 428 * change to all listeners that have already been notified 429 * (ignoring any further vetos), 430 * suppress notifications to all other listeners, and 431 * then rethrow the PropertyVetoException. 432 * <p> 433 * No event is fired if old and new are equal. 434 * <p> 435 * This is merely a convenience wrapper around the more general 436 * fireVetoableChange method that takes Object values. 437 * 438 * @param propertyName The programmatic name of the property 439 * that was changed. 440 * @param oldValue The old value of the property. 441 * @param newValue The new value of the property. 442 * @exception PropertyVetoException if the recipient wishes the property 443 * change to be rolled back. 444 */ 445 public void fireVetoableChange(String propertyName, 446 int oldValue, int newValue) throws PropertyVetoException { 447 if (oldValue != newValue) { 448 multicast(new PropertyChangeEvent(source, 449 propertyName, 450 new Integer(oldValue), 451 new Integer(newValue))); 452 } 453 } 454 455 456 /** 457 * Report a vetoable property update to any registered listeners. 458 * Notifications are sent serially (although in no particular order) 459 * to the list of listeners, 460 * aborting if one throws PropertyVetoException. Upon this exception, 461 * fire a new event reverting this 462 * change to all listeners that have already been notified 463 * (ignoring any further vetos), 464 * suppress notifications to all other listeners, and 465 * then rethrow the PropertyVetoException. 466 * <p> 467 * No event is fired if old and new are equal. 468 * <p> 469 * This is merely a convenience wrapper around the more general 470 * fireVetoableChange method that takes Object values. 471 * 472 * @param propertyName The programmatic name of the property 473 * that was changed. 474 * @param oldValue The old value of the property. 475 * @param newValue The new value of the property. 476 * @exception PropertyVetoException if the recipient wishes the property 477 * change to be rolled back. 478 */ 479 public void fireVetoableChange(String propertyName, 480 boolean oldValue, boolean newValue) throws PropertyVetoException { 481 if (oldValue != newValue) { 482 multicast(new PropertyChangeEvent(source, 483 propertyName, 484 new Boolean(oldValue), 485 new Boolean(newValue))); 486 } 487 } 488 489 /** 490 * Report a vetoable property update to any registered listeners. 491 * Notifications are sent serially (although in no particular order) 492 * to the list of listeners, 493 * aborting if one throws PropertyVetoException. Upon this exception, 494 * fire a new event reverting this 495 * change to all listeners that have already been notified 496 * (ignoring any further vetos), 497 * suppress notifications to all other listeners, and 498 * then rethrow the PropertyVetoException. 499 * <p> 500 * No event is fired if old and new are equal and non-null. 501 * 502 * equal and non-null. 503 * @param evt The PropertyChangeEvent object. 504 * @exception PropertyVetoException if the recipient wishes the property 505 * change to be rolled back. 506 */ 507 public void fireVetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 508 Object oldValue = evt.getOldValue(); 509 Object newValue = evt.getNewValue(); 510 if (oldValue == null || newValue == null || !oldValue.equals(newValue)) 511 multicast(evt); 512 } 513 514 /** 515 * Check if there are any listeners for a specific property. 516 * If propertyName is null, return whether there are any listeners at all. 517 * 518 * @param propertyName the property name. 519 * @return true if there are one or more listeners for the given property 520 * 521 */ 522 public boolean hasListeners(String propertyName) { 523 524 VetoableChangeMulticaster child; 525 526 synchronized (this) { 527 if (listeners.length > 0) 528 return true; 529 else if (propertyName == null || children == null) 530 return false; 531 else { 532 child = (VetoableChangeMulticaster)children.get(propertyName); 533 if (child == null) 534 return false; 535 } 536 } 537 538 return child.hasListeners(null); 539 } 540 541 542 /** 543 * @serialData Null terminated list of <code>VetoableChangeListeners</code>. 544 * <p> 545 * At serialization time we skip non-serializable listeners and 546 * only serialize the serializable listeners. 547 * 548 */ 549 private synchronized void writeObject(ObjectOutputStream s) throws IOException { 550 s.defaultWriteObject(); 551 552 for (int i = 0; i < listeners.length; i++) { 553 VetoableChangeListener l = listeners[i]; 554 if (listeners[i] instanceof Serializable) { 555 s.writeObject(listeners[i]); 556 } 557 } 558 s.writeObject(null); 559 } 560 561 562 private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { 563 listeners = new VetoableChangeListener[0]; // paranoically reset 564 s.defaultReadObject(); 565 566 Object listenerOrNull; 567 while (null != (listenerOrNull = s.readObject())) { 568 addVetoableChangeListener((VetoableChangeListener)listenerOrNull); 569 } 570 } 571 572 } 573 |
|||
Java API By Example, From Geeks To Geeks. |
Conditions of Use |
About Us
© 2002 - 2005, KickJava.com, or its affiliates
|