package org.hypergraphdb.util;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* <p>
* A variation on a map where values are managed like resources: a put
* increments a reference count on an existing key-value entry and a remove
* actually delete the entry when the reference count goes to zero. This
* was implemented to help with the transactions framework.
* </p>
*
* @author Borislav Iordanov
*
* @param <K>
* @param <V>
*/
public class RefCountedMap<K, V> implements Map<K, V>
{
Map<K, Pair<V, AtomicInteger>> implementation = null;
@SuppressWarnings("unchecked")
public RefCountedMap(Map implementation)
{
if (implementation == null)
this.implementation = new ConcurrentHashMap<K, Pair<V, AtomicInteger>>();
else
this.implementation = (Map<K, Pair<V, AtomicInteger>>)implementation;
}
public void clear()
{
implementation.clear();
}
public boolean containsKey(Object key)
{
return implementation.containsKey(key);
}
public boolean containsValue(Object value)
{
return implementation.containsValue(value);
}
public Set<java.util.Map.Entry<K, V>> entrySet()
{
throw new UnsupportedOperationException();
}
public synchronized V get(Object key)
{
Pair<V, AtomicInteger> p = implementation.get(key);
return p == null ? null : p.getFirst();
}
public boolean isEmpty()
{
return implementation.isEmpty();
}
public Set<K> keySet()
{
return implementation.keySet();
}
@SuppressWarnings("unchecked")
public synchronized V put(K key, V value)
{
Pair<V, AtomicInteger> p = null;
if (implementation instanceof ConcurrentMap)
{
p = new Pair<V, AtomicInteger>(value, new AtomicInteger(0));
Pair<V, AtomicInteger> existing = (Pair<V, AtomicInteger>)
((ConcurrentMap)implementation).putIfAbsent(key, p);
if (existing != null)
p = existing;
}
else synchronized (implementation)
{
p = implementation.get(key);
if (p == null)
{
p = new Pair<V, AtomicInteger>(value, new AtomicInteger(0));
implementation.put(key, p);
}
}
int current = p.getSecond().incrementAndGet();
return current > 1 ? value : null;
}
public void putAll(Map<? extends K, ? extends V> m)
{
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public synchronized V remove(Object key)
{
Pair<V, AtomicInteger> p = implementation.get(key);
if (p == null)
return null;
else if (p.getSecond().decrementAndGet() == 0)
{
implementation.remove(key);
}
return p.getFirst();
}
public int size()
{
return implementation.size();
}
public Collection<V> values()
{
throw new UnsupportedOperationException();
}
public boolean equals(Object o)
{
return implementation.equals(o);
}
public int hashCode()
{
return implementation.hashCode();
}
}