001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.util;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.VetoableChangeListener;
046: import java.util.EventListener;
047: import javax.swing.event.ChangeListener;
048: import javax.swing.event.DocumentListener;
049:
050: /** A generic weak listener factory.
051: * Creates a weak implementation of a listener of type <CODE>lType</CODE>.
052: *
053: * In the following examples, I'll use following naming:<BR>
054: * There are four objects involved in weak listener usage:<UL>
055: * <LI>The event <em>source</em> object
056: * <LI>The <em>observer</em> - object that wants to listen on <em>source</em>
057: * <LI>The <em>listener</em> - the implementation of the corresponding
058: * <code>*Listener</code> interface, sometimes the observer itself but
059: * often some observer's inner class delegating the events to the observer.
060: * <LI>The <em>weak listener</em> implementation.
061: * </UL>
062: * The examples are written for ChangeListener. The <code>Utilities</code>
063: * have factory methods for the most common listeners used in NetBeans
064: * and also one universal factory method you can use for other listeners.
065: *
066: * <H2>How to use it:</H2>
067: * Here is an example how to write a listener/observer and make it listen
068: * on some source:
069: * <pre>
070: * public class ListenerObserver implements ChangeListener {
071: * private void registerTo(Source source) {
072: * source.addChangeListener({@link
073: #change(javax.swing.event.ChangeListener, java.lang.Object)
074: * WeakListeners.change} (this, source));
075: * }
076: *
077: * public void stateChanged(ChangeEvent e) {
078: * doSomething();
079: * }
080: * }
081: * </pre>
082: * You can also factor out the listener implementation to some other class
083: * if you don't want to expose the stateChanged method (better technique):
084: * <pre>
085: * public class Observer {
086: * <b>private Listener listener;</b>
087: *
088: * private void registerTo(Source source) {
089: * <b>listener = new Listener()</b>;
090: * source.addChangeListener({@link
091: #change(javax.swing.event.ChangeListener, java.lang.Object)
092: * WeakListeners.change} (listener, source));
093: * }
094: *
095: * private class Listener implements ChangeListener {
096: * public void stateChanged(ChangeEvent e) {
097: * doSomething();
098: * }
099: * }
100: * }
101: * </pre>
102: * Note: The observer keeps the reference to the listener, it won't work
103: * otherwise, see below.
104: *
105: * <P>You can also use the universal factory for other listeners:
106: * <pre>
107: * public class Observer implements SomeListener {
108: * private void registerTo(Source source) {
109: * source.addSomeListener((SomeListener){@link
110: * #create(java.lang.Class, java.util.EventListener, java.lang.Object)
111: * WeakListeners.create} (
112: * SomeListener.class, this, source));
113: * }
114: *
115: * public void someEventHappened(SomeEvent e) {
116: * doSomething();
117: * }
118: * }
119: * </pre>
120: *
121: * <H2>How to <font color=red>not</font> use it:</H2>
122: * Here are examples of a common mistakes done when using <em>weak listener</em>:
123: * <pre>
124: * public class Observer {
125: * private void registerTo(Source source) {
126: * source.addChangeListener(WeakListeners.change(<b>new Listener()</b>, source));
127: * }
128: *
129: * private class Listener implements ChangeListener {
130: * public void stateChanged(ChangeEvent e) {
131: * doSomething();
132: * }
133: * }
134: * }
135: * </pre>
136: * Mistake: There is nobody holding strong reference to the Listener instance,
137: * so it may be freed on the next GC cycle.
138: *
139: * <BR><pre>
140: * public class ListenerObserver implements ChangeListener {
141: * private void registerTo(Source source) {
142: * source.addChangeListener(WeakListeners.change(this, <b>null</b>));
143: * }
144: *
145: * public void stateChanged(ChangeEvent e) {
146: * doSomething();
147: * }
148: * }
149: * </pre>
150: * Mistake: The weak listener is unable to unregister itself from the source
151: * once the listener is freed. For explanation, read below.
152: *
153: <H2>How does it work:</H2>
154: * <P>The <em>weak listener</em> is used as a reference-weakening wrapper
155: * around the listener. It is itself strongly referenced from the implementation
156: * of the source (e.g. from its <code>EventListenerList</code>) but it references
157: * the listener only through <code>WeakReference</code>. It also weak-references
158: * the source. Listener, on the other hand, usually strongly references
159: * the observer (typically through the outer class reference).
160: *
161: * This means that: <OL>
162: * <LI>If the listener is not strong-referenced from elsewhere, it can be
163: * thrown away on the next GC cycle. This is why you can't use
164: * <code>WeakListeners.change(new MyListener(), ..)</code> as the only reference
165: * to the listener will be the weak one from the weak listener.
166: * <LI>If the listener-observer pair is not strong-referenced from elsewhere
167: * it can be thrown away on the next GC cycle. This is what the
168: * <em>weak listener</em> was invented for.
169: * <LI>If the source is not strong-referenced from anywhere, it can be
170: * thrown away on the next GC cycle taking the weak listener with it,
171: * but not the listener and the observer if they are still strong-referenced
172: * (unusual case, but possible).
173: * </OL>
174: *
175: * <P>Now what happens when the listener/observer is removed from memory:<UL>
176: * <LI>The weak listener is notified that the reference to the listener was cleared.
177: * <LI>It tries to unregister itself from the source. This is why it needs
178: * the reference to the source for the registration. The unregistration
179: * is done using reflection, usually looking up the method
180: * <code>remove<listenerType></code> of the source and calling it.
181: * </UL>
182: *
183: * <P>This may fail if the source don't have the expected <code>remove*</code>
184: * method and/or if you provide wrong reference to source. In that case
185: * the weak listener instance will stay in memory and registered by the source,
186: * while the listener and observer will be freed.
187: *
188: * <P>There is still one fallback method - if some event come to a weak listener
189: * and the listener is already freed, the weak listener tries to unregister
190: * itself from the object the event came from.
191: *
192: * @since 4.10
193: */
194: public final class WeakListeners {
195: /** No instances.
196: */
197: private WeakListeners() {
198: }
199:
200: /** Generic factory method to create weak listener for any listener
201: * interface.
202: *
203: * @param lType the type of listener to create. It can be any interface,
204: * but only interfaces are allowed.
205: * @param l the listener to delegate to, <CODE>l</CODE> must be an instance
206: * of <CODE>lType</CODE>
207: * @param source the source that the listener should detach from when
208: * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
209: * @return an instance of <CODE>lType</CODE> delegating all the interface
210: * calls to <CODE>l</CODE>.
211: */
212: public static <T extends EventListener> T create(Class<T> lType,
213: T l, Object source) {
214: if (!lType.isInterface()) {
215: throw new IllegalArgumentException("Not interface: "
216: + lType);
217: }
218:
219: return WeakListenerImpl.create(lType, lType, l, source);
220: }
221:
222: /** The most generic factory method to create weak listener for any listener
223: * interface that moreover behaves like a listener of another type.
224: * This can be useful to correctly remove listeners from a source when
225: * hierarchies of listeners are used.
226: * <P>
227: * For example {@link javax.naming.event.EventContext} allows to add an
228: * instance of {@link javax.naming.event.ObjectChangeListener} but using
229: * method <code>addNamingListener</code>. Method <code>removeNamingListener</code>
230: * is then used to remove it. To help the weak listener support to correctly
231: * find the right method one have to use:
232: * <PRE>
233: * ObjectChangeListener l = (ObjectChangeListener)WeakListeners.create (
234: * ObjectChangeListener.class, // the actual class of the returned listener
235: * NamingListener.class, // but it always will be used as NamingListener
236: * yourObjectListener,
237: * someContext
238: * );
239: * someContext.addNamingListener ("", 0, l);
240: * </PRE>
241: * This will correctly create <code>ObjectChangeListener</code>
242: * and unregister it by
243: * calling <code>removeNamingListener</code>.
244: *
245: * @param lType the type the listener shall implement. It can be any interface,
246: * but only interfaces are allowed.
247: * @param apiType the interface the returned object will be used as. It
248: * shall be equal to <code>lType</code> or its superinterface
249: * @param l the listener to delegate to, <CODE>l</CODE> must be an instance
250: * of <CODE>lType</CODE>
251: * @param source the source that the listener should detach from when
252: * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
253: * @return an instance of <CODE>lType</CODE> delegating all the interface
254: * calls to <CODE>l</CODE>.
255: * @since 4.12
256: */
257: public static <T extends EventListener> T create(Class<T> lType,
258: Class<? super T> apiType, T l, Object source) {
259: if (!lType.isInterface()) {
260: throw new IllegalArgumentException("Not interface: "
261: + lType);
262: }
263:
264: if (!apiType.isInterface()) {
265: throw new IllegalArgumentException("Not interface: "
266: + apiType);
267: }
268:
269: if (!apiType.isAssignableFrom(lType)) {
270: throw new IllegalArgumentException(apiType
271: + " has to be assignableFrom " + lType); // NOI18N
272: }
273:
274: return WeakListenerImpl.create(lType, apiType, l, source);
275: }
276:
277: /** Creates a weak implementation of PropertyChangeListener.
278: *
279: * @param l the listener to delegate to
280: * @param source the source that the listener should detach from when
281: * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
282: * @return a PropertyChangeListener delegating to <CODE>l</CODE>.
283: */
284: public static PropertyChangeListener propertyChange(
285: PropertyChangeListener l, Object source) {
286: WeakListenerImpl.PropertyChange wl = new WeakListenerImpl.PropertyChange(
287: l);
288: wl.setSource(source);
289:
290: return wl;
291: }
292:
293: /** Creates a weak implementation of VetoableChangeListener.
294: *
295: * @param l the listener to delegate to
296: * @param source the source that the listener should detach from when
297: * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
298: * @return a VetoableChangeListener delegating to <CODE>l</CODE>.
299: */
300: public static VetoableChangeListener vetoableChange(
301: VetoableChangeListener l, Object source) {
302: WeakListenerImpl.VetoableChange wl = new WeakListenerImpl.VetoableChange(
303: l);
304: wl.setSource(source);
305:
306: return wl;
307: }
308:
309: /** Creates a weak implementation of DocumentListener.
310: *
311: * @param l the listener to delegate to
312: * @param source the source that the listener should detach from when
313: * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
314: * @return a DocumentListener delegating to <CODE>l</CODE>.
315: */
316: public static DocumentListener document(DocumentListener l,
317: Object source) {
318: WeakListenerImpl.Document wl = new WeakListenerImpl.Document(l);
319: wl.setSource(source);
320:
321: return wl;
322: }
323:
324: /** Creates a weak implementation of ChangeListener.
325: *
326: * @param l the listener to delegate to
327: * @param source the source that the listener should detach from when
328: * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
329: * @return a ChangeListener delegating to <CODE>l</CODE>.
330: */
331: public static ChangeListener change(ChangeListener l, Object source) {
332: WeakListenerImpl.Change wl = new WeakListenerImpl.Change(l);
333: wl.setSource(source);
334:
335: return wl;
336: }
337: }
|