package edu.stanford.nlp.ling;
import java.util.HashMap;
import java.util.Set;
import edu.stanford.nlp.ling.AnnotationLookup.KeyLookup;
import edu.stanford.nlp.ling.CoreAnnotations.AfterAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.BeforeAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.CategoryAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.CurrentAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.DocIDAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.IndexAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.LemmaAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.NERAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.SentenceIndexAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.TagAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.ValueAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.WordAnnotation;
import edu.stanford.nlp.process.Morphology;
import edu.stanford.nlp.util.ArrayCoreMap;
import edu.stanford.nlp.util.CoreMap;
/**
* A class that bridges the gap between old-style JavaNLP Labels and
* the new core object infrastructure. Instances of this class can be
* used (almost) anywhere that the now-defunct FeatureLabel family could be
* used. This data structure is backed by an {@link ArrayCoreMap}.
*
* @author dramage
* @author rafferty
*/
public class CoreLabel extends ArrayCoreMap implements Label, HasWord, HasTag, HasCategory,
HasContext, HasIndex {
private static final long serialVersionUID = 2L;
/**
* Should warnings be printed when converting from MapLabel family.
*/
private static final boolean VERBOSE = false;
/** Default constructor, calls super() */
public CoreLabel() {
super();
}
/**
* Returns a new CoreLabel instance based on the contents of the given
* CoreLabel. It copies the contents of the other CoreLabel.
* <i>Implementation note:</i> this is a the same as the constructor
* that takes a CoreMap, but is needed to ensure unique most specific
* type inference for selecting a constructor at compile-time.
*
* @param label The CoreLabel to copy
*/
public CoreLabel(CoreLabel label) {
this((CoreMap) label);
}
/**
* Returns a new CoreLabel instance based on the contents of the given
* CoreMap. It copies the contents of the other CoreMap.
*
* @param label The CoreMap to copy
*/
@SuppressWarnings({"unchecked", "RedundantCast"}) // Eclipse needs the redundant cast to Classs (Nov 2008)....
public CoreLabel(CoreMap label) {
super(label.size());
Set<Class<?>> otherKeys = label.keySet();
for (Class key : otherKeys) {
set((Class)key, label.get(key));
}
}
/**
* Returns a new CoreLabel instance based on the contents of the given
* label. Just the value() is copied.
*
* @param label Basis for this label
*/
public CoreLabel(Label label) {
super(1); // the initial capacity is just 1 item stored in the label
// just a Label: only know about value()
this.setValue(label.value());
}
/**
* This constructor attempts to parse the String keys
* into Class keys. It's mainly useful for reading from
* a file. A best effort attempt is made to correctly
* parse the keys according to the String lookup function
* in {@link CoreAnnotations}.
*
* @param keys Array of Strings that are class names
* @param values Array of values (as String)
*/
public CoreLabel(String[] keys, String[] values) {
super(keys.length);
//this.map = new ArrayCoreMap();
initFromStrings(keys, values);
}
/**
* Class that all "generic" annotations extend
* This allows you to read in arbitrary values from a file as features, for example.
*/
public static interface GenericAnnotation<T> extends CoreAnnotation<T> { }
//Unchecked is below because eclipse can't handle the level of type inference if we correctly parameterize GenericAnnotation with String
@SuppressWarnings("unchecked")
public static HashMap<String, Class<? extends GenericAnnotation>> genericKeys = new HashMap<String, Class<? extends GenericAnnotation>>();
@SuppressWarnings("unchecked")
public static HashMap<Class<? extends GenericAnnotation>, String> genericValues = new HashMap<Class<? extends GenericAnnotation>, String>();
@SuppressWarnings("unchecked")
private void initFromStrings(String[] keys, String[] values) {
for (int i = 0; i < Math.min(keys.length, values.length); i++) {
String key = keys[i];
String value = values[i];
KeyLookup lookup = AnnotationLookup.getCoreKey(key);
//now work with the key we got above
if (lookup == null) {
if(genericKeys.containsKey(key)) {
this.set(genericKeys.get(key), value);
} else {
GenericAnnotation<String> newKey = new GenericAnnotation<String>() {
public Class<String> getType() { return String.class;} };
this.set(newKey.getClass(), values[i]);
genericKeys.put(keys[i], newKey.getClass());
genericValues.put(newKey.getClass(), keys[i]);
}
// unknown key; ignore
if (VERBOSE) {
System.err.println("CORE: CoreLabel.fromAbstractMapLabel: " +
"Unknown key "+key);
}
} else {
try {
Class<?> valueClass = AnnotationLookup.getValueType(lookup.coreKey);
if(valueClass.equals(String.class)) {
this.set((Class<? extends CoreAnnotation>)lookup.coreKey, values[i]);
} else if(valueClass == Integer.class) {
this.set((Class<? extends CoreAnnotation>)lookup.coreKey, Integer.parseInt(values[i]));
} else if(valueClass == Double.class) {
this.set((Class<? extends CoreAnnotation>)lookup.coreKey, Double.parseDouble(values[i]));
} else if(valueClass == Long.class) {
this.set((Class<? extends CoreAnnotation>)lookup.coreKey, Long.parseLong(values[i]));
}
} catch(Exception e) {
e.printStackTrace();
// unexpected value type
System.err.println("CORE: CoreLabel.initFromStrings: "
+ "Bad type for " + key
+ ". Value was: " + value
+ "; expected "+AnnotationLookup.getValueType(lookup.coreKey));
}
}
}
}
/**
* Return a factory for this kind of label
*
* @return The label factory
*/
public static LabelFactory factory() {
return new LabelFactory() {
public Label newLabel(String labelStr) {
CoreLabel label = new CoreLabel();
label.setValue(labelStr);
return label;
}
public Label newLabel(String labelStr, int options) {
return newLabel(labelStr);
}
public Label newLabel(Label oldLabel) {
return new CoreLabel(oldLabel);
}
public Label newLabelFromString(String encodedLabelStr) {
throw new UnsupportedOperationException("This code branch left blank" +
" because we do not understand what this method should do.");
}
};
}
/**
* {@inheritDoc}
*/
public LabelFactory labelFactory() {
return CoreLabel.factory();
}
/**
* Return a non-null String value for a key.
* This method is included for backwards compatibility with AbstractMapLabel.
* It is guaranteed to not return null; if the key is not present or
* has a null value, it returns the empty string (""). It is only valid to
* call this method when key is paired with a value of type String.
*
* @param <KEY> A key type with a String value
* @param key The key to return the value of.
* @return "" if the key is not in the map or has the value <code>null</code>
* and the String value of the key otherwise
*/
public <KEY extends Key<CoreMap, String>> String getString(Class<KEY> key) {
String value = get(key);
if (value == null) {
return "";
}
return value;
}
/**
* {@inheritDoc}
*/
// public int size() {
// return map.size();
// }
/**
* {@inheritDoc}
*/
public void setFromString(String labelStr) {
throw new UnsupportedOperationException("Cannot set from string");
}
/**
* {@inheritDoc}
*/
public final void setValue(String value) {
set(ValueAnnotation.class, value);
}
/**
* {@inheritDoc}
*/
public final String value() {
return get(ValueAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setWord(String word) {
set(WordAnnotation.class, word);
}
/**
* {@inheritDoc}
*/
public String word() {
return get(WordAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setTag(String tag) {
set(TagAnnotation.class, tag);
}
/**
* {@inheritDoc}
*/
public String tag() {
return get(TagAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setCategory(String category) {
set(CategoryAnnotation.class, category);
}
/**
* {@inheritDoc}
*/
public String category() {
return get(CategoryAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setAfter(String after) {
set(AfterAnnotation.class, after);
}
/**
* {@inheritDoc}
*/
public void appendAfter(String after) {
set(AfterAnnotation.class, getString(AfterAnnotation.class)+after);
}
/**
* {@inheritDoc}
*/
public String after() {
return getString(AfterAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setBefore(String before) {
set(BeforeAnnotation.class, before);
}
/**
* {@inheritDoc}
*/
public void prependBefore(String before) {
set(BeforeAnnotation.class, before+getString(BeforeAnnotation.class));
}
/**
* {@inheritDoc}
*/
public String before() {
return getString(BeforeAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setCurrent(String current) {
set(CurrentAnnotation.class, current);
}
/**
* {@inheritDoc}
*/
public String current() {
return getString(CurrentAnnotation.class);
}
/**
* {@inheritDoc}
*/
public String docID() {
return get(DocIDAnnotation.class);
}
/**
* {@inheritDoc}
*/
public void setDocID(String docID) {
set(DocIDAnnotation.class, docID);
}
/**
* Return the named entity class of the label (or null if none).
*
* @return String the word value for the label
*/
public String ner() {
return get(NERAnnotation.class);
}
public void setNER(String ner) {
set(NERAnnotation.class, ner);
}
/**
* Return the lemma of the label (or null if none).
*
* @return String the word value for the label
*/
public String lemma() {
return get(LemmaAnnotation.class);
}
public void setLemma(String lemma) {
set(LemmaAnnotation.class, lemma);
}
public void lemmatize(boolean synchronous) {
if (!synchronous) {
setLemma(Morphology.stemStatic(value(), tag(), true).value());
} else {
setLemma(Morphology.stemStaticSynchronized(value(), tag(), true).value());
}
}
/**
* {@inheritDoc}
*/
public int index() {
Integer n = get(IndexAnnotation.class);
if(n == null)
return -1;
return n;
}
/**
* {@inheritDoc}
*/
public void setIndex(int index) {
set(IndexAnnotation.class, index);
}
/**
* {@inheritDoc}
*/
public int sentIndex() {
Integer n = get(SentenceIndexAnnotation.class);
if(n == null)
return -1;
return n;
}
/**
* {@inheritDoc}
*/
public void setSentIndex(int sentIndex) {
set(SentenceIndexAnnotation.class, sentIndex);
}
/** {@inheritDoc} */
// public <VALUE, KEY extends edu.stanford.nlp.util.TypesafeMap.Key<CoreMap, VALUE>>
// VALUE get(Class<KEY> key) {
// return map.get(key);
// }
/** {@inheritDoc} */
// public <VALUE, KEY extends edu.stanford.nlp.util.TypesafeMap.Key<CoreMap, VALUE>>
// boolean has(Class<KEY> key) {
// return map.has(key);
// }
/** {@inheritDoc} */
// public Set<Class<?>> keySet() {
// return map.keySet();
// }
/** {@inheritDoc} */
// public <VALUE, KEY extends edu.stanford.nlp.util.TypesafeMap.Key<CoreMap, VALUE>>
// VALUE remove(Class<KEY> key) {
// return map.remove(key);
// }
/** {@inheritDoc} */
// public <VALUEBASE, VALUE extends VALUEBASE, KEY extends edu.stanford.nlp.util.TypesafeMap.Key<CoreMap, VALUEBASE>>
// VALUE set(Class<KEY> key, VALUE value) {
// return map.set(key, value);
// }
/** {@inheritDoc} */
// public <VALUE, KEY extends Key<CoreMap, VALUE>>
// boolean containsKey(Class<KEY> key) {
// return map.containsKey(key);
// }
// @Override
// public String toString() {
// return map.toString();
// }
// @Override
// public boolean equals(Object other) {
// if (other instanceof CyclicCoreLabel) {
// // CyclicCoreLabel overrides our equality, use its
// return other.equals(this);
// } else if (other instanceof CoreLabel) {
// // If its a CoreLabel, compare our map with its
// return map.equals(((CoreLabel)other).map);
// } else if (other instanceof CoreMap) {
// // If its any other type of CoreMap, compare our map with it directly
// return map.equals(other);
// } else {
// return false;
// }
// }
// @Override
// public int hashCode() {
// return map.hashCode();
// }
}