/**
* 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!");
}
}