/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.util; import java.beans.*; import java.lang.ref.*; import java.lang.reflect.*; import java.util.EventListener; import java.util.EventObject; import java.awt.event.FocusListener; import java.awt.event.FocusEvent; import javax.swing.event.*; import org.openide.ErrorManager; import org.openide.filesystems.*; import org.openide.loaders.OperationListener; import org.openide.loaders.OperationEvent; import org.openide.nodes.*; /** * A listener wrapper that delegates to another listener but hold * only weak reference to it, so it does not prevent it to be finalized. * * In the following examples, I'll use following naming:<BR> * There are four objects involved in WeakListener usage:<UL> * <LI>The event <em>source</em> object * <LI>The <em>observer</em> - object that wants to listen on <em>source</em> * <LI>The <em>listener</em> - the implementation of the corresponding * <code>*Listener</code> interface, sometimes the observer itself but * often some observer's inner class delegating the events to the observer. * <LI>The <em>WeakListener</em> implementation. * </UL> * The examples are written for ChangeListener. The <code>WeakListener</code> * have factory methods for the most common listeners used in NetBeans * and also one universal factory method you can use for other listeners. * * <H2>How to use it:</H2> * Here is an example how to write a listener/observer and make it listen * on some source: * <pre> * public class ListenerObserver implements ChangeListener { * private void registerTo(Source source) { * source.addChangeListener({@link #change(javax.swing.event.ChangeListener, java.lang.Object) * WeakListener.change} (this, source)); * } * * public void stateChanged(ChangeEvent e) { * doSomething(); * } * } * </pre> * You can also factor out the listener implementation to some other class * if you don't want to expose the stateChanged method (better technique): * <pre> * public class Observer { * <b>private Listener listener;</b> * * private void registerTo(Source source) { * <b>listener = new Listener()</b>; * source.addChangeListener({@link #change(javax.swing.event.ChangeListener, java.lang.Object) * WeakListener.change} (listener, source)); * } * * private class Listener implements ChangeListener { * public void stateChanged(ChangeEvent e) { * doSomething(); * } * } * } * </pre> * Note: The observer keeps the reference to the listener, it won't work * otherwise, see below. * * <P>You can also use the universal factory for other listeners: * <pre> * public class Observer implements SomeListener { * private void registerTo(Source source) { * source.addSomeListener((SomeListener){@link * #create(java.lang.Class, java.util.EventListener, java.lang.Object) * WeakListener.create} ( * SomeListener.class, this, source)); * } * * public void someEventHappened(SomeEvent e) { * doSomething(); * } * } * </pre> * * <H2>How to <font color=red>not</font> use it:</H2> * Here are examples of a common mistakes done when using <code>WeakListener</code>: * <pre> * public class Observer { * private void registerTo(Source source) { * source.addChangeListener(WeakListener.change(<b>new Listener()</b>, source)); * } * * private class Listener implements ChangeListener { * public void stateChanged(ChangeEvent e) { * doSomething(); * } * } * } * </pre> * Mistake: There is nobody holding strong reference to the Listener instance, * so it may be freed on the next GC cycle. * * <BR><pre> * public class ListenerObserver implements ChangeListener { * private void registerTo(Source source) { * source.addChangeListener(WeakListener.change(this, <b>null</b>)); * } * * public void stateChanged(ChangeEvent e) { * doSomething(); * } * } * </pre> * Mistake: The WeakListener is unable to unregister itself from the source * once the listener is freed. For explanation, read below. * <H2>How does it work:</H2> * <P>The <code>WeakListener</code> is used as a reference-weakening wrapper * around the listener. It is itself strongly referenced from the implementation * of the source (e.g. from its <code>EventListenerList</code>) but it references * the listener only through <code>WeakReference</code>. It also weak-references * the source. Listener, on the other hand, usually strongly references * the observer (typically through the outer class reference). * * This means that: <OL> * <LI>If the listener is not strong-referenced from elsewhere, it can be * thrown away on the next GC cycle. This is why you can't use * <code>WeakListener.change(new MyListener(), ..)</code> as the only reference * to the listener will be the weak one from the WeakListener. * <LI>If the listener-observer pair is not strong-referenced from elsewhere * it can be thrown away on the next GC cycle. This is what the * <code>WeakListener</code> was invented for. * <LI>If the source is not strong-referenced from anywhere, it can be * thrown away on the next GC cycle taking the WeakListener with it, * but not the listener and the observer if they are still strong-referenced * (unusual case, but possible). * </OL> * * <P>Now what happens when the listener/observer is removed from memory:<UL> * <LI>The WeakListener is notified that the reference to the listener was cleared. * <LI>It tries to unregister itself from the source. This is why it needs * the reference to the source for the registration. The unregistration * is done using reflection, usually looking up the method * <code>remove<listenerType></code> of the source and calling it. * </UL> * * <P>This may fail if the source don't have the expected <code>remove*</code> * method and/or if you provide wrong reference to source. In that case * the WeakListener instance will stay in memory and registered by the source, * while the listener and observer will be freed. * * <P>There is still one fallback method - if some event come to a WeakListener * and the listener is already freed, the WeakListener tries to unregister * itself from the object the event came from. * * @author Jaroslav Tulach */ public abstract class WeakListener implements java.util.EventListener { /** weak reference to listener */ private Reference ref; /** class of the listener */ Class listenerClass; /** weak reference to source */ private Reference source; /** * @param listenerClass class/interface of the listener * @param l listener to delegate to, <code>l</code> must be an instance of * listenerClass */ protected WeakListener (Class listenerClass, java.util.EventListener l) { this.listenerClass = listenerClass; ref = new ListenerReference (l, this); if (!listenerClass.isAssignableFrom(l.getClass())) { throw new IllegalArgumentException(getClass().getName() + " constructor is calling WeakListner.<init> with illegal arguments"); // NOI18N } } /** Setter for the source field. If a WeakReference to an underlying listener is * cleared and enqueued, that is, the original listener is garbage collected, * then the source field is used for deregistration of this WeakListener, thus making * it eligible for garbage collection if no more references exist. * * This method is particularly useful in cases where the underlying listener was * garbage collected and the event source, on which this listener is listening on, * is quiet, i.e. does not fire any events for long periods. In this case, this listener * is not removed from the event source until an event is fired. If the source field is * set however, WeakListeners that lost their underlying listeners are removed * as soon as the ReferenceQueue notifies the WeakListener. * * @param source is any Object or <code>null</code>, though only setting an object * that has an appropriate remove*listenerClass*Listener method and on which this listener is listening on, * is useful. */ protected final void setSource (Object source) { if (source == null) { this.source = null; } else { this.source = new WeakReference (source); } } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected abstract String removeMethodName (); /** Getter for the target listener. * @param ev the event the we want to distribute * @return null if there is no listener because it has been finalized */ protected final java.util.EventListener get (java.util.EventObject ev) { Object l = ref.get (); // get the consumer // if the event consumer is gone, unregister us from the event producer if (l == null) removeListener (ev == null ? null : ev.getSource ()); return (EventListener)l; } /** Tries to find a remove method and invoke it. * It tries unregister itself from the registered source first * and then from the passed eventSource if they are not same. * * @param eventSource the source object to unregister from or null */ private void removeListener (Object eventSource) { Object[] params = new Object[] { getImplementator() }; Object src = source == null ? null : source.get(); try { Method m = null; if (src != null) { m = getRemoveMethod(src); if (m != null) m.invoke (src, params); } if (eventSource != src && eventSource != null) { m = getRemoveMethod(eventSource); if (m != null) m.invoke (eventSource, params); } if (m == null && source == null) { // can't remove the listener ErrorManager.getDefault().log (ErrorManager.WARNING, "Can't remove " + listenerClass.getName() + "source=" + source + ", src=" + src + ", eventSource=" + eventSource); } } catch (Exception ex) { // from invoke(), should not happen ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ex); } } /* can return null */ private final Method getRemoveMethod(Object src) { final Class[] clarray = new Class[] { listenerClass }; String methodName = removeMethodName(); Class methodClass = src.getClass(); Method m = null; try { m = methodClass.getMethod(methodName, clarray); } catch (NoSuchMethodException e) { do { try { m = methodClass.getDeclaredMethod(methodName, clarray); } catch (NoSuchMethodException ex) { } methodClass = methodClass.getSuperclass(); } while ((m == null) && (methodClass != Object.class)); } if (m != null && (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(m.getDeclaringClass().getModifiers()))) { m.setAccessible(true); } return m; } Object getImplementator() { return this; } public String toString () { Object listener = ref.get(); return getClass().getName() + "[" + (listener == null ? "null" : listener.getClass().getName() + "]"); } // // Methods for establishing connections // /** Creates a weak implementation of NodeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a NodeListener delegating to <CODE>l</CODE>. */ public static NodeListener node (NodeListener l, Object source) { WeakListener.Node wl = new WeakListener.Node (l); wl.setSource (source); return wl; } /** Creates a weak implementation of PropertyChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a PropertyChangeListener delegating to <CODE>l</CODE>. */ public static PropertyChangeListener propertyChange (PropertyChangeListener l, Object source) { WeakListener.PropertyChange wl = new WeakListener.PropertyChange (l); wl.setSource (source); return wl; } /** Creates a weak implementation of VetoableChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a VetoableChangeListener delegating to <CODE>l</CODE>. */ public static VetoableChangeListener vetoableChange (VetoableChangeListener l, Object source) { WeakListener.VetoableChange wl = new WeakListener.VetoableChange (l); wl.setSource (source); return wl; } /** Creates a weak implementation of FileChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a FileChangeListener delegating to <CODE>l</CODE>. */ public static FileChangeListener fileChange (FileChangeListener l, Object source) { WeakListener.FileChange wl = new WeakListener.FileChange (l); wl.setSource (source); return wl; } /** Creates a weak implementation of FileStatusListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a FileStatusListener delegating to <CODE>l</CODE>. */ public static FileStatusListener fileStatus (FileStatusListener l, Object source) { WeakListener.FileStatus wl = new WeakListener.FileStatus (l); wl.setSource (source); return wl; } /** Creates a weak implementation of RepositoryListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a RepositoryListener delegating to <CODE>l</CODE>. */ public static RepositoryListener repository (RepositoryListener l, Object source) { WeakListener.Repository wl = new WeakListener.Repository (l); wl.setSource (source); return wl; } /** Creates a weak implementation of DocumentListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a DocumentListener delegating to <CODE>l</CODE>. */ public static DocumentListener document (DocumentListener l, Object source) { WeakListener.Document wl = new WeakListener.Document (l); wl.setSource (source); return wl; } /** Creates a weak implementation of ChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a ChangeListener delegating to <CODE>l</CODE>. */ public static ChangeListener change (ChangeListener l, Object source) { WeakListener.Change wl = new WeakListener.Change (l); wl.setSource (source); return wl; } /** Creates a weak implementation of FocusListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a FocusListener delegating to <CODE>l</CODE>. */ public static FocusListener focus (FocusListener l, Object source) { WeakListener.Focus wl = new WeakListener.Focus (l); wl.setSource (source); return wl; } /** Creates a weak implementation of OperationListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return a OperationListener delegating to <CODE>l</CODE>. */ public static OperationListener operation (OperationListener l, Object source) { WeakListener.Operation wl = new WeakListener.Operation (l); wl.setSource (source); return wl; } /** A generic WeakListener factory. * Creates a weak implementation of a listener of type <CODE>lType</CODE>. * * @param lType the type of listener to create. It can be any interface, * but only interfaces are allowed. * @param l the listener to delegate to, <CODE>l</CODE> must be an instance * of <CODE>lType</CODE> * @param source the source that the listener should detach from when * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE> * @return an instance of <CODE>lType</CODE> delegating all the interface * calls to <CODE>l</CODE>. */ public static EventListener create (Class lType, EventListener l, Object source) { ProxyListener pl = new ProxyListener (lType, l); pl.setSource (source); return (EventListener)pl.proxy; } /** Weak property change listener * @deprecated use appropriate method instead */ public static class PropertyChange extends WeakListener implements PropertyChangeListener { /** Constructor. * @param l listener to delegate to */ public PropertyChange (PropertyChangeListener l) { super (PropertyChangeListener.class, l); } /** Constructor. * @param clazz required class * @param l listener to delegate to */ PropertyChange (Class clazz, PropertyChangeListener l) { super (clazz, l); } /** Tests if the object we reference to still exists and * if so, delegate to it. Otherwise remove from the source * if it has removePropertyChangeListener method. */ public void propertyChange (PropertyChangeEvent ev) { PropertyChangeListener l = (PropertyChangeListener)super.get (ev); if (l != null) l.propertyChange (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removePropertyChangeListener"; // NOI18N } } /** Weak vetoable change listener * @deprecated use appropriate method instead */ public static class VetoableChange extends WeakListener implements VetoableChangeListener { /** Constructor. * @param l listener to delegate to */ public VetoableChange (VetoableChangeListener l) { super (VetoableChangeListener.class, l); } /** Tests if the object we reference to still exists and * if so, delegate to it. Otherwise remove from the source * if it has removePropertyChangeListener method. */ public void vetoableChange (PropertyChangeEvent ev) throws PropertyVetoException { VetoableChangeListener l = (VetoableChangeListener)super.get (ev); if (l != null) l.vetoableChange (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeVetoableChangeListener"; // NOI18N } } /** Weak file change listener. * @deprecated use appropriate method instead */ public static class FileChange extends WeakListener implements FileChangeListener { /** Constructor. * @param l listener to delegate to */ public FileChange (FileChangeListener l) { super (FileChangeListener.class, l); } /** Fired when a new folder has been created. This action can only be * listened in folders containing the created file up to the root of * file system. * * @param ev the event describing context where action has taken place */ public void fileFolderCreated (FileEvent ev) { FileChangeListener l = (FileChangeListener)super.get (ev); if (l != null) l.fileFolderCreated (ev); } /** Fired when a new file has been created. This action can only be * listened in folders containing the created file up to the root of * file system. * * @param ev the event describing context where action has taken place */ public void fileDataCreated (FileEvent ev) { FileChangeListener l = (FileChangeListener)super.get (ev); if (l != null) l.fileDataCreated (ev); } /** Fired when a file has been changed. * @param ev the event describing context where action has taken place */ public void fileChanged (FileEvent ev) { FileChangeListener l = (FileChangeListener)super.get (ev); if (l != null) l.fileChanged (ev); } /** Fired when a file has been deleted. * @param ev the event describing context where action has taken place */ public void fileDeleted (FileEvent ev) { FileChangeListener l = (FileChangeListener)super.get (ev); if (l != null) l.fileDeleted (ev); } /** Fired when a file has been renamed. * @param ev the event describing context where action has taken place * and the original name and extension. */ public void fileRenamed (FileRenameEvent ev) { FileChangeListener l = (FileChangeListener)super.get (ev); if (l != null) l.fileRenamed (ev); } /** Fired when a file attribute has been changed. * @param ev the event describing context where action has taken place, * the name of attribute and old and new value. */ public void fileAttributeChanged (FileAttributeEvent ev) { FileChangeListener l = (FileChangeListener)super.get (ev); if (l != null) l.fileAttributeChanged (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeFileChangeListener"; // NOI18N } } /** Weak file status listener. * @deprecated use appropriate method instead */ public static class FileStatus extends WeakListener implements FileStatusListener { /** Constructor. */ public FileStatus (FileStatusListener l) { super (FileStatusListener.class, l); } /** Notifies listener about change in annotataion of a few files. * @param ev event describing the change */ public void annotationChanged(FileStatusEvent ev) { FileStatusListener l = (FileStatusListener)super.get (ev); if (l != null) l.annotationChanged (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeFileStatusListener"; // NOI18N } } /** Weak file system pool listener. * @deprecated use appropriate method instead */ public static class Repository extends WeakListener implements RepositoryListener { /** Constructor. * @param l listener to delegate to */ public Repository (RepositoryListener l) { super (RepositoryListener.class, l); } /** Called when new file system is added to the pool. * @param ev event describing the action */ public void fileSystemAdded (RepositoryEvent ev) { RepositoryListener l = (RepositoryListener)super.get (ev); if (l != null) l.fileSystemAdded (ev); } /** Called when a file system is deleted from the pool. * @param ev event describing the action */ public void fileSystemRemoved (RepositoryEvent ev) { RepositoryListener l = (RepositoryListener)super.get (ev); if (l != null) l.fileSystemRemoved (ev); } /** Called when a Repository is reordered. */ public void fileSystemPoolReordered(RepositoryReorderedEvent ev) { RepositoryListener l = (RepositoryListener)super.get (ev); if (l != null) l.fileSystemPoolReordered (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeRepositoryListener"; // NOI18N } } /** Weak document modifications listener. * This class if final only for performance reasons, * can be happily unfinaled if desired. * @deprecated use appropriate method instead */ public static final class Document extends WeakListener implements DocumentListener { /** Constructor. * @param l listener to delegate to */ public Document (final DocumentListener l) { super (DocumentListener.class, l); } /** Gives notification that an attribute or set of attributes changed. * @param ev event describing the action */ public void changedUpdate(DocumentEvent ev) { final DocumentListener l = docGet(ev); if (l != null) l.changedUpdate(ev); } /** Gives notification that there was an insert into the document. * @param ev event describing the action */ public void insertUpdate(DocumentEvent ev) { final DocumentListener l = docGet(ev); if (l != null) l.insertUpdate(ev); } /** Gives notification that a portion of the document has been removed. * @param ev event describing the action */ public void removeUpdate(DocumentEvent ev) { final DocumentListener l = docGet(ev); if (l != null) l.removeUpdate(ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeDocumentListener"; // NOI18N } /** Getter for the target listener. * @param event the event the we want to distribute * @return null if there is no listener because it has been finalized */ private DocumentListener docGet (DocumentEvent ev) { DocumentListener l = (DocumentListener)super.ref.get (); if (l == null) super.removeListener (ev.getDocument()); return l; } } // end of Document inner class /** Weak swing change listener. * This class if final only for performance reasons, * can be happily unfinaled if desired. * @deprecated use appropriate method instead */ public static final class Change extends WeakListener implements ChangeListener { /** Constructor. * @param l listener to delegate to */ public Change (ChangeListener l) { super (ChangeListener.class, l); } /** Called when new file system is added to the pool. * @param ev event describing the action */ public void stateChanged (final ChangeEvent ev) { ChangeListener l = (ChangeListener)super.get(ev); if (l != null) l.stateChanged (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeChangeListener"; // NOI18N } } /** Weak version of listener for changes in one node. * This class if final only for performance reasons, * can be happily unfinaled if desired. * @deprecated use appropriate method instead */ public static final class Node extends WeakListener.PropertyChange implements NodeListener { /** Constructor. * @param l listener to delegate to */ public Node (NodeListener l) { super (NodeListener.class, l); } /** Delegates to the original listener. */ public void childrenAdded (NodeMemberEvent ev) { NodeListener l = (NodeListener)super.get (ev); if (l != null) l.childrenAdded (ev); } /** Delegates to the original listener. */ public void childrenRemoved (NodeMemberEvent ev) { NodeListener l = (NodeListener)super.get (ev); if (l != null) l.childrenRemoved (ev); } /** Delegates to the original listener. */ public void childrenReordered (NodeReorderEvent ev) { NodeListener l = (NodeListener)super.get (ev); if (l != null) l.childrenReordered (ev); } /** Delegates to the original listener. */ public void nodeDestroyed (NodeEvent ev) { NodeListener l = (NodeListener)super.get (ev); if (l != null) l.nodeDestroyed (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeNodeListener"; // NOI18N } } /** Weak version of focus listener. * This class if final only for performance reasons, * can be happily unfinaled if desired. * @deprecated use appropriate method instead */ public static final class Focus extends WeakListener implements FocusListener { /** Constructor. * @param l listener to delegate to */ public Focus (FocusListener l) { super (FocusListener.class, l); } /** Delegates to the original listener. */ public void focusGained(FocusEvent ev) { FocusListener l = (FocusListener)super.get (ev); if (l != null) l.focusGained (ev); } /** Delegates to the original listener. */ public void focusLost(FocusEvent ev) { FocusListener l = (FocusListener)super.get (ev); if (l != null) l.focusLost (ev); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeFocusListener"; // NOI18N } } /** Weak property change listener */ final static class Operation extends WeakListener implements OperationListener { /** Constructor. * @param l listener to delegate to */ public Operation (OperationListener l) { super (OperationListener.class, l); } /** Method name to use for removing the listener. * @return name of method of the source object that should be used * to remove the listener from listening on source of events */ protected String removeMethodName () { return "removeOperationListener"; // NOI18N } /** Object has been recognized by * {@link DataLoaderPool#findDataObject}. * This allows listeners * to attach additional cookies, etc. * * @param ev event describing the action */ public void operationPostCreate(OperationEvent ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationPostCreate (ev); } /** Object has been successfully copied. * @param ev event describing the action */ public void operationCopy(OperationEvent.Copy ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationCopy (ev); } /** Object has been successfully moved. * @param ev event describing the action */ public void operationMove(OperationEvent.Move ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationMove (ev); } /** Object has been successfully deleted. * @param ev event describing the action */ public void operationDelete(OperationEvent ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationDelete (ev); } /** Object has been successfully renamed. * @param ev event describing the action */ public void operationRename(OperationEvent.Rename ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationRename (ev); } /** A shadow of a data object has been created. * @param ev event describing the action */ public void operationCreateShadow (OperationEvent.Copy ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationCreateShadow (ev); } /** New instance of an object has been created. * @param ev event describing the action */ public void operationCreateFromTemplate(OperationEvent.Copy ev) { OperationListener l = (OperationListener)super.get (ev); if (l != null) l.operationCreateFromTemplate (ev); } } /** Proxy interface that delegates to listeners. */ private static class ProxyListener extends WeakListener implements InvocationHandler { /** proxy generated for this listener */ public final Object proxy; /** Equals method */ private static Method equalsMth; /** */ private static Method getEquals() { if (equalsMth == null) { try { equalsMth = Object.class.getMethod("equals", new Class[] { Object.class }); // NOI18N } catch (NoSuchMethodException e) { e.printStackTrace(); } } return equalsMth; } /** @param listener listener to delegate to */ public ProxyListener (Class c, java.util.EventListener listener) { super (c, listener); proxy = Proxy.newProxyInstance ( c.getClassLoader (), new Class[] { c }, this ); } public java.lang.Object invoke ( Object proxy, Method method, Object[] args ) throws Throwable { if (method.getDeclaringClass () == Object.class) { // a method from object => call it on your self if (method == getEquals()) { boolean ret = equals(args[0]); return (ret ? Boolean.TRUE : Boolean.FALSE); } return method.invoke (this, args); } // listeners method EventObject ev = args[0] instanceof EventObject ? (EventObject)args[0] : null; Object listener = super.get (ev); if (listener != null) { return method.invoke (listener, args); } else { return null; } } /** Remove method name is composed from the name of the listener. */ protected String removeMethodName () { String name = listenerClass.getName (); // strip package name int dot = name.lastIndexOf('.'); name = name.substring (dot + 1); // in case of inner interfaces/classes we also strip the outer // class' name int i = name.lastIndexOf('$'); // NOI18N if (i >= 0) { name = name.substring(i + 1); } return "remove".concat(name); // NOI18N } /** To string prints class. */ public String toString () { return super.toString () + "[" + listenerClass + "]"; // NOI18N } /** Equal is extended to equal also with proxy object. */ public boolean equals (Object obj) { return proxy == obj || this == obj; } Object getImplementator() { return proxy; } } /** Reference that also holds ref to WeakListener. */ private static final class ListenerReference extends WeakReference implements Runnable { private static Class lastClass; private static String lastMethodName; private static Method lastRemove; private static Object LOCK = new Object (); final WeakListener weakListener; public ListenerReference ( Object ref, WeakListener weakListener ) { super (ref, Utilities.activeReferenceQueue()); this.weakListener = weakListener; } public void run () { ListenerReference lr = this; // prepare array for passing arguments to getMethod/invoke Object[] params = new Object[1]; Class[] types = new Class[1]; Object src = null; // On whom we're listening Method remove = null; WeakListener ref = lr.weakListener; if (ref.source == null || (src = ref.source.get()) == null) return; Class methodClass = src.getClass(); String methodName = ref.removeMethodName(); synchronized (LOCK) { if (lastClass == methodClass && lastMethodName == methodName && lastRemove != null) { remove = lastRemove; } } // get the remove method or use the last one if (remove == null) { types[0] = ref.listenerClass; remove = null; try { remove = methodClass.getMethod(methodName, types); } catch (NoSuchMethodException e) { for (;;) { methodClass = methodClass.getSuperclass (); if (methodClass == null) break; try { remove = methodClass.getDeclaredMethod(methodName, types); break; } catch (NoSuchMethodException ex) { } } } if (remove == null) { ErrorManager.getDefault().log (ErrorManager.WARNING, "Can't remove " + ref.listenerClass.getName() + "from " + src); // NOI18N return; } else { if ( !Modifier.isPublic(remove.getModifiers()) || !Modifier.isPublic(remove.getDeclaringClass().getModifiers()) ) { remove.setAccessible(true); } synchronized (LOCK) { lastClass = methodClass; lastMethodName = methodName; lastRemove = remove; } } } else { // already resolved if (remove == null) return; // there was no such method } params[0] = ref.getImplementator(); // Whom to unregister try { remove.invoke (src, params); } catch (Exception ex) { // from invoke(), should not happen ErrorManager.getDefault().annotate(ex, "Problem encountered while calling " + methodClass + "." + methodName + "(...) on " + src); // NOI18N ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } } } }