1 /*
2  * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package javax.swing;
26 
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.io.*;
30 import java.lang.ref.WeakReference;
31 import java.lang.ref.ReferenceQueue;
32 
33 /**
34  * A package-private PropertyChangeListener which listens for
35  * property changes on an Action and updates the properties
36  * of an ActionEvent source.
37  * <p>
38  * Subclasses must override the actionPropertyChanged method,
39  * which is invoked from the propertyChange method as long as
40  * the target is still valid.
41  * </p>
42  * <p>
43  * WARNING WARNING WARNING WARNING WARNING WARNING:<br>
44  * Do NOT create an annonymous inner class that extends this!  If you do
45  * a strong reference will be held to the containing class, which in most
46  * cases defeats the purpose of this class.
47  *
48  * @param <T> the type of JComponent the underlying Action is attached to
49  *
50  * @author Georges Saab
51  * @see AbstractButton
52  */
53 @SuppressWarnings("serial") // Bound of type variable  is not serializable across versions
54 abstract class ActionPropertyChangeListener<T extends JComponent>
55         implements PropertyChangeListener, Serializable {
56     private static ReferenceQueue<JComponent> queue;
57 
58     // WeakReference's aren't serializable.
59     private transient OwnedWeakReference<T> target;
60     // The Component's that reference an Action do so through a strong
61     // reference, so that there is no need to check for serialized.
62     private Action action;
63 
getQueue()64     private static ReferenceQueue<JComponent> getQueue() {
65         synchronized(ActionPropertyChangeListener.class) {
66             if (queue == null) {
67                 queue = new ReferenceQueue<JComponent>();
68             }
69         }
70         return queue;
71     }
72 
ActionPropertyChangeListener(T c, Action a)73     public ActionPropertyChangeListener(T c, Action a) {
74         super();
75         setTarget(c);
76         this.action = a;
77     }
78 
79     /**
80      * PropertyChangeListener method.  If the target has been gc'ed this
81      * will remove the <code>PropertyChangeListener</code> from the Action,
82      * otherwise this will invoke actionPropertyChanged.
83      */
propertyChange(PropertyChangeEvent e)84     public final void propertyChange(PropertyChangeEvent e) {
85         T target = getTarget();
86         if (target == null) {
87             getAction().removePropertyChangeListener(this);
88         } else {
89             actionPropertyChanged(target, getAction(), e);
90         }
91     }
92 
93     /**
94      * Invoked when a property changes on the Action and the target
95      * still exists.
96      */
actionPropertyChanged(T target, Action action, PropertyChangeEvent e)97     protected abstract void actionPropertyChanged(T target, Action action,
98                                                   PropertyChangeEvent e);
99 
setTarget(T c)100     private void setTarget(T c) {
101         ReferenceQueue<JComponent> queue = getQueue();
102         // Check to see whether any old buttons have
103         // been enqueued for GC.  If so, look up their
104         // PCL instance and remove it from its Action.
105         OwnedWeakReference<?> r;
106         while ((r = (OwnedWeakReference)queue.poll()) != null) {
107             ActionPropertyChangeListener<?> oldPCL = r.getOwner();
108             Action oldAction = oldPCL.getAction();
109             if (oldAction!=null) {
110                 oldAction.removePropertyChangeListener(oldPCL);
111             }
112         }
113         this.target = new OwnedWeakReference<T>(c, queue, this);
114     }
115 
getTarget()116     public T getTarget() {
117         if (target == null) {
118             // Will only happen if serialized and real target was null
119             return null;
120         }
121         return this.target.get();
122     }
123 
getAction()124     public Action getAction() {
125           return action;
126     }
127 
writeObject(ObjectOutputStream s)128     private void writeObject(ObjectOutputStream s) throws IOException {
129         s.defaultWriteObject();
130         s.writeObject(getTarget());
131     }
132 
133     @SuppressWarnings("unchecked")
readObject(ObjectInputStream s)134     private void readObject(ObjectInputStream s)
135                      throws IOException, ClassNotFoundException {
136         s.defaultReadObject();
137         T target = (T)s.readObject();
138         if (target != null) {
139             setTarget(target);
140         }
141     }
142 
143 
144     private static class OwnedWeakReference<U extends JComponent> extends
145                               WeakReference<U> {
146         private ActionPropertyChangeListener<?> owner;
147 
OwnedWeakReference(U target, ReferenceQueue<? super U> queue, ActionPropertyChangeListener<?> owner)148         OwnedWeakReference(U target, ReferenceQueue<? super U> queue,
149                            ActionPropertyChangeListener<?> owner) {
150             super(target, queue);
151             this.owner = owner;
152         }
153 
getOwner()154         public ActionPropertyChangeListener<?> getOwner() {
155             return owner;
156         }
157     }
158 }
159