1 /*
   2  * Copyright (c) 1997, 2015, 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 static final 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      * @return array of ListenerType-listener pairs
 125      */
 126     public Object[] getListenerList() {
 127         return listenerList;
 128     }
 129 
 130     /**
 131      * Return an array of all the listeners of the given type.
 132      *
 133      * @param <T> the type of {@code EventListener} to search for
 134      * @param t the type of {@code EventListener} classes to be returned
 135      * @return all of the listeners of the specified type.
 136      * @exception  ClassCastException if the supplied class
 137      *          is not assignable to EventListener
 138      *
 139      * @since 1.3
 140      */
 141     public <T extends EventListener> T[] getListeners(Class<T> t) {
 142         Object[] lList = listenerList;
 143         int n = getListenerCount(lList, t);
 144         @SuppressWarnings("unchecked")
 145         T[] result = (T[])Array.newInstance(t, n);
 146         int j = 0;
 147         for (int i = lList.length-2; i>=0; i-=2) {
 148             if (lList[i] == t) {
 149                 @SuppressWarnings("unchecked")
 150                 T tmp = (T)lList[i+1];
 151                 result[j++] = tmp;
 152             }
 153         }
 154         return result;
 155     }
 156 
 157     /**
 158      * Returns the total number of listeners for this listener list.
 159      *
 160      * @return an integer count of total number of listeners
 161      */
 162     public int getListenerCount() {
 163         return listenerList.length/2;
 164     }
 165 
 166     /**
 167      * Returns the total number of listeners of the supplied type
 168      * for this listener list.
 169      *
 170      * @param t the type of listeners to count
 171      * @return the number of listeners of type {@code t}
 172      */
 173     public int getListenerCount(Class<?> t) {
 174         Object[] lList = listenerList;
 175         return getListenerCount(lList, t);
 176     }
 177 
 178     private int getListenerCount(Object[] list, Class<?> t) {
 179         int count = 0;
 180         for (int i = 0; i < list.length; i+=2) {
 181             if (t == (Class)list[i])
 182                 count++;
 183         }
 184         return count;
 185     }
 186 
 187     /**
 188      * Adds the listener as a listener of the specified type.
 189      *
 190      * @param <T> the type of {@code EventListener} to add
 191      * @param t the type of the {@code EventListener} class to add
 192      * @param l the listener to be added
 193      */
 194     public synchronized <T extends EventListener> void add(Class<T> t, T l) {
 195         if (l==null) {
 196             // In an ideal world, we would do an assertion here
 197             // to help developers know they are probably doing
 198             // something wrong
 199             return;
 200         }
 201         if (!t.isInstance(l)) {
 202             throw new IllegalArgumentException("Listener " + l +
 203                                          " is not of type " + t);
 204         }
 205         if (listenerList == NULL_ARRAY) {
 206             // if this is the first listener added,
 207             // initialize the lists
 208             listenerList = new Object[] { t, l };
 209         } else {
 210             // Otherwise copy the array and add the new listener
 211             int i = listenerList.length;
 212             Object[] tmp = new Object[i+2];
 213             System.arraycopy(listenerList, 0, tmp, 0, i);
 214 
 215             tmp[i] = t;
 216             tmp[i+1] = l;
 217 
 218             listenerList = tmp;
 219         }
 220     }
 221 
 222     /**
 223      * Removes the listener as a listener of the specified type.
 224      *
 225      * @param <T> the type of {@code EventListener}
 226      * @param t the type of the listener to be removed
 227      * @param l the listener to be removed
 228      */
 229     public synchronized <T extends EventListener> void remove(Class<T> t, T l) {
 230         if (l ==null) {
 231             // In an ideal world, we would do an assertion here
 232             // to help developers know they are probably doing
 233             // something wrong
 234             return;
 235         }
 236         if (!t.isInstance(l)) {
 237             throw new IllegalArgumentException("Listener " + l +
 238                                          " is not of type " + t);
 239         }
 240         // Is l on the list?
 241         int index = -1;
 242         for (int i = listenerList.length-2; i>=0; i-=2) {
 243             if ((listenerList[i]==t) && (listenerList[i+1].equals(l) == true)) {
 244                 index = i;
 245                 break;
 246             }
 247         }
 248 
 249         // If so,  remove it
 250         if (index != -1) {
 251             Object[] tmp = new Object[listenerList.length-2];
 252             // Copy the list up to index
 253             System.arraycopy(listenerList, 0, tmp, 0, index);
 254             // Copy from two past the index, up to
 255             // the end of tmp (which is two elements
 256             // shorter than the old list)
 257             if (index < tmp.length)
 258                 System.arraycopy(listenerList, index+2, tmp, index,
 259                                  tmp.length - index);
 260             // set the listener array to the new array or null
 261             listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp;
 262             }
 263     }
 264 
 265     // Serialization support.
 266     private void writeObject(ObjectOutputStream s) throws IOException {
 267         Object[] lList = listenerList;
 268         s.defaultWriteObject();
 269 
 270         // Save the non-null event listeners:
 271         for (int i = 0; i < lList.length; i+=2) {
 272             Class<?> t = (Class)lList[i];
 273             EventListener l = (EventListener)lList[i+1];
 274             if ((l!=null) && (l instanceof Serializable)) {
 275                 s.writeObject(t.getName());
 276                 s.writeObject(l);
 277             }
 278         }
 279 
 280         s.writeObject(null);
 281     }
 282 
 283     private void readObject(ObjectInputStream s)
 284         throws IOException, ClassNotFoundException {
 285         listenerList = NULL_ARRAY;
 286         s.defaultReadObject();
 287         Object listenerTypeOrNull;
 288 
 289         while (null != (listenerTypeOrNull = s.readObject())) {
 290             ClassLoader cl = Thread.currentThread().getContextClassLoader();
 291             EventListener l = (EventListener)s.readObject();
 292             String name = (String) listenerTypeOrNull;
 293             ReflectUtil.checkPackageAccess(name);
 294             @SuppressWarnings("unchecked")
 295             Class<EventListener> tmp = (Class<EventListener>)Class.forName(name, true, cl);
 296             add(tmp, l);
 297         }
 298     }
 299 
 300     /**
 301      * Returns a string representation of the EventListenerList.
 302      */
 303     public String toString() {
 304         Object[] lList = listenerList;
 305         String s = "EventListenerList: ";
 306         s += lList.length/2 + " listeners: ";
 307         for (int i = 0 ; i <= lList.length-2 ; i+=2) {
 308             s += " type " + ((Class)lList[i]).getName();
 309             s += " listener " + lList[i+1];
 310         }
 311         return s;
 312     }
 313 }