001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.io.IOException;
017: import java.io.ObjectInputStream;
018: import java.io.ObjectOutputStream;
019: import java.io.Serializable;
020: import java.lang.ref.WeakReference;
021: import java.lang.reflect.Array;
022: import java.util.EventListener;
023:
024: import javax.swing.event.EventListenerList;
025:
026: /**
027: * Class that can hold the list of event listeners in a "weak" manner. The
028: * advantage is that the listener doesn't need to be explicitly removed. Because
029: * of the use of the <tt>WeakReference</tt> it gets forgotten automatically if
030: * it was garbage collected. There must be at least one non-weak reference to
031: * the listener (otherwise it would become garbage-collected). One of the ways
032: * is to store the listener in some instance variable in the class that adds the
033: * listener. Please note that the methods <tt>getListenerCount()</tt> and
034: * <tt>getListenerCount(Class t)</tt> give the count that doesn't reflect
035: * whether some listeners were garbage collected.
036: *
037: * @author Miloslav Metelka
038: * @version 1.00
039: */
040:
041: public class WeakEventListenerList extends EventListenerList {
042:
043: private int listenerSize;
044:
045: /**
046: *
047: */
048: public synchronized Object[] getListenerList() {
049: int tgtInd = 0;
050: Object[] ret = new Object[listenerSize];
051: for (int i = 1; i < listenerSize; i += 2) {
052: Object l = ((WeakReference) listenerList[i]).get();
053: if (l != null) {
054: ret[tgtInd++] = listenerList[i - 1];
055: ret[tgtInd++] = l;
056: } else { // listener was garbage collected
057: /*
058: * Remove the listener and its class. This could be done in a
059: * more efficient but much less readable way batching the
060: * successive removes into one.
061: */
062: System.arraycopy(listenerList, i + 1, listenerList,
063: i - 1, listenerSize - i - 1);
064: listenerSize -= 2;
065: i -= 2;
066: }
067: }
068:
069: if (ret.length != tgtInd) {
070: Object[] tmp = new Object[tgtInd];
071: System.arraycopy(ret, 0, tmp, 0, tgtInd);
072: ret = tmp;
073: }
074:
075: return ret;
076: }
077:
078: public synchronized EventListener[] getListeners(Class t) {
079: int tgtInd = 0;
080: EventListener[] ret = (EventListener[]) Array.newInstance(t,
081: listenerSize);
082: for (int i = 0; i < listenerSize; i++) {
083: if (listenerList[i++] == t) {
084: EventListener l = (EventListener) ((WeakReference) listenerList[i])
085: .get();
086: if (l != null) {
087: ret[tgtInd++] = l;
088: } else { // listener was garbage collected
089: /*
090: * Remove the listener and its class. This could be done in
091: * a more efficient but much less readable way batching the
092: * successive removes into one.
093: */
094: System.arraycopy(listenerList, i + 1, listenerList,
095: i - 1, listenerSize - i - 1);
096: listenerSize -= 2;
097: i -= 2;
098: }
099: }
100: }
101:
102: if (ret.length != tgtInd) {
103: EventListener[] tmp = (EventListener[]) Array.newInstance(
104: t, tgtInd);
105: System.arraycopy(ret, 0, tmp, 0, tgtInd);
106: ret = tmp;
107: }
108:
109: return ret;
110: }
111:
112: /**
113: * Adds the listener as a listener of the specified type.
114: *
115: * @param t
116: * the type of the listener to be added
117: * @param l
118: * the listener to be added
119: */
120: public synchronized void add(Class t, EventListener l) {
121: if (l == null) {
122: // In an ideal world, we would do an assertion here
123: // to help developers know they are probably doing
124: // something wrong
125: return;
126: }
127:
128: if (!t.isInstance(l)) {
129: throw new IllegalArgumentException("Listener " + l
130: + " is not of type " + t);
131: }
132:
133: if (listenerSize == 0) {
134: listenerList = new Object[] { t, new WeakReference(l) };
135: listenerSize = 2;
136: } else {
137: if (listenerSize == listenerList.length) { // reallocate
138: Object[] tmp = new Object[listenerSize * 2];
139: System.arraycopy(listenerList, 0, tmp, 0, listenerSize);
140: listenerList = tmp;
141: }
142:
143: listenerList[listenerSize++] = t;
144: listenerList[listenerSize++] = new WeakReference(l);
145: }
146: }
147:
148: /**
149: * Removes the listener as a listener of the specified type.
150: *
151: * @param t
152: * the type of the listener to be removed
153: * @param l
154: * the listener to be removed
155: */
156: public synchronized void remove(Class t, EventListener l) {
157: if (l == null) {
158: // In an ideal world, we would do an assertion here
159: // to help developers know they are probably doing
160: // something wrong
161: return;
162: }
163:
164: if (!t.isInstance(l)) {
165: throw new IllegalArgumentException("Listener " + l
166: + " is not of type " + t);
167: }
168:
169: // Is l on the list?
170: int index = -1;
171: for (int i = listenerSize - 2; i >= 0; i -= 2) {
172: if ((listenerList[i] == t)
173: && (((WeakReference) listenerList[i + 1]).get())
174: .equals(l)) {
175: index = i;
176: break;
177: }
178: }
179:
180: // If so, remove it
181: if (index >= 0) {
182: System.arraycopy(listenerList, index + 2, listenerList,
183: index, listenerSize - index - 2);
184: listenerSize -= 2;
185: }
186: }
187:
188: private synchronized void writeObject(ObjectOutputStream os)
189: throws IOException {
190: os.defaultWriteObject();
191:
192: // Save the non-null event listeners:
193: for (int i = 0; i < listenerSize; i += 2) {
194: Class t = (Class) listenerList[i];
195: EventListener l = (EventListener) ((WeakReference) listenerList[i + 1])
196: .get();
197: if ((l != null) && (l instanceof Serializable)) {
198: os.writeObject(t.getName());
199: os.writeObject(l);
200: }
201: }
202:
203: os.writeObject(null);
204: }
205:
206: private void readObject(ObjectInputStream is) throws IOException,
207: ClassNotFoundException {
208: is.defaultReadObject();
209: Object listenerTypeOrNull;
210:
211: while (null != (listenerTypeOrNull = is.readObject())) {
212: EventListener l = (EventListener) is.readObject();
213: add(Class.forName((String) listenerTypeOrNull), l);
214: }
215: }
216:
217: public synchronized String toString() {
218: StringBuffer sb = new StringBuffer("WeakEventListenerList: ");
219: sb.append(listenerSize / 2);
220: sb.append(" listeners:\n");
221: for (int i = 0; i < listenerSize; i += 2) {
222: sb.append(" type " + ((Class) listenerList[i]).getName());
223: sb.append(" listener "
224: + ((WeakReference) listenerList[i + 1]).get());
225: }
226: return sb.toString();
227: }
228:
229: }
|