
org.enhydra.xml.xmlc.deferredparsing.Cache Maven / Gradle / Ivy
The newest version!
/*
* Cache.java
*
* Created: Fri Dec 21 11:38:14 2001
* Author : Ole Arndt
*/
package org.enhydra.xml.xmlc.deferredparsing;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* A Cache for arbitrary objects. It holds its objects with soft
* references. Because broken handling of soft references in
* current JVM implementations, we also hold additional hard
* references to the most recently used entries.
*
* @author Ole Arndt
* @version $Id: Cache.java,v 1.3 2005/01/26 08:29:24 jkjome Exp $
*/
public class Cache implements Map {
/**
* A simple class for holding references to
* the most recently used entries.
*/
protected static class MRUCache {
private static final class Entry {
public Object ref;
public Cache.MRUCache.Entry prev;
public Cache.MRUCache.Entry next;
}
/** an array to hold the references. */
private Entry[] cache;
/** list of free entries */
private Entry freelist;
/** Current index into ref array. */
private Entry header;
/**
* Creates a new MRUCache
instance.
*
* @param num an int
value
*/
MRUCache(int num) {
cache = new Entry[num];
for (int i = 0; i < cache.length; ++i)
cache[i] = new Entry();
header = new Entry();
this.clear();
}
/**
* Describe getCapacity
method here.
*
* @return an int
value
*/
int getCapacity() {
return cache.length;
}
/**
* Describe add
method here.
*
* @param obj an Object
value
* @return an Object
value
*/
synchronized Object add(Object obj) {
// if object is already at front of cache
// return immediately
if (obj == null || obj == header.next.ref)
return obj;
Entry entry = null;
if ((entry = find(obj)) != null) {
extractEntry(entry);
} else if (freelist != null) {
entry = freelist;
freelist = entry.next;
} else {
// remove from back
entry = header.prev;
extractEntry(entry);
}
entry.ref = obj;
// insert to front
entry.next = header.next;
entry.prev = header;
entry.next.prev = entry;
header.next = entry;
return obj;
}
/**
* Remove an object from the cache.
*
* @param obj an Object
value
*/
synchronized void remove(Object obj) {
Entry entry = null;
if (obj != null && (entry = find(obj)) != null) {
extractEntry(entry);
entry.ref = null;
entry.next = freelist;
freelist = entry;
}
}
/**
* Clear the MRU cache.
*/
synchronized void clear() {
header.next = header.prev = header;
for (int i = 0; i < (cache.length - 1); ++i) {
cache[i].next = cache[i + 1];
cache[i].ref = null;
}
freelist = cache[0];
cache[cache.length - 1].next = null;
cache[cache.length - 1].ref = null;
}
/**
* Find an entry by object.
*
* @param obj an Object
value
* @return an Object
value
*/
private Entry find(Object obj) {
for (Entry e = header.next; e != header; e = e.next) {
if (e.ref == obj)
return e;
}
return null;
}
/**
* Extract an entry.
*
* @param e an Entry
value
*/
private void extractEntry(Entry entry) {
entry.next.prev = entry.prev;
entry.prev.next = entry.next;
}
}
/**
* An inner class to hold the mappings.
*/
private class MapEntry extends WeakReference implements Map.Entry {
/** the key */
private Object key;
/**
* Creates a new MapEntry
instance.
*
* @param key an Object
value
* @param value an Object
value
* @param queue the reference queue this entry will be enqueued on.
*/
MapEntry(Object key, Object value, ReferenceQueue queue) {
super(value, queue);
this.key = key;
}
/**
* Get the Key value.
* @return the Key value.
*/
public Object getKey() {
return key;
}
/**
* Get the Value value.
* @return the Value value.
*/
public Object getValue() {
return this.get();
}
/**
* Set the Value value.
* @param value The new Value value.
*/
public Object setValue(Object value) {
return Cache.this.put(key, value);
}
public boolean equals(Object o) {
return (o!=null) &&
(o instanceof MapEntry) &&
(key.equals(((MapEntry)o).getKey()));
}
public int hashCode() {
return key.hashCode();
}
}
/**
* An iterator for the entry set.
*/
private class ValueIterator implements Iterator {
Iterator it;
public ValueIterator(Iterator it) {
this.it = it;
}
public boolean hasNext() {
return it.hasNext();
}
public Object next() {
return mru.add(((MapEntry) it.next()).getValue());
}
public void remove() {
it.remove();
}
}
/** the default size for the MRU cache */
public static final int DEFAULT_NUM_MRU_ENTRIES = 64;
/** the reference queue for collecting old references */
private ReferenceQueue queue = new ReferenceQueue();
/** the underlying map */
private HashMap map = new HashMap();
/** the MRU cache */
private MRUCache mru;
/**
* Creates a new Cache
instance with the
* default value for most recently used entries.
*
*/
public Cache () {
this(DEFAULT_NUM_MRU_ENTRIES);
}
/**
* Creates a new Cache
instance with an
* explicit value for most recently used entries cache.
*
* @param numMRUEntries an int
value
*/
public Cache (int numMRUEntries) {
mru = new MRUCache(numMRUEntries);
}
/**
* Creates a new Cache
instance.
* The give Map
will be copied.
*
* @param other a Map
value
*/
public Cache (Map other) {
this();
putAll(other);
}
/**
* Creates a new Cache
instance.
* The give Map
will be copied.
*
* @param other a Map
value
*/
public Cache (int numMRUEntries, Map other) {
this(numMRUEntries);
putAll(other);
}
/**
* Get hash code of map.
*
* @return an int
value
*/
public int hashCode() {
return map.hashCode();
}
/**
* Compare two Cache
instances.
*
* @param cache other cache to compare to.
* @return true if caches are equal, false otherwise.
*/
public boolean equals(Object cache) {
return (cache instanceof Cache) && map.equals(((Cache)cache).map);
}
/**
* Get the current size of the cache.
*
* @return the size of the cache.
*/
public int size() {
return map.size();
}
/**
* Describe clear
method here.
*
*/
public void clear() {
mru.clear();
map.clear();
}
/**
* Associates the specified value with the specified key in this cache.
* If the cache previously contained a mapping for this key, the old value is replaced.
* Note that this cache can not map null
values.
*
* @param key key with which the specified value is to be associated.
* @param value value to be associated with the specified key.
* @return previous value associated with specified key, or null if there was no mapping for key.
* @throws NullPointerException if value
is null
.
*/
public synchronized Object put(Object key, Object value) {
// cannot map null values, they do not differ from
// cleared soft references.
if (value == null)
return new UnsupportedOperationException("Cache does not support null values");
// clean up before us, so we don't fill the memory
// with keys if user calls only #put and never #get
cleanup();
MapEntry entry = (MapEntry) map.put(key, new MapEntry(key, value, queue));
return (entry != null) ? entry.getValue() : null;
}
/**
* Returns the value to which this cache maps the specified key.
* If the value is not found, returns null
*
* @param key key whose associated value is to be returned.
* @return the value to which this cache maps the specified key,
* or null
if the cache contains no mapping for
* this key.
*/
public synchronized Object get(Object key) {
Object value = null;
MapEntry entry = null;
cleanup();
if ((entry = (MapEntry) map.get(key)) != null)
value = entry.getValue();
return mru.add(value);
}
/**
* Return a collection of all values this cache contains.
* It is not guaranteed, that an iterator from the collection
* will return only non null values. It is likely some soft (weak) references
* will go away while traversing the Collection. The iterator will return
* null
in this case.
*
* @return a Collection
containing a values of the cache.
*/
public Collection values() {
return new AbstractCollection() {
public int size() {
return Cache.this.size();
}
public Iterator iterator() {
return getValueIterator();
}
};
}
/**
* Removes the mapping for this key from this cache.
*
* @param key key whose mapping is to be removed from the cache.
* @return previous value associated with specified key, or null if there was no mapping for key.
*/
public synchronized Object remove(Object key) {
cleanup();
mru.remove(key);
MapEntry entry = (MapEntry) map.remove(key);
return (entry != null) ? entry.getValue() : null;
}
/**
* Return a Set
of all keys.
*
* @return a Set
of all keys.
*/
public Set keySet() {
cleanup();
return map.keySet();
}
/**
* Returns a set view of the mappings contained in this cache.
* Each element in the returned set is a Map.Entry. The set is backed by the cache,
* so changes to the cache are reflected in the set, and vice-versa.
* If the map is modified while an iteration over the set is in progress,
* the results of the iteration are undefined.
* The set supports element removal, which removes the corresponding mapping from the map,
* via the Iterator.remove, Set.remove, removeAll, retainAll and clear operations.
* It does not support the add or addAll operations.
*
* @return a Set
view of all values.
*/
public Set entrySet() {
cleanup();
return map.entrySet();
}
/**
* Map interface, isEmpty
method.
*
* @return true if map is empty, false otherwise
* @see java.util.Map#isEmpty
*/
public boolean isEmpty() {
cleanup();
return map.isEmpty();
}
/**
* Describe containsValue
method here.
*
* @param obj an Object
value
* @return a boolean
value
*/
public boolean containsValue(Object obj) {
for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
MapEntry entry = (MapEntry) it.next();
Object value = entry.getValue();
if (value != null && value.equals(obj))
return true;
}
return false;
}
/**
* Describe containsKey
method here.
*
* @param key an Object
value
* @return a boolean
value
*/
public boolean containsKey(Object key) {
return map.containsKey(key);
}
/**
* Describe putAll
method here.
*
* @param cache a Map
value
*/
public void putAll(Map cache) {
for (Iterator it = cache.keySet().iterator(); it.hasNext(); ) {
Object key = it.next();
put(key, cache.get(key));
}
}
/**
* Clean up the expired references from the reference queue.
*/
private synchronized void cleanup() {
MapEntry entry;
while ((entry = (MapEntry)queue.poll()) != null) {
map.remove(entry.getKey());
}
}
private Iterator getValueIterator() {
cleanup();
return new ValueIterator(map.entrySet().iterator());
}
} // Cache
© 2015 - 2025 Weber Informatics LLC | Privacy Policy