code: 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();
  	}
  
  }
  