001: /**
002: * This class was taken from<br/>
003: * <a href="http://www.gruntz.ch/papers/references/SoftHashMap.java">SoftHashMap (Java Code)</a><br/>
004: * <a href="http://www.gruntz.ch/papers/references/">References API (PDF Artikel)</a><br/>
005: * by Dominik Gruntz, Fachhochschule Aargau/Nordwestschweiz<br/>
006: * @author <a href="mailto:d.gruntz@fh-aargau.ch">Dominik Gruntz</a>
007: */package junitx.ddtunit.util;
008:
009: import java.lang.ref.Reference;
010: import java.lang.ref.ReferenceQueue;
011: import java.lang.ref.SoftReference;
012: import java.util.AbstractMap;
013: import java.util.AbstractSet;
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import java.util.LinkedList;
017: import java.util.Map;
018: import java.util.NoSuchElementException;
019: import java.util.Set;
020:
021: public class SoftHashMap<K, V> extends AbstractMap<K, V> {
022: private Map<K, Reference> innerMap = null;
023:
024: private ReferenceQueue refQueue = new ReferenceQueue();
025:
026: /** The number of "hard" references to hold internally. */
027: private final int HARD_SIZE;
028:
029: /** The FIFO list of hard references, order of last access. */
030: private final LinkedList hardCache = new LinkedList();
031:
032: public SoftHashMap() {
033: innerMap = new HashMap<K, Reference>();
034: this .HARD_SIZE = 100;
035: }
036:
037: public SoftHashMap(int initialCapacity) {
038: innerMap = new HashMap<K, Reference>(initialCapacity);
039: this .HARD_SIZE = initialCapacity;
040: }
041:
042: public SoftHashMap(int initialCapacity, float loadFactor) {
043: innerMap = new HashMap<K, Reference>(initialCapacity,
044: loadFactor);
045: this .HARD_SIZE = initialCapacity;
046: }
047:
048: public Object get(String key) {
049: Reference res = innerMap.get(key);
050: Object result = null;
051: // We get the SoftReference represented by that key
052: if (res != null) {
053: // From the SoftReference we get the value, which can be
054: // null if it was not in the map, or it was removed in
055: // the processQueue() method defined below
056: result = ((Reference) res).get();
057: if (result == null) {
058: // If the value has been garbage collected, remove the
059: // entry from the HashMap.
060: innerMap.remove(key);
061: } else {
062: // We now add this object to the beginning of the hard
063: // reference queue. One reference can occur more than
064: // once, because lookups of the FIFO queue are slow, so
065: // we don't want to search through it each time to remove
066: // duplicates.
067: hardCache.addFirst(result);
068: if (hardCache.size() > HARD_SIZE) {
069: // Remove the last entry if list longer than HARD_SIZE
070: hardCache.removeLast();
071: }
072: }
073: }
074: return result;
075: }
076:
077: public V put(K key, V value) {
078: processQueue();
079: Reference ref = (Reference) new SoftEntry<K>(key, value,
080: refQueue);
081: Reference res = innerMap.put(key, ref);
082: return res == null ? null : (V) res.get();
083: }
084:
085: private Set entrySet = null;
086:
087: public Set entrySet() {
088: if (entrySet == null)
089: entrySet = new EntrySet();
090: return entrySet;
091: }
092:
093: private void processQueue() {
094: Reference ref;
095: while ((ref = refQueue.poll()) != null) {
096: SoftEntry sEntry = (SoftEntry) ref;
097: innerMap.remove(sEntry.key);
098: }
099: }
100:
101: public int size() {
102: processQueue();
103: // return entrySet().size();
104: return innerMap.size();
105: }
106:
107: public V remove(Object key) {
108: processQueue();
109: Reference res = innerMap.remove(key);
110: return res == null ? null : (V) res.get();
111: }
112:
113: public void clear() {
114: hardCache.clear();
115: processQueue();
116: innerMap.clear();
117: }
118:
119: private static class SoftEntry<K> extends SoftReference {
120: private K key; // neccessary so that freed objects can be removed
121:
122: private SoftEntry(K key, Object value, ReferenceQueue queue) {
123: super (value, queue);
124: this .key = key;
125: }
126: }
127:
128: private class EntrySet extends AbstractSet {
129: private Set entrySet = innerMap.entrySet();
130:
131: public int size() {
132: int s = 0;
133: for (Iterator iter = iterator(); iter.hasNext(); iter
134: .next())
135: s++;
136: return s;
137: }
138:
139: public boolean isEmpty() {
140: // default implementation computes size which is inefficient
141: return !(iterator().hasNext());
142: }
143:
144: public boolean remove(Object o) {
145: processQueue();
146: return super .remove(o);
147: }
148:
149: public Iterator iterator() {
150:
151: return new Iterator() {
152: Iterator it = entrySet.iterator();
153:
154: Entry next = null;
155:
156: Object value = null;
157:
158: /*
159: * Strong reference to key, so that the GC will leave it alone
160: * as long as this Entry exists
161: */
162:
163: public boolean hasNext() {
164: while (it.hasNext()) {
165: final Entry entry = (Entry) it.next();
166: SoftEntry se = (SoftEntry) entry.getValue();
167: value = null;
168: if ((se != null)
169: && ((value = se.get()) == null)) {
170: /* Weak key has been cleared by GC */
171: continue;
172: }
173: next = new Map.Entry() {
174: public Object getKey() {
175: return entry.getKey();
176: }
177:
178: public Object getValue() {
179: return value;
180: }
181:
182: public Object setValue(Object v) {
183: Object res = value;
184: value = v;
185: entry.setValue(new SoftEntry(
186: (String) entry.getKey(), value,
187: refQueue));
188: return res;
189: }
190:
191: public boolean equals(Object x) {
192: if (!(x instanceof Map.Entry))
193: return false;
194: Map.Entry e = (Map.Entry) x;
195: Object key = getKey();
196: return key == null ? e.getKey() == null
197: : key.equals(e.getKey())
198: && value == null ? e
199: .getValue() == null
200: : value.equals(e
201: .getValue());
202: }
203:
204: public int hashCode() {
205: Object key = getKey();
206: return (((key == null) ? 0 : key
207: .hashCode()) ^ ((value == null) ? 0
208: : value.hashCode()));
209: }
210:
211: };
212: return true;
213: }
214: return false;
215: }
216:
217: public Object next() {
218: if ((next == null) && !hasNext())
219: throw new NoSuchElementException();
220: Entry e = next;
221: next = null;
222: return e;
223: }
224:
225: public void remove() {
226: it.remove();
227: }
228:
229: };
230: }
231: }
232: }
|