1 52 53 package freemarker.cache; 54 55 import java.lang.ref.ReferenceQueue ; 56 import java.lang.ref.SoftReference ; 57 import java.util.HashMap ; 58 import java.util.Map ; 59 60 77 public class MruCacheStorage implements CacheStorage 78 { 79 private final MruEntry strongHead = new MruEntry(); 80 private final MruEntry softHead = new MruEntry(); 81 { 82 softHead.linkAfter(strongHead); 83 } 84 private final Map map = new HashMap (); 85 private final ReferenceQueue refQueue = new ReferenceQueue (); 86 private final int maxStrongSize; 87 private final int maxSoftSize; 88 private int strongSize = 0; 89 private int softSize = 0; 90 91 97 public MruCacheStorage(int maxStrongSize, int maxSoftSize) { 98 if(maxStrongSize < 0) throw new IllegalArgumentException ("maxStrongSize < 0"); 99 if(maxSoftSize < 0) throw new IllegalArgumentException ("maxSoftSize < 0"); 100 this.maxStrongSize = maxStrongSize; 101 this.maxSoftSize = maxSoftSize; 102 } 103 104 public Object get(Object key) { 105 removeClearedReferences(); 106 MruEntry entry = (MruEntry)map.get(key); 107 if(entry == null) { 108 return null; 109 } 110 relinkEntryAfterStrongHead(entry, null); 111 Object value = entry.getValue(); 112 if(value instanceof MruReference) { 113 return ((MruReference)value).get(); 115 } 116 return value; 117 } 118 119 public void put(Object key, Object value) { 120 removeClearedReferences(); 121 MruEntry entry = (MruEntry)map.get(key); 122 if(entry == null) { 123 entry = new MruEntry(key, value); 124 map.put(key, entry); 125 linkAfterStrongHead(entry); 126 } 127 else { 128 relinkEntryAfterStrongHead(entry, value); 129 } 130 131 } 132 133 public void remove(Object key) { 134 removeClearedReferences(); 135 MruEntry entry = (MruEntry)map.remove(key); 136 if(entry != null) { 137 unlinkEntryAndInspectIfSoft(entry); 138 } 139 } 140 141 public void clear() { 142 strongHead.makeHead(); 143 softHead.linkAfter(strongHead); 144 map.clear(); 145 strongSize = softSize = 0; 146 while(refQueue.poll() != null); 148 } 149 150 private void relinkEntryAfterStrongHead(MruEntry entry, Object newValue) { 151 if(unlinkEntryAndInspectIfSoft(entry) && newValue == null) { 152 MruReference mref = (MruReference)entry.getValue(); 154 Object strongValue = mref.get(); 155 if (strongValue != null) { 156 entry.setValue(strongValue); 157 linkAfterStrongHead(entry); 158 } else { 159 map.remove(mref.getKey()); 160 } 161 } else { 162 if (newValue != null) { 163 entry.setValue(newValue); 164 } 165 linkAfterStrongHead(entry); 166 } 167 } 168 169 private void linkAfterStrongHead(MruEntry entry) { 170 entry.linkAfter(strongHead); 171 if(strongSize == maxStrongSize) { 172 MruEntry lruStrong = softHead.getPrevious(); 174 if(lruStrong != strongHead) { 178 lruStrong.unlink(); 179 if(maxSoftSize > 0) { 180 lruStrong.linkAfter(softHead); 181 lruStrong.setValue(new MruReference(lruStrong, refQueue)); 182 if(softSize == maxSoftSize) { 183 MruEntry lruSoft = strongHead.getPrevious(); 185 lruSoft.unlink(); 186 map.remove(lruSoft.getKey()); 187 } 188 else { 189 ++softSize; 190 } 191 } 192 else { 193 map.remove(lruStrong.getKey()); 194 } 195 } 196 } 197 else { 198 ++strongSize; 199 } 200 } 201 202 private boolean unlinkEntryAndInspectIfSoft(MruEntry entry) { 203 entry.unlink(); 204 if(entry.getValue() instanceof MruReference) { 205 --softSize; 206 return true; 207 } 208 else { 209 --strongSize; 210 return false; 211 } 212 } 213 214 private void removeClearedReferences() { 215 for(;;) { 216 MruReference ref = (MruReference)refQueue.poll(); 217 if(ref == null) { 218 break; 219 } 220 remove(ref.getKey()); 221 } 222 } 223 224 private static final class MruEntry 225 { 226 private MruEntry prev; 227 private MruEntry next; 228 private final Object key; 229 private Object value; 230 231 234 MruEntry() 235 { 236 makeHead(); 237 key = value = null; 238 } 239 240 MruEntry(Object key, Object value) { 241 this.key = key; 242 this.value = value; 243 } 244 245 Object getKey() { 246 return key; 247 } 248 249 Object getValue() { 250 return value; 251 } 252 253 void setValue(Object value) { 254 this.value = value; 255 } 256 257 MruEntry getPrevious() { 258 return prev; 259 } 260 261 void linkAfter(MruEntry entry) { 262 next = entry.next; 263 entry.next = this; 264 prev = entry; 265 next.prev = this; 266 } 267 268 void unlink() { 269 next.prev = prev; 270 prev.next = next; 271 prev = null; 272 next = null; 273 } 274 275 void makeHead() { 276 prev = next = this; 277 } 278 } 279 280 private static class MruReference extends SoftReference 281 { 282 private final Object key; 283 284 MruReference(MruEntry entry, ReferenceQueue queue) { 285 super(entry.getValue(), queue); 286 this.key = entry.getKey(); 287 } 288 289 Object getKey() { 290 return key; 291 } 292 } 293 } | Popular Tags |