content: package edu.berkeley.nlp.lm.collections;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.concurrent.Semaphore;
  
  /**
   * Maintains a two-way map between a set of objects and contiguous integers from
   * 0 to the number of objects.
   * 
   * @author Dan Klein
   * @author Adam Pauls
   */
  public class Indexer<E extends Comparable<E>> implements Serializable
  {
  	private static final long serialVersionUID = -8769544079136550516L;
  
  	protected ArrayList<E> objects;
  
  	protected TIntMap<E> indexes;
  
  	protected boolean locked = false;
  
  	private Semaphore sem;
  
  	public void lock() {
  		this.locked = true;
  	}
  
  	public E getObject(final int index) {
  		return objects.get(index);
  	}
  
  	public boolean add(final E elem) {
  		final int oldSize = size();
  		return getIndex(elem) >= oldSize;
  	}
  
  	/**
  	 * Returns the number of objects indexed.
  	 */
  	public int size() {
  		return objects.size();
  	}
  
  	/**
  	 * Returns the index of the given object, or -1 if the object is not present
  	 * in the indexer.
  	 * 
  	 * @param o
  	 * @return
  	 */
  	public int indexOf(final E o) {
  		final int index = indexes.get(o, -1);
  
  		return index;
  	}
  
  	/**
  	 * Return the index of the element If doesn't exist, add it.
  	 */
  	public int getIndex(final E e) {
  		if (e == null) return -1;
  
  		if (sem != null) sem.acquireUninterruptibly();
  		int index = indexes.get(e, -1);
  		if (index < 0) {
  			if (locked) throw new RuntimeException("Attempt to add to locked indexer");
  			index = size();
  			objects.add(e);
  			assert size() >= 0 : "Too many objects in indexer";
  			indexes.put(e, index);
  		}
  		if (sem != null) sem.release();
  		return index;
  	}
  
  	public Indexer(final boolean sync) {
  		objects = new ArrayList<E>();
  		indexes = new TIntMap<E>();
  		this.sem = sync ? new Semaphore(1) : null;
  	}
  
  	public Indexer() {
  		this(false);
  	}
  
  	public Indexer(final Collection<? extends E> c) {
  		this();
  		for (final E a : c)
  			getIndex(a);
  	}
  
  	/**
  	 * Save some space my compacting underlying maps and lists.
  	 */
  	public void trim() {
  		objects.trimToSize();
  		//		indexes.switchToSortedList();
  	}
  
  	public Iterable<E> getObjects() {
  		return Collections.unmodifiableList(objects);
  	}
  
  }
  