1 /* EventListenerList.java -- 2 Copyright (C) 2002, 2004 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., 59 Temple Place, Suite 330, Boston, MA 19 02111-1307 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 java.io.Serializable; 41 import java.lang.reflect.Array; 42 import java.util.EventListener; 43 44 45 /** 46 * A utility class for keeping track of {@link EventListener}s. 47 * 48 * <p><b>Example for using this class:</b> 49 * 50 * <blockquote><pre> import java.util.EventListener; 51 * import javax.swing.event.EventListenerList; 52 * 53 * class Foo 54 * { 55 * protected final EventListenerList listeners = new EventListenerList(); 56 * protected BarClosedEvent barClosedEvent = null; 57 * 58 * public void addBarListener(BarListener l) 59 * { 60 * listeners.<a href="#add(java.lang.Class, java.util.EventListener)" 61 * >add</a>(BarListener.class, l); 62 * } 63 * 64 * public void removeBarListener(BarListener l) 65 * { 66 * listeners.<a href="#remove(java.lang.Class, java.util.EventListener)" 67 * >remove</a>(BarListener.class, l); 68 * } 69 * 70 * protected void fireBarClosedEvent() 71 * { 72 * Object[] l = listeners.<a href="#getListenerList()" 73 * >getListenerList()</a>; 74 * 75 * for (int i = l.length - 2; i >= 0; i -= 2) 76 * if (l[i] == BarListener.class) 77 * { 78 * // Create the event on demand, when it is needed the first time. 79 * if (barClosedEvent == null) 80 * barClosedEvent = new BarClosedEvent(this); 81 * 82 * ((BarClosedListener) l[i + 1]).barClosed(barClosedEvent); 83 * } 84 * } 85 * }</pre></blockquote> 86 * 87 * @author <a href="mailto:aselkirk@sympatico.ca">Andrew Selkirk</a> 88 * @author <a href="mailto:brawer@dandelis.ch">Sascha Brawer</a> 89 */ 90 public class EventListenerList 91 implements Serializable 92 { 93 /** 94 * An ID for serializing instances of this class; verified with the 95 * serialver tool of Sun J2SE 1.4.1_01. 96 */ 97 static final long serialVersionUID = -5677132037850737084L; 98 99 100 /** 101 * An empty array that is shared by all instances of this class that 102 * have no listeners. 103 */ 104 private static final Object[] NO_LISTENERS = new Object[0]; 105 106 107 /** 108 * An array with all currently registered listeners. The array has 109 * twice as many elements as there are listeners. For an even 110 * integer <code>i</code>, <code>listenerList[i]</code> indicates 111 * the registered class, and <code>listenerList[i+1]</code> is the 112 * listener. 113 */ 114 protected transient Object[] listenerList = NO_LISTENERS; 115 116 117 /** 118 * EventListenerList constructor 119 */ EventListenerList()120 public EventListenerList() 121 { 122 } 123 124 125 /** 126 * Registers a listener of a specific type. 127 * 128 * @param t the type of the listener. 129 * 130 * @param listener the listener to add, which must be an instance of 131 * <code>t</code>, or of a subclass of <code>t</code>. 132 * 133 * @throws IllegalArgumentException if <code>listener</code> is not 134 * an instance of <code>t</code> (or a subclass thereof). 135 * 136 * @throws Exception if <code>t</code> is <code>null</code>. 137 */ add(Class t, EventListener listener)138 public void add(Class t, EventListener listener) 139 { 140 int oldLength; 141 Object[] newList; 142 143 if (listener == null) 144 return; 145 146 if (!t.isInstance(listener)) 147 throw new IllegalArgumentException(); 148 149 oldLength = listenerList.length; 150 newList = new Object[oldLength + 2]; 151 if (oldLength > 0) 152 System.arraycopy(listenerList, 0, newList, 0, oldLength); 153 154 newList[oldLength] = t; 155 newList[oldLength + 1] = listener; 156 listenerList = newList; 157 } 158 159 160 /** 161 * Determines the number of listeners. 162 */ getListenerCount()163 public int getListenerCount() 164 { 165 return listenerList.length / 2; 166 } 167 168 169 /** 170 * Determines the number of listeners of a particular class. 171 * 172 * @param t the type of listeners to be counted. In order to get 173 * counted, a subscribed listener must be exactly of class 174 * <code>t</code>. Thus, subclasses of <code>t</code> will not be 175 * counted. 176 */ getListenerCount(Class t)177 public int getListenerCount(Class t) 178 { 179 int result = 0; 180 for (int i = 0; i < listenerList.length; i += 2) 181 if (t == listenerList[i]) 182 ++result; 183 184 return result; 185 } 186 187 188 /** 189 * Get a list of listenerType/listener pairs 190 * @returns Listener list 191 */ getListenerList()192 public Object[] getListenerList() 193 { 194 return listenerList; 195 } 196 197 198 /** 199 * Retrieves the currently subscribed listeners of a particular 200 * type. For a listener to be returned, it must have been 201 * registered with exactly the type <code>c</code>; subclasses are 202 * not considered equal. 203 * 204 * <p>The returned array can always be cast to <code>c[]</code>. 205 * Since it is a newly allocated copy, the caller may arbitrarily 206 * modify the array. 207 * 208 * @param c the class which was passed to {@link #add}. 209 * 210 * @throws ClassCastException if <code>c</code> does not implement 211 * the {@link EventListener} interface. 212 * 213 * @throws NullPointerException if <code>c</code> is 214 * <code>null</code>. 215 * 216 * @returns an array of <code>c</code> whose elements are the 217 * currently subscribed listeners of the specified type. If there 218 * are no such listeners, an empty array is returned. 219 * 220 * @since 1.3 221 */ getListeners(Class c)222 public EventListener[] getListeners(Class c) 223 { 224 int count, f; 225 EventListener[] result; 226 227 count = getListenerCount(c); 228 result = (EventListener[]) Array.newInstance(c, count); 229 f = 0; 230 for (int i = 0; i < listenerList.length; i += 2) 231 if (listenerList[i] == c) 232 result[f++] = (EventListener) listenerList[i + 1]; 233 234 return result; 235 } 236 237 238 /** 239 * Removes a listener of a specific type. 240 * 241 * @param t the type of the listener. 242 * 243 * @param listener the listener to remove, which must be an instance 244 * of <code>t</code>, or of a subclass of <code>t</code>. 245 * 246 * @throws IllegalArgumentException if <code>listener</code> is not 247 * an instance of <code>t</code> (or a subclass thereof). 248 * 249 * @throws Exception if <code>t</code> is <code>null</code>. 250 */ remove(Class t, EventListener listener)251 public void remove(Class t, EventListener listener) 252 { 253 Object[] oldList, newList; 254 int oldLength; 255 256 if (listener == null) 257 return; 258 259 if (!t.isInstance(listener)) 260 throw new IllegalArgumentException(); 261 262 oldList = listenerList; 263 oldLength = oldList.length; 264 for (int i = 0; i < oldLength; i += 2) 265 if (oldList[i] == t && oldList[i + 1] == listener) 266 { 267 if (oldLength == 2) 268 newList = NO_LISTENERS; 269 else 270 { 271 newList = new Object[oldLength - 2]; 272 if (i > 0) 273 System.arraycopy(oldList, 0, newList, 0, i); 274 if (i < oldLength - 2) 275 System.arraycopy(oldList, i + 2, newList, i, 276 oldLength - 2 - i); 277 } 278 listenerList = newList; 279 return; 280 } 281 } 282 283 284 /** 285 * Returns a string representation of this object that may be useful 286 * for debugging purposes. 287 */ toString()288 public String toString() 289 { 290 StringBuffer buf = new StringBuffer("EventListenerList: "); 291 buf.append(listenerList.length / 2); 292 buf.append(" listeners: "); 293 for (int i = 0; i < listenerList.length; i += 2) 294 { 295 buf.append(" type "); 296 buf.append(((Class) listenerList[i]).getName()); 297 buf.append(" listener "); 298 buf.append(listenerList[i + 1]); 299 } 300 return buf.toString(); 301 } 302 } 303