001 package railo.commons.lang; 002 003 import java.lang.ref.ReferenceQueue; 004 import java.lang.ref.SoftReference; 005 import java.util.AbstractMap; 006 import java.util.HashMap; 007 import java.util.LinkedList; 008 import java.util.Map; 009 import java.util.Set; 010 011 import railo.runtime.type.Sizeable; 012 013 public class SoftHashMap extends AbstractMap implements Sizeable { 014 /** The internal HashMap that will hold the SoftReference. */ 015 private final Map hash = new HashMap(); 016 /** The number of "hard" references to hold internally. */ 017 private final int HARD_SIZE; 018 /** The FIFO list of hard references, order of last access. */ 019 private final LinkedList hardCache = new LinkedList(); 020 /** Reference queue for cleared SoftReference objects. */ 021 private final ReferenceQueue queue = new ReferenceQueue(); 022 023 public SoftHashMap() { 024 HARD_SIZE = -1; 025 } 026 public SoftHashMap(int hardSize) { 027 HARD_SIZE = hardSize; 028 } 029 030 public Object get(Object key) { 031 Object result = null; 032 // We get the SoftReference represented by that key 033 SoftReference soft_ref = (SoftReference)hash.get(key); 034 if (soft_ref != null) { 035 result = soft_ref.get(); 036 if (result == null) { 037 hash.remove(key); 038 } else { 039 hardCache.addFirst(result); 040 // Remove the last entry if list longer than HARD_SIZE 041 if (HARD_SIZE > 0 && hardCache.size() > HARD_SIZE) { 042 hardCache.removeLast(); 043 } 044 } 045 } 046 return result; 047 } 048 049 private void processQueue() { 050 SoftValue sv; 051 while ((sv = (SoftValue)queue.poll()) != null) { 052 hash.remove(sv.key); 053 } 054 } 055 056 public Object put(Object key, Object value) { 057 processQueue(); 058 return hash.put(key, new SoftValue(value, queue, key)); 059 } 060 061 public Object remove(Object key) { 062 // throw out garbage collected values first 063 processQueue(); 064 return hash.remove(key); 065 } 066 067 public void clear() { 068 hardCache.clear(); 069 processQueue(); 070 hash.clear(); 071 } 072 073 public int size() { 074 processQueue(); 075 return hash.size(); 076 } 077 078 public Set entrySet() { 079 // no, no, you may NOT do that!!! GRRR 080 throw new UnsupportedOperationException(); 081 } 082 083 private static class SoftValue extends SoftReference { 084 private final Object key; // always make data member final 085 private SoftValue(Object k, ReferenceQueue q, Object key) { 086 super(k, q); 087 this.key = key; 088 } 089 } 090 091 public long sizeOf() { 092 return SizeOf.size(hash); 093 } 094 095 }