001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 2006 JBoss Inc. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.scopedpool;
017:
018: import java.lang.ref.ReferenceQueue;
019: import java.lang.ref.SoftReference;
020: import java.util.AbstractMap;
021: import java.util.HashMap;
022: import java.util.Map;
023: import java.util.Set;
024:
025: /**
026: * This Map will remove entries when the value in the map has been cleaned from
027: * garbage collection
028: *
029: * @version <tt>$Revision: 1.3 $</tt>
030: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
031: */
032: public class SoftValueHashMap extends AbstractMap implements Map {
033: private static class SoftValueRef extends SoftReference {
034: public Object key;
035:
036: private SoftValueRef(Object key, Object val, ReferenceQueue q) {
037: super (val, q);
038: this .key = key;
039: }
040:
041: private static SoftValueRef create(Object key, Object val,
042: ReferenceQueue q) {
043: if (val == null)
044: return null;
045: else
046: return new SoftValueRef(key, val, q);
047: }
048:
049: }
050:
051: /**
052: * Returns a set of the mappings contained in this hash table.
053: */
054: public Set entrySet() {
055: processQueue();
056: return hash.entrySet();
057: }
058:
059: /* Hash table mapping WeakKeys to values */
060: private Map hash;
061:
062: /* Reference queue for cleared WeakKeys */
063: private ReferenceQueue queue = new ReferenceQueue();
064:
065: /*
066: * Remove all invalidated entries from the map, that is, remove all entries
067: * whose values have been discarded.
068: */
069: private void processQueue() {
070: SoftValueRef ref;
071: while ((ref = (SoftValueRef) queue.poll()) != null) {
072: if (ref == (SoftValueRef) hash.get(ref.key)) {
073: // only remove if it is the *exact* same WeakValueRef
074: //
075: hash.remove(ref.key);
076: }
077: }
078: }
079:
080: /* -- Constructors -- */
081:
082: /**
083: * Constructs a new, empty <code>WeakHashMap</code> with the given initial
084: * capacity and the given load factor.
085: *
086: * @param initialCapacity
087: * The initial capacity of the <code>WeakHashMap</code>
088: *
089: * @param loadFactor
090: * The load factor of the <code>WeakHashMap</code>
091: *
092: * @throws IllegalArgumentException
093: * If the initial capacity is less than zero, or if the load
094: * factor is nonpositive
095: */
096: public SoftValueHashMap(int initialCapacity, float loadFactor) {
097: hash = new HashMap(initialCapacity, loadFactor);
098: }
099:
100: /**
101: * Constructs a new, empty <code>WeakHashMap</code> with the given initial
102: * capacity and the default load factor, which is <code>0.75</code>.
103: *
104: * @param initialCapacity
105: * The initial capacity of the <code>WeakHashMap</code>
106: *
107: * @throws IllegalArgumentException
108: * If the initial capacity is less than zero
109: */
110: public SoftValueHashMap(int initialCapacity) {
111: hash = new HashMap(initialCapacity);
112: }
113:
114: /**
115: * Constructs a new, empty <code>WeakHashMap</code> with the default
116: * initial capacity and the default load factor, which is <code>0.75</code>.
117: */
118: public SoftValueHashMap() {
119: hash = new HashMap();
120: }
121:
122: /**
123: * Constructs a new <code>WeakHashMap</code> with the same mappings as the
124: * specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with
125: * an initial capacity of twice the number of mappings in the specified map
126: * or 11 (whichever is greater), and a default load factor, which is
127: * <tt>0.75</tt>.
128: *
129: * @param t the map whose mappings are to be placed in this map.
130: */
131: public SoftValueHashMap(Map t) {
132: this (Math.max(2 * t.size(), 11), 0.75f);
133: putAll(t);
134: }
135:
136: /* -- Simple queries -- */
137:
138: /**
139: * Returns the number of key-value mappings in this map. <strong>Note:</strong>
140: * <em>In contrast with most implementations of the
141: * <code>Map</code> interface, the time required by this operation is
142: * linear in the size of the map.</em>
143: */
144: public int size() {
145: processQueue();
146: return hash.size();
147: }
148:
149: /**
150: * Returns <code>true</code> if this map contains no key-value mappings.
151: */
152: public boolean isEmpty() {
153: processQueue();
154: return hash.isEmpty();
155: }
156:
157: /**
158: * Returns <code>true</code> if this map contains a mapping for the
159: * specified key.
160: *
161: * @param key
162: * The key whose presence in this map is to be tested.
163: */
164: public boolean containsKey(Object key) {
165: processQueue();
166: return hash.containsKey(key);
167: }
168:
169: /* -- Lookup and modification operations -- */
170:
171: /**
172: * Returns the value to which this map maps the specified <code>key</code>.
173: * If this map does not contain a value for this key, then return
174: * <code>null</code>.
175: *
176: * @param key
177: * The key whose associated value, if any, is to be returned.
178: */
179: public Object get(Object key) {
180: processQueue();
181: SoftReference ref = (SoftReference) hash.get(key);
182: if (ref != null)
183: return ref.get();
184: return null;
185: }
186:
187: /**
188: * Updates this map so that the given <code>key</code> maps to the given
189: * <code>value</code>. If the map previously contained a mapping for
190: * <code>key</code> then that mapping is replaced and the previous value
191: * is returned.
192: *
193: * @param key
194: * The key that is to be mapped to the given <code>value</code>
195: * @param value
196: * The value to which the given <code>key</code> is to be
197: * mapped
198: *
199: * @return The previous value to which this key was mapped, or
200: * <code>null</code> if if there was no mapping for the key
201: */
202: public Object put(Object key, Object value) {
203: processQueue();
204: Object rtn = hash.put(key, SoftValueRef.create(key, value,
205: queue));
206: if (rtn != null)
207: rtn = ((SoftReference) rtn).get();
208: return rtn;
209: }
210:
211: /**
212: * Removes the mapping for the given <code>key</code> from this map, if
213: * present.
214: *
215: * @param key
216: * The key whose mapping is to be removed.
217: *
218: * @return The value to which this key was mapped, or <code>null</code> if
219: * there was no mapping for the key.
220: */
221: public Object remove(Object key) {
222: processQueue();
223: return hash.remove(key);
224: }
225:
226: /**
227: * Removes all mappings from this map.
228: */
229: public void clear() {
230: processQueue();
231: hash.clear();
232: }
233: }
|