// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source:
// /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/util/cacheHandler/CacheHandler.java,v
// $
// $RCSfile: CacheHandler.java,v $
// $Revision: 1.2 $
// $Date: 2006/12/15 18:39:53 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.util.cacheHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A base cache support object. Based on the
* com.bbn.openmap.layer.util.cacheHandler package components, this CacheHandler
* uses Objects instead of Strings and will be replacing the earlier version.
*
* @author dietrick
*/
public abstract class CacheHandler {
protected CacheObject[] objs;
protected int logicalClock;
public static Logger logger = Logger.getLogger("com.bbn.openmap.util.cacheHandler.CacheHandler");
public static int DEFAULT_MAX_CACHE_SIZE = 25;
/**
* Standard default constructor
*/
public CacheHandler() {
initCache(DEFAULT_MAX_CACHE_SIZE);
}
/**
* Constructor used when you know the limits
*/
public CacheHandler(int max_size) {
initCache(max_size);
}
/**
* Set the size, reset the logical clock
*/
private void initCache(int max_size) {
if (objs != null && objs.length == max_size) {
clear();
} else {
objs = new CacheObject[max_size];
}
logicalClock = 0;
}
/**
* Remove all the objects from the cache.
*/
public void clear() {
if (objs != null) {
for (int i = 0; i < objs.length; i++) {
objs[i] = null;
}
}
}
/**
* Need to clear memory, get gc moving, and ready for new objects
*/
public void resetCache() {
initCache(objs.length);
}
/**
* Need to clear memory, get gc moving, and ready for new objects. Delete
* the current Hashtable and create a new one with the new capacity.
*
* @param max_size the capacity of the Hashtable.
*/
public void resetCache(int max_size) {
initCache(max_size);
}
/**
* Get the current size of the cache.
*/
public int getCacheSize() {
return objs.length;
}
/**
* The main call to retrieve something from the cache
*/
public Object get(Object key) {
CacheObject ret = searchCache(key);
if (ret != null)
return ret.obj;
ret = load(key);
if (ret == null)
return null;
replaceLeastUsed(ret);
return ret.obj;
}
/**
* Called from get if the key doesn't exist, to "load" the new object into
* the cache before returning it. This function should define how a
* CacheObject is created, or loaded from the file system, or whatever.
*/
public abstract CacheObject load(Object key);
/**
* Search the cache for a match -return null if not found. The key search is
* case insensitive.
*/
public CacheObject searchCache(Object key) {
for (int i = 0; i < objs.length; i++) {
CacheObject co = objs[i];
if (co == null) {
// Since we load 0 -> length - 1, if we get a null
// one, the rest are null, too.
break;
} else if (co.id.equals(key)) {
return co;
}
}
return null;
}
/**
* If there is space in the cache, put the object in. If there isn't space,
* find the least used object, and replace it.
*/
protected void replaceLeastUsed(CacheObject newObj) {
// If the cache has room...
int i;
for (i = objs.length - 1; i >= 0; i--) {
if (objs[i] == null) {
// Somewhere in a partially filled cache, keep looking
// for the last taken place...
if (i == 0) {
// there is nothing in the cache.
objs[0] = newObj;
if (logger.isLoggable(Level.FINE)) {
logger.fine("was empty - added " + newObj.id);
}
return;
} else {
continue;
}
} else if (i == objs.length - 1) {
// We're at the end, and there is no empty space -
// we'll need to look at the LRU clock.
break;
} else {
// We are at the index of the last taken spot, and the
// next place is available.
objs[i + 1] = newObj;
if (logger.isLoggable(Level.FINE)) {
logger.fine("had room - added " + newObj.id + " to the "
+ i + " spot.");
}
return;
}
}
// If we get here, we need to replace something in the cache.
int minClock = logicalClock + 1;
int LUIndex = -1;
for (i = objs.length - 1; i >= 0; i--) {
if (objs[i].older(minClock)) {
LUIndex = i;
minClock = objs[i].cachedTime;
}
}
if (LUIndex != -1) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Tossing " + objs[LUIndex].id
+ " from cache[" + LUIndex + "] to add " + newObj.id);
}
objs[LUIndex] = newObj;
newObj.cachedTime = logicalClock++;
}
}
/**
* Return a ListIterator of the cache objects.
*/
public java.util.ListIterator<CacheObject> listIterator() {
return java.util.Arrays.asList(objs).listIterator();
}
}