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