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    }