Source code: com/eireneh/util/EventListenerList.java
1
2 package com.eireneh.util;
3
4 import java.io.*;
5 import java.util.*;
6
7 /**
8 * A class which holds a list of EventListeners.
9 * This code is lifted from javax.sw*ng.event.EventListnerList. It is
10 * very useful in non GUI code which does not need the rest of sw*ng.
11 * So I copied it here to save the need for sw*ngall.jar
12 *
13 * <p>A single instance
14 * can be used to hold all listeners (of all types) for the instance
15 * using the lsit. It is the responsiblity of the class using the
16 * EventListenerList to provide type-safe API (preferably conforming
17 * to the JavaBeans spec) and methods which dispatch event notification
18 * methods to appropriate Event Listeners on the list.
19 *
20 * The main benefits which this class provides are that it is relatively
21 * cheap in the case of no listeners, and provides serialization for
22 * eventlistener lists in a single place, as well as a degree of MT safety
23 * (when used correctly).
24 *
25 * Usage example:
26 * Say one is defining a class which sends out FooEvents, and wantds
27 * to allow users of the class to register FooListeners and receive
28 * notification when FooEvents occur. The following should be added
29 * to the class definition:
30 <pre>
31 EventListenerList listenrList = new EventListnerList();
32 FooEvent fooEvent = null;
33
34 public void addFooListener(FooListener l) {
35 listenerList.add(FooListener.class, l);
36 }
37
38 public void removeFooListener(FooListener l) {
39 listenerList.remove(FooListener.class, l);
40 }
41
42
43 // Notify all listeners that have registered interest for
44 // notification on this event type. The event instance
45 // is lazily created using the parameters passed into
46 // the fire method.
47
48 protected void firefooXXX() {
49 // Guaranteed to return a non-null array
50 Object[] listeners = listenerList.getListenerList();
51 // Process the listeners last to first, notifying
52 // those that are interested in this event
53 for (int i = listeners.length-2; i>=0; i-=2) {
54 if (listeners[i]==FooListener.class) {
55 // Lazily create the event:
56 if (fooEvent == null)
57 fooEvent = new FooEvent(this);
58 ((FooListener)listeners[i+1]).fooXXX(fooEvent);
59 }
60 }
61 }
62 </pre>
63 * foo should be changed to the appropriate name, and Method to the
64 * appropriate method name (one fire method should exist for each
65 * notification method in the FooListener interface).
66 * <p>
67 * <strong>Warning:</strong>
68 * Serialized objects of this class will not be compatible with
69 * future Sw*ng releases. The current serialization support is appropriate
70 * for short term storage or RMI between applications running the same
71 * version of Sw*ng. A future release of Sw*ng will provide support for
72 * long term persistence.
73 *
74 * @version 1.23 10/01/98
75 * @author Georges Saab
76 * @author Hans Muller
77 * @author James Gosling
78 */
79 public class EventListenerList implements Serializable {
80 /* A null array to be shared by all empty listener lists*/
81 private final static Object[] NULL_ARRAY = new Object[0];
82 /* The list of ListenerType - Listener pairs */
83 protected transient Object[] listenerList = NULL_ARRAY;
84
85 /**
86 * This passes back the event listener list as an array
87 * of ListenerType - listener pairs. Note that for
88 * performance reasons, this implementation passes back
89 * the actual data structure in which the listner data
90 * is stored internally!
91 * This method is guaranteed to pass back a non-null
92 * array, so that no null-checking is required in
93 * fire methods. A zero-length array of Object should
94 * be returned if there are currently no listeners.
95 *
96 * WARNING!!! Absolutely NO modification of
97 * the data contained in this array should be made -- if
98 * any such manipulation is necessary, it should be done
99 * on a copy of the array returned rather than the array
100 * itself.
101 */
102 public Object[] getListenerList() {
103 return listenerList;
104 }
105
106 /**
107 * Return the total number of listeners for this listenerlist
108 */
109 public int getListenerCount() {
110 return listenerList.length/2;
111 }
112
113 /**
114 * Return the total number of listeners of the supplied type
115 * for this listenerlist.
116 */
117 public int getListenerCount(Class t) {
118 int count = 0;
119 Object[] lList = listenerList;
120 for (int i = 0; i < lList.length; i+=2) {
121 if (t == (Class)lList[i])
122 count++;
123 }
124 return count;
125 }
126 /**
127 * Add the listener as a listener of the specified type.
128 * @param t the type of the listener to be added
129 * @param l the listener to be added
130 */
131 public synchronized void add(Class t, EventListener l) {
132 if (l==null) {
133 // In an ideal world, we would do an assertion here
134 // to help developers know they are probably doing
135 // something wrong
136 return;
137 }
138 if (!t.isInstance(l)) {
139 throw new IllegalArgumentException("Listener " + l +
140 " is not of type " + t);
141 }
142 if (listenerList == NULL_ARRAY) {
143 // if this is the first listener added,
144 // initialize the lists
145 listenerList = new Object[] { t, l };
146 } else {
147 // Otherwise copy the array and add the new listener
148 int i = listenerList.length;
149 Object[] tmp = new Object[i+2];
150 System.arraycopy(listenerList, 0, tmp, 0, i);
151
152 tmp[i] = t;
153 tmp[i+1] = l;
154
155 listenerList = tmp;
156 }
157 }
158
159 /**
160 * Remove the listener as a listener of the specified type.
161 * @param t the type of the listener to be removed
162 * @param l the listener to be removed
163 */
164 public synchronized void remove(Class t, EventListener l) {
165 if (l ==null) {
166 // In an ideal world, we would do an assertion here
167 // to help developers know they are probably doing
168 // something wrong
169 return;
170 }
171 if (!t.isInstance(l)) {
172 throw new IllegalArgumentException("Listener " + l +
173 " is not of type " + t);
174 }
175 // Is l on the list?
176 int index = -1;
177 for (int i = listenerList.length-2; i>=0; i-=2) {
178 if ((listenerList[i]==t) && (listenerList[i+1].equals(l) == true)) {
179 index = i;
180 break;
181 }
182 }
183
184 // If so, remove it
185 if (index != -1) {
186 Object[] tmp = new Object[listenerList.length-2];
187 // Copy the list up to index
188 System.arraycopy(listenerList, 0, tmp, 0, index);
189 // Copy from two past the index, up to
190 // the end of tmp (which is two elements
191 // shorter than the old list)
192 if (index < tmp.length)
193 System.arraycopy(listenerList, index+2, tmp, index,
194 tmp.length - index);
195 // set the listener array to the new array or null
196 listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp;
197 }
198 }
199
200 // Serialization support.
201 private void writeObject(ObjectOutputStream s) throws IOException {
202 Object[] lList = listenerList;
203 s.defaultWriteObject();
204
205 // Save the non-null event listeners:
206 for (int i = 0; i < lList.length; i+=2) {
207 Class t = (Class)lList[i];
208 EventListener l = (EventListener)lList[i+1];
209 if ((l!=null) && (l instanceof Serializable)) {
210 s.writeObject(t.getName());
211 s.writeObject(l);
212 }
213 }
214
215 s.writeObject(null);
216 }
217
218 private void readObject(ObjectInputStream s)
219 throws IOException, ClassNotFoundException {
220 listenerList = NULL_ARRAY;
221 s.defaultReadObject();
222 Object listenerTypeOrNull;
223
224 while (null != (listenerTypeOrNull = s.readObject())) {
225 EventListener l = (EventListener)s.readObject();
226 add(Class.forName((String)listenerTypeOrNull), l);
227 }
228 }
229
230 /**
231 * Return a string representation of the EventListenerList.
232 */
233 public String toString() {
234 Object[] lList = listenerList;
235 String s = "EventListenerList: ";
236 s += lList.length/2 + " listeners: ";
237 for (int i = 0 ; i <= lList.length-2 ; i+=2) {
238 s += " type " + ((Class)lList[i]).getName();
239 s += " listener " + lList[i+1];
240 }
241 return s;
242 }
243 }