Frames | No Frames |
1: /* EventListenerList.java -- 2: Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing.event; 39: 40: import gnu.java.lang.CPStringBuilder; 41: 42: import java.io.IOException; 43: import java.io.ObjectInputStream; 44: import java.io.ObjectOutputStream; 45: import java.io.Serializable; 46: import java.lang.reflect.Array; 47: import java.util.EventListener; 48: 49: 50: /** 51: * A utility class for keeping track of {@link EventListener}s. 52: * 53: * <p><b>Example for using this class:</b> 54: * 55: * <blockquote><pre> import java.util.EventListener; 56: * import javax.swing.event.EventListenerList; 57: * 58: * class Foo 59: * { 60: * protected final EventListenerList listeners = new EventListenerList(); 61: * protected BarClosedEvent barClosedEvent = null; 62: * 63: * public void addBarListener(BarListener l) 64: * { 65: * listeners.<a href="#add(java.lang.Class, java.util.EventListener)" 66: * >add</a>(BarListener.class, l); 67: * } 68: * 69: * public void removeBarListener(BarListener l) 70: * { 71: * listeners.<a href="#remove(java.lang.Class, java.util.EventListener)" 72: * >remove</a>(BarListener.class, l); 73: * } 74: * 75: * protected void fireBarClosedEvent() 76: * { 77: * Object[] l = listeners.<a href="#getListenerList()" 78: * >getListenerList()</a>; 79: * 80: * for (int i = l.length - 2; i >= 0; i -= 2) 81: * if (l[i] == BarListener.class) 82: * { 83: * // Create the event on demand, when it is needed the first time. 84: * if (barClosedEvent == null) 85: * barClosedEvent = new BarClosedEvent(this); 86: * 87: * ((BarClosedListener) l[i + 1]).barClosed(barClosedEvent); 88: * } 89: * } 90: * }</pre></blockquote> 91: * 92: * @author Andrew Selkirk (aselkirk@sympatico.ca) 93: * @author Sascha Brawer (brawer@dandelis.ch) 94: */ 95: public class EventListenerList 96: implements Serializable 97: { 98: /** 99: * An ID for serializing instances of this class; verified with the 100: * serialver tool of Sun J2SE 1.4.1_01. 101: */ 102: static final long serialVersionUID = -5677132037850737084L; 103: 104: 105: /** 106: * An empty array that is shared by all instances of this class that 107: * have no listeners. 108: */ 109: private static final Object[] NO_LISTENERS = new Object[0]; 110: 111: 112: /** 113: * An array with all currently registered listeners. The array has 114: * twice as many elements as there are listeners. For an even 115: * integer <code>i</code>, <code>listenerList[i]</code> indicates 116: * the registered class, and <code>listenerList[i + 1]</code> is the 117: * listener. 118: */ 119: protected transient Object[] listenerList = NO_LISTENERS; 120: 121: 122: /** 123: * EventListenerList constructor 124: */ 125: public EventListenerList() 126: { 127: // Nothing to do here. 128: } 129: 130: 131: /** 132: * Registers a listener of a specific type. 133: * 134: * @param t the type of the listener. 135: * 136: * @param listener the listener to add, which must be an instance of 137: * <code>t</code>, or of a subclass of <code>t</code>. 138: * 139: * @throws IllegalArgumentException if <code>listener</code> is not 140: * an instance of <code>t</code> (or a subclass thereof). 141: * 142: * @throws NullPointerException if <code>t</code> is <code>null</code>. 143: */ 144: public <T extends EventListener> void add(Class<T> t, T listener) 145: { 146: int oldLength; 147: Object[] newList; 148: 149: if (listener == null) 150: return; 151: 152: if (!t.isInstance(listener)) 153: throw new IllegalArgumentException(); 154: 155: oldLength = listenerList.length; 156: newList = new Object[oldLength + 2]; 157: if (oldLength > 0) 158: System.arraycopy(listenerList, 0, newList, 0, oldLength); 159: 160: newList[oldLength] = t; 161: newList[oldLength + 1] = listener; 162: listenerList = newList; 163: } 164: 165: 166: /** 167: * Determines the number of listeners. 168: */ 169: public int getListenerCount() 170: { 171: return listenerList.length / 2; 172: } 173: 174: 175: /** 176: * Determines the number of listeners of a particular class. 177: * 178: * @param t the type of listeners to be counted. In order to get 179: * counted, a subscribed listener must be exactly of class 180: * <code>t</code>. Thus, subclasses of <code>t</code> will not be 181: * counted. 182: */ 183: public int getListenerCount(Class<?> t) 184: { 185: int result = 0; 186: for (int i = 0; i < listenerList.length; i += 2) 187: if (t == listenerList[i]) 188: ++result; 189: 190: return result; 191: } 192: 193: 194: /** 195: * Returns an array containing a sequence of listenerType/listener pairs, one 196: * for each listener. 197: * 198: * @return An array containing the listener types and references. 199: */ 200: public Object[] getListenerList() 201: { 202: // returning the internal storage is a bad idea, but tests show that the 203: // reference implementation does this... 204: return listenerList; 205: } 206: 207: 208: /** 209: * Retrieves the currently subscribed listeners of a particular 210: * type. For a listener to be returned, it must have been 211: * registered with exactly the type <code>c</code>; subclasses are 212: * not considered equal. 213: * 214: * <p>The returned array can always be cast to <code>c[]</code>. 215: * Since it is a newly allocated copy, the caller may arbitrarily 216: * modify the array. 217: * 218: * @param c the class which was passed to {@link #add}. 219: * 220: * @throws ClassCastException if <code>c</code> does not implement 221: * the {@link EventListener} interface. 222: * 223: * @throws NullPointerException if <code>c</code> is 224: * <code>null</code>. 225: * 226: * @return an array of <code>c</code> whose elements are the 227: * currently subscribed listeners of the specified type. If there 228: * are no such listeners, an empty array is returned. 229: * 230: * @since 1.3 231: */ 232: public <T extends EventListener> T[] getListeners(Class<T> c) 233: { 234: int count, f; 235: EventListener[] result; 236: 237: count = getListenerCount(c); 238: result = (EventListener[]) Array.newInstance(c, count); 239: f = 0; 240: for (int i = listenerList.length - 2; i >= 0; i -= 2) 241: if (listenerList[i] == c) 242: result[f++] = (EventListener) listenerList[i + 1]; 243: 244: return (T[]) result; 245: } 246: 247: 248: /** 249: * Removes a listener of a specific type. 250: * 251: * @param t the type of the listener. 252: * 253: * @param listener the listener to remove, which must be an instance 254: * of <code>t</code>, or of a subclass of <code>t</code>. 255: * 256: * @throws IllegalArgumentException if <code>listener</code> is not 257: * an instance of <code>t</code> (or a subclass thereof). 258: * 259: * @throws NullPointerException if <code>t</code> is <code>null</code>. 260: */ 261: public <T extends EventListener> void remove(Class<T> t, T listener) 262: { 263: Object[] oldList, newList; 264: int oldLength; 265: 266: if (listener == null) 267: return; 268: 269: if (!t.isInstance(listener)) 270: throw new IllegalArgumentException(); 271: 272: oldList = listenerList; 273: oldLength = oldList.length; 274: for (int i = 0; i < oldLength; i += 2) 275: if (oldList[i] == t && oldList[i + 1] == listener) 276: { 277: if (oldLength == 2) 278: newList = NO_LISTENERS; 279: else 280: { 281: newList = new Object[oldLength - 2]; 282: if (i > 0) 283: System.arraycopy(oldList, 0, newList, 0, i); 284: if (i < oldLength - 2) 285: System.arraycopy(oldList, i + 2, newList, i, 286: oldLength - 2 - i); 287: } 288: listenerList = newList; 289: return; 290: } 291: } 292: 293: 294: /** 295: * Returns a string representation of this object that may be useful 296: * for debugging purposes. 297: */ 298: public String toString() 299: { 300: CPStringBuilder buf = new CPStringBuilder("EventListenerList: "); 301: buf.append(listenerList.length / 2); 302: buf.append(" listeners: "); 303: for (int i = 0; i < listenerList.length; i += 2) 304: { 305: buf.append(" type "); 306: buf.append(((Class) listenerList[i]).getName()); 307: buf.append(" listener "); 308: buf.append(listenerList[i + 1]); 309: } 310: return buf.toString(); 311: } 312: 313: /** 314: * Serializes an instance to an ObjectOutputStream. 315: * 316: * @param out the stream to serialize to 317: * 318: * @throws IOException if something goes wrong 319: */ 320: private void writeObject(ObjectOutputStream out) 321: throws IOException 322: { 323: out.defaultWriteObject(); 324: for (int i = 0; i < listenerList.length; i += 2) 325: { 326: Class cl = (Class) listenerList[i]; 327: EventListener l = (EventListener) listenerList[i + 1]; 328: if (l != null && l instanceof Serializable) 329: { 330: out.writeObject(cl.getName()); 331: out.writeObject(l); 332: } 333: } 334: // Write end marker. 335: out.writeObject(null); 336: } 337: 338: /** 339: * Deserializes an instance from an ObjectInputStream. 340: * 341: * @param in the input stream 342: * 343: * @throws ClassNotFoundException if a serialized class can't be found 344: * @throws IOException if something goes wrong 345: */ 346: private <T extends EventListener> void readObject(ObjectInputStream in) 347: throws ClassNotFoundException, IOException 348: { 349: listenerList = NO_LISTENERS; 350: in.defaultReadObject(); 351: Object type; 352: ClassLoader cl = Thread.currentThread().getContextClassLoader(); 353: while ((type = in.readObject()) != null) 354: { 355: EventListener l = (EventListener) in.readObject(); 356: add(((Class<T>) Class.forName((String) type, true, cl)), (T) l); 357: } 358: } 359: }