1 /** 2 * Redistribution and use of this software and associated documentation ("Software"), with or 3 * without modification, are permitted provided that the following conditions are met: 4 * 5 * 1. Redistributions of source code must retain copyright statements and notices. Redistributions 6 * must also contain a copy of this document. 7 * 8 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 9 * conditions and the following disclaimer in the documentation and/or other materials provided with 10 * the distribution. 11 * 12 * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software 13 * without prior written permission of Intalio, Inc. For written permission, please contact 14 * info@exolab.org. 15 * 16 * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in 17 * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of 18 * Intalio, Inc. 19 * 20 * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/). 21 * 22 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS 25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 29 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * 32 * Contribution(s): 33 * 34 * - Jeff Norris, Jeff.Norris@jpl.nasa.gov - Original Author 35 * 36 * $Id$ 37 */ 38 39 package org.exolab.castor.util; 40 41 import java.io.IOException; 42 import java.io.ObjectInputStream; 43 import java.io.ObjectOutputStream; 44 import java.io.Serializable; 45 import java.util.EventListener; 46 47 /** 48 * <p> 49 * This class is an efficient repository for EventListeners based on javax.swing.EventListenerList. 50 * </p> 51 * 52 * <p> 53 * This modification of javax.swing.EventListenerList retains the core functionality of that class 54 * but changes the basic API and adds a few more features, as summarized below: 55 * <p> 56 * 57 * <ol> 58 * <li>javax.swing.EventListenerList requires all listeners to be added in conjunction with a class 59 * object that identified the type of the listener. This implementation's add methods simply take 60 * the listener object. 61 * 62 * <li>The listener list returned from javax.swing.EventListenerList had to be iterated over in a 63 * cumbersome manner because the listeners were stored along with their Class objects in the array. 64 * Since listener classes are not stored in this listener list, the returned listener array can be 65 * iterated over normally (1 element at a time). 66 * 67 * <li>The remove method in javax.swing.EventListenerList had return type "void". This 68 * implementation's remove method returns true if the specified listener was found in the listener 69 * array and false otherwise. 70 * 71 * <li>This implementation adds {@link #add(EventListener, int)}, which allows the addition of a 72 * listener at a specific position in the array. 73 * 74 * <li>The add and remove methods in this implementation throw IllegalArgumentExceptions when their 75 * arguments are null. 76 * </ol> 77 * 78 * <p> 79 * As is the case with javax.swing.EventListenerList, this class provides multi-threaded safety 80 * through a copy-on-modify strategy. It is optimized to provide high performance when events are 81 * being fired, with slightly slower performance than the Collection API when listeners are being 82 * added and removed. Like its predecessor, this class will never return a null array from 83 * getListenerList. 84 * </p> 85 * 86 * <p> 87 * The most important thing to keep in mind when using this class is that the array returned by 88 * getListenerList is the actual internal array of this class and MUST NOT BE MODIFIED UNDER ANY 89 * CIRCUMSTANCES. Below is an example of how to use this class, borrowed (and slightly modified) 90 * from the javax.swing.EventListenerList documentation: 91 * </p> 92 * 93 * <p> 94 * Usage example: Say one is defining a class that sends out FooEvents, and one wants to allow users 95 * of the class to register FooListeners and receive notification when FooEvents occur. The 96 * following should be added to the class definition: 97 * </p> 98 * 99 * <pre> 100 * EventListenerList listenerList = new EventListenerList(); 101 * FooEvent fooEvent = null; 102 * 103 * public void addFooListener(FooListener l) { 104 * listenerList.add(l); 105 * } 106 * 107 * public void removeFooListener(FooListener l) { 108 * listenerList.remove(l); 109 * } 110 * 111 * // Notify all listeners that have registered interest for 112 * // notification on this event type. The event instance 113 * // is lazily created using the parameters passed into 114 * // the fire method. 115 * 116 * protected void fireFooXXX() { 117 * // Guaranteed to return a non-null array 118 * EventListener[] listeners = listenerList.getListenerList(); 119 * // Process the listeners last to first, notifying 120 * // those that are interested in this event 121 * for (int i = 0; i < listeners.length; i++) { 122 * // Lazily create the event: 123 * if (fooEvent == null) 124 * fooEvent = new FooEvent(this); 125 * ((FooListener) listeners[i]).fooXXX(fooEvent); 126 * } 127 * } 128 * </pre> 129 * 130 * <p> 131 * foo should be changed to the appropriate name, and fireFooXxx to the appropriate method name. One 132 * fire method should exist for each notification method in the FooListener interface. 133 * </p> 134 * 135 * <p> 136 * The authors of javax.swing.EventListenerList are Georges Saab, Hans Muller, and James Gosling. 137 * </p> 138 * 139 * @author <a href="mailto:Jeff.Norris@jpl.nasa.gov">Jeff Norris</a> 140 * @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $ 141 */ 142 public class EventListenerList implements Serializable { 143 /** SerialVersionUID */ 144 private static final long serialVersionUID = 4472874989562384564L; 145 146 /** 147 * A null array to be shared by all empty listener lists 148 */ 149 private final static EventListener[] NULL_ARRAY = new EventListener[0]; 150 151 /** 152 * The internal list of listeners that is returned from getListenerList 153 */ 154 protected transient EventListener[] listenerList = NULL_ARRAY; 155 156 /** 157 * <p> 158 * Passes back the event listener list as an array of EventListeners. 159 * </p> 160 * 161 * <p> 162 * Note that for performance reasons, this implementation passes back the actual data structure in 163 * which the listener data is stored internally! This method is guaranteed to pass back a non-null 164 * array, so that no null-checking is required in fire methods. A zero-length array of Object will 165 * be returned if there are currently no listeners. 166 * </p> 167 * 168 * WARNING!!! Absolutely NO modification of the data contained in this array should be made -- if 169 * any such manipulation is necessary, it should be done on a copy of the array returned rather 170 * than the array itself. 171 */ 172 public EventListener[] getListenerList() { 173 return listenerList; 174 } 175 176 /** 177 * <p> 178 * Returns the total number of listeners in this listener list. 179 * </p> 180 * 181 */ 182 public int getListenerCount() { 183 return listenerList.length; 184 } 185 186 /** 187 * <p> 188 * Adds the listener to the end of the listener list. 189 * </p> 190 * 191 * @param newListener the listener to be added 192 * @exception IllegalArgumentException if the specified newListener is null 193 */ 194 public synchronized void add(EventListener newListener) { 195 196 if (newListener == null) 197 throw new IllegalArgumentException("Listener to add must not be null."); 198 199 if (listenerList == NULL_ARRAY) { 200 // if this is the first listener added, 201 // initialize the lists 202 listenerList = new EventListener[] {newListener}; 203 } else { 204 // Otherwise copy the array and add the new listener 205 int oldLength = listenerList.length; 206 EventListener[] tmp = new EventListener[oldLength + 1]; 207 System.arraycopy(listenerList, 0, tmp, 0, oldLength); 208 209 tmp[oldLength] = newListener; 210 211 listenerList = tmp; 212 } 213 } 214 215 /** 216 * <p> 217 * Adds the listener at the specified index in the listener list. 218 * </p> 219 * 220 * @param newListener the listener to be added 221 * @exception IllegalArgumentException if the specified newListener is null, or the specified 222 * index is less than zero or greater than the length of the listener list array. 223 */ 224 public synchronized void add(EventListener newListener, int index) { 225 if (newListener == null) 226 throw new IllegalArgumentException("Listener to add must not be null."); 227 228 if ((index < 0) || (index > listenerList.length)) 229 throw new IllegalArgumentException("Index to add listener (" + index 230 + ") is out of bounds. List length is " + listenerList.length); 231 232 if (listenerList == NULL_ARRAY) { 233 // if this is the first listener added, initialize the lists 234 listenerList = new EventListener[] {newListener}; 235 } else { 236 // Otherwise copy the array and add the new listener 237 int oldLength = listenerList.length; 238 EventListener[] tmp = new EventListener[oldLength + 1]; 239 // Copy up to the index where the new listener should go 240 System.arraycopy(listenerList, 0, tmp, 0, index); 241 // Skip a cell and copy the rest of the list 242 System.arraycopy(listenerList, index, tmp, index + 1, oldLength - index); 243 // Insert the new listener 244 tmp[index] = newListener; 245 246 listenerList = tmp; 247 } 248 } 249 250 /** 251 * Removes the listener as a listener of the specified type. 252 * 253 * @param listenerToRemove the listener to be removed 254 * @exception IllegalArgumentException if the specified listener is null 255 */ 256 public synchronized boolean remove(EventListener listenerToRemove) { 257 if (listenerToRemove == null) 258 throw new IllegalArgumentException("Listener to remove must " + "not be null."); 259 // Is listenerToRemove on the list? 260 int index = -1; 261 for (int i = listenerList.length - 1; i >= 0; i--) { 262 if (listenerList[i].equals(listenerToRemove) == true) { 263 index = i; 264 break; 265 } 266 } 267 268 // If so, remove it 269 if (index != -1) { 270 EventListener[] tmp = new EventListener[listenerList.length - 1]; 271 // Copy the list up to index 272 System.arraycopy(listenerList, 0, tmp, 0, index); 273 // Copy from one past the index, up to the end of tmp (which is 274 // one element shorter than the old list) 275 if (index < tmp.length) 276 System.arraycopy(listenerList, index + 1, tmp, index, tmp.length - index); 277 // set the listener array to the new array or null 278 listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp; 279 return true; 280 } 281 return false; 282 } 283 284 // Serialization support. 285 private void writeObject(ObjectOutputStream s) throws IOException { 286 Object[] lList = listenerList; 287 s.defaultWriteObject(); 288 289 // Save the non-null event listeners: 290 for (int i = 0; i < lList.length; i += 1) { 291 EventListener l = (EventListener) lList[i]; 292 if ((l != null) && (l instanceof Serializable)) { 293 s.writeObject(l); 294 } 295 } 296 297 s.writeObject(null); 298 } 299 300 private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { 301 listenerList = NULL_ARRAY; 302 s.defaultReadObject(); 303 EventListener listenerOrNull; 304 305 while (null != (listenerOrNull = (EventListener) s.readObject())) { 306 add(listenerOrNull); 307 } 308 } 309 310 /** 311 * Returns a string representation of the EventListenerList. 312 */ 313 public String toString() { 314 Object[] lList = listenerList; 315 String s = "EventListenerList: "; 316 s += lList.length + " listeners: "; 317 for (int i = 0; i < lList.length; i++) { 318 s += " listener " + lList[i + 1]; 319 } 320 return s; 321 } 322 323 } // -- class: EventListenerList