/**
* SoftReferenceCache.java Created Mar 18, 2009 by Andrew Butler, PSL
*/
//package prisms.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
/**
* A thread-safe cache that keeps its values as {@link java.lang.ref.SoftReference}s so that the
* cache is, in effect, managed by the JVM and kept as small as is required
*
* @param The type of key used to store values with
* @param The type of values to store
*/
public class SoftReferenceCache implements Map
{
java.util.HashMap> theCache;
private java.util.concurrent.locks.ReentrantReadWriteLock theLock;
private java.lang.ref.ReferenceQueue theRefQueue;
/**
* Creates a SoftReferenceCache
*/
public SoftReferenceCache()
{
theCache = new java.util.HashMap>();
theLock = new java.util.concurrent.locks.ReentrantReadWriteLock();
theRefQueue = new java.lang.ref.ReferenceQueue();
}
/**
* @see java.util.Map#get(java.lang.Object)
*/
public V get(Object key)
{
Lock lock = theLock.readLock();
lock.lock();
try
{
KeyedSoftReference val = theCache.get(key);
return val == null ? null : val.get();
} finally
{
lock.unlock();
}
}
/**
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
public V put(K key, V value)
{
Lock lock = theLock.writeLock();
lock.lock();
try
{
removeQueued();
KeyedSoftReference val = theCache.get(key);
theCache.put(key, new KeyedSoftReference(key, value, theRefQueue));
return val == null ? null : val.get();
} finally
{
lock.unlock();
}
}
/**
* @see java.util.Map#remove(java.lang.Object)
*/
public V remove(Object key)
{
Lock lock = theLock.writeLock();
lock.lock();
try
{
removeQueued();
KeyedSoftReference val = theCache.remove(key);
return val == null ? null : val.get();
} finally
{
lock.unlock();
}
}
/**
* @see java.util.Map#clear()
*/
public void clear()
{
Lock lock = theLock.writeLock();
lock.lock();
try
{
theCache.clear();
} finally
{
lock.unlock();
}
}
/**
* @see java.util.Map#size()
*/
public int size()
{
return theCache.size();
}
/**
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty()
{
return theCache.isEmpty();
}
/**
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key)
{
Lock lock = theLock.readLock();
lock.lock();
try
{
KeyedSoftReference val = theCache.get(key);
return val == null || val.isEnqueued();
} finally
{
lock.unlock();
}
}
/**
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value)
{
throw new UnsupportedOperationException("The containsValue method is not supported by "
+ getClass().getName());
}
/**
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map extends K, ? extends V> m)
{
Lock lock = theLock.writeLock();
lock.lock();
try
{
removeQueued();
for(java.util.Map.Entry extends K, ? extends V> entry : m.entrySet())
{
theCache.put(entry.getKey(), new KeyedSoftReference(entry.getKey(), entry
.getValue(), theRefQueue));
}
} finally
{
lock.unlock();
}
}
/**
* @see java.util.Map#keySet()
*/
public Set keySet()
{
Lock lock = theLock.writeLock();
lock.lock();
final Object [] keys;
try
{
removeQueued();
keys = theCache.keySet().toArray();
} finally
{
lock.unlock();
}
return new java.util.AbstractSet()
{
@Override
public Iterator iterator()
{
return new java.util.Iterator()
{
private int index = 0;
public boolean hasNext()
{
return index < keys.length - 1;
}
public K next()
{
index++;
return (K) keys[index - 1];
}
public void remove()
{
SoftReferenceCache.this.remove(keys[index - 1]);
}
};
}
@Override
public int size()
{
return keys.length;
}
};
}
/**
* @see java.util.Map#entrySet()
*/
public Set> entrySet()
{
throw new UnsupportedOperationException("The entrySet method is not supported by "
+ getClass().getName());
}
/**
* @see java.util.Map#values()
*/
public Collection values()
{
throw new UnsupportedOperationException("The values method is not supported by "
+ getClass().getName());
}
/**
* Removes all entries in {@link #theRefQueue} from the map since their references have been
* deleted. The write lock from {@link #theLock} MUST be obtained by the thread before calling
* this method.
*/
private void removeQueued()
{
KeyedSoftReference ref = (KeyedSoftReference) theRefQueue.poll();
while(ref != null)
{
theCache.remove(ref.theKey);
ref = (KeyedSoftReference) theRefQueue.poll();
}
}
private class KeyedSoftReference extends java.lang.ref.SoftReference
{
final K theKey;
KeyedSoftReference(K key, V2 referent, java.lang.ref.ReferenceQueue super V2> queue)
{
super(referent, queue);
theKey = key;
}
}
}