/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.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; public class SoftHashMap extends AbstractMap { /** 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; } @Override 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); } } @Override public Object put(Object key, Object value) { processQueue(); return hash.put(key, new SoftValue(value, queue, key)); } @Override public Object remove(Object key) { // throw out garbage collected values first processQueue(); return hash.remove(key); } @Override public void clear() { hardCache.clear(); processQueue(); hash.clear(); } @Override public int size() { processQueue(); return hash.size(); } @Override 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; } } }