package JsonClient.Java.cachestuff;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
/**
* Default implementation of a Cache. Backed by a HashMap.
*
* @author Justin Nelson
*
* @param <TKey>
* @param <TValue>
*/
public class DefaultCache<TKey, TValue> implements Cache<TKey, TValue> {
private final long defaultMaxAge;
private int maxSize = 10000;
private final Map<TKey, CacheItem> storage;
/**
* Creates an empty cache
*
* @param defaultMaxAgeL
*/
public DefaultCache(long defaultMaxAgeL) {
if (defaultMaxAgeL < 1)
throw new IllegalArgumentException("The max age must be greater than 0");
storage = new HashMap<TKey, CacheItem>();
defaultMaxAge = defaultMaxAgeL;
}
@Override
public TValue get(TKey key) {
if (key == null)
throw new NullPointerException("The key must not be null");
CacheItem obj = storage.get(key);
if (obj == null) {
return null;
}
if (obj.isExpired()) {
delete(key);
return null;
}
return obj.data;
}
@Override
public TValue put(TKey key, TValue value) {
return put(key, value, defaultMaxAge);
}
@Override
public TValue put(TKey key, TValue value, long duration) {
if (key == null) {
throw new NullPointerException("The key must not be null");
}
if (duration <= 0) {
// Should we allow 0 or negative durations? And just not cache the
// item at all?
throw new IllegalArgumentException("Durration must be greater than 0");
}
CacheItem oldItem = storage.put(key, new CacheItem(value, duration));
if (entryCount() > maxSize)
clean();
return oldItem == null ? null : oldItem.data;
}
@Override
public void clean() {
Iterator<Entry<TKey, CacheItem>> iter = storage.entrySet().iterator();
while (iter.hasNext()) {
Entry<TKey, CacheItem> entry = iter.next();
if (entry.getValue().isExpired())
iter.remove();
}
// Should I make sure that size is below max size?
// Deciding against it.
}
@Override
public int entryCount() {
return storage.size();
}
@Override
public TValue delete(TKey key) {
if (key == null)
throw new NullPointerException("The key must not be null");
return storage.remove(key).data;
}
/**
* Sets the maximum size of the cache
*
* @param size
* the new size of the cache
* @param cleanNow
* whether or not to clean the cache now
*/
public void setMaxSize(int size, boolean cleanNow) {
if (size < 1)
throw new IllegalArgumentException("Size most be greater than 0");
maxSize = size;
if (cleanNow)
clean();
}
/**
* The wrapper for an item in the cache. Keeps track of the data and the
* time the item was stored and how long it should be kept.
*
* @author Justin Nelson
*
*/
class CacheItem {
private TValue data;
private long expiresTime;
private CacheItem(TValue data, long maxAge) {
this.data = data;
this.expiresTime = System.currentTimeMillis() + maxAge;
}
private boolean isExpired() {
return System.currentTimeMillis() >= expiresTime;
}
}
}