/*******************************************************************************
* Copyright (c) 2004, 2005
* Thomas Hallgren, Kenneth Olwing, Mitch Sonies
* Pontus Rydin, Nils Unden, Peer Torngren
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed above, as Initial Contributors under such license.
* The text of such license is available at www.eclipse.org.
*******************************************************************************/
package org.eclipse.buckminster.core.helpers;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author thhal
*/
public class TimedHashMap<K, V> implements Map<K, V> {
public interface EvictionPolicy<EK, EV> {
void evict(Entry<EK, EV> entry);
}
final class TimedEntry extends TimerTask implements Map.Entry<K, V> {
private final K key;
private V value;
TimedEntry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public void run() {
TimedEntry val = null;
synchronized (map) {
val = map.remove(key);
}
if (val != null && evictionPolicy != null)
evictionPolicy.evict(val);
}
@Override
public V setValue(V value) {
V old = value;
this.remove();
this.value = value;
timer.schedule(this, keepAliveTime);
return old;
}
void remove() {
this.cancel();
if (evictionPolicy != null)
evictionPolicy.evict(this);
}
}
private static final Timer timer = new Timer();
private final long keepAliveTime;
private final EvictionPolicy<K, V> evictionPolicy;
private final HashMap<K, TimedEntry> map = new HashMap<K, TimedEntry>();
public TimedHashMap(long keepAliveTime, EvictionPolicy<K, V> evictionPolicy) {
this.keepAliveTime = keepAliveTime;
this.evictionPolicy = evictionPolicy;
}
public void cancel(K key) {
TimedEntry entry = map.get(key);
if (entry != null)
entry.cancel();
}
@Override
public void clear() {
synchronized (map) {
for (TimedEntry entry : map.values())
entry.remove();
map.clear();
}
}
@Override
public boolean containsKey(Object key) {
synchronized (map) {
return map.containsKey(key);
}
}
@Override
public boolean containsValue(Object value) {
synchronized (map) {
for (Entry<K, V> te : map.values()) {
Object tv = te.getValue();
if (value == null) {
if (tv == null)
return true;
} else if (value.equals(tv))
return true;
}
}
return false;
}
@Override
public Set<Entry<K, V>> entrySet() {
final Iterator<TimedEntry> entries = map.values().iterator();
return new AbstractSet<Entry<K, V>>() {
@Override
public Iterator<Entry<K, V>> iterator() {
return new Iterator<Entry<K, V>>() {
@Override
public boolean hasNext() {
return entries.hasNext();
}
@Override
public Entry<K, V> next() {
return entries.next();
}
@Override
public void remove() {
entries.remove();
}
};
}
@Override
public int size() {
return map.size();
}
};
}
@Override
public V get(Object key) {
synchronized (map) {
Entry<K, V> te = map.get(key);
return te == null ? null : te.getValue();
}
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public V put(K key, V value) {
V oldVal;
TimedEntry entry = new TimedEntry(key, value);
synchronized (map) {
TimedEntry oldEntry = map.get(key);
if (oldEntry != null) {
oldVal = oldEntry.getValue();
oldEntry.remove();
} else
oldVal = null;
map.put(key, entry);
}
if (scheduleOnPut())
timer.schedule(entry, keepAliveTime);
return oldVal;
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
for (Entry<? extends K, ? extends V> entry : t.entrySet())
this.put(entry.getKey(), entry.getValue());
}
@Override
public V remove(Object key) {
synchronized (map) {
TimedEntry te = map.remove(key);
if (te == null)
return null;
V oldVal = te.getValue();
te.remove();
return oldVal;
}
}
public void schedule(K key) {
TimedEntry entry = map.get(key);
if (entry != null)
timer.schedule(entry, keepAliveTime);
}
public boolean scheduleOnPut() {
return true;
}
@Override
public int size() {
return map.size();
}
@Override
public Collection<V> values() {
final Iterator<TimedEntry> entries = map.values().iterator();
return new AbstractCollection<V>() {
@Override
public Iterator<V> iterator() {
return new Iterator<V>() {
@Override
public boolean hasNext() {
return entries.hasNext();
}
@Override
public V next() {
return entries.next().getValue();
}
@Override
public void remove() {
entries.remove();
}
};
}
@Override
public int size() {
return map.size();
}
};
}
}