All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.impl.matchers.WeakReferenceMatcherEditor Maven / Gradle / Ivy

/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.matchers;

import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.MatcherEditor;

import javax.swing.event.EventListenerList;
import java.lang.ref.WeakReference;

/**
 * This {@link MatcherEditor} exists to aid with garbage collection of
 * {@link Listener} objects. It is particularly useful when a long-lived
 * {@link MatcherEditor} exists and many short-lived {@link Listener} objects
 * must be added and removed.
 *
 * 

Rather than attaching each {@link Listener} to the long-lived * {@link MatcherEditor} with hard references and managing the {@link Listener} * registrations manually, it is considerably easier to contruct a * {@link WeakReferenceMatcherEditor} which removes {@link Listener}s after * they are unreachable and have been garbage collected. * *

Common usage of this class resembles: *

 * MatcherEditor myCustomMatcherEditor = ...
 * MatcherEditor weakRefMatcherEditor = Matchers.weakReferenceProxy(myCustomMatcherEditor);
 *
 * // customMatcherEditorListener will be removed when it is garbage collected
 * MatcherEditor.Listener customMatcherEditorListener = ...
 * weakRefMatcherEditor.addMatcherEditorListener(customMatcherEditorListener);
 * 
* * @author James Lemieux */ public final class WeakReferenceMatcherEditor implements MatcherEditor, MatcherEditor.Listener { /** The Listeners for this MatcherEditor. */ private final EventListenerList listenerList = new EventListenerList(); /** The last Matcher that was broadcast from this MatcherEditor. */ private MatcherEditor source; /** * Construct a MatcherEditor which acts as a weak proxy for the given * source. That is, it rebroadcasts MatcherEvents it receives * from the source to its own weak listeners until it, itself, * is no longer reachable, at which time it stops listening to the * source. * * @param source the MatcherEditor to decorate with weak proxying */ public WeakReferenceMatcherEditor(MatcherEditor source) { this.source = source; // listen to the source weakly so we clean ourselves up when we're extinct source.addMatcherEditorListener(new WeakMatcherEditorListener(source, this)); } /** * Return the current {@link Matcher} specified by the decorated * {@link MatcherEditor}. * * @return a non-null {@link Matcher} */ public Matcher getMatcher() { return this.source.getMatcher(); } /** * Wrap the given listener in a {@link WeakReference} and * notify it when the decorated {@link MatcherEditor} fires {@link Matcher} * changes. The weak listener will only be notified while it is reachable * via hard references, and will be cleaned up the next time a new * {@link MatcherEditor.Event} is fired. */ public void addMatcherEditorListener(Listener listener) { this.listenerList.add(Listener.class, new WeakMatcherEditorListener(this, listener)); } /** {@inheritDoc} */ public void removeMatcherEditorListener(Listener listener) { final Object[] listeners = this.listenerList.getListenerList(); // we remove the given listener by identity for (int i = listeners.length - 2; i >= 0; i -= 2) { final Object currentObject = listeners[i+1]; if (currentObject == listener) this.listenerList.remove(MatcherEditor.Listener.class, listener); // if the given listener is a WeakMatcherEditorListener, check if // the currentObject is actually its referent if (currentObject instanceof WeakMatcherEditorListener) { final WeakMatcherEditorListener weakMatcherEditorListener = (WeakMatcherEditorListener) currentObject; final Listener currentListener = weakMatcherEditorListener.getDecoratedListener(); if (currentListener == listener) this.listenerList.remove(MatcherEditor.Listener.class, weakMatcherEditorListener); } } } /** * Indicates a changes has occurred in the delegate Matcher produced by the * MatcherEditor. * * @param matcherEvent a MatcherEditor.Event describing the change in the * delegate Matcher produced by the MatcherEditor */ public void changedMatcher(Event matcherEvent) { final Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) ((Listener) listeners[i+1]).changedMatcher(matcherEvent); } /** * This is the crux of this MatcherEditor. It wraps a {@link Listener} in a * {@link WeakReference} so that its garbage collection is not affected by * being registered with a {@link MatcherEditor}. Instead, each time it is * notified that the {@link Matcher} changed it must test the availability * of the underlying listener. If it is available, it is notified. If it is * unavailable, it removes itself from listening. */ private class WeakMatcherEditorListener implements Listener { /** The WeakReference housing the true MatcherEditor.Listener. */ private final WeakReference> weakListener; /** The editor that this Listener is listening to. */ private final MatcherEditor editor; /** * Construct a WeakMatcherEditorListener which wraps the given * listener, which is assumed to listen to the given * editor, in a {@link WeakReference}. * * @param editor the {@link MatcherEditor} from which to remove the * listener after it has been garbage collected * @param listener the {@link Listener} containing the true logic for * reacting to matcher changes */ public WeakMatcherEditorListener(MatcherEditor editor, Listener listener) { this.weakListener = new WeakReference>(listener); this.editor = editor; } /** * Return the underlying {@link Listener} from the {@link WeakReference}. */ public Listener getDecoratedListener() { return this.weakListener.get(); } /** * This method tests for the existence of the underlying {@link Listener} * and if it still exists (i.e. has not been garbage collected) it is * notified of the matcherEvent. Otherwise, it is removed * from the {@link MatcherEditor} and will never be notified again. * * @param matcherEvent a MatcherEditor.Event describing the change in the * Matcher produced by the MatcherEditor */ public void changedMatcher(Event matcherEvent) { // fetch the underlying MatcherEditor.Listener final Listener matcherEditorListener = this.weakListener.get(); // if it has been garbage collected, stop listening to the MatcherEditor if (matcherEditorListener == null) { this.editor.removeMatcherEditorListener(this); } else { // otherwise fire the event as though it originated from this WeakReferenceMatcherEditor matcherEvent = new MatcherEditor.Event(WeakReferenceMatcherEditor.this, matcherEvent.getType(), matcherEvent.getMatcher()); matcherEditorListener.changedMatcher(matcherEvent); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy