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