/*
 * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package javax.swing;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;

/**
 * A package-private PropertyChangeListener which listens for
 * property changes on an Action and updates the properties
 * of an ActionEvent source.
 * <p>
 * Subclasses must override the actionPropertyChanged method,
 * which is invoked from the propertyChange method as long as
 * the target is still valid.
 * </p>
 * <p>
 * WARNING WARNING WARNING WARNING WARNING WARNING:<br>
 * Do NOT create an annonymous inner class that extends this!  If you do
 * a strong reference will be held to the containing class, which in most
 * cases defeats the purpose of this class.
 *
 * @param T the type of JComponent the underlying Action is attached to
 *
 * @author Georges Saab
 * @see AbstractButton
 */
abstract class ActionPropertyChangeListener<T extends JComponent>
        implements PropertyChangeListener, Serializable {
    private static ReferenceQueue<JComponent> queue;

    // WeakReference's aren't serializable.
    private transient OwnedWeakReference<T> target;
    // The Component's that reference an Action do so through a strong
    // reference, so that there is no need to check for serialized.
    private Action action;

    private static ReferenceQueue<JComponent> getQueue() {
        synchronized(ActionPropertyChangeListener.class) {
            if (queue == null) {
                queue = new ReferenceQueue<JComponent>();
            }
        }
        return queue;
    }

    public ActionPropertyChangeListener(T c, Action a) {
        super();
        setTarget(c);
        this.action = a;
    }

    /**
     * PropertyChangeListener method.  If the target has been gc'ed this
     * will remove the <code>PropertyChangeListener</code> from the Action,
     * otherwise this will invoke actionPropertyChanged.
     */
    public final void propertyChange(PropertyChangeEvent e) {
        T target = getTarget();
        if (target == null) {
            getAction().removePropertyChangeListener(this);
        } else {
            actionPropertyChanged(target, getAction(), e);
        }
    }

    /**
     * Invoked when a property changes on the Action and the target
     * still exists.
     */
    protected abstract void actionPropertyChanged(T target, Action action,
                                                  PropertyChangeEvent e);

    private void setTarget(T c) {
        ReferenceQueue<JComponent> queue = getQueue();
        // Check to see whether any old buttons have
        // been enqueued for GC.  If so, look up their
        // PCL instance and remove it from its Action.
        OwnedWeakReference r;
        while ((r = (OwnedWeakReference)queue.poll()) != null) {
            ActionPropertyChangeListener oldPCL = r.getOwner();
            Action oldAction = oldPCL.getAction();
            if (oldAction!=null) {
                oldAction.removePropertyChangeListener(oldPCL);
            }
        }
        this.target = new OwnedWeakReference<T>(c, queue, this);
    }

    public T getTarget() {
        if (target == null) {
            // Will only happen if serialized and real target was null
            return null;
        }
        return this.target.get();
    }

    public Action getAction() {
          return action;
    }

    private void writeObject(ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        s.writeObject(getTarget());
    }

    @SuppressWarnings("unchecked")
    private void readObject(ObjectInputStream s)
                     throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        T target = (T)s.readObject();
        if (target != null) {
            setTarget(target);
        }
    }


    private static class OwnedWeakReference<U extends JComponent> extends
                              WeakReference<U> {
        private ActionPropertyChangeListener owner;

        OwnedWeakReference(U target, ReferenceQueue<? super U> queue,
                           ActionPropertyChangeListener owner) {
            super(target, queue);
            this.owner = owner;
        }

        public ActionPropertyChangeListener getOwner() {
            return owner;
        }
    }
}
