package arkref.ext.fig.basic;
import java.util.Map;
import java.util.Set;
/**
* Canonicalizes objects. Given an object, the intern() method returns a
* canonical representation of that object, that is, an object which equals()
* the input. Furthermore, given two objects x and y, it is guaranteed that if
* x.equals(y), then intern(x) == intern(y). The default behavior is that the
* interner is backed by a HashMap and the canonical version of an object x is
* simply the first object that equals(x) which is passed to the interner. In
* this case, it can be true that intern(x) == x. The backing map can be
* specified by passing a MapFactory on construction (though the only standard
* option which makes much sense is the WeakHashMap, which is slower than a
* HashMap, but which allows unneeded keys to be reclaimed by the garbage
* collector). The source of canonical elements can be changed by specifying an
* Interner.Factory on construction.
*
* @author Dan Klein
*/
public class Interner <T> {
/**
* The source of canonical objects when a non-interned object is presented to
* the interner. The default implementation is an identity map.
*/
public static interface CanonicalFactory <T> {
T build(T object);
}
static class IdentityCanonicalFactory <T> implements CanonicalFactory<T> {
public T build(T object) {
return object;
}
}
Map<T, T> canonicalMap;
CanonicalFactory<T> cf;
public Set<T> getCanonicalElements() {
return canonicalMap.keySet();
}
public boolean isCanonical(T x) {
return canonicalMap.containsKey(x);
}
/**
* Returns a canonical representation of the given object. If the object has
* no canonical representation, one is built using the interner's
* CanonicalFactory. The default is that new objects will be their own
* canonical instances.
*
* @param object
* @return a canonical representation of that object
*/
public T intern(T object) {
T canonical = canonicalMap.get(object);
if (canonical == null) {
canonical = cf.build(object);
canonicalMap.put(canonical, canonical);
}
return canonical;
}
// Like intern, but don't save object if it's not interned already.
public T getCanonical(T object) {
T canonical = canonicalMap.get(object);
return canonical == null ? object : canonical;
}
public int size( ) {
return canonicalMap.size();
}
public Interner() {
this(new MapFactory.HashMapFactory<T,T>(), new IdentityCanonicalFactory<T>());
}
public Interner(MapFactory<T,T> mf) {
this(mf, new IdentityCanonicalFactory<T>());
}
public Interner(CanonicalFactory<T> f) {
this(new MapFactory.HashMapFactory<T,T>(), f);
}
public Interner(MapFactory<T,T> mf, CanonicalFactory<T> cf) {
canonicalMap = mf.buildMap();
this.cf = cf;
}
}