code: package edu.berkeley.nlp.io;
  
  import java.io.Serializable;
  import java.util.Collection;
  import java.util.Map;
  
  import edu.berkeley.nlp.util.IntPair;
  import edu.berkeley.nlp.util.MapFactory;
  
  /**
   * An abstract class for Label objects which store attributes in a Map. Also
   * implements HasWord, HasTag, HasCategory, and HasContext by storing the words,
   * tags, etc. under standardized keys in the Map.
   * <p/>
   * For convenience, this class also contains standardized keys for storing many
   * other possible label attributes, such as head words, ner tags, etc.
   * 
   * @author grenager
   */
  public abstract class AbstractMapLabel implements Label, Serializable {
  
  	static final int initCapacity = 5;
  
  	// THE STANDARD KEYS
  
  	/**
  	 * The standard key for storing a value in the map, as a String.
  	 */
  	public static final String VALUE_KEY = "value";
  
  	/**
  	 * The standard key for storing a tag in the map.
  	 */
  	public static final String TAG_KEY = "tag";
  
  	/**
  	 * The standard key for storing a word in the map, as a String.
  	 */
  	public static final String WORD_KEY = "word";
  
  	/**
  	 * The standard key for storing a lemma in the map.
  	 */
  	public static final String LEMMA_KEY = "lemma";
  
  	/**
  	 * The standard key for storing a category in the map, as a String.
  	 */
  	public static final String CATEGORY_KEY = "cat";
  
  	/**
  	 * The standard key for storing a projected category in the map, as a
  	 * String. For any word (leaf node), the projected category is the syntactic
  	 * category of the maximal constituent headed by the word. Used in
  	 * SemanticGraph.
  	 */
  	public static final String PROJ_CAT_KEY = "pcat";
  
  	/**
  	 * The standard key for storing a head word in the map as a pointer to
  	 * another node.
  	 */
  	public static final String HEAD_WORD_KEY = "hw";
  
  	/**
  	 * The standard key for storing a head tag in the map as a pointer to
  	 * another node.
  	 */
  	public static final String HEAD_TAG_KEY = "ht";
  
  	/**
  	 * The standard key for storing an integer index in the map.
  	 */
  	public static final String INDEX_KEY = "idx";
  
  	/**
  	 * The standard key for a propbank label which is of type Argument
  	 */
  	public static final String ARG_KEY = "arg";
  
  	/**
  	 * Another key used for propbank - to signify core arg nodes or predicate
  	 * nodes
  	 */
  	public static final String MARKING_KEY = "mark";
  
  	/**
  	 * The standard key for Semantic Head Word which is a String
  	 */
  	public static final String SEMANTIC_HEAD_WORD_KEY = "shw";
  
  	/**
  	 * The standard key for Semantic Head Word POS which is a String
  	 */
  	public static final String SEMANTIC_HEAD_POS_KEY = "shp";
  
  	/**
  	 * Probank key for the Verb sense given in the Propbank Annotation, should
  	 * only be in the verbnode
  	 */
  	public static final String VERB_SENSE_KEY = "vs";
  
  	/**
  	 * The standard key for storing category with functional tags.
  	 */
  	public static final String CATEGORY_FUNCTIONAL_TAG_KEY = "cft";
  
  	/**
  	 * the standard key for the NER label.
  	 */
  	public static final String NER_KEY = "ner";
  
  	/**
  	 * the standard key for the coref label.
  	 */
  	public static final String COREF_KEY = "coref";
  
  	/**
  	 * The standard key for the "shape" of a word: a String representing the
  	 * type of characters in a word, such as "Xx" for a capitalized word. See
  	 * {@link edu.stanford.nlp.process.WordShapeClassifier} for functions for
  	 * making shape strings.
  	 */
  	public static final String SHAPE_KEY = "shape";
  
  	/**
  	 * The Standard key for storing the left terminal number relative to the
  	 * root of the tree of the leftmost terminal dominated by the current node
  	 */
  	public static final String LEFT_TERM_KEY = "LEFT_TERM";
  
  	/**
  	 * The standard key for the parent which is a String
  	 */
  	public static final String PARENT_KEY = "PARENT";
  
  	/**
  	 * The standard key for span which is a String
  	 */
  	public static final String SPAN_KEY = "SPAN";
  
  	/**
  	 * the standard key for the String that comes before this word (from the
  	 * InvertiblePTBTokenizer)
  	 */
  	public static final String BEFORE_KEY = "before";
  
  	/**
  	 * the standard key for the String that comes after this word (from the
  	 * InvertiblePTBTokenizer)
  	 */
  	public static final String AFTER_KEY = "after";
  
  	/**
  	 * the standard key for the actual, unmangled, pre-PTB'd word (from the
  	 * InvertiblePTBTokenizer)
  	 */
  	public static final String CURRENT_KEY = "current";
  
  	/**
  	 * The standard key for the answer which is a String
  	 */
  	public static final String ANSWER_KEY = "answer";
  
  	/**
  	 * The standard key for gold answer which is a String
  	 */
  	public static final String GOLDANSWER_KEY = "goldAnswer";
  
  	/**
  	 * The standard key for the features which is a Collection
  	 */
  	public static final String FEATURES_KEY = "features";
  
  	/**
  	 * The standard key for the semantic interpretation
  	 */
  	public static final String INTERPRETATION_KEY = "interpretation";
  
  	/**
  	 * The standard key for the semantic role label
  	 */
  	public static final String ROLE_KEY = "srl";
  
  	/**
  	 * The standard key for the gazetteer information
  	 */
  	public static final String GAZETTEER_KEY = "gazetteer";
  
  	public static final String STEM_KEY = "stem";
  
  	public static final String POLARITY_KEY = "polarity";
  
  	/**
  	 * for Chinese: character level information, segmentation
  	 */
  	public static final String CH_CHAR_KEY = "char";
  	public static final String CH_ORIG_SEG_KEY = "orig_seg"; // the segmentation
  																// info existing
  																// in the
  																// original text
  	public static final String CH_SEG_KEY = "seg"; // the segmentation
  													// information from the
  													// segmenter
  
  	/** This key is at present only used in CraigslistDemo. */
  	public static final String BEGIN_POSITION_KEY = "BEGIN_POS";
  
  	/** The character offset of last character of token in source. */
  	public static final String END_POSITION_KEY = "END_POS";
  
  	/**
  	 * The Map which stores all of the attributes for this label, and the label
  	 * value itself.
  	 */
  	protected Map map;
  
  	/**
  	 * The MapFactory which will be used to make new Maps in this
  	 * AbstractMapLabel.
  	 */
  	protected MapFactory mapFactory;
  
  	protected AbstractMapLabel() {
  		this(null);
  	}
  
  	protected AbstractMapLabel(MapFactory mapFactory) {
  		if (mapFactory == null) {
  			this.mapFactory = new MapFactory.HashMapFactory();
  		} else {
  			this.mapFactory = mapFactory;
  		}
  		this.map = this.mapFactory.buildMap();
  	}
  
  	// DIRECT MAP FUNCTIONALITY
  
  	/**
  	 * Return the <code>Map</code> contained in this label.
  	 * 
  	 * @return the <code>Map</code> contained in this AbstractMapLabel
  	 */
  	public Map map() {
  		return map;
  	}
  
  	// yikes! [commented out by dramage 4/5/06]
  	//
  	// /**
  	// * Set the <code>Map</code> contained in this AbstractMapLabel to the
  	// * supplied <code>Map</code>.
  	// *
  	// * @param map the new <code>Map</code> for this label
  	// */
  	// public void setMap(Map map) {
  	// this.map = map;
  	// }
  
  	/**
  	 * Returns the value to which the map contained in this label maps the
  	 * specified key. Returns <code>null</code> if the map contains no mapping
  	 * for this key. (Analogous to {@link Map#get
  	 * <code>Map.get(Object key)</code>}.)
  	 * 
  	 * @param key
  	 *            key whose associated value is to be returned.
  	 * @return the value to which the map contained in this label maps the
  	 *         specified key, or <code>null</code> if the map contains no
  	 *         mapping for this key.
  	 */
  	public Object get(Object key) {
  		Object v = map.get(key);
  		if (v == null) {
  			return "";
  		}
  		return v;
  	}
  
  	/**
  	 * Associates the specified value with the specified key in the map
  	 * contained in this label. (Analogous to {@link Map#put
  	 * <code>Map.put(Object key, Object value)</code>}.) If the map previously
  	 * contained a mapping for this key, the old value is replaced by the
  	 * specified value.
  	 * 
  	 * @param key
  	 *            key with which the specified value is to be associated.
  	 * @param value
  	 *            value to be associated with the specified key.
  	 * @return previous value associated with specified key, or
  	 *         <code>null</code> if there was no mapping for key.
  	 */
  	public Object put(Object key, Object value) {
  		return map.put(key, value);
  	}
  
  	// LABEL METHODS
  
  	/**
  	 * @return the value for the label
  	 */
  	public String value() {
  		return (String) map.get(VALUE_KEY);
  	}
  
  	/**
  	 * Set the value for the label.
  	 * 
  	 * @param value
  	 *            the value for the label
  	 */
  	public void setValue(final String value) {
  		map.put(VALUE_KEY, value);
  	}
  
  	/**
  	 * Set value for the label from a String.
  	 * 
  	 * @param str
  	 *            the string value for the label
  	 */
  	public void setFromString(final String str) {
  		setValue(str);
  	}
  
  	// HASCATEGORY METHODS
  
  	/**
  	 * Return the category of the label (or <code>null</code> if none), which is
  	 * stored in the map under the key {@link AbstractMapLabel#CATEGORY_KEY
  	 * <code>CATEGORY_KEY</code>}.
  	 * 
  	 * @return the category for the label
  	 */
  	public String category() {
  		Object cat = map.get(CATEGORY_KEY);
  		if (cat != null && cat instanceof String) {
  			return (String) cat;
  		} else {
  			return null;
  		}
  	}
  
  	/**
  	 * Set the category for the label.
  	 * 
  	 * @param category
  	 *            the category for the label
  	 */
  	public void setCategory(final String category) {
  		map.put(CATEGORY_KEY, category);
  	}
  
  	// HASWORD METHODS
  
  	/**
  	 * Return the word of the label, stored in the map under the key
  	 * <code>WORD_KEY</code>.
  	 * 
  	 * @return The word for this label
  	 */
  	public String word() {
  		return (String) map.get(WORD_KEY);
  	}
  
  	/**
  	 * Set the word for the label.
  	 * 
  	 * @param word
  	 *            the head word for the label
  	 */
  	public void setWord(String word) {
  		map.put(WORD_KEY, word);
  	}
  
  	/**
  	 * The span of this node as begin and end positions if it exists
  	 * 
  	 * @return The span
  	 */
  	public IntPair span() {
  		return (IntPair) map.get(SPAN_KEY);
  	}
  
  	public void setSpan(String span) {
  		map.put(SPAN_KEY, span);
  	}
  
  	/**
  	 * Return the head word of the label (or <code>null</code> if none), which
  	 * is stored in the map under the key {@link AbstractMapLabel#HEAD_WORD_KEY
  	 * <code>HEAD_WORD_KEY</code>}.
  	 * 
  	 * @return the head word for the label
  	 */
  	public Object headWord() {
  		return map.get(HEAD_WORD_KEY);
  	}
  
  	/**
  	 * Set a pointer to the head-word for the label.
  	 */
  	public void setHeadWord(Object headWordPtr) {
  		map.put(HEAD_WORD_KEY, headWordPtr);
  		if (headWordPtr instanceof HasWord) {
  			setWord(((HasWord) headWordPtr).word());
  		} else if (headWordPtr instanceof Label) {
  			setWord(((Label) headWordPtr).value());
  		}
  	}
  
  	/**
  	 * Returns the semantic head of the phrase if it exists, and null otherwise
  	 */
  	public String getSemanticWord() {
  		Object word = map.get(SEMANTIC_HEAD_WORD_KEY);
  		return word != null ? word.toString() : null;
  	}
  
  	/**
  	 * Set the semantic head of the phrase
  	 */
  	public void setSemanticWord(final String hWord) {
  		map.put(SEMANTIC_HEAD_WORD_KEY, hWord);
  	}
  
  	/**
  	 * Returns the semantic head pos of the phrase if it exists, and null
  	 * otherwise
  	 */
  	public String getSemanticTag() {
  		Object word = map.get(SEMANTIC_HEAD_POS_KEY);
  		return word != null ? word.toString() : null;
  	}
  
  	/**
  	 * Set the semantic head pos of the phrase
  	 */
  	public void setSemanticTag(final String hTag) {
  		map.put(SEMANTIC_HEAD_POS_KEY, hTag);
  	}
  
  	/**
  	 * Return the head tag of the label (or <code>null</code> if none), which is
  	 * stored in the map under the key {@link AbstractMapLabel#TAG_KEY
  	 * <code>TAG_KEY</code>}.
  	 * 
  	 * @return the head tag for the label
  	 */
  	public String tag() {
  		Object tag = map.get(TAG_KEY);
  		if (tag != null && tag instanceof String) {
  			return (String) tag;
  		} else {
  			return null;
  		}
  	}
  
  	/**
  	 * Set the head tag for the label by storing it in the map under the key
  	 * {@link AbstractMapLabel#HEAD_TAG_KEY <code>HEAD_TAG_KEY</code>}.
  	 * 
  	 * @param tag
  	 *            the head tag for the label
  	 */
  	public void setTag(final String tag) {
  		map.put(TAG_KEY, tag);
  	}
  
  	public Object headTag() {
  		return map.get(HEAD_TAG_KEY);
  	}
  
  	/**
  	 * Set a pointer to the head-word for the label.
  	 */
  	public void setHeadTag(Object headTagPtr) {
  		map.put(HEAD_TAG_KEY, headTagPtr);
  		if (headTagPtr instanceof HasTag) {
  			setTag(((HasTag) headTagPtr).tag());
  		} else if (headTagPtr instanceof Label) {
  			setTag(((Label) headTagPtr).value());
  		}
  	}
  
  	/**
  	 * Return the NER type of the word, which is stored in the map under the key
  	 * {@link AbstractMapLabel#NER_KEY <code>NER_KEY</code>}.
  	 * 
  	 * @return The NER label of the word
  	 */
  	public String ner() {
  		Object ner = map.get(NER_KEY);
  		return (String) ner;
  	}
  
  	/**
  	 * Set the NER label for the word, using the key
  	 * {@link AbstractMapLabel#NER_KEY <code>NER_KEY</code>}.
  	 * 
  	 * @param ner
  	 *            The String NER label of the word
  	 */
  	public void setNER(String ner) {
  		map.put(NER_KEY, ner);
  	}
  
  	/**
  	 * Return the shape attribute of the word, which is stored in the map under
  	 * the key {@link AbstractMapLabel#SHAPE_KEY <code>SHAPE_KEY</code>}.
  	 * 
  	 * @return The shape of the word.
  	 */
  	public String shape() {
  		return (String) map.get(SHAPE_KEY);
  	}
  
  	/**
  	 * Set the shape property for the word, using the key
  	 * {@link AbstractMapLabel#SHAPE_KEY <code>SHAPE_KEY</code>}.
  	 * 
  	 * @param shape
  	 *            A String giving the "shape" of the word.
  	 */
  	public void setShape(String shape) {
  		map.put(SHAPE_KEY, shape);
  	}
  
  	/**
  	 * Return the index of the label (or -1 if none), which is stored in the map
  	 * under the key {@link AbstractMapLabel#INDEX_KEY <code>INDEX_KEY</code>}.
  	 * 
  	 * @return the index for the label
  	 */
  	public int index() {
  		Object index = map.get(INDEX_KEY);
  		if (index != null && index instanceof Integer) {
  			return ((Integer) index).intValue();
  		} else {
  			return -1;
  		}
  	}
  
  	/**
  	 * Set the index for the label by storing it in the contained map under the
  	 * key {@link AbstractMapLabel#INDEX_KEY <code>INDEX_KEY</code>}.
  	 * 
  	 * WARNING: do NOT call this if the vertice is already in a SemanticGraph.
  	 * Doing so will disrupt the equality criteria for the map, and will throw
  	 * off routines that check to see if this vertice is in the SemanticGraph.
  	 */
  	public void setIndex(int index) {
  		map.put(INDEX_KEY, new Integer(index));
  	}
  
  	/**
  	 * Return the beginning character offset of the label (or -1 if none). This
  	 * is stored in the map under the key
  	 * {@link AbstractMapLabel#BEGIN_POSITION_KEY} <code>INDEX_KEY</code>.
  	 * 
  	 * @return the beginning position for the label
  	 */
  	public int beginPosition() {
  		Object index = map.get(BEGIN_POSITION_KEY);
  		if (index != null && index instanceof Integer) {
  			return ((Integer) index).intValue();
  		} else {
  			return -1;
  		}
  	}
  
  	/**
  	 * Set the beginning character offset for the label by storing it in the
  	 * contained map under the key {@link AbstractMapLabel#BEGIN_POSITION_KEY}
  	 * <code>INDEX_KEY</code>. Setting this key to "-1" can be used to indicate
  	 * no valid value.
  	 * 
  	 * @param beginPos
  	 *            The beginning position
  	 */
  	public void setBeginPosition(int beginPos) {
  		map.put(BEGIN_POSITION_KEY, new Integer(beginPos));
  	}
  
  	/**
  	 * Return the ending character offset of the label (or -1 if none). This is
  	 * stored in the map under the key {@link AbstractMapLabel#END_POSITION_KEY}
  	 * <code>INDEX_KEY</code>.
  	 * 
  	 * @return the end position for the label
  	 */
  	public int endPosition() {
  		Object index = map.get(END_POSITION_KEY);
  		if (index != null && index instanceof Integer) {
  			return ((Integer) index).intValue();
  		} else {
  			return -1;
  		}
  	}
  
  	/**
  	 * Set the ending character offset for the label by storing it in the
  	 * contained map under the key {@link AbstractMapLabel#END_POSITION_KEY}
  	 * <code>INDEX_KEY</code>. Setting this key to "-1" can be used to indicate
  	 * no valid value.
  	 * 
  	 * @param endPos
  	 *            The end position
  	 */
  	public void setEndPosition(int endPos) {
  		map.put(END_POSITION_KEY, new Integer(endPos));
  	}
  
  	// HAS CONTEXT METHODS
  
  	/**
  	 * Return the String before the word, which is stored in the map under the
  	 * key {@link AbstractMapLabel#BEFORE_KEY <code>BEFORE_KEY</code>}.
  	 * 
  	 * @return the String before the word
  	 */
  	public String before() {
  		Object before = map.get(BEFORE_KEY);
  		if (before == null) {
  			before = "";
  		}
  		return (String) before;
  	}
  
  	/**
  	 * Set the String before the word by storing it in the map under the key
  	 * {@link AbstractMapLabel#BEFORE_KEY <code>BEFORE_KEY</code>}.
  	 * 
  	 * @param before
  	 *            the String before the word
  	 */
  	public void setBefore(String before) {
  		map.put(BEFORE_KEY, before);
  	}
  
  	/**
  	 * Prepend this String to the current before String
  	 * 
  	 * @param before
  	 *            the String to be prepended
  	 */
  	public void prependBefore(String before) {
  		String oldBefore = before();
  		if (oldBefore == null) {
  			oldBefore = "";
  		}
  		setBefore(before + oldBefore);
  	}
  
  	/**
  	 * Return the String which is the unmangled word, which is stored in the map
  	 * under the key {@link AbstractMapLabel#CURRENT_KEY
  	 * <code>CURRENT_KEY</code>}.
  	 * 
  	 * @return the unmangled word
  	 */
  	public String current() {
  		Object current = map.get(CURRENT_KEY);
  		if (current == null) {
  			current = "";
  		}
  		return (String) current;
  	}
  
  	/**
  	 * Set the String which is the unmangled word, which is stored in the map
  	 * under the key {@link AbstractMapLabel#CURRENT_KEY
  	 * <code>CURRENT_KEY</code>}.
  	 * 
  	 * @param current
  	 *            the unmangled word
  	 */
  	public void setCurrent(String current) {
  		map.put(CURRENT_KEY, current);
  	}
  
  	/**
  	 * Return the String after the word, which is stored in the map under the
  	 * key {@link AbstractMapLabel#AFTER_KEY <code>AFTER_KEY</code>}.
  	 * 
  	 * @return the String after the word
  	 */
  	public String after() {
  		Object after = map.get(AFTER_KEY);
  		if (after == null) {
  			after = "";
  		}
  		return (String) after;
  	}
  
  	/**
  	 * Set the String after the word by storing it in the map under the key
  	 * {@link AbstractMapLabel#AFTER_KEY <code>AFTER_KEY</code>}.
  	 * 
  	 * @param after
  	 *            The String after the word
  	 */
  	public void setAfter(String after) {
  		map.put(AFTER_KEY, after);
  	}
  
  	/**
  	 * Append this String to the current after String
  	 * 
  	 * @param after
  	 *            The String to be prepended
  	 */
  	public void appendAfter(String after) {
  		String oldAfter = after();
  		if (oldAfter == null) {
  			oldAfter = "";
  		}
  		setAfter(oldAfter + after);
  	}
  
  	/**
  	 * convenience method for getting answer *
  	 */
  	public String answer() {
  		return (String) get(ANSWER_KEY);
  	}
  
  	/**
  	 * convenience method for setting answer *
  	 */
  	public void setAnswer(String answer) {
  		put(ANSWER_KEY, answer);
  	}
  
  	/**
  	 * convenience method for getting gold answer *
  	 */
  	public String goldAnswer() {
  		return (String) get(GOLDANSWER_KEY);
  	}
  
  	/**
  	 * convenience method for setting gold answer *
  	 */
  	public void setGoldAnswer(String goldAnswer) {
  		put(GOLDANSWER_KEY, goldAnswer);
  	}
  
  	public Collection getFeatures() {
  		return (Collection) map.get(FEATURES_KEY);
  	}
  
  	public void setFeatures(Collection features) {
  		map.put(FEATURES_KEY, features);
  	}
  
  	public Object interpretation() {
  		return map.get(INTERPRETATION_KEY);
  	}
  
  	public void setInterpretation(Object interpretation) {
  		map.put(INTERPRETATION_KEY, interpretation);
  	}
  
  	public String getLemma() {
  		return (String) map.get(LEMMA_KEY);
  	}
  
  	public void setLemma(String lemma) {
  		map.put(LEMMA_KEY, lemma);
  	}
  
  	public String getRole() {
  		return (String) map.get(ROLE_KEY);
  	}
  
  	public void setRole(String role) {
  		map.put(ROLE_KEY, role);
  	}
  
  	private static final long serialVersionUID = -980833749513621054L;
  
  }
  