001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.lang;
020
021import java.lang.ref.ReferenceQueue;
022import java.lang.ref.SoftReference;
023import java.util.AbstractMap;
024import java.util.HashMap;
025import java.util.LinkedList;
026import java.util.Map;
027import java.util.Set;
028
029import lucee.runtime.type.Sizeable;
030
031public class SoftHashMap extends AbstractMap implements Sizeable {
032  /** The internal HashMap that will hold the SoftReference. */
033  private final Map hash = new HashMap();
034  /** The number of "hard" references to hold internally. */
035  private final int HARD_SIZE;
036  /** The FIFO list of hard references, order of last access. */
037  private final LinkedList hardCache = new LinkedList();
038  /** Reference queue for cleared SoftReference objects. */
039  private final ReferenceQueue queue = new ReferenceQueue();
040
041  public SoftHashMap() { 
042    HARD_SIZE = -1;
043  }
044  public SoftHashMap(int hardSize) { 
045    HARD_SIZE = hardSize; 
046  }
047
048  public Object get(Object key) {
049    Object result = null;
050    // We get the SoftReference represented by that key
051    SoftReference soft_ref = (SoftReference)hash.get(key);
052    if (soft_ref != null) {
053      result = soft_ref.get();
054      if (result == null) {
055        hash.remove(key);
056      } else {
057        hardCache.addFirst(result);
058        // Remove the last entry if list longer than HARD_SIZE
059        if (HARD_SIZE > 0 && hardCache.size() > HARD_SIZE) {
060          hardCache.removeLast();
061        }
062      }
063    }
064    return result;
065  }
066
067  private void processQueue() {
068    SoftValue sv;
069    while ((sv = (SoftValue)queue.poll()) != null) {
070      hash.remove(sv.key); 
071    }
072  }
073
074  public Object put(Object key, Object value) {
075    processQueue(); 
076    return hash.put(key, new SoftValue(value, queue, key));
077  }
078
079  public Object remove(Object key) {
080    // throw out garbage collected values first
081    processQueue(); 
082    return hash.remove(key);
083  }
084
085  public void clear() {
086    hardCache.clear();
087    processQueue(); 
088    hash.clear();
089  }
090
091  public int size() {
092    processQueue(); 
093    return hash.size();
094  }
095
096  public Set entrySet() {
097    // no, no, you may NOT do that!!! GRRR
098    throw new UnsupportedOperationException();
099  }
100  
101  private static class SoftValue extends SoftReference {
102    private final Object key; // always make data member final
103    private SoftValue(Object k, ReferenceQueue q, Object key) {
104      super(k, q);
105      this.key = key;
106    }
107  }
108
109        public long sizeOf() {
110                return SizeOf.size(hash);
111        }
112
113}