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