package com.jidesoft.swing;

import javax.swing.event.ChangeEvent;
import java.awt.event.ItemEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;

/**
 * How to use this:
 * <code><pre>
 * KeyboardFocusManager focusManager =
 * KeyboardFocusManager.getCurrentKeyboardFocusManager();
 * <p/>
 * // instead of registering directly use weak listener
 * // focusManager.addPropertyChangeListener(focusOwnerListener);
 * <p/>
 * focusManager.addPropertyChangeListener(
 * new WeakPropertyChangeListener(focusOwnerListener, focusManager));
 * </pre></code>
 * <p/>
 * How does this work:
 * <p/>
 * Instead of registering propertyChangeListener directly to keyboardFocusManager, we wrap it inside
 * WeakPropertyChangeListener and register this weak listener to keyboardFocusManager. This weak listener acts a
 * delegate. It receives the propertyChangeEvents from keyboardFocusManager and delegates it the wrapped listener.
 * <p/>
 * The interesting part of this weak listener, it hold a weakReference to the original propertyChangeListener. so this
 * delegate is eligible for garbage collection which it is no longer reachable via references. When it gets garbage
 * collection, the weakReference will be pointing to null. On next propertyChangeEvent notification from
 * keyboardFocusManager, it find that the weakReference is pointing to null, and unregisters itself from
 * keyboardFocusManager. Thus the weak listener will also become eligible for garbage collection in next gc cycle.
 * <p/>
 * This concept is not something new. If you have a habit of looking into swing sources, you will find that
 * AbstractButton actually adds a weak listener to its action. The weak listener class used for this is :
 * javax.swing.AbstractActionPropertyChangeListener; This class is package-private, so you don't find it in javadoc.
 * <p/>
 * The full-fledged, generic implementation of weak listeners is available in Netbeans OpenAPI: WeakListeners.java . It
 * is worth to have a look at it.
 *
 * @author Santhosh Kumar T - santhosh@in.fiorano.com
 */
public class WeakPropertyChangeListener implements PropertyChangeListener {
    private WeakReference<PropertyChangeListener> _listenerRef;
    private Object _src;

    public WeakPropertyChangeListener(PropertyChangeListener listener, Object src) {
        _listenerRef = new WeakReference(listener);
        _src = src;
    }

    public void propertyChange(PropertyChangeEvent evt) {
        PropertyChangeListener listener = _listenerRef.get();
        if (listener == null) {
            removeListener();
        }
        else
            listener.propertyChange(evt);
    }

    public void itemStateChanged(ItemEvent e) {

    }

    public void stateChanged(ChangeEvent e) {

    }

    private void removeListener() {
        try {
            Method method = _src.getClass().getMethod("removePropertyChangeListener"
                    , new Class[]{PropertyChangeListener.class});
            method.invoke(_src, new Object[]{this});
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

