package org.korsakow.ide.util;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Obviously, most of the methods in this object are not reliable to call several times.
* For example: <code>assert myMap.size()==myMap.size()</code> may well fail.
*
* This class uses teh policy of clean-on-write, meaning stale entries are removed whenever the map is modified.
*
* This class actually just wraps a Map of weak references and provides a constructor allowing the user to specify their own backend.
*
* @author d
*
* @param <K>
* @param <V>
*/
public class WeakReferenceMap<K, V> implements Map<K, V>
{
private Map<K, WeakReference<V>> map;
private static class WeakEntry<K, V> implements Entry<K, V>
{
private Entry<K, WeakReference<V>> entry;
public WeakEntry(Entry<K, WeakReference<V>> entry)
{
this.entry = entry;
}
public K getKey() {
return entry.getKey();
}
public V getValue() {
return entry.getValue().get();
}
public V setValue(V value) {
entry.setValue(new WeakReference<V>(value));
return value;
}
}
/**
* Constructs a WeakReferenceMap backed by a HashMap
*/
public WeakReferenceMap()
{
this(new HashMap<K, WeakReference<V>>());
}
public WeakReferenceMap(Map<K, WeakReference<V>> innerMap)
{
this.map = innerMap;
}
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(new WeakReference<V>((V)value));
}
public Set<Entry<K, V>> entrySet() {
Set<Entry<K,V>> entries = new HashSet<Entry<K,V>>();
for (Entry<K,WeakReference<V>> entry : map.entrySet())
entries.add(new WeakEntry(entry));
return entries;
}
public V get(Object key) {
WeakReference<V> ref = map.get(key);
return ref!=null?ref.get():null;
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<K> keySet() {
return map.keySet();
}
public V put(K key, V value) {
map.put(key, new WeakReference<V>(value));
cleanup();
return value;
}
public void putAll(Map<? extends K, ? extends V> t) {
for (K key : t.keySet())
put(key, t.get(key));
cleanup();
}
public V remove(Object key) {
WeakReference<V> ref = map.remove(key);
cleanup();
return ref!=null?ref.get():null;
}
public int size() {
return map.size();
}
public Collection<V> values() {
Collection<V> values = new ArrayList<V>();
for (WeakReference<V> ref : map.values())
{
V v = ref.get();
if (v != null)
values.add(v);
}
return values;
}
/**
* Removes stale entries.
*/
private void cleanup()
{
Set<K> keys = new HashSet<K>(map.keySet());
for (K key : keys)
{
WeakReference<V> ref = map.get(key);
if (ref.get() == null)
map.remove(key);
}
}
}