/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven;
import java.lang.ref.*;
import java.util.*;
public class CacheMap<K, V> extends AbstractMap<K, V> {
private final Map<K, Reference<V>> back;
private final ReferenceQueue<V> cleanq = new ReferenceQueue<V>();
private final RefType reftype;
/* Because multiple inheritence would be too good. */
interface Ref<K> {
K key();
}
static class SRef<K, V> extends SoftReference<V> implements Ref<K> {
final K key;
SRef(K key, V val, ReferenceQueue<V> queue) {
super(val, queue);
this.key = key;
}
public K key() {return(this.key);}
}
static class WRef<K, V> extends WeakReference<V> implements Ref<K> {
final K key;
WRef(K key, V val, ReferenceQueue<V> queue) {
super(val, queue);
this.key = key;
}
public K key() {return(this.key);}
}
public static enum RefType {
SOFT {
public <K, V> Reference<V> mkref(K k, V v, ReferenceQueue<V> cleanq) {
return(new SRef<K, V>(k, v, cleanq));
}
}, WEAK {
public <K, V> Reference<V> mkref(K k, V v, ReferenceQueue<V> cleanq) {
return(new WRef<K, V>(k, v, cleanq));
}
};
public abstract <K, V> Reference<V> mkref(K k, V v, ReferenceQueue<V> cleanq);
}
public CacheMap(RefType type) {
this.reftype = type;
this.back = new HashMap<K, Reference<V>>();
}
public CacheMap() {
this(RefType.SOFT);
}
public CacheMap(Map<K, V> m) {
this();
putAll(m);
}
public boolean containsKey(Object k) {
return(get(k) != null);
}
private class IteredEntry implements Entry<K, V> {
private final K k;
private V v;
private IteredEntry(K k, V v) {
this.k = k; this.v = v;
}
public K getKey() {return(k);}
public V getValue() {return(v);}
public boolean equals(Object o) {
return((o instanceof CacheMap.IteredEntry) && (((IteredEntry)o).k == k));
}
public int hashCode() {
return(k.hashCode());
}
public V setValue(V nv) {
return(put(k, this.v = nv));
}
}
private Set<Entry<K, V>> entries = null;
public Set<Entry<K, V>> entrySet() {
if(entries == null)
entries = new AbstractSet<Entry<K, V>>() {
public int size() {
clean();
return(back.size());
}
public Iterator<Entry<K, V>> iterator() {
clean();
final Iterator<Entry<K, Reference<V>>> iter = back.entrySet().iterator();
return(new Iterator<Entry<K, V>>() {
private K nk;
private V nv;
public boolean hasNext() {
while(true) {
if(nv != null)
return(true);
if(!iter.hasNext())
return(false);
Entry<K, Reference<V>> e = iter.next();
K k = e.getKey();
V v = e.getValue().get();
if(v != null) {
nk = k; nv = v;
return(true);
}
}
}
public Entry<K, V> next() {
if(!hasNext())
throw(new NoSuchElementException());
Entry<K, V> ret = new IteredEntry(nk, nv);
nk = null; nv = null;
return(ret);
}
public void remove() {
iter.remove();
}
});
}
public void clear() {
back.clear();
}
};
return(entries);
}
private void clean() {
Reference<? extends V> ref;
while((ref = cleanq.poll()) != null) {
Ref rr = (Ref)ref;
remove(rr.key());
}
}
public V get(Object k) {
clean();
Reference<V> ref = back.get(k);
return((ref == null)?null:(ref.get()));
}
public V put(K k, V v) {
clean();
Reference<V> old = back.put(k, reftype.mkref(k, v, cleanq));
return((old == null)?null:(old.get()));
}
public V remove(Object k) {
clean();
Reference<V> ref = back.remove(k);
return((ref == null)?null:(ref.get()));
}
}