/*
* Copyright 2002-2006 (C) TJDO.
* All rights reserved.
*
* This software is distributed under the terms of the TJDO License version 1.0.
* See the terms of the TJDO License in the documentation provided with this software.
*
* $Id: ReferenceValueMap.java,v 1.8 2006/09/08 16:11:28 jackknifebarber Exp $
*/
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/*
* Copyright 2002-2006 (C) TJDO.
* All rights reserved.
*
* This software is distributed under the terms of the TJDO License version 1.0.
* See the terms of the TJDO License in the documentation provided with this software.
*
* $Id: SoftValueMap.java,v 1.5 2006/09/08 16:11:28 jackknifebarber Exp $
*/
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
/**
* A java.util.Map
implementation with soft values.
*
* The values are stored as soft references.
* If map entry value object is not actively being used, i.e. no other object
* has a strong reference to it, it may become garbage collected at the
* discretion of the garbage collector (typically if the VM is low on memory).
* If this happens, the entry in the SoftValueMap
corresponding to
* the value object will also be removed.
*
* @author Mike Martin
* @version $Revision: 1.5 $
*
* @see SoftReference
*/
public class SoftValueMap extends ReferenceValueMap
{
public SoftValueMap()
{
super(new HashMap());
}
public SoftValueMap(int initialCapacity)
{
super(new HashMap(initialCapacity));
}
public SoftValueMap(int initialCapacity, float loadFactor)
{
super(new HashMap(initialCapacity, loadFactor));
}
public SoftValueMap(Map m)
{
super(new HashMap(m));
}
protected Reference newReference(Object value)
{
return new SoftReference(value, refQueue);
}
}
/**
* A java.util.Map
implementation using reference values.
*
* The values are stored in the map as references.
* If the garbage collector clears the reference, the corresponding key is
* automatically removed from the map.
*
* @author Mike Martin
* @version $Revision: 1.8 $
*/
abstract class ReferenceValueMap extends AbstractMap
{
protected final ReferenceQueue refQueue = new ReferenceQueue();
/** Backing map. */
private final Map backing;
ReferenceValueMap(Map backing)
{
this.backing = backing;
}
/**
* Returns a new Reference
object to be inserted into the map.
* Subclasses must implement this method to construct Reference
* objects of the desired type (e.g. SoftReference
, etc.).
*
* @param value
* The associated value to be referenced.
*
* @return
* A new Reference
object to be inserted into the map.
*/
protected abstract Reference newReference(Object value);
private void reap()
{
Reference ref;
while ((ref = refQueue.poll()) != null)
backing.values().remove(ref);
}
public Object put(Object key, Object value)
{
reap();
return backing.put(key, newReference(value));
}
public Object get(Object key)
{
reap();
Object v = backing.get(key);
return (v instanceof Reference) ? ((Reference)v).get() : v;
}
public int size()
{
reap();
return backing.size();
}
public boolean isEmpty()
{
reap();
return backing.isEmpty();
}
public boolean containsKey(Object key)
{
reap();
return backing.containsKey(key);
}
public boolean containsValue(Object value)
{
reap();
return super.containsValue(value);
}
public Set keySet()
{
reap();
return backing.keySet();
}
public Collection values()
{
reap();
return super.values();
}
public Set entrySet()
{
reap();
return new EntrySet();
}
public Object remove(Object key)
{
reap();
return backing.remove(key);
}
public int hashCode()
{
reap();
return super.hashCode();
}
public boolean equals(Object o)
{
reap();
return super.equals(o);
}
public String toString()
{
reap();
return super.toString();
}
static boolean eq(Object o1, Object o2)
{
return o1 == null ? o2 == null : o1.equals(o2);
}
private class EntrySet extends AbstractSet
{
/** Backing set. */
private final Set set = backing.entrySet();
public Iterator iterator()
{
return new Iterator()
{
private Iterator i = set.iterator();
public boolean hasNext()
{
return i.hasNext();
}
public void remove()
{
i.remove();
}
public Object next()
{
final Map.Entry ent = (Map.Entry)i.next();
return new Map.Entry()
{
public Object getKey()
{
return ent.getKey();
}
public Object getValue()
{
Object v = ent.getValue();
return (v instanceof Reference) ? ((Reference)v).get() : v;
}
public Object setValue(Object v)
{
Object oldVal = getValue();
ent.setValue(newReference(v));
return oldVal;
}
public boolean equals(Object o)
{
if (o == this)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
return eq(ent.getKey(), e.getKey())
&& eq(ent.getValue(), e.getValue());
}
public int hashCode()
{
Object key = ent.getKey();
Object val = ent.getValue();
return (key == null ? 0 : key.hashCode())
^ (val == null ? 0 : val.hashCode());
}
public String toString()
{
return ent.getKey() + "=" + ent.getValue();
}
};
}
};
}
public int size()
{
reap();
return set.size();
}
public boolean isEmpty()
{
reap();
return set.isEmpty();
}
public boolean contains(Object o)
{
reap();
return super.contains(o);
}
public Object[] toArray()
{
reap();
return super.toArray();
}
public Object[] toArray(Object[] a)
{
reap();
return super.toArray(a);
}
public boolean remove(Object o)
{
reap();
return super.remove(o);
}
public boolean containsAll(Collection c)
{
reap();
return super.containsAll(c);
}
public boolean removeAll(Collection c)
{
reap();
return super.removeAll(c);
}
public boolean retainAll(Collection c)
{
reap();
return super.retainAll(c);
}
public void clear()
{
set.clear();
}
public String toString()
{
reap();
return super.toString();
}
}
}