| ||||
|
Code - Class EDU.oswego.cs.dl.util.concurrent.PropertyChangeMulticaster1 /* 2 File: PropertyChangeMulticaster.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.PropertyChangeListener; 21 import java.beans.PropertyChangeEvent; 22 import java.util.HashMap; 23 import java.io.Serializable; 24 import java.io.ObjectOutputStream; 25 import java.io.ObjectInputStream; 26 import java.io.IOException; 27 28 /** 29 * This class is interoperable with java.beans.PropertyChangeSupport, 30 * but relies on a streamlined copy-on-write scheme similar to 31 * that used in CopyOnWriteArrayList. This leads to much better 32 * performance in most event-intensive programs. It also adheres to clarified 33 * semantics of add and remove operations. 34 * <p> 35 * <b>Sample usage.</b> 36 * 37 * <pre> 38 * class Thing { 39 * protected Color myColor = Color.red; // an example property 40 * 41 * protected PropertyChangeMulticaster listeners = 42 * new PropertyChangeMulticaster(this); 43 * 44 * // registration methods, including: 45 * void addListener(PropertyChangeListener l) { 46 * // Use the `ifAbsent' version to avoid duplicate notifications 47 * listeners.addPropertyChangeListenerIfAbsent(l); 48 * } 49 * 50 * public synchronized Color getColor() { // accessor 51 * return myColor; 52 * } 53 * 54 * // internal synchronized state change method; returns old value 55 * protected synchronized Color assignColor(Color newColor) { 56 * Color oldColor = myColor; 57 * myColor = newColor; 58 * return oldColor; 59 * } 60 * 61 * public void setColor(Color newColor) { 62 * // atomically change state 63 * Color oldColor = assignColor(newColor); 64 * // broadcast change notification without holding synch lock 65 * listeners.firePropertyChange("color", oldColor, newColor); 66 * } 67 * } 68 * </pre> 69 * <p>[<a HREF="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] 70 **/ 71 72 public class PropertyChangeMulticaster implements Serializable { 73 74 // In order to allow this class to be lifted out without using 75 // the whole package, the basic mechanics of CopyOnWriteArrayList 76 // are used here, but not the class itself. 77 // This also makes it barely faster. 78 79 /** 80 * The array of listeners. Copied on each update 81 **/ 82 83 protected transient PropertyChangeListener[] listeners = new PropertyChangeListener[0]; 84 85 86 /** 87 * The object to be provided as the "source" for any generated events. 88 * @serial 89 */ 90 protected final Object source; 91 92 /** 93 * HashMap for managing listeners for specific properties. 94 * Maps property names to PropertyChangeMulticaster objects. 95 * @serial 96 */ 97 protected HashMap children; 98 99 /** 100 * Return the child associated with property, or null if no such 101 **/ 102 103 protected synchronized PropertyChangeMulticaster getChild(String propertyName) { 104 return (children == null)? null : 105 ((PropertyChangeMulticaster)children.get(propertyName)); 106 } 107 108 109 /** 110 * Constructs a <code>PropertyChangeMulticaster</code> object. 111 * 112 * @param sourceBean The bean to be given as the source for any events. 113 * @exception NullPointerException if sourceBean is null 114 */ 115 116 public PropertyChangeMulticaster(Object sourceBean) { 117 if (sourceBean == null) { 118 throw new NullPointerException(); 119 } 120 121 source = sourceBean; 122 } 123 124 /** 125 * Add a VetoableChangeListener to the listener list. 126 * The listener is registered for all properties. 127 * If the listener is added multiple times, it will 128 * receive multiple change notifications upon any firePropertyChange 129 * 130 * @param listener The PropertyChangeListener to be added 131 * @exception NullPointerException If listener is null 132 */ 133 134 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 135 136 if (listener == null) throw new NullPointerException(); 137 138 int len = listeners.length; 139 PropertyChangeListener[] newArray = new PropertyChangeListener[len + 1]; 140 if (len > 0) 141 System.arraycopy(listeners, 0, newArray, 0, len); 142 newArray[len] = listener; 143 listeners = newArray; 144 } 145 146 147 /** 148 * Add a PropertyChangeListener to the listener list if it is 149 * not already present. 150 * The listener is registered for all properties. 151 * The operation maintains Set semantics: If the listener is already 152 * registered, the operation has no effect. 153 * 154 * @param listener The PropertyChangeListener to be added 155 * @exception NullPointerException If listener is null 156 */ 157 158 public synchronized void addPropertyChangeListenerIfAbsent(PropertyChangeListener listener) { 159 160 if (listener == null) throw new NullPointerException(); 161 162 // Copy while checking if already present. 163 int len = listeners.length; 164 PropertyChangeListener[] newArray = new PropertyChangeListener[len + 1]; 165 for (int i = 0; i < len; ++i) { 166 newArray[i] = listeners[i]; 167 if (listener.equals(listeners[i])) 168 return; // already present -- throw away copy 169 } 170 newArray[len] = listener; 171 listeners = newArray; 172 } 173 174 175 /** 176 * Remove a PropertyChangeListener from the listener list. 177 * It removes at most one occurrence of the given listener. 178 * If the listener was added multiple times it must be removed 179 * mulitple times. 180 * This removes a PropertyChangeListener that was registered 181 * for all properties, and has no effect if registered for only 182 * one or more specified properties. 183 * 184 * @param listener The PropertyChangeListener to be removed 185 */ 186 187 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { 188 189 int newlen = listeners.length-1; 190 if (newlen < 0 || listener == null) return; 191 192 // Copy while searching for element to remove 193 194 PropertyChangeListener[] newArray = new PropertyChangeListener[newlen]; 195 196 for (int i = 0; i < newlen; ++i) { 197 if (listener.equals(listeners[i])) { 198 // copy remaining and exit 199 for (int k = i + 1; k <= newlen; ++k) newArray[k-1] = listeners[k]; 200 listeners = newArray; 201 return; 202 } 203 else 204 newArray[i] = listeners[i]; 205 } 206 207 // special-case last cell 208 if (listener.equals(listeners[newlen])) 209 listeners = newArray; 210 } 211 212 /** 213 * Add a PropertyChangeListener for a specific property. The listener 214 * will be invoked only when a call on firePropertyChange names that 215 * specific property. However, if a listener is registered both for all 216 * properties and a specific property, it will receive multiple 217 * notifications upon changes to that property. 218 * 219 * @param propertyName The name of the property to listen on. 220 * @param listener The PropertyChangeListener to be added 221 * @exception NullPointerException If listener is null 222 */ 223 224 public void addPropertyChangeListener(String propertyName, 225 PropertyChangeListener listener) { 226 227 if (listener == null) throw new NullPointerException(); 228 229 PropertyChangeMulticaster child = null; 230 231 synchronized(this) { 232 if (children == null) 233 children = new HashMap(); 234 else 235 child = (PropertyChangeMulticaster)children.get(propertyName); 236 237 if (child == null) { 238 child = new PropertyChangeMulticaster(source); 239 children.put(propertyName, child); 240 } 241 } 242 243 child.addPropertyChangeListener(listener); 244 } 245 246 /** 247 * Add a PropertyChangeListener for a specific property, if it is not 248 * already registered. The listener 249 * will be invoked only when a call on firePropertyChange names that 250 * specific property. 251 * 252 * @param propertyName The name of the property to listen on. 253 * @param listener The PropertyChangeListener to be added 254 * @exception NullPointerException If listener is null 255 */ 256 257 public void addPropertyChangeListenerIfAbsent(String propertyName, 258 PropertyChangeListener listener) { 259 260 if (listener == null) throw new NullPointerException(); 261 262 PropertyChangeMulticaster child = null; 263 264 synchronized(this) { 265 if (children == null) 266 children = new HashMap(); 267 else 268 child = (PropertyChangeMulticaster)children.get(propertyName); 269 270 if (child == null) { 271 child = new PropertyChangeMulticaster(source); 272 children.put(propertyName, child); 273 } 274 } 275 276 child.addPropertyChangeListenerIfAbsent(listener); 277 } 278 279 /** 280 * Remove a PropertyChangeListener for a specific property. 281 * Affects only the given property. 282 * If the listener is also registered for all properties, 283 * then it will continue to be registered for them. 284 * 285 * @param propertyName The name of the property that was listened on. 286 * @param listener The PropertyChangeListener to be removed 287 */ 288 289 public void removePropertyChangeListener(String propertyName, 290 PropertyChangeListener listener) { 291 292 PropertyChangeMulticaster child = getChild(propertyName); 293 if (child != null) 294 child.removePropertyChangeListener(listener); 295 } 296 297 298 /** 299 * Helper method to relay evt to all listeners. 300 * Called by all public firePropertyChange methods. 301 **/ 302 303 protected void multicast(PropertyChangeEvent evt) { 304 305 PropertyChangeListener[] array; // bind in synch block below 306 PropertyChangeMulticaster child = null; 307 308 synchronized (this) { 309 array = listeners; 310 311 if (children != null && evt.getPropertyName() != null) 312 child = (PropertyChangeMulticaster)children.get(evt.getPropertyName()); 313 } 314 315 for (int i = 0; i < array.length; ++i) 316 array[i].propertyChange(evt); 317 318 if (child != null) 319 child.multicast(evt); 320 321 } 322 323 324 /** 325 * Report a bound property update to any registered listeners. 326 * No event is fired if old and new are equal and non-null. 327 * 328 * @param propertyName The programmatic name of the property 329 * that was changed. 330 * @param oldValue The old value of the property. 331 * @param newValue The new value of the property. 332 */ 333 public void firePropertyChange(String propertyName, 334 Object oldValue, Object newValue) { 335 336 if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { 337 multicast(new PropertyChangeEvent(source, 338 propertyName, 339 oldValue, 340 newValue)); 341 } 342 343 } 344 345 /** 346 * Report an int bound property update to any registered listeners. 347 * No event is fired if old and new are equal and non-null. 348 * <p> 349 * This is merely a convenience wrapper around the more general 350 * firePropertyChange method that takes Object values. 351 * 352 * @param propertyName The programmatic name of the property 353 * that was changed. 354 * @param oldValue The old value of the property. 355 * @param newValue The new value of the property. 356 */ 357 public void firePropertyChange(String propertyName, 358 int oldValue, int newValue) { 359 if (oldValue != newValue) { 360 multicast(new PropertyChangeEvent(source, 361 propertyName, 362 new Integer(oldValue), 363 new Integer(newValue))); 364 } 365 } 366 367 368 /** 369 * Report a boolean bound property update to any registered listeners. 370 * No event is fired if old and new are equal and non-null. 371 * <p> 372 * This is merely a convenience wrapper around the more general 373 * firePropertyChange method that takes Object values. 374 * 375 * @param propertyName The programmatic name of the property 376 * that was changed. 377 * @param oldValue The old value of the property. 378 * @param newValue The new value of the property. 379 */ 380 public void firePropertyChange(String propertyName, 381 boolean oldValue, boolean newValue) { 382 if (oldValue != newValue) { 383 multicast(new PropertyChangeEvent(source, 384 propertyName, 385 new Boolean(oldValue), 386 new Boolean(newValue))); 387 } 388 } 389 390 /** 391 * Fire an existing PropertyChangeEvent to any registered listeners. 392 * No event is fired if the given event's old and new values are 393 * equal and non-null. 394 * @param evt The PropertyChangeEvent object. 395 */ 396 public void firePropertyChange(PropertyChangeEvent evt) { 397 Object oldValue = evt.getOldValue(); 398 Object newValue = evt.getNewValue(); 399 if (oldValue == null || newValue == null || !oldValue.equals(newValue)) 400 multicast(evt); 401 } 402 403 /** 404 * Check if there are any listeners for a specific property. 405 * If propertyName is null, return whether there are any listeners at all. 406 * 407 * @param propertyName the property name. 408 * @return true if there are one or more listeners for the given property 409 * 410 */ 411 public boolean hasListeners(String propertyName) { 412 413 PropertyChangeMulticaster child; 414 415 synchronized (this) { 416 if (listeners.length > 0) 417 return true; 418 else if (propertyName == null || children == null) 419 return false; 420 else { 421 child = (PropertyChangeMulticaster)children.get(propertyName); 422 if (child == null) 423 return false; 424 } 425 } 426 427 return child.hasListeners(null); 428 } 429 430 431 /** 432 * @serialData Null terminated list of <code>PropertyChangeListeners</code>. 433 * <p> 434 * At serialization time we skip non-serializable listeners and 435 * only serialize the serializable listeners. 436 * 437 */ 438 private synchronized void writeObject(ObjectOutputStream s) throws IOException { 439 s.defaultWriteObject(); 440 441 for (int i = 0; i < listeners.length; i++) { 442 PropertyChangeListener l = listeners[i]; 443 if (listeners[i] instanceof Serializable) { 444 s.writeObject(listeners[i]); 445 } 446 } 447 s.writeObject(null); 448 } 449 450 451 private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { 452 listeners = new PropertyChangeListener[0]; // paranoically reset 453 s.defaultReadObject(); 454 455 Object listenerOrNull; 456 while (null != (listenerOrNull = s.readObject())) { 457 addPropertyChangeListener((PropertyChangeListener)listenerOrNull); 458 } 459 } 460 461 } 462 |
|||
Java API By Example, From Geeks To Geeks. |
Conditions of Use |
About Us
© 2002 - 2005, KickJava.com, or its affiliates
|