001/* 002 * $Id: WeakEventListenerList.java 3190 2009-01-20 17:47:52Z kschaefe $ 003 * 004 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021package org.jdesktop.swingx.event; 022 023import java.io.Serializable; 024import java.lang.ref.WeakReference; 025import java.lang.reflect.Array; 026import java.util.ArrayList; 027import java.util.EventListener; 028import java.util.List; 029 030/** 031 * A class that holds a list of EventListeners. A single instance 032 * can be used to hold all listeners (of all types) for the instance 033 * using the list. It is the responsibility of the class using the 034 * EventListenerList to provide type-safe API (preferably conforming 035 * to the JavaBeans spec) and methods which dispatch event notification 036 * methods to appropriate Event Listeners on the list. 037 * 038 * The main benefit that this class provides is that it releases 039 * garbage collected listeners (internally uses weak references). <p> 040 * 041 * PENDING: serialization support 042 * 043 * 044 * Usage example: 045 * Say one is defining a class that sends out FooEvents, and one wants 046 * to allow users of the class to register FooListeners and receive 047 * notification when FooEvents occur. The following should be added 048 * to the class definition: 049 * <pre> 050 * EventListenerList listenerList = new EventListenerList(); 051 * FooEvent fooEvent = null; 052 * 053 * public void addFooListener(FooListener l) { 054 * listenerList.add(FooListener.class, l); 055 * } 056 * 057 * public void removeFooListener(FooListener l) { 058 * listenerList.remove(FooListener.class, l); 059 * } 060 * 061 * 062 * // Notify all listeners that have registered interest for 063 * // notification on this event type. The event instance 064 * // is lazily created using the parameters passed into 065 * // the fire method. 066 * 067 * 068 * protected void fireFooXXX() { 069 * // Guaranteed to return a non-null array 070 * FooListener[] listeners = listenerList.getListeners(FooListener.class); 071 * // Process the listeners last to first, notifying 072 * // those that are interested in this event 073 * for (FooListener listener: listeners) { 074 * // Lazily create the event: 075 * if (fooEvent == null) 076 * fooEvent = new FooEvent(this); 077 * listener.fooXXX(fooEvent); 078 * } 079 * } 080 * } 081 * </pre> 082 * foo should be changed to the appropriate name, and fireFooXxx to the 083 * appropriate method name. One fire method should exist for each 084 * notification method in the FooListener interface. 085 * <p> 086 * <strong>Warning:</strong> 087 * Serialized objects of this class will not be compatible with 088 * future Swing releases. The current serialization support is 089 * appropriate for short term storage or RMI between applications running 090 * the same version of Swing. As of 1.4, support for long term storage 091 * of all JavaBeans<sup><font size="-2">TM</font></sup> 092 * has been added to the <code>java.beans</code> package. 093 * Please see {@link java.beans.XMLEncoder}. 094 * 095 * @version 1.37 11/17/05 096 * @author Georges Saab 097 * @author Hans Muller 098 * @author James Gosling 099 */ 100public class WeakEventListenerList implements Serializable { 101 102 protected transient List<WeakReference<? extends EventListener>> weakReferences; 103 protected transient List<Class<? extends EventListener>> classes; 104 105 /** 106 * Passes back the event listener list as an array 107 * of ListenerType-listener pairs. 108 * As a side-effect, cleans out any 109 * garbage collected listeners before building the array. 110 * 111 * @return a array of listenerType-listener pairs. 112 */ 113 public Object[] getListenerList() { 114 List<? extends EventListener> listeners = cleanReferences(); 115 Object[] result = new Object[listeners.size() * 2]; 116 for (int i = 0; i < listeners.size(); i++) { 117 result[2*i + 1] = listeners.get(i); 118 result[2*i] = getClasses().get(i); 119 } 120 return result; 121 } 122 123 /** 124 * Returns a list of strongly referenced EventListeners. Removes 125 * internal weak references to garbage collected listeners. 126 * 127 * @return 128 */ 129 @SuppressWarnings("unchecked") 130 private synchronized <T extends EventListener> List<T> cleanReferences() { 131 List<T> listeners = new ArrayList<T>(); 132 for (int i = getReferences().size() - 1; i >= 0; i--) { 133 134 Object listener = getReferences().get(i).get(); 135 if (listener == null) { 136 getReferences().remove(i); 137 getClasses().remove(i); 138 } else { 139 listeners.add(0, (T) listener); 140 } 141 } 142 return listeners; 143 } 144 145 private List<WeakReference<? extends EventListener>> getReferences() { 146 if (weakReferences == null) { 147 weakReferences = new ArrayList<WeakReference<? extends EventListener>>(); 148 } 149 return weakReferences; 150 } 151 152 private List<Class<? extends EventListener>> getClasses() { 153 if (classes == null) { 154 classes = new ArrayList<Class<? extends EventListener>>(); 155 156 } 157 return classes; 158 } 159 /** 160 * Return an array of all the listeners of the given type. 161 * As a side-effect, cleans out any 162 * garbage collected listeners before building the array. 163 * @return all of the listeners of the specified type. 164 * @exception ClassCastException if the supplied class 165 * is not assignable to EventListener 166 * 167 * @since 1.3 168 */ 169 @SuppressWarnings("unchecked") 170 public <T extends EventListener> T[] getListeners(Class<T> t) { 171 List<T> liveListeners = cleanReferences(); 172 List<T> listeners = new ArrayList<T>(); 173 for (int i = 0; i < liveListeners.size(); i++) { 174 if (getClasses().get(i) == t) { 175 listeners.add(liveListeners.get(i)); 176 } 177 } 178 T[] result = (T[])Array.newInstance(t, listeners.size()); 179 return listeners.toArray(result); 180 } 181 182 /** 183 * Adds the listener as a listener of the specified type. 184 * As a side-effect, cleans out any garbage collected 185 * listeners before adding. 186 * @param t the type of the listener to be added 187 * @param l the listener to be added 188 */ 189 public synchronized <T extends EventListener> void add(Class<T> t, T l) { 190 if (l==null) { 191 // In an ideal world, we would do an assertion here 192 // to help developers know they are probably doing 193 // something wrong 194 return; 195 } 196 if (!t.isInstance(l)) { 197 throw new IllegalArgumentException("Listener " + l + 198 " is not of type " + t); 199 } 200 cleanReferences(); 201 getReferences().add(new WeakReference<T>(l)); 202 getClasses().add(t); 203 } 204 205 /** 206 * Removes the listener as a listener of the specified type. 207 * @param t the type of the listener to be removed 208 * @param l the listener to be removed 209 */ 210 public synchronized <T extends EventListener> void remove(Class<T> t, T l) { 211 if (l ==null) { 212 // In an ideal world, we would do an assertion here 213 // to help developers know they are probably doing 214 // something wrong 215 return; 216 } 217 if (!t.isInstance(l)) { 218 throw new IllegalArgumentException("Listener " + l + 219 " is not of type " + t); 220 } 221 for (int i = 0; i < getReferences().size(); i++) { 222 if (l.equals(getReferences().get(i).get()) && 223 (t == getClasses().get(i))) { 224 getReferences().remove(i); 225 getClasses().remove(i); 226 break; 227 } 228 } 229 } 230 231// // Serialization support. 232// private void writeObject(ObjectOutputStream s) throws IOException { 233// Object[] lList = listenerList; 234// s.defaultWriteObject(); 235// 236// // Save the non-null event listeners: 237// for (int i = 0; i < lList.length; i+=2) { 238// Class t = (Class)lList[i]; 239// EventListener l = (EventListener)lList[i+1]; 240// if ((l!=null) && (l instanceof Serializable)) { 241// s.writeObject(t.getName()); 242// s.writeObject(l); 243// } 244// } 245// 246// s.writeObject(null); 247// } 248// 249// private void readObject(ObjectInputStream s) 250// throws IOException, ClassNotFoundException { 251// listenerList = NULL_ARRAY; 252// s.defaultReadObject(); 253// Object listenerTypeOrNull; 254// 255// while (null != (listenerTypeOrNull = s.readObject())) { 256// ClassLoader cl = Thread.currentThread().getContextClassLoader(); 257// EventListener l = (EventListener)s.readObject(); 258// add((Class<EventListener>)Class.forName((String)listenerTypeOrNull, true, cl), l); 259// } 260// } 261 262// /** 263// * Returns a string representation of the EventListenerList. 264// */ 265// public String toString() { 266// Object[] lList = listenerList; 267// String s = "EventListenerList: "; 268// s += lList.length/2 + " listeners: "; 269// for (int i = 0 ; i <= lList.length-2 ; i+=2) { 270// s += " type " + ((Class)lList[i]).getName(); 271// s += " listener " + lList[i+1]; 272// } 273// return s; 274// } 275}