Development Class Java

/**
 * 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 m)
  {
    Lock lock = theLock.writeLock();
    lock.lock();
    try
    {
      removeQueued();
      for(java.util.Map.Entry 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 queue)
    {
      super(referent, queue);
      theKey = key;
    }
  }
}