/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.core.cache; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * A hash map with soft values (i.e.: values that are collected only when the vm gets out of space). * * Not synchronized (must be done from the outside) -- note that removeStaleEntries must also be called from the * outside (we don't want the overhead of the call in each operation -- ideally it's done in another thread, but * for that the synchronization must be done -- in which case, we can't do it here). * * @author fabioz */ public final class SoftHashMap<Key, Val> extends AbstractMap<Key, Val> { /** * Create our own reference so that we can know the key that is garbage-collected. */ private final static class SoftValue<Key, Val> extends SoftReference<Val> { private final Key key; private SoftValue(Val value, Key key, ReferenceQueue<Val> queue) { super(value, queue); this.key = key; } } /** * This is the internal map that contains our key->soft val references. */ private final Map<Key, SoftValue<Key, Val>> map = new HashMap<Key, SoftValue<Key, Val>>(); /** * Queue where we store the references. Used so that we clear the dead references from time to time. */ private ReferenceQueue<Val> queue = new ReferenceQueue<Val>(); public SoftHashMap() { } public Val get(Object key) { Val res = null; SoftValue<Key, Val> sr = map.get(key); if (sr != null) { res = sr.get(); if (res == null) { //Note that if the key is garbage-collected before the object it contains, it won't be even added //to the queue, but as we got it here, it means that it was probably already in the queue but //the queue still wasn't processed, meaning that processing the queue should do the trick of //removing it (and any other stale entries). removeStaleEntries(); } } return res; } /** * Remove the keys that were added to the queue (i.e.: the soft references that were garbage-collected should * be removed). */ public void removeStaleEntries() { while (true) { @SuppressWarnings("unchecked") SoftValue<Key, Val> softValue = (SoftValue<Key, Val>) queue.poll(); if (softValue != null) { SoftValue<Key, Val> curr = map.get(softValue.key); //Check if the map still has that reference... in theory, what could happen is that if a key //is added twice, the old one may still be in the queue (and thus, we'd be removing the new key) if (curr == softValue) { map.remove(softValue.key); } } else { return; } } } public Val put(Key key, Val value) { map.put(key, new SoftValue<Key, Val>(value, key, queue)); return value; } /** * Different from the map, it'll always return null here (never the object that was there as we don't want the * overhead of getting it from the soft ref). */ public Val remove(Object key) { map.remove(key); return null; } public int size() { return map.size(); } public void clear() { map.clear(); //No need to poll the entries of the current queue, just create a new one and let the old be garbage-collected. queue = new ReferenceQueue<Val>(); } //Not implemented... @SuppressWarnings("rawtypes") public Set entrySet() { throw new UnsupportedOperationException("Not implemented!"); } }