1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.event; 26 27 import java.io.*; 28 import java.util.*; 29 import java.lang.reflect.Array; 30 import sun.reflect.misc.ReflectUtil; 31 32 /** 33 * A class that holds a list of EventListeners. A single instance 34 * can be used to hold all listeners (of all types) for the instance 35 * using the list. It is the responsiblity of the class using the 36 * EventListenerList to provide type-safe API (preferably conforming 37 * to the JavaBeans spec) and methods which dispatch event notification 38 * methods to appropriate Event Listeners on the list. 39 * 40 * The main benefits that this class provides are that it is relatively 41 * cheap in the case of no listeners, and it provides serialization for 42 * event-listener lists in a single place, as well as a degree of MT safety 43 * (when used correctly). 44 * 45 * Usage example: 46 * Say one is defining a class that sends out FooEvents, and one wants 47 * to allow users of the class to register FooListeners and receive 48 * notification when FooEvents occur. The following should be added 49 * to the class definition: 50 * <pre> 51 * EventListenerList listenerList = new EventListenerList(); 52 * FooEvent fooEvent = null; 53 * 54 * public void addFooListener(FooListener l) { 55 * listenerList.add(FooListener.class, l); 56 * } 57 * 58 * public void removeFooListener(FooListener l) { 59 * listenerList.remove(FooListener.class, l); 60 * } 61 * 62 * 63 * // Notify all listeners that have registered interest for 64 * // notification on this event type. The event instance 65 * // is lazily created using the parameters passed into 66 * // the fire method. 67 * 68 * protected void fireFooXXX() { 69 * // Guaranteed to return a non-null array 70 * Object[] listeners = listenerList.getListenerList(); 71 * // Process the listeners last to first, notifying 72 * // those that are interested in this event 73 * for (int i = listeners.length-2; i>=0; i-=2) { 74 * if (listeners[i]==FooListener.class) { 75 * // Lazily create the event: 76 * if (fooEvent == null) 77 * fooEvent = new FooEvent(this); 78 * ((FooListener)listeners[i+1]).fooXXX(fooEvent); 79 * } 80 * } 81 * } 82 * </pre> 83 * foo should be changed to the appropriate name, and fireFooXxx to the 84 * appropriate method name. One fire method should exist for each 85 * notification method in the FooListener interface. 86 * <p> 87 * <strong>Warning:</strong> 88 * Serialized objects of this class will not be compatible with 89 * future Swing releases. The current serialization support is 90 * appropriate for short term storage or RMI between applications running 91 * the same version of Swing. As of 1.4, support for long term storage 92 * of all JavaBeans™ 93 * has been added to the <code>java.beans</code> package. 94 * Please see {@link java.beans.XMLEncoder}. 95 * 96 * @author Georges Saab 97 * @author Hans Muller 98 * @author James Gosling 99 */ 100 @SuppressWarnings("serial") 101 public class EventListenerList implements Serializable { 102 /* A null array to be shared by all empty listener lists*/ 103 private static final Object[] NULL_ARRAY = new Object[0]; 104 /** The list of ListenerType - Listener pairs */ 105 protected transient Object[] listenerList = NULL_ARRAY; 106 107 /** 108 * Passes back the event listener list as an array 109 * of ListenerType-listener pairs. Note that for 110 * performance reasons, this implementation passes back 111 * the actual data structure in which the listener data 112 * is stored internally! 113 * This method is guaranteed to pass back a non-null 114 * array, so that no null-checking is required in 115 * fire methods. A zero-length array of Object should 116 * be returned if there are currently no listeners. 117 * 118 * WARNING!!! Absolutely NO modification of 119 * the data contained in this array should be made -- if 120 * any such manipulation is necessary, it should be done 121 * on a copy of the array returned rather than the array 122 * itself. 123 * 124 * @return array of ListenerType-listener pairs 125 */ 126 public Object[] getListenerList() { 127 return listenerList; 128 } 129 130 /** 131 * Return an array of all the listeners of the given type. 132 * 133 * @param <T> the type of {@code EventListener} to search for 134 * @param t the type of {@code EventListener} classes to be returned 135 * @return all of the listeners of the specified type. 136 * @exception ClassCastException if the supplied class 137 * is not assignable to EventListener 138 * 139 * @since 1.3 140 */ 141 public <T extends EventListener> T[] getListeners(Class<T> t) { 142 Object[] lList = listenerList; 143 int n = getListenerCount(lList, t); 144 @SuppressWarnings("unchecked") 145 T[] result = (T[])Array.newInstance(t, n); 146 int j = 0; 147 for (int i = lList.length-2; i>=0; i-=2) { 148 if (lList[i] == t) { 149 @SuppressWarnings("unchecked") 150 T tmp = (T)lList[i+1]; 151 result[j++] = tmp; 152 } 153 } 154 return result; 155 } 156 157 /** 158 * Returns the total number of listeners for this listener list. 159 * 160 * @return an integer count of total number of listeners 161 */ 162 public int getListenerCount() { 163 return listenerList.length/2; 164 } 165 166 /** 167 * Returns the total number of listeners of the supplied type 168 * for this listener list. 169 * 170 * @param t the type of listeners to count 171 * @return the number of listeners of type {@code t} 172 */ 173 public int getListenerCount(Class<?> t) { 174 Object[] lList = listenerList; 175 return getListenerCount(lList, t); 176 } 177 178 private int getListenerCount(Object[] list, Class<?> t) { 179 int count = 0; 180 for (int i = 0; i < list.length; i+=2) { 181 if (t == (Class)list[i]) 182 count++; 183 } 184 return count; 185 } 186 187 /** 188 * Adds the listener as a listener of the specified type. 189 * 190 * @param <T> the type of {@code EventListener} to add 191 * @param t the type of the {@code EventListener} class to add 192 * @param l the listener to be added 193 */ 194 public synchronized <T extends EventListener> void add(Class<T> t, T l) { 195 if (l==null) { 196 // In an ideal world, we would do an assertion here 197 // to help developers know they are probably doing 198 // something wrong 199 return; 200 } 201 if (!t.isInstance(l)) { 202 throw new IllegalArgumentException("Listener " + l + 203 " is not of type " + t); 204 } 205 if (listenerList == NULL_ARRAY) { 206 // if this is the first listener added, 207 // initialize the lists 208 listenerList = new Object[] { t, l }; 209 } else { 210 // Otherwise copy the array and add the new listener 211 int i = listenerList.length; 212 Object[] tmp = new Object[i+2]; 213 System.arraycopy(listenerList, 0, tmp, 0, i); 214 215 tmp[i] = t; 216 tmp[i+1] = l; 217 218 listenerList = tmp; 219 } 220 } 221 222 /** 223 * Removes the listener as a listener of the specified type. 224 * 225 * @param <T> the type of {@code EventListener} 226 * @param t the type of the listener to be removed 227 * @param l the listener to be removed 228 */ 229 public synchronized <T extends EventListener> void remove(Class<T> t, T l) { 230 if (l ==null) { 231 // In an ideal world, we would do an assertion here 232 // to help developers know they are probably doing 233 // something wrong 234 return; 235 } 236 if (!t.isInstance(l)) { 237 throw new IllegalArgumentException("Listener " + l + 238 " is not of type " + t); 239 } 240 // Is l on the list? 241 int index = -1; 242 for (int i = listenerList.length-2; i>=0; i-=2) { 243 if ((listenerList[i]==t) && (listenerList[i+1].equals(l) == true)) { 244 index = i; 245 break; 246 } 247 } 248 249 // If so, remove it 250 if (index != -1) { 251 Object[] tmp = new Object[listenerList.length-2]; 252 // Copy the list up to index 253 System.arraycopy(listenerList, 0, tmp, 0, index); 254 // Copy from two past the index, up to 255 // the end of tmp (which is two elements 256 // shorter than the old list) 257 if (index < tmp.length) 258 System.arraycopy(listenerList, index+2, tmp, index, 259 tmp.length - index); 260 // set the listener array to the new array or null 261 listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp; 262 } 263 } 264 265 // Serialization support. 266 private void writeObject(ObjectOutputStream s) throws IOException { 267 Object[] lList = listenerList; 268 s.defaultWriteObject(); 269 270 // Save the non-null event listeners: 271 for (int i = 0; i < lList.length; i+=2) { 272 Class<?> t = (Class)lList[i]; 273 EventListener l = (EventListener)lList[i+1]; 274 if ((l!=null) && (l instanceof Serializable)) { 275 s.writeObject(t.getName()); 276 s.writeObject(l); 277 } 278 } 279 280 s.writeObject(null); 281 } 282 283 private void readObject(ObjectInputStream s) 284 throws IOException, ClassNotFoundException { 285 listenerList = NULL_ARRAY; 286 s.defaultReadObject(); 287 Object listenerTypeOrNull; 288 289 while (null != (listenerTypeOrNull = s.readObject())) { 290 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 291 EventListener l = (EventListener)s.readObject(); 292 String name = (String) listenerTypeOrNull; 293 ReflectUtil.checkPackageAccess(name); 294 @SuppressWarnings("unchecked") 295 Class<EventListener> tmp = (Class<EventListener>)Class.forName(name, true, cl); 296 add(tmp, l); 297 } 298 } 299 300 /** 301 * Returns a string representation of the EventListenerList. 302 */ 303 public String toString() { 304 Object[] lList = listenerList; 305 String s = "EventListenerList: "; 306 s += lList.length/2 + " listeners: "; 307 for (int i = 0 ; i <= lList.length-2 ; i+=2) { 308 s += " type " + ((Class)lList[i]).getName(); 309 s += " listener " + lList[i+1]; 310 } 311 return s; 312 } 313 }