001: /*
002: * Copyright (C) 2004 NNL Technology AB
003: * Visit www.infonode.net for information about InfoNode(R)
004: * products and how to contact NNL Technology AB.
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
019: * MA 02111-1307, USA.
020: */
021:
022: // $Id: PropertyMapWeakListenerManager.java,v 1.8 2005/03/09 16:57:26 jesper Exp $
023: package net.infonode.properties.propertymap;
024:
025: import net.infonode.properties.base.Property;
026: import net.infonode.properties.util.PropertyChangeListener;
027:
028: import javax.swing.*;
029: import java.lang.ref.ReferenceQueue;
030: import java.lang.ref.WeakReference;
031: import java.lang.reflect.InvocationTargetException;
032: import java.util.ArrayList;
033: import java.util.Map;
034: import java.util.WeakHashMap;
035:
036: /**
037: * Handles weak {@link PropertyMap} listeners which are garbage collected and removed from the {@link PropertyMap}
038: * object on which it listens when there are no strong or soft references to the listeners.
039: *
040: * @author $Author: jesper $
041: * @version $Revision: 1.8 $
042: * @since IDW 1.2.0
043: */
044: public class PropertyMapWeakListenerManager {
045: private PropertyMapWeakListenerManager() {
046: }
047:
048: private static class ListenerRef extends WeakReference {
049: private PropertyMap map;
050:
051: protected ListenerRef(Object referent, ReferenceQueue q,
052: PropertyMap map) {
053: super (referent, q);
054: this .map = map;
055: }
056:
057: public PropertyMap getMap() {
058: return map;
059: }
060:
061: public void removeFromMap() {
062: map = null;
063: }
064:
065: }
066:
067: private static class MapListenerRef extends ListenerRef implements
068: PropertyMapListener {
069: MapListenerRef(PropertyMapListener referent, ReferenceQueue q,
070: PropertyMap map) {
071: super (referent, q, map);
072: map.addListener(this );
073: }
074:
075: public void removeFromMap() {
076: getMap().removeListener(this );
077: super .removeFromMap();
078: }
079:
080: public void propertyValuesChanged(PropertyMap propertyMap,
081: Map changes) {
082: PropertyMapListener l = (PropertyMapListener) get();
083:
084: if (l != null)
085: l.propertyValuesChanged(propertyMap, changes);
086: }
087: }
088:
089: private static class PropertyChangeListenerRef extends ListenerRef
090: implements PropertyChangeListener {
091: private Property property;
092:
093: PropertyChangeListenerRef(PropertyChangeListener referent,
094: ReferenceQueue q, PropertyMap map, Property property) {
095: super (referent, q, map);
096: this .property = property;
097: map.addPropertyChangeListener(property, this );
098: }
099:
100: public Property getProperty() {
101: return property;
102: }
103:
104: public void removeFromMap() {
105: getMap().removePropertyChangeListener(property, this );
106: super .removeFromMap();
107: }
108:
109: public void propertyChanged(Property property,
110: Object valueContainer, Object oldValue, Object newValue) {
111: PropertyChangeListener l = (PropertyChangeListener) get();
112:
113: if (l != null)
114: l.propertyChanged(property, valueContainer, oldValue,
115: newValue);
116: }
117: }
118:
119: private static class TreeListenerRef extends ListenerRef implements
120: PropertyMapTreeListener {
121: TreeListenerRef(PropertyMapTreeListener referent,
122: ReferenceQueue q, PropertyMap map) {
123: super (referent, q, map);
124: map.addTreeListener(this );
125: }
126:
127: public void removeFromMap() {
128: getMap().removeTreeListener(this );
129: super .removeFromMap();
130: }
131:
132: public void propertyValuesChanged(Map changes) {
133: PropertyMapTreeListener l = (PropertyMapTreeListener) get();
134:
135: if (l != null)
136: l.propertyValuesChanged(changes);
137: }
138: }
139:
140: private static WeakHashMap listenerMap = new WeakHashMap();
141: private static WeakHashMap propertyChangeListenerMap = new WeakHashMap();
142: private static WeakHashMap treeListenerMap = new WeakHashMap();
143: private static ReferenceQueue refQueue = new ReferenceQueue();
144:
145: private static ListenerRef ref;
146: private static Runnable refRemover = new Runnable() {
147: public void run() {
148: while (ref != null) {
149: ref.removeFromMap();
150: ref = (ListenerRef) refQueue.poll();
151: }
152: }
153: };
154:
155: static {
156: Thread thread = new Thread(new Runnable() {
157: public void run() {
158: try {
159: while (true) {
160: ref = (ListenerRef) refQueue.remove();
161: SwingUtilities.invokeAndWait(refRemover);
162: }
163: } catch (InterruptedException e) {
164: } catch (InvocationTargetException e) {
165: }
166: }
167: });
168: thread.setDaemon(true);
169: thread.start();
170: }
171:
172: private static void addToMap(WeakHashMap map, Object key,
173: Object value) {
174: ArrayList l = (ArrayList) map.get(key);
175:
176: if (l == null) {
177: l = new ArrayList(2);
178: map.put(key, l);
179: }
180:
181: l.add(value);
182: }
183:
184: private static void removeFromMap(WeakHashMap map, Object key,
185: PropertyMap propertyMap) {
186: ArrayList l = (ArrayList) map.get(key);
187:
188: if (l != null) {
189: for (int i = 0; i < l.size(); i++) {
190: ListenerRef ref = (ListenerRef) l.get(i);
191:
192: if (ref.getMap() == propertyMap) {
193: ref.removeFromMap();
194: l.remove(i);
195:
196: if (l.size() == 0) {
197: map.remove(key);
198: }
199:
200: return;
201: }
202: }
203: }
204: }
205:
206: private static void removeFromMap(WeakHashMap map, Object key,
207: PropertyMap propertyMap, Property property) {
208: ArrayList l = (ArrayList) map.get(key);
209:
210: if (l != null) {
211: for (int i = 0; i < l.size(); i++) {
212: PropertyChangeListenerRef ref = (PropertyChangeListenerRef) l
213: .get(i);
214:
215: if (ref.getMap() == propertyMap
216: && ref.getProperty() == property) {
217: ref.removeFromMap();
218: l.remove(i);
219:
220: if (l.size() == 0) {
221: map.remove(key);
222: }
223:
224: return;
225: }
226: }
227: }
228: }
229:
230: /**
231: * Adds a weak listener to a {@link PropertyMap}.
232: *
233: * @param map the {@link PropertyMap}
234: * @param listener the listener
235: */
236: public static void addWeakListener(PropertyMap map,
237: PropertyMapListener listener) {
238: MapListenerRef l = new MapListenerRef(listener, refQueue, map);
239: addToMap(listenerMap, listener, l);
240: }
241:
242: /**
243: * Adds a weak property change listener to a {@link PropertyMap}.
244: *
245: * @param map the {@link PropertyMap}
246: * @param property the property to listen to changes on
247: * @param listener the listener
248: */
249: public static void addWeakPropertyChangeListener(PropertyMap map,
250: Property property, PropertyChangeListener listener) {
251: PropertyChangeListenerRef l = new PropertyChangeListenerRef(
252: listener, refQueue, map, property);
253: addToMap(propertyChangeListenerMap, listener, l);
254: }
255:
256: /**
257: * Adds a weak tree listener to a {@link PropertyMap}.
258: *
259: * @param map the {@link PropertyMap}
260: * @param listener the listener
261: */
262: public static void addWeakTreeListener(PropertyMap map,
263: PropertyMapTreeListener listener) {
264: TreeListenerRef l = new TreeListenerRef(listener, refQueue, map);
265: addToMap(treeListenerMap, listener, l);
266: }
267:
268: /**
269: * Removes a listener previously added with {@link #addWeakListener(PropertyMap, PropertyMapListener)}.
270: *
271: * @param map the map on which the listener was added
272: * @param listener the listener
273: */
274: public static void removeWeakListener(PropertyMap map,
275: PropertyMapListener listener) {
276: removeFromMap(listenerMap, listener, map);
277: }
278:
279: /**
280: * Removes a listener previously added with
281: * {@link #addWeakPropertyChangeListener(PropertyMap, net.infonode.properties.base.Property, net.infonode.properties.util.PropertyChangeListener)}.
282: *
283: * @param map the map on which the listener was added
284: * @param property the property on which the listener listens to changes
285: * @param listener the listener
286: */
287: public static void removeWeakPropertyChangeListener(
288: PropertyMap map, Property property,
289: PropertyChangeListener listener) {
290: removeFromMap(propertyChangeListenerMap, listener, map,
291: property);
292: }
293:
294: /**
295: * Removes a listener previously added with {@link #addWeakTreeListener(PropertyMap, PropertyMapTreeListener)}.
296: *
297: * @param map the map on which the listener was added
298: * @param listener the listener
299: */
300: public static void removeWeakTreeListener(PropertyMap map,
301: PropertyMapTreeListener listener) {
302: removeFromMap(treeListenerMap, listener, map);
303: }
304:
305: }
|