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}