package edu.berkeley.nlp.util; import java.util.Collection; import java.util.Map; /** * 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; /** * 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; } /** * Does the interner already have * a canonical copy of this object? * * @param object * @return whether or not the interner contains the object * @author aria42 */ public boolean contains(T object) { return canonicalMap.containsKey(object); } /** * How many interned objects are there? * * @param size * @author aria42 */ public int size() { return canonicalMap.size(); } /** * Returns a collection of the canonical objects * in the interner * @return canonicalObjects * @author aria42 */ public Collection<T> getCanonicalObjects() { return canonicalMap.values(); } 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; } /** * Clear the contents of the interner * @author aria42 */ public void clear() { canonicalMap.clear(); } }