1 /*
2 * Copyright 1999-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 abstract class ActionPropertyChangeListener<T extends JComponent>
54 implements PropertyChangeListener, Serializable {
55 private static ReferenceQueue<JComponent> queue;
56
57 // WeakReference's aren't serializable.
58 private transient OwnedWeakReference<T> target;
59 // The Component's that reference an Action do so through a strong
60 // reference, so that there is no need to check for serialized.
61 private Action action;
62
63 private static ReferenceQueue<JComponent> getQueue() {
64 synchronized(ActionPropertyChangeListener.class) {
65 if (queue == null) {
66 queue = new ReferenceQueue<JComponent>();
67 }
68 }
69 return queue;
70 }
71
72 public ActionPropertyChangeListener(T c, Action a) {
73 super();
74 setTarget(c);
75 this.action = a;
76 }
77
78 /**
79 * PropertyChangeListener method. If the target has been gc'ed this
80 * will remove the <code>PropertyChangeListener</code> from the Action,
81 * otherwise this will invoke actionPropertyChanged.
82 */
83 public final void propertyChange(PropertyChangeEvent e) {
84 T target = getTarget();
85 if (target == null) {
86 getAction().removePropertyChangeListener(this);
87 } else {
88 actionPropertyChanged(target, getAction(), e);
89 }
90 }
91
92 /**
93 * Invoked when a property changes on the Action and the target
94 * still exists.
95 */
96 protected abstract void actionPropertyChanged(T target, Action action,
97 PropertyChangeEvent e);
98
99 private void setTarget(T c) {
100 ReferenceQueue<JComponent> queue = getQueue();
101 // Check to see whether any old buttons have
102 // been enqueued for GC. If so, look up their
103 // PCL instance and remove it from its Action.
104 OwnedWeakReference r;
105 while ((r = (OwnedWeakReference)queue.poll()) != null) {
106 ActionPropertyChangeListener oldPCL = r.getOwner();
107 Action oldAction = oldPCL.getAction();
108 if (oldAction!=null) {
109 oldAction.removePropertyChangeListener(oldPCL);
110 }
111 }
112 this.target = new OwnedWeakReference<T>(c, queue, this);
113 }
114
115 public T getTarget() {
116 if (target == null) {
117 // Will only happen if serialized and real target was null
118 return null;
119 }
120 return this.target.get();
121 }
122
123 public Action getAction() {
124 return action;
125 }
126
127 private void writeObject(ObjectOutputStream s) throws IOException {
128 s.defaultWriteObject();
129 s.writeObject(getTarget());
130 }
131
132 @SuppressWarnings("unchecked")
133 private void readObject(ObjectInputStream s)
134 throws IOException, ClassNotFoundException {
135 s.defaultReadObject();
136 T target = (T)s.readObject();
137 if (target != null) {
138 setTarget(target);
139 }
140 }
141
142
143 private static class OwnedWeakReference<U extends JComponent> extends
144 WeakReference<U> {
145 private ActionPropertyChangeListener owner;
146
147 OwnedWeakReference(U target, ReferenceQueue<? super U> queue,
148 ActionPropertyChangeListener owner) {
149 super(target, queue);
150 this.owner = owner;
151 }
152
153 public ActionPropertyChangeListener getOwner() {
154 return owner;
155 }
156 }
157 }