// ----------------------------------------------------------------------
// This code is developed as part of the Java CoG Kit project
// The terms of the license can be found at http://www.cogkit.org/license
// This message may not be removed or altered.
// ----------------------------------------------------------------------

/*
 * Created on May 11, 2004
 */
package org.globus.cog.karajan.util;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;

/*
 * Most system defined elements are not cached (they are considered to be
 * relatively fast). User defined elements are cached. Functions that have both
 * arguments and return value as primitive types are cached using a combination
 * of MRU and MFU. Elements that accept variable arguments should not be cached
 */
public class Cache implements Serializable {
	private static final long serialVersionUID = -8944421098726077701L;
	
	private final static int ENTRY_POOL_MAX_SIZE = 25;
	private final static int MAX_CACHE_SIZE = 1000;
	private final static int CACHE_PURGE_SIZE = 100;
	private HashMap primitives;
	
	public Cache() {
		primitives = new HashMap();
	}

	public Object getCachedValue(Object key) {
		Value value = (Value) primitives.get(key);
		if (value != null) {
			value.hits++;
			value.lastHit = System.currentTimeMillis();
			return value.object;
		}
		return null;
	}

	public boolean isCached(Object key) {
		return primitives.containsKey(key);
	}

	public void addValue(Object key, Object value) {
		primitives.put(key, new Value(value));
		checkSize();
	}

	private void checkSize() {
		if (primitives.size() > MAX_CACHE_SIZE) {
			long[] scores = new long[primitives.size()];
			int index = 0;
			Iterator i = primitives.values().iterator();
			while (i.hasNext()) {
				Value v = (Value) i.next();
				scores[index++] = score(v.hits, v.lastHit);
			}
			Arrays.sort(scores);
			long threshold = scores[CACHE_PURGE_SIZE];
			i = primitives.keySet().iterator();
			while (i.hasNext()) {
				Object key = i.next();
				Value v = (Value) primitives.get(key);
				if (score(v.hits, v.lastHit) < threshold) {
					i.remove();
				}
			}
		}
	}

	private long score(int hits, long lastHit) {
		return hits + lastHit / 10000;
	}

	private static class Value {

		public int hits;
		public long lastHit;
		public Object object;

		public Value(Object object) {
			hits = 0;
			lastHit = System.currentTimeMillis();
			this.object = object;
		}
	}

	public interface Cacheable {

		/*
		 * This allows objects to implement their own way of verifying whether
		 * they are cached or not. The method should return null if it results
		 * in a hit. Otherwise, it should return some metadata that can be used
		 * for further verification, and which will be passed as an argument at
		 * the next verification.
		 */
		Object verify(Object stored);
	}
}
@@ -1124,16 +1124,62 @@
 ives;%0A%09%0A
+%09private int maxCacheSize = MAX_CACHE_SIZE;%0A%09%0A
 %09public 
@@ -1184,24 +1184,24 @@
 c Cache() %7B%0A
-
 %09%09primitives
@@ -1670,16 +1670,36 @@
 %7B%0A%09%09if (
+maxCacheSize %3E 0 && 
 primitiv
@@ -1710,30 +1710,28 @@
 ize() %3E 
-MAX_CACHE_SIZE
+maxCacheSize
 ) %7B%0A%09%09%09l
@@ -2280,24 +2280,24 @@
  lastHit) %7B%0A
-
 %09%09return hit
@@ -2315,24 +2315,173 @@
  / 10000;%0A%09%7D
+%0A%09%0A%09public int getMaxCacheSize() %7B%0A%09%09return maxCacheSize;%0A%09%7D%0A%0A%09public void setMaxCacheSize(int maxCacheSize) %7B%0A%09%09this.maxCacheSize = maxCacheSize;%0A%09%7D
 %0A%0A%09private s

allow control over cache size