package org.itsnat.droid.impl.domparser;
import org.itsnat.droid.impl.dom.TimestampExtended;
import org.itsnat.droid.impl.dom.TimestampExtendedComparator;
import org.itsnat.droid.impl.util.MiscUtil;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* Created by jmarranz on 27/10/14.
*/
public class ResourceCache<T>
{
public static final int MAX_ENTRIES = 30;
protected Map<String,ResourceTimestamped<T>> registryByKey = new HashMap<String,ResourceTimestamped<T>>();
protected TreeMap<TimestampExtended,ResourceTimestamped<T>> registryByTimestamp = new TreeMap<TimestampExtended,ResourceTimestamped<T>>(new TimestampExtendedComparator()); // Recuerda que es un SortedMap de menor a mayor por defecto.
public ResourceCache()
{
}
public synchronized void clear()
{
registryByKey.clear();
registryByTimestamp.clear();
}
public synchronized T get(String key)
{
ResourceTimestamped<T> resourceTimestamped = registryByKey.get(key);
if (resourceTimestamped == null)
return null;
// Actualizamos el timestamp para que quede constancia de que al requerirlo es porque es de uso frecuente y no merece perderse respecto a otros con menos uso
TimestampExtended timestamp = resourceTimestamped.getTimestampExtended();
registryByTimestamp.remove(timestamp);
timestamp.update();
generateUniqueTimestamp(timestamp);
registryByTimestamp.put(timestamp, resourceTimestamped); // Así hacemos ver que esta página se está usando recientemente y no será candidata a eliminarse
return resourceTimestamped.getResource();
}
private synchronized void remove(String key)
{
ResourceTimestamped<T> resourceTimestamped = registryByKey.remove(key);
if (resourceTimestamped == null) return;
registryByTimestamp.remove(resourceTimestamped.getTimestampExtended());
}
public synchronized void put(String key,T resource)
{
remove(key);
if (registryByKey.size() >= MAX_ENTRIES) // El > es por si acaso, un == sería suficiente
{
Iterator<Map.Entry<TimestampExtended,ResourceTimestamped<T>>> it = registryByTimestamp.entrySet().iterator();
Map.Entry<TimestampExtended,ResourceTimestamped<T>> oldestItem = it.next();
it.remove();
ResourceTimestamped<T> resourceTimestampedToRemove = oldestItem.getValue();
registryByKey.remove(resourceTimestampedToRemove.getKey());
}
ResourceTimestamped<T> resourceTimestamped = new ResourceTimestamped<T>(key,resource);
ResourceTimestamped<T> res;
res = registryByKey.put(key,resourceTimestamped);
if (res != null) throw MiscUtil.internalError();
TimestampExtended timestamp = resourceTimestamped.getTimestampExtended();
generateUniqueTimestamp(timestamp);
res = registryByTimestamp.put(timestamp, resourceTimestamped);
if (res != null) throw MiscUtil.internalError();
}
private synchronized void generateUniqueTimestamp(TimestampExtended timestamp)
{
// Es posible cargar varios recursos en el mismo ms por ej en el caso de "values" contenidos en el mismo archivo
// Evitamos que haya 2 con el nuevo timestamp extendido, de esta forma nos aseguramos la unicidad del registro dado un timestamp/complementary
while (true)
{
ResourceTimestamped<T> resourceTimestampedOld = registryByTimestamp.get(timestamp);
if (resourceTimestampedOld == null) break;
timestamp.incComplementary();
}
}
}