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