package pl.net.bluesoft.util.lang;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: POlszewski
* Date: 2012-01-09
* Time: 16:24:29
*/
public class ExpiringCache<K, V> {
private final long expirationMillis;
private final long removeExpiredFrequency;
private long lastRemoveExpiredTime;
private final Map<K, V> items = new HashMap<K,V>();
private final Map<K, Long> times = new HashMap<K, Long>();
public static interface NewValueCallback<K, V> {
V getNewValue(K key);
}
public ExpiringCache(long expirationMillis) {
this(expirationMillis, expirationMillis);
}
public ExpiringCache(long expirationMillis, long removeExpiredFrequency) {
this.expirationMillis = expirationMillis;
this.removeExpiredFrequency = removeExpiredFrequency;
this.lastRemoveExpiredTime = System.currentTimeMillis();
}
public synchronized boolean isValueAvailable(K key) {
return times.containsKey(key) && !isExpired(key, System.currentTimeMillis());
}
public synchronized void put(K key, V value) {
periodicRemoveExpired();
items.put(key, value);
times.put(key, System.currentTimeMillis());
}
public synchronized V get(K key) {
periodicRemoveExpired();
if (isValueAvailable(key)) {
return items.get(key);
}
return null;
}
public synchronized V get(K key, NewValueCallback<K, V> newValueCallback) {
periodicRemoveExpired();
if (isValueAvailable(key)) {
return items.get(key);
}
V value = newValueCallback.getNewValue(key);
put(key, value);
return value;
}
public synchronized void clear() {
items.clear();
times.clear();
}
public synchronized void removeExpired() {
List<K> toRemove = new ArrayList<K>();
long time = System.currentTimeMillis();
for (K key : times.keySet()) {
if (isExpired(key, time)) {
toRemove.add(key);
}
}
for (K key : toRemove) {
items.remove(key);
times.remove(key);
}
lastRemoveExpiredTime = System.currentTimeMillis();
}
private void periodicRemoveExpired() {
if (System.currentTimeMillis() - lastRemoveExpiredTime >= removeExpiredFrequency) {
removeExpired();
}
}
private boolean isExpired(K key, long time) {
return time - times.get(key) >= expirationMillis;
}
}