001 /*
002 * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.event;
026
027 import java.io.*;
028 import java.util.*;
029 import java.lang.reflect.Array;
030
031 /**
032 * A class that holds a list of EventListeners. A single instance
033 * can be used to hold all listeners (of all types) for the instance
034 * using the list. It is the responsiblity of the class using the
035 * EventListenerList to provide type-safe API (preferably conforming
036 * to the JavaBeans spec) and methods which dispatch event notification
037 * methods to appropriate Event Listeners on the list.
038 *
039 * The main benefits that this class provides are that it is relatively
040 * cheap in the case of no listeners, and it provides serialization for
041 * event-listener lists in a single place, as well as a degree of MT safety
042 * (when used correctly).
043 *
044 * Usage example:
045 * Say one is defining a class that sends out FooEvents, and one wants
046 * to allow users of the class to register FooListeners and receive
047 * notification when FooEvents occur. The following should be added
048 * to the class definition:
049 * <pre>
050 * EventListenerList listenerList = new EventListenerList();
051 * FooEvent fooEvent = null;
052 *
053 * public void addFooListener(FooListener l) {
054 * listenerList.add(FooListener.class, l);
055 * }
056 *
057 * public void removeFooListener(FooListener l) {
058 * listenerList.remove(FooListener.class, l);
059 * }
060 *
061 *
062 * // Notify all listeners that have registered interest for
063 * // notification on this event type. The event instance
064 * // is lazily created using the parameters passed into
065 * // the fire method.
066 *
067 * protected void fireFooXXX() {
068 * // Guaranteed to return a non-null array
069 * Object[] listeners = listenerList.getListenerList();
070 * // Process the listeners last to first, notifying
071 * // those that are interested in this event
072 * for (int i = listeners.length-2; i>=0; i-=2) {
073 * if (listeners[i]==FooListener.class) {
074 * // Lazily create the event:
075 * if (fooEvent == null)
076 * fooEvent = new FooEvent(this);
077 * ((FooListener)listeners[i+1]).fooXXX(fooEvent);
078 * }
079 * }
080 * }
081 * </pre>
082 * foo should be changed to the appropriate name, and fireFooXxx to the
083 * appropriate method name. One fire method should exist for each
084 * notification method in the FooListener interface.
085 * <p>
086 * <strong>Warning:</strong>
087 * Serialized objects of this class will not be compatible with
088 * future Swing releases. The current serialization support is
089 * appropriate for short term storage or RMI between applications running
090 * the same version of Swing. As of 1.4, support for long term storage
091 * of all JavaBeans<sup><font size="-2">TM</font></sup>
092 * has been added to the <code>java.beans</code> package.
093 * Please see {@link java.beans.XMLEncoder}.
094 *
095 * @version 1.43 05/05/07
096 * @author Georges Saab
097 * @author Hans Muller
098 * @author James Gosling
099 */
100 public class EventListenerList implements Serializable {
101 /* A null array to be shared by all empty listener lists*/
102 private final static Object[] NULL_ARRAY = new Object[0];
103 /* The list of ListenerType - Listener pairs */
104 protected transient Object[] listenerList = NULL_ARRAY;
105
106 /**
107 * Passes back the event listener list as an array
108 * of ListenerType-listener pairs. Note that for
109 * performance reasons, this implementation passes back
110 * the actual data structure in which the listener data
111 * is stored internally!
112 * This method is guaranteed to pass back a non-null
113 * array, so that no null-checking is required in
114 * fire methods. A zero-length array of Object should
115 * be returned if there are currently no listeners.
116 *
117 * WARNING!!! Absolutely NO modification of
118 * the data contained in this array should be made -- if
119 * any such manipulation is necessary, it should be done
120 * on a copy of the array returned rather than the array
121 * itself.
122 */
123 public Object[] getListenerList() {
124 return listenerList;
125 }
126
127 /**
128 * Return an array of all the listeners of the given type.
129 * @return all of the listeners of the specified type.
130 * @exception ClassCastException if the supplied class
131 * is not assignable to EventListener
132 *
133 * @since 1.3
134 */
135 public <T extends EventListener> T[] getListeners(Class<T> t) {
136 Object[] lList = listenerList;
137 int n = getListenerCount(lList, t);
138 T[] result = (T[]) Array.newInstance(t, n);
139 int j = 0;
140 for (int i = lList.length - 2; i >= 0; i -= 2) {
141 if (lList[i] == t) {
142 result[j++] = (T) lList[i + 1];
143 }
144 }
145 return result;
146 }
147
148 /**
149 * Returns the total number of listeners for this listener list.
150 */
151 public int getListenerCount() {
152 return listenerList.length / 2;
153 }
154
155 /**
156 * Returns the total number of listeners of the supplied type
157 * for this listener list.
158 */
159 public int getListenerCount(Class<?> t) {
160 Object[] lList = listenerList;
161 return getListenerCount(lList, t);
162 }
163
164 private int getListenerCount(Object[] list, Class t) {
165 int count = 0;
166 for (int i = 0; i < list.length; i += 2) {
167 if (t == (Class) list[i])
168 count++;
169 }
170 return count;
171 }
172
173 /**
174 * Adds the listener as a listener of the specified type.
175 * @param t the type of the listener to be added
176 * @param l the listener to be added
177 */
178 public synchronized <T extends EventListener> void add(Class<T> t,
179 T l) {
180 if (l == null) {
181 // In an ideal world, we would do an assertion here
182 // to help developers know they are probably doing
183 // something wrong
184 return;
185 }
186 if (!t.isInstance(l)) {
187 throw new IllegalArgumentException("Listener " + l
188 + " is not of type " + t);
189 }
190 if (listenerList == NULL_ARRAY) {
191 // if this is the first listener added,
192 // initialize the lists
193 listenerList = new Object[] { t, l };
194 } else {
195 // Otherwise copy the array and add the new listener
196 int i = listenerList.length;
197 Object[] tmp = new Object[i + 2];
198 System.arraycopy(listenerList, 0, tmp, 0, i);
199
200 tmp[i] = t;
201 tmp[i + 1] = l;
202
203 listenerList = tmp;
204 }
205 }
206
207 /**
208 * Removes the listener as a listener of the specified type.
209 * @param t the type of the listener to be removed
210 * @param l the listener to be removed
211 */
212 public synchronized <T extends EventListener> void remove(
213 Class<T> t, T l) {
214 if (l == null) {
215 // In an ideal world, we would do an assertion here
216 // to help developers know they are probably doing
217 // something wrong
218 return;
219 }
220 if (!t.isInstance(l)) {
221 throw new IllegalArgumentException("Listener " + l
222 + " is not of type " + t);
223 }
224 // Is l on the list?
225 int index = -1;
226 for (int i = listenerList.length - 2; i >= 0; i -= 2) {
227 if ((listenerList[i] == t)
228 && (listenerList[i + 1].equals(l) == true)) {
229 index = i;
230 break;
231 }
232 }
233
234 // If so, remove it
235 if (index != -1) {
236 Object[] tmp = new Object[listenerList.length - 2];
237 // Copy the list up to index
238 System.arraycopy(listenerList, 0, tmp, 0, index);
239 // Copy from two past the index, up to
240 // the end of tmp (which is two elements
241 // shorter than the old list)
242 if (index < tmp.length)
243 System.arraycopy(listenerList, index + 2, tmp, index,
244 tmp.length - index);
245 // set the listener array to the new array or null
246 listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp;
247 }
248 }
249
250 // Serialization support.
251 private void writeObject(ObjectOutputStream s) throws IOException {
252 Object[] lList = listenerList;
253 s.defaultWriteObject();
254
255 // Save the non-null event listeners:
256 for (int i = 0; i < lList.length; i += 2) {
257 Class t = (Class) lList[i];
258 EventListener l = (EventListener) lList[i + 1];
259 if ((l != null) && (l instanceof Serializable)) {
260 s.writeObject(t.getName());
261 s.writeObject(l);
262 }
263 }
264
265 s.writeObject(null);
266 }
267
268 private void readObject(ObjectInputStream s) throws IOException,
269 ClassNotFoundException {
270 listenerList = NULL_ARRAY;
271 s.defaultReadObject();
272 Object listenerTypeOrNull;
273
274 while (null != (listenerTypeOrNull = s.readObject())) {
275 ClassLoader cl = Thread.currentThread()
276 .getContextClassLoader();
277 EventListener l = (EventListener) s.readObject();
278 add((Class<EventListener>) Class.forName(
279 (String) listenerTypeOrNull, true, cl), l);
280 }
281 }
282
283 /**
284 * Returns a string representation of the EventListenerList.
285 */
286 public String toString() {
287 Object[] lList = listenerList;
288 String s = "EventListenerList: ";
289 s += lList.length / 2 + " listeners: ";
290 for (int i = 0; i <= lList.length - 2; i += 2) {
291 s += " type " + ((Class) lList[i]).getName();
292 s += " listener " + lList[i + 1];
293 }
294 return s;
295 }
296 }
|