/* * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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; } } }