public interface Annotator extends Stage {
    
    /**
     * The abstract annotation method. All classes that perform some kind of 
     * annotation (splitting, PoS tagging, entity linking...) must inherit from
     * Annotator. They annotate the blackboard given as first parameter or a 
     * component of the blackboard, given as second parameter. 
     * 
     * @param blackboard blackboard to annotate
     * @param component component to annotate.
     */
    public void annotate(Blackboard blackboard, DocumentComponent component);
    
    
    @Override
    default void run(Blackboard b) {
        this.annotate(b, b.getStructure());
    }    
    
}
 public class Blackboard {

    /**
     * The default document identifier.
     */
    private static final String DEFAULT_DOCUMENT_ID = "DocumentRoot";

    /**
     * The full raw text of the document.
     */
    private String rawText;

    /**
     * The root block of the document.
     */
    private DocumentComponent document;

    /**
     * Container for the n-grams of the document. Every n-gram is part of a
     * specific list identifying the type of n-gram. The types of n-grams are
     * the key for searching in the main n-gram list.
     */
    private Map<String, Map<String, Gram>> generalNGramsContainer;

    /**
     * Document-wide annotations. This space can be used to add annotations that
     * belong to the whole document, as for example extracted concepts, or
     * tagset used by a POS-tagger, or the overall sentiment.
     */
    private List<Annotation> annotations;

    /**
     * Instantiates an empty blackboard.
     */
    public Blackboard() {
        createDocument("");
    }

    /**
     * Initializes the blackboard with a new document. This will destroy any
     * information previously held by the blackboard.
     *
     * @param rawText the text of the new document.
     * @param documentId the output-friendly identifier of the document
     */
    public final void createDocument(String rawText, String documentId) {
        this.rawText = rawText;
        this.document = new DocumentComposite(rawText, documentId);
        this.generalNGramsContainer = new HashMap<>();
        this.annotations = new ArrayList<>();
    }

    /**
     * Initializes the blackboard with a new document. This will destroy any
     * information previously held by the blackboard.
     *
     * @param rawText the text of the new document.
     */
    public final void createDocument(String rawText) {
        this.rawText = rawText;
        this.document = new DocumentComposite(rawText, DEFAULT_DOCUMENT_ID);
        this.generalNGramsContainer = new HashMap<>();
        this.annotations = new ArrayList<>();
    }

    /**
     * Gets the root of the document.
     *
     * @return the {@link it.uniud.ailab.dcore.persistence.DocumentComponent}
     * root object.
     */
    @JsonIgnore
    public DocumentComponent getStructure() {
        return document;
    }

    /**
     * Gets the raw text (i.e. unprocessed) of the document.
     *
     * @return the original document string.
     */
    public String getText() {
        return rawText;
    }

    /**
     * Adds a Gram in the Gram Container, merging grams with the same
     * identifier. If the gram is already present, the method updates it adding
     * the new occurrence. Annotations of the new gram are <b>not</b> merged
     * into the old gram. This is because it's good practice to annotate grams
     * only when they've <b>all</b> been added into the blackboard.
     *
     * @param unit the concept unit where the gram appears
     * @param newGram the gram to add
     */
    public void addGram(DocumentComponent unit, Gram newGram) {

        Map<String, Gram> grams = generalNGramsContainer.get(newGram.getType());
        if (grams == null) {
            grams = new HashMap<>();
        }
        Gram gram = grams.get(newGram.getIdentifier());

        // Deep clone the object instead of referencing the found one.
        // this way, we're free to modify it by adding annotations without
        // modifying the old object.
        if (gram == null) {
            Gram cloned = (new Cloner()).deepClone(newGram);
            grams.put(cloned.getIdentifier(), cloned);
            gram = cloned;
        } else {
            // add the surface of the new gram
            gram.addSurfaces(newGram.getSurfaces(), newGram.getTokenLists());
        }

        gram.addAppaerance(unit);
        unit.addGram(gram);
        generalNGramsContainer.put(newGram.getType(), grams);
    }

    public void addGram(Gram newGram) {

        Map<String, Gram> grams = generalNGramsContainer.get(newGram.getType());
        if (grams == null) {
            grams = new HashMap<>();
        }
        Gram gram = grams.get(newGram.getIdentifier());

        // Deep clone the object instead of referencing the found one.
        // this way, we're free to modify it by adding annotations without
        // modifying the old object.
        if (gram == null) {
            Gram cloned = (new Cloner()).deepClone(newGram);
            grams.put(cloned.getIdentifier(), cloned);
        } else {
            // add the surface of the new gram
            gram.addSurfaces(newGram.getSurfaces(), newGram.getTokenLists());
        }

        generalNGramsContainer.put(newGram.getType(), grams);
    }
    
    /**
     * Get the all the different kind of grams found in the document. This
     * grams are divided by type, stored in a Map using their identifier as 
     * key.
     * 
     * @return all the maps found in the document.
     */
    public Map<String,Map<String,Gram>> getGrams() {
        return generalNGramsContainer;
    }

    /**
     * Get all the grams of a given type found in the blackboard.
     * 
     * @param <T> the type of the grams to achieve
     * @param gramType the identifier of the gram type
     * @return a collection with all the grams with match type and identifier,
     * null if there is no match.
     */
    public <T> Collection<T> getGramsByType(String gramType) {        
        return 
                generalNGramsContainer.containsKey(gramType) ?
                (Collection<T>)generalNGramsContainer.get(gramType).values() :
                null;
    }

    /**
     * Retrieves the keyphrases found in the document.
     *
     * @return a collection of
     * {@link it.uniud.ailab.dcore.persistence.Keyphrase}s.
     */
    @Deprecated
    @JsonIgnore
    public List<Gram> getKeyphrases() {

        Map<String, Gram> kps = generalNGramsContainer.get(Keyphrase.KEYPHRASE);
        return kps != null ? new ArrayList(kps.values()) : new ArrayList();
    }

    /**
     * Removes a keyphrase from the document because it's no more relevant, or
     * useful, or for whatever reason an annotator thinks so.
     *
     * @param g the gram to remove.
     */
    @Deprecated
    public void removeKeyphrase(Keyphrase g) {
        removeGram(Keyphrase.KEYPHRASE,g);
    }
    
    /**
     * Removes a gram from the document because it's no more relevant, or
     * useful, or for whatever reason an annotator thinks so.
     *
     * @param type the type of the gram to remove
     * @param g the gram to remove.
     */
    public void removeGram(String type,Gram g) {
        generalNGramsContainer.get(type)
                .remove(g.getIdentifier());

        for (Sentence s : DocumentUtils.getSentences(document)) {
            s.removeGram(g);
        }
    }

    /**
     * Adds an annotation in the blackboard.
     *
     * @param a the annotation to add
     */
    public void addAnnotation(Annotation a) {
        annotations.add(a);
    }

    /**
     * Get all the annotations.
     *
     * @return the annotations stored in the blackboard
     */
    public List<Annotation> getAnnotations() {
        return annotations;
    }

    /**
     * Gets the annotations produced by a specific annotator.
     *
     * @param annotator the annotator identifier
     * @return the annotations produced by the specified annotator
     */
    public List<Annotation> getAnnotations(String annotator) {
        return annotations.stream().filter((a)
                -> (a.getAnnotator().equals(annotator))).
                collect(Collectors.toList());
    }

    /**
     * Remove an annotation from the blackboard.
     * 
     * @param ann the annotation to remove.
     */
    public void removeAnnotation(Annotation ann) {
        annotations.remove(ann);
    }
    
    /**
     * Get the language of the document root.
     * 
     * @return the language of the document root.
     */
    public String getDocumentLanguage() {
        return getStructure().getLanguage().getLanguage();
    }

}
 public class DefaultAnnotations {
    
    public static final String SENTENCE_INDEX = "SentenceIndex";
    
    public static final String SENTENCE_COUNT = "SentenceCount";
    
    public static final String CHAR_COUNT = "CharCount";
    
    public static final String WORD_COUNT = "WordCount";
    
    public static final String PHRASES_COUNT = "PhrasesCount";
    
    public static final String IS_NER = "IsANer";
    
} public class FeatureAnnotation 
        extends Annotation
        implements ScoredAnnotation {
    
    /**
     * A feature.
     * 
     * @param annotator the annotator that generated the feature.
     * @param score the value of the feature.
     */
    public FeatureAnnotation(String annotator,double score) {
        super(annotator);
        
        super.addNumber(score);
    }
    
    /**
     * Get the value of the feature.
     * 
     * @return the value of the feature
     */
    @Override
    public double getScore() {
        return super.getNumberAt(0).doubleValue();
    }
} public class NERAnnotation 
        extends Annotation {
    
    /**
     * A feature.
     * 
     * @param annotator the annotator that generated the feature.
     * @param nerTag the value of the feature.
     */
    public NERAnnotation(String annotator, String nerTag) {
        super(annotator);
        
        super.addString(nerTag);
    }
    
    public String getNerTag(){
        return super.getStringAt(0);
    }
    
} public abstract class DocumentComponent extends Annotable {

    private final String text;
    private Locale language;

    /**
     * Creates a document component.
     *
     * @param text the text of the component
     * @param language the language of the component
     * @param identifier the unique identifier for the component
     */
    public DocumentComponent(String text, Locale language, String identifier) {
        super(identifier);
        this.text = text;
        this.language = language;
    }

    // <editor-fold desc="getters and setters">
    /**
     * Returns the text of the component
     *
     * @return the text of the component.
     */
    public String getText() {
        return text;
    }

    /**
     * Sets the language of the component.
     *
     * @param language the language of the component
     */
    public void setLanguage(Locale language) {
        this.language = language;
        if (hasComponents()) {
            for (DocumentComponent c : getComponents()) {
                c.setLanguage(language);
            }
        }
    }

    /**
     * Returns the language of the component.
     *
     * @return the language of the component.
     * @see <a href="http://tools.ietf.org/html/rfc5646">RFC5646</a>
     * specification to know about language tags used by Java.
     */
    public Locale getLanguage() {
        return this.language;
    }

    /**
     * Check if the component is a leaf ( so it can be a
     * {@link it.uniud.ailab.dcore.persistence.Sentence}), or if it has
     * children, so it's surely a
     * {@link it.uniud.ailab.dcore.persistence.DocumentComposite}.
     *
     * @return true if the component has children; otherwise false.
     */
    public boolean hasComponents() {
        List<DocumentComponent> comps = getComponents();
        return !(comps == null);
    }

    // </editor-fold>
    // <editor-fold desc="abstract methods">
    /**
     * Returns the children of the document component, or null if the current
     * concept unit has no children (a sentence, the leaf of the tree).
     *
     * @return the children of the document component, or null if the current
     * concept unit has no children.
     */
    public abstract List<DocumentComponent> getComponents();
    
    /**
     * Adds a gram to the component.
     *
     * @param g the gram to add
     */
    public abstract void addGram(Gram g);

    /**
     * Returns the gram associated with the component.
     *
     * @return the gram associated with the component.
     */
    public abstract List<Gram> getGrams();
    
    /**
     * Remote a gram from the component.
     * 
     * @param g the gram tor remove
     */
    public abstract void removeGram(Gram g);

    // </editor-fold>

    /**
     * Get the string representation of the component.
     *
     * @return the string that represent the component (which has been set in
     * the constructor).
     */
    @Override
    public String toString() {
        return text;
    }

} public class DocumentComposite extends DocumentComponent {

    /**
     * The children of the current instance.
     */
    private List<DocumentComponent> components;

    /**
     * Create a document composite.
     * 
     * @param text the text of the composite.
     * @param language the language of the composite.
     * @param identifier the output friendly identifier of the composite
     */
    public DocumentComposite(String text, Locale language,String identifier) {
        super(text, language,identifier);
        components = new ArrayList<>();
    }
    
    /**
     * Create a composite with no language associated. This requires
     * a call to setLanguage before many of the annotators can actually work.
     * 
     * @param text the text of the composite.
     * @param id the output friendly identifier of the composite
     */
    public DocumentComposite(String text,String id) {
        this(text, null,id);
    }

    
//    /**
//     * Create a composite with no language associated. This requires
//     * a call to setLanguage before many of the annotators can actually work.
//     * 
//     * @param text the text of the composite.
//     */
//    public DocumentComposite(String text) {
//        this(text, null,text.substring(0, 17) + "...");
//    }

    //<editor-fold desc="Getters and setters">
     /**
     * Gets the sub-components of the composite.
     * 
     * @return the sub-components
     */
    @Override
    public List<DocumentComponent> getComponents() {
        return components;
    }

    /**
     * Add a component to the composite.
     * 
     * @param component the component to add.
     */
    public void addComponent(DocumentComponent component) {
        this.components.add(component);
    }

    /**
     * Sets the components of the composite, deleting the previous set ones
     * (if any).
     * 
     * @param components the new components of the composite.
     */
    public void setComponents(List<DocumentComponent> components) {
        this.components = components;
    }
    //</editor-fold>

    /**
     * You can't add a gram on a composite, since a Gram which is not associated
     * with a particular sentence has no sense. In other words, if I associate
     * a gram with a composite, how can I know what sub-component he belongs to?
     * 
     * @param g the gram to add
     */
    @Override
    public void addGram(Gram g) {
        throw new UnsupportedOperationException("You can't add Grams on a Composite object."); 
    }

    /**
     * Get all the grams of the component; more specifically, get all the grams
     * of all its sub-components.
     * 
     * @return the gram that appear in the component.
     */
    @Override
    public List<Gram> getGrams() {
        List<Gram> ret = new ArrayList<>();
        for (DocumentComponent c : getComponents())
        {
            ret.addAll(c.getGrams());
        }
        
        return ret;
    }

    @Override
    public void removeGram(Gram g) {
        throw new UnsupportedOperationException("You can't remove grams from a composite.");
    }
} public class Mention extends Gram{

    /**
     * The list of n-grams representing the references to the anaphor.
     */
    private final List<Reference> referencesTokenList;
    
    public static final String MENTION = "Mention";
    
    
    public Mention(String identifier, List<Token> sequence, String surface) {
        super(identifier, sequence, surface,MENTION);
        referencesTokenList = new ArrayList<>();
    }
    
    public List<Token> getAnaphorToken(){
        return this.getTokens();
    }
    
    public String getAnaphor(){
        return this.getIdentifier();
    }
    
    public void addReference(String identifier, List<Token> tokens, String type){
        Reference ref = new Reference(identifier, tokens, type);
        referencesTokenList.add(ref);
    }
    
    public List<Reference> getReferences(){
        return referencesTokenList;
    }
    
    public class Reference{
        
        String identifier;
        List<Token> tokens;
        String type;
        
        public Reference(String identier,List<Token> tokens, String type){
            this.identifier = identier;
            this.tokens = tokens;
            this.type = type;
        }
        
        public String getType(){
            return type;
        }
        
        public List<Token> getTokens(){
            return tokens;
        }
        
        public String getIdentifier(){
            return identifier;
        }
    }
}
 public class Sentence extends DocumentComponent {
    
    // <editor-fold desc="Private fields">

    /**
     * The {@link it.uniud.ailab.dcore.persistence.Token}s that form the sentence.
     */
    private List<Token> tokenizedSentence;

    /**
     * The {@link it.uniud.ailab.dcore.persistence.Keyphrase}s that have been detected 
     * in the sentence.
     */
    private List<Gram> grams;
    
    // </editor-fold>
    
    /**
     * Creates a sentence with the specified text and language.
     * 
     * @param text the text of the sentence
     * @param language the language of the sentence
     * @param identifier the output friendly identifier of the sentence
     */    
    public Sentence(String text,Locale language,String identifier) {
        super(text,language,identifier);
        tokenizedSentence = new ArrayList<>();
        grams = new ArrayList<>(); 
    }
    
    /**
     * Creates a sentence with the specified text. This requires
     * a call to setLanguage before many of the annotators can actually work.
     * 
     * @param text the text of the sentence
     * @param identifier the output friendly identifier of the sentence
     */
    public Sentence(String text,String identifier) {
        this(text,null,identifier);
    }
    
    /**
     * Sets the tokens of the sentence.
     * 
     * @param tokenzedSentence the tokens of the sentence.
     */
    public void setTokens(List<Token> tokenzedSentence) {
        this.tokenizedSentence = tokenzedSentence;
    }
    
    /**
     * Appends a tokens at the end of the token list of the sentence.
     * 
     * @param t the token to add
     */
    public void addToken(Token t) {
        this.tokenizedSentence.add(t);
    }
    
    /**
     * Returns the tokens of the sentence.
     * 
     * @return the tokens of the sentence.
     */
    public List<Token> getTokens() {
        return tokenizedSentence;
    }      
    
    /**
     * Returns an array containing the POS tags of the tokens of the sentence.
     * 
     * @return an array containing the POS tags of the sentence.
     */
    public String[] getPosTaggedSentence() {
        String[] output = new String[tokenizedSentence.size()];
        for (int i = 0; i < tokenizedSentence.size(); i++) {
            output[i] = tokenizedSentence.get(i).getPoS();
        }
        return output;
    }    
    // </editor-fold>
    
    /**
     * Get the text of the sentence.
     * 
     * @return the text of the sentence.
     */
    @Override
    public String toString() {
        return getText();
    }

    /**
     * A sentence has no sub-components in the document model, so a null value
     * is returned.
     * 
     * @return null, because the sentence has no children.
     */
    @Override
    public List<DocumentComponent> getComponents() {
        return null;
    }

    /**
     * Add a n-gram to the sentence.
     * 
     * @param g the gram to add. 
     */
    @Override
    public void addGram(Gram g) {
        grams.add(g);
    }
    
    /**
     * Get all the grams of a sentence.
     * 
     * @return the grams that have been found in the sentence.
     */
    @Override
    public List<Gram> getGrams() {
        return grams;
    }

    /**
     * Removes a gram from the sentence.
     * 
     * @param gramToRemove the gram to remove
     */
    @Override
    public void removeGram(Gram gramToRemove) {

        for (Iterator<Gram> gramIterator = grams.iterator(); 
                gramIterator.hasNext();)
        {
            Gram gram = gramIterator.next();
            
            if (gram.getIdentifier().equals(gramToRemove.getIdentifier())) {
                gramIterator.remove();
            }
                
        }
    }
}
 public class Token extends Annotable {

    /**
     * The stem of the word.
     */
    private String stem;
    
    /**
     * The part-of-speech tag of the word.
     */
    private String PoS;
    
    /**
     * The lemmatized form of the token, that is obtained removing inflectional 
     * endings only and returning the base or dictionary form of a word.
     */
    private String lemma;

    /**
     * Creates a token.
     * 
     * @param text the text of the token.
     */
    public Token(String text){
        super(text);
    }
    
    // <editor-fold desc="Id, stem and PoS">
    /**
     * Set the stem of the token; if you don't have a stemmer for a certain
     * language, the lemmatized version of the word should work fine as well.
     * 
     * @param stem the stemmed token
     */
    public void setStem(String stem) {
        this.stem = stem;
    }
    
    /**
     * Set the POS tag of the token.
     * 
     * @param PoS the POS tag.
     */
    public void setPoS(String PoS) {
        this.PoS = PoS;
    }   
    
    /**
     * Set the lemmatize form of the token.
     * 
     * @param lemma the lemma for the token. 
     */
    public void setLemma(String lemma){
        this.lemma = lemma;
    }
    
    /**
     * Returns the text of the token.
     * 
     * @return the text of the token.
     */
    public String getText() {
        return super.getIdentifier();
    }

    /**
     * Returns the stem of the token.
     * 
     * @return the stem of the token.
     */
    public String getStem() {
        return stem;
    }
    
    /**
     * Returns the POS tag of the token.
     * 
     * @return the POS tag of the token.
     */
    public String getPoS() {
        return PoS;
    }    
    
    /**
     * Returns the lemmatize form of the token.
     * 
     * @return the lemma for token. 
     */
    public String getLemma(){
        return lemma;
    }
    // </editor-fold>
    
    
    // <editor-fold desc="Annotations">    
    /**
     * Gets all the annotations associated with the token that have been
     * generated by a specific annotator.
     * 
     * @param annotator the identifier of an annotator.
     * @return the annotations generated by the specified annotator.
     */
//    public List<TextAnnotation> getAnnotations(String annotator) {
//        List<TextAnnotation> ret = new ArrayList<>();
//        for (TextAnnotation ann : this.getAnnotations())
//        {
//            if (ann.getAnnotator().equals(annotator))  {
//                ret.add(ann);
//            }
//        }
//        return ret;
//    }
    
    /**
     * Check if the token has been annotated by a given annotator. Please note
     * that to retrieve all the annotations generated by an annotator you should
     * use getAnnotations() instead.
     * 
     * @param annotator the identifier of an annotator.
     * @return the first annotation in the list generated by the given annotator.
     */
//    public TextAnnotation hasAnnotation(String annotator) {
//        TextAnnotation a = null;
//        for (TextAnnotation b : this.getAnnotations())
//        {
//            if (b.getAnnotator().equals(annotator))  {
//                a = b;
//                break;
//            }
//        }
//        return a;
//    }
    // </editor-fold>
    
    /**
     * A full string representation of the token, which returns not only the text,
     * but also the stem and the annotations of the token.
     * 
     * @return 
     */
    @Override
    public String toString() {
        String ret = getText() + " {(POS:" + getPoS() + "), " 
                + "(Lemma: " + getLemma() + "), "
                + "(Stem: " +getStem()+ ")";
        for (Annotation a : getAnnotations()) {
            if (a instanceof TextAnnotation)
                ret = ret + ", (" + a.getAnnotator() + ":" + 
                        ((TextAnnotation) a).getAnnotation() + ")";
        }
        return ret + "}";
    }            

    /**
     * Two tokens are equal if they have the same text, stem and POS tag. 
     * Tokens with different annotation may just refer to same word in different
     * sentences; while the annotations are different, the word is the same.
     * 
     * For example, "Engineering" per se and the word "Engineering" in
     * "Software Engineering" should be treated as equal, even if they may
     * be annotated with different Wikipedia entities.
     * 
     * @param obj the token to compare with
     * @return true if the tokens are equal, false otherwise
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Token other = (Token) obj;
        if (!getText().equals(other.getText())) {
            return false;
        }
        if (!stem.equals(other.stem)) {
            return false;
        }
        return PoS.equals(other.PoS);
    }

    @Override
    public String getIdentifier() {
        return getText();
    }

   
}
import edu.stanford.nlp.dcoref.CorefChain;
import edu.stanford.nlp.dcoref.CorefCoreAnnotations;
import edu.stanford.nlp.ling.CoreAnnotations.NamedEntityTagAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.SentencesAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.TokensAnnotation;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.pipeline.Annotation;
import edu.stanford.nlp.pipeline.StanfordCoreNLP;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.TreeCoreAnnotations;
import edu.stanford.nlp.util.CoreMap;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import it.uniud.ailab.dcore.annotation.Annotator;
import it.uniud.ailab.dcore.Blackboard;
import it.uniud.ailab.dcore.annotation.DefaultAnnotations;
import it.uniud.ailab.dcore.annotation.annotations.FeatureAnnotation;
import it.uniud.ailab.dcore.annotation.annotations.NERAnnotation;
import it.uniud.ailab.dcore.persistence.DocumentComponent;
import it.uniud.ailab.dcore.persistence.DocumentComposite;
import it.uniud.ailab.dcore.persistence.Mention;
import it.uniud.ailab.dcore.persistence.Sentence;
import it.uniud.ailab.dcore.persistence.Token;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
 * Copyright (C) 2015 Artificial Intelligence
 * Laboratory @ University of Udine.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package it.uniud.ailab.dcore.wrappers.external;


/**
 * A bootstrapper annotator for the English language developed using the
 * Stanford Core NLP library. This annotator splits the document, tokenizes it
 * and performs PoS tagging and Named Entity Recognition togethere with parsing
 * to detect coreferences in the document. This annotator supports only the
 * English language.
 *
 * @author Giorgia Chiaradia
 */
public class StanfordBootstrapperAnnotator implements Annotator {

    /**
     * The Stanford NLP pipeline. The field is marked static to be optimized for
     * re-use, so that subsequent calls of annotate() don't have to reload
     * definitions every time, even for different instances of the annotator.
     */
    private static StanfordCoreNLP pipeline = null;

    /**
     * A counter that keeps track of the number of sentences identified by the
     * Annotator, used as identifier for the generated Sentences.
     */
    private int sentenceCounter = 0;

    /**
     * Identifier for the Coreference annotation.
     */
    public static final String COREFERENCE = "Coreference";

    /**
     * Annotate the document by splitting the document, tokenizing it,
     * performing PoS tagging and Named Entity Recognition using the Stanford
     * Core NLP tools.
     *
     * @param component the component to annotate.
     */
    @Override
    public void annotate(Blackboard blackboard, DocumentComponent component) {

        if (pipeline == null) {
            // creates a StanfordCoreNLP object, with POS tagging, lemmatization, 
            //NER, parsing, and coreference resolution 
            Properties props = new Properties();
            props.put("annotators", "tokenize, ssplit, pos, parse, lemma, ner, dcoref");
            pipeline = new StanfordCoreNLP(props);

        }

        // read some text in the text variable
        String text = component.getText();

        // create an empty Annotation just with the given text
        Annotation document = new Annotation(text);

        // run all Annotators on this text
        pipeline.annotate(document);

        //get the graph for coreference resolution
        Map<Integer, CorefChain> graph
                = document.get(CorefCoreAnnotations.CorefChainAnnotation.class);

        //prepare the map for coreference graph of document
        Map<String, Collection<Set<CorefChain.CorefMention>>> coreferenceGraph
                = new HashMap<>();

        for (CorefChain corefChain : graph.values()) {

            //get the representative mention, that is the word recall in other sentences
            CorefChain.CorefMention cm = corefChain.getRepresentativeMention();

            //eliminate auto-references
            if (corefChain.getMentionMap().size() <= 1) {
                continue;
            }

            //get the stemmed form of the references, so the comparison with 
            //grams will be easier
            List<CoreLabel> tks = document.get(SentencesAnnotation.class)
                    .get(cm.sentNum - 1).get(TokensAnnotation.class);
            //list of tokens which compose the anaphor

            List<Token> anaphorsTokens = new ArrayList<>();
            for (int i = cm.startIndex - 1; i < cm.endIndex - 1; i++) {
                CoreLabel current = tks.get(i);
                Token t = new Token(current.word());
                t.setPoS(current.tag());
                t.setLemma(current.lemma());
                anaphorsTokens.add(t);
            }

            //the mention n-gram which is formed by the anaphor and a 
            //list of references
            Mention mention