package railo.commons.lang; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.AbstractMap; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Set; import railo.runtime.type.Sizeable; public class SoftHashMap extends AbstractMap implements Sizeable { /** The internal HashMap that will hold the SoftReference. */ private final Map hash = new HashMap(); /** The number of "hard" references to hold internally. */ private final int HARD_SIZE; /** The FIFO list of hard references, order of last access. */ private final LinkedList hardCache = new LinkedList(); /** Reference queue for cleared SoftReference objects. */ private final ReferenceQueue queue = new ReferenceQueue(); public SoftHashMap() { HARD_SIZE = -1; } public SoftHashMap(int hardSize) { HARD_SIZE = hardSize; } public Object get(Object key) { Object result = null; // We get the SoftReference represented by that key SoftReference soft_ref = (SoftReference)hash.get(key); if (soft_ref != null) { result = soft_ref.get(); if (result == null) { hash.remove(key); } else { hardCache.addFirst(result); // Remove the last entry if list longer than HARD_SIZE if (HARD_SIZE > 0 && hardCache.size() > HARD_SIZE) { hardCache.removeLast(); } } } return result; } private void processQueue() { SoftValue sv; while ((sv = (SoftValue)queue.poll()) != null) { hash.remove(sv.key); } } public Object put(Object key, Object value) { processQueue(); return hash.put(key, new SoftValue(value, queue, key)); } public Object remove(Object key) { // throw out garbage collected values first processQueue(); return hash.remove(key); } public void clear() { hardCache.clear(); processQueue(); hash.clear(); } public int size() { processQueue(); return hash.size(); } public Set entrySet() { // no, no, you may NOT do that!!! GRRR throw new UnsupportedOperationException(); } private static class SoftValue extends SoftReference { private final Object key; // always make data member final private SoftValue(Object k, ReferenceQueue q, Object key) { super(k, q); this.key = key; } } public long sizeOf() { return SizeOf.size(hash); } }