1. /*
  2. * @(#)EventListenerList.java 1.34 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.event;
  8. import java.io.*;
  9. import java.util.*;
  10. import java.lang.reflect.Array;
  11. /**
  12. * A class that holds a list of EventListeners. A single instance
  13. * can be used to hold all listeners (of all types) for the instance
  14. * using the list. It is the responsiblity of the class using the
  15. * EventListenerList to provide type-safe API (preferably conforming
  16. * to the JavaBeans spec) and methods which dispatch event notification
  17. * methods to appropriate Event Listeners on the list.
  18. *
  19. * The main benefits that this class provides are that it is relatively
  20. * cheap in the case of no listeners, and it provides serialization for
  21. * event-listener lists in a single place, as well as a degree of MT safety
  22. * (when used correctly).
  23. *
  24. * Usage example:
  25. * Say one is defining a class that sends out FooEvents, and one wants
  26. * to allow users of the class to register FooListeners and receive
  27. * notification when FooEvents occur. The following should be added
  28. * to the class definition:
  29. * <pre>
  30. * EventListenerList listenerList = new EventListenerList();
  31. * FooEvent fooEvent = null;
  32. *
  33. * public void addFooListener(FooListener l) {
  34. * listenerList.add(FooListener.class, l);
  35. * }
  36. *
  37. * public void removeFooListener(FooListener l) {
  38. * listenerList.remove(FooListener.class, l);
  39. * }
  40. *
  41. *
  42. * // Notify all listeners that have registered interest for
  43. * // notification on this event type. The event instance
  44. * // is lazily created using the parameters passed into
  45. * // the fire method.
  46. *
  47. * protected void fireFooXXX() {
  48. * // Guaranteed to return a non-null array
  49. * Object[] listeners = listenerList.getListenerList();
  50. * // Process the listeners last to first, notifying
  51. * // those that are interested in this event
  52. * for (int i = listeners.length-2; i>=0; i-=2) {
  53. * if (listeners[i]==FooListener.class) {
  54. * // Lazily create the event:
  55. * if (fooEvent == null)
  56. * fooEvent = new FooEvent(this);
  57. * ((FooListener)listeners[i+1]).fooXXX(fooEvent);
  58. * }
  59. * }
  60. * }
  61. * </pre>
  62. * foo should be changed to the appropriate name, and fireFooXxx to the
  63. * appropriate method name. One fire method should exist for each
  64. * notification method in the FooListener interface.
  65. * <p>
  66. * <strong>Warning:</strong>
  67. * Serialized objects of this class will not be compatible with
  68. * future Swing releases. The current serialization support is
  69. * appropriate for short term storage or RMI between applications running
  70. * the same version of Swing. As of 1.4, support for long term storage
  71. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  72. * has been added to the <code>java.beans</code> package.
  73. * Please see {@link java.beans.XMLEncoder}.
  74. *
  75. * @version 1.34 01/23/03
  76. * @author Georges Saab
  77. * @author Hans Muller
  78. * @author James Gosling
  79. */
  80. public class EventListenerList implements Serializable {
  81. /* A null array to be shared by all empty listener lists*/
  82. private final static Object[] NULL_ARRAY = new Object[0];
  83. /* The list of ListenerType - Listener pairs */
  84. protected transient Object[] listenerList = NULL_ARRAY;
  85. /**
  86. * 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 listener 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. * Return an array of all the listeners of the given type.
  107. * @return all of the listeners of the specified type.
  108. * @exception ClassCastException if the supplied class
  109. * is not assignable to EventListener
  110. *
  111. * @since 1.3
  112. */
  113. public EventListener[] getListeners(Class t) {
  114. Object[] lList = listenerList;
  115. int n = getListenerCount(lList, t);
  116. EventListener[] result = (EventListener[])Array.newInstance(t, n);
  117. int j = 0;
  118. for (int i = lList.length-2; i>=0; i-=2) {
  119. if (lList[i] == t) {
  120. result[j++] = (EventListener)lList[i+1];
  121. }
  122. }
  123. return result;
  124. }
  125. /**
  126. * Returns the total number of listeners for this listener list.
  127. */
  128. public int getListenerCount() {
  129. return listenerList.length2;
  130. }
  131. /**
  132. * Returns the total number of listeners of the supplied type
  133. * for this listener list.
  134. */
  135. public int getListenerCount(Class t) {
  136. Object[] lList = listenerList;
  137. return getListenerCount(lList, t);
  138. }
  139. private int getListenerCount(Object[] list, Class t) {
  140. int count = 0;
  141. for (int i = 0; i < list.length; i+=2) {
  142. if (t == (Class)list[i])
  143. count++;
  144. }
  145. return count;
  146. }
  147. /**
  148. * Adds the listener as a listener of the specified type.
  149. * @param t the type of the listener to be added
  150. * @param l the listener to be added
  151. */
  152. public synchronized void add(Class t, EventListener l) {
  153. if (l==null) {
  154. // In an ideal world, we would do an assertion here
  155. // to help developers know they are probably doing
  156. // something wrong
  157. return;
  158. }
  159. if (!t.isInstance(l)) {
  160. throw new IllegalArgumentException("Listener " + l +
  161. " is not of type " + t);
  162. }
  163. if (listenerList == NULL_ARRAY) {
  164. // if this is the first listener added,
  165. // initialize the lists
  166. listenerList = new Object[] { t, l };
  167. } else {
  168. // Otherwise copy the array and add the new listener
  169. int i = listenerList.length;
  170. Object[] tmp = new Object[i+2];
  171. System.arraycopy(listenerList, 0, tmp, 0, i);
  172. tmp[i] = t;
  173. tmp[i+1] = l;
  174. listenerList = tmp;
  175. }
  176. }
  177. /**
  178. * Removes the listener as a listener of the specified type.
  179. * @param t the type of the listener to be removed
  180. * @param l the listener to be removed
  181. */
  182. public synchronized void remove(Class t, EventListener l) {
  183. if (l ==null) {
  184. // In an ideal world, we would do an assertion here
  185. // to help developers know they are probably doing
  186. // something wrong
  187. return;
  188. }
  189. if (!t.isInstance(l)) {
  190. throw new IllegalArgumentException("Listener " + l +
  191. " is not of type " + t);
  192. }
  193. // Is l on the list?
  194. int index = -1;
  195. for (int i = listenerList.length-2; i>=0; i-=2) {
  196. if ((listenerList[i]==t) && (listenerList[i+1].equals(l) == true)) {
  197. index = i;
  198. break;
  199. }
  200. }
  201. // If so, remove it
  202. if (index != -1) {
  203. Object[] tmp = new Object[listenerList.length-2];
  204. // Copy the list up to index
  205. System.arraycopy(listenerList, 0, tmp, 0, index);
  206. // Copy from two past the index, up to
  207. // the end of tmp (which is two elements
  208. // shorter than the old list)
  209. if (index < tmp.length)
  210. System.arraycopy(listenerList, index+2, tmp, index,
  211. tmp.length - index);
  212. // set the listener array to the new array or null
  213. listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp;
  214. }
  215. }
  216. // Serialization support.
  217. private void writeObject(ObjectOutputStream s) throws IOException {
  218. Object[] lList = listenerList;
  219. s.defaultWriteObject();
  220. // Save the non-null event listeners:
  221. for (int i = 0; i < lList.length; i+=2) {
  222. Class t = (Class)lList[i];
  223. EventListener l = (EventListener)lList[i+1];
  224. if ((l!=null) && (l instanceof Serializable)) {
  225. s.writeObject(t.getName());
  226. s.writeObject(l);
  227. }
  228. }
  229. s.writeObject(null);
  230. }
  231. private void readObject(ObjectInputStream s)
  232. throws IOException, ClassNotFoundException {
  233. listenerList = NULL_ARRAY;
  234. s.defaultReadObject();
  235. Object listenerTypeOrNull;
  236. while (null != (listenerTypeOrNull = s.readObject())) {
  237. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  238. EventListener l = (EventListener)s.readObject();
  239. add(Class.forName((String)listenerTypeOrNull, true, cl), l);
  240. }
  241. }
  242. /**
  243. * Returns a string representation of the EventListenerList.
  244. */
  245. public String toString() {
  246. Object[] lList = listenerList;
  247. String s = "EventListenerList: ";
  248. s += lList.length2 + " listeners: ";
  249. for (int i = 0 ; i <= lList.length-2 ; i+=2) {
  250. s += " type " + ((Class)lList[i]).getName();
  251. s += " listener " + lList[i+1];
  252. }
  253. return s;
  254. }
  255. }