PronominalCoref.java
0001 /*
0002  *  PronominalCoref.java
0003  *
0004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Marin Dimitrov, 30/Dec/2001
0013  *
0014  *  $Id: PronominalCoref.java 17829 2014-04-14 15:39:44Z markagreenwood $
0015  */
0016 
0017 package gate.creole.coref;
0018 
0019 import gate.Annotation;
0020 import gate.AnnotationSet;
0021 import gate.Document;
0022 import gate.Factory;
0023 import gate.FeatureMap;
0024 import gate.Gate;
0025 import gate.Node;
0026 import gate.Resource;
0027 import gate.creole.ANNIEConstants;
0028 import gate.creole.AbstractLanguageAnalyser;
0029 import gate.creole.ExecutionException;
0030 import gate.creole.ResourceInstantiationException;
0031 import gate.creole.Transducer;
0032 import gate.util.Benchmark;
0033 import gate.util.Benchmarkable;
0034 import gate.util.Err;
0035 import gate.util.Files;
0036 import gate.util.SimpleFeatureMapImpl;
0037 
0038 import java.net.MalformedURLException;
0039 import java.net.URL;
0040 import java.util.ArrayList;
0041 import java.util.Arrays;
0042 import java.util.Comparator;
0043 import java.util.HashMap;
0044 import java.util.HashSet;
0045 import java.util.Iterator;
0046 import java.util.List;
0047 import java.util.Map;
0048 import java.util.Set;
0049 
0050 public class PronominalCoref extends AbstractLanguageAnalyser
0051                               implements ANNIEConstants,
0052                               Benchmarkable {
0053 
0054   private static final long serialVersionUID = 3860815557386683264L;
0055 
0056   public static final String COREF_DOCUMENT_PARAMETER_NAME = "document";
0057 
0058   public static final String COREF_ANN_SET_PARAMETER_NAME = "annotationSetName";
0059 
0060   /** --- */
0061   private static final boolean DEBUG = false;
0062 
0063   //JAPE grammars
0064   private static final String QT_GRAMMAR_URL = Files.getGateResource(
0065               "/creole/coref/quoted_text.jape").toString();
0066   private static final String PLEON_GRAMMAR_URL = Files.getGateResource(
0067           "/creole/coref/pleonasm.jape").toString();
0068 
0069   //annotation types
0070   private static final String QUOTED_TEXT_TYPE = "QuotedText";
0071   private static final String PLEONASTIC_TYPE = "PleonasticIt";
0072 
0073   //annotation features
0074   private static final String PRP_CATEGORY = "PRP";
0075   private static final String PRP$_CATEGORY = "PRP$";
0076 
0077   //scope
0078   private static final int SENTENCES_IN_SCOPE = 3;
0079   /** --- */
0080   private static AnnotationOffsetComparator ANNOTATION_OFFSET_COMPARATOR;
0081   /** --- */
0082   private String annotationSetName;
0083   /** --- */
0084   private Transducer qtTransducer;
0085   /** --- */
0086   private Transducer pleonTransducer;
0087   /** --- */
0088   private AnnotationSet defaultAnnotations;
0089   /** --- */
0090   private Sentence[] textSentences;
0091   /** --- */
0092   private Quote[] quotedText;
0093   /** --- */
0094   private Annotation[] pleonasticIt;
0095   /** --- */
0096   private Map<Annotation,String> personGender;
0097   /** --- */
0098   private HashMap<Annotation,Annotation> anaphor2antecedent;
0099   /** --- */
0100   private static final FeatureMap PRP_RESTRICTION;
0101 
0102   private boolean resolveIt = true;
0103   
0104   /** default ORGANIZATIONS,LOCATION**/
0105   private Set<String> inanimatedSet;
0106   
0107   private String inanimatedEntityTypes;
0108   
0109   private String benchmarkId;
0110 
0111   /** --- */
0112   static {
0113     ANNOTATION_OFFSET_COMPARATOR = new AnnotationOffsetComparator();
0114     PRP_RESTRICTION = new SimpleFeatureMapImpl();
0115     PRP_RESTRICTION.put(TOKEN_CATEGORY_FEATURE_NAME,PRP_CATEGORY);
0116   }
0117 
0118   /** Initialise this resource, and return it. */
0119   @Override
0120   public Resource init() throws ResourceInstantiationException {
0121 
0122     personGender = new HashMap<Annotation,String>();
0123     anaphor2antecedent = new HashMap<Annotation,Annotation>();
0124     inanimatedSet = new HashSet<String>();
0125     
0126     //1. initialise quoted text transducer
0127     URL qtGrammarURL = null;
0128     try {
0129       qtGrammarURL = new URL(QT_GRAMMAR_URL);
0130     catch(MalformedURLException mue) {
0131       throw new ResourceInstantiationException(mue);
0132     }
0133     FeatureMap params = Factory.newFeatureMap();
0134     params.put(Transducer.TRANSD_GRAMMAR_URL_PARAMETER_NAME, qtGrammarURL);
0135     params.put(Transducer.TRANSD_ENCODING_PARAMETER_NAME, "UTF-8");
0136     if (qtTransducer == null) {
0137       features = Factory.newFeatureMap();
0138       Gate.setHiddenAttribute(features, true);
0139       qtTransducer = (Transducer)Factory.createResource("gate.creole.Transducer",
0140               params, features);
0141       qtTransducer.setName("PronominalCoref-QT " + System.currentTimeMillis());
0142     }
0143     else {
0144       qtTransducer.setParameterValues(params);
0145       qtTransducer.reInit();
0146     }
0147     
0148 
0149     //2. initialise pleonastic transducer
0150     URL pleonGrammarURL = null;
0151     try {
0152       pleonGrammarURL = new URL(PLEON_GRAMMAR_URL);
0153     }
0154     catch(MalformedURLException mue) {
0155       throw new ResourceInstantiationException(mue);
0156     }
0157     params = Factory.newFeatureMap();
0158     params.put(Transducer.TRANSD_GRAMMAR_URL_PARAMETER_NAME, pleonGrammarURL);
0159     params.put(Transducer.TRANSD_ENCODING_PARAMETER_NAME, "UTF-8");
0160     if (pleonTransducer == null) {
0161       features = Factory.newFeatureMap();
0162       Gate.setHiddenAttribute(features, true);
0163       pleonTransducer = (Transducer)Factory.createResource("gate.creole.Transducer",
0164               params, features);
0165       pleonTransducer.setName("PronominalCoref-Pleon " + System.currentTimeMillis());
0166     }
0167     else {
0168       pleonTransducer.setParameterValues(params);
0169       pleonTransducer.reInit();
0170     }
0171     
0172     return this;
0173   // init()
0174 
0175   @Override
0176   public void cleanup() {
0177     super.cleanup();
0178     Factory.deleteResource(qtTransducer);
0179     Factory.deleteResource(pleonTransducer);
0180   }
0181 
0182   /** Set the document to run on. */
0183   @Override
0184   public void setDocument(Document newDocument) {
0185 
0186     //0. precondition
0187 //    assert (null != newDocument);
0188 
0189     //1. set doc for aggregated components
0190     qtTransducer.setDocument(newDocument);
0191     pleonTransducer.setDocument(newDocument);
0192 
0193     //3. delegate
0194     super.setDocument(newDocument);
0195   }
0196 
0197   /** --- */
0198   public void setAnnotationSetName(String annotationSetName) {
0199     this.annotationSetName = annotationSetName;
0200   }
0201 
0202 
0203   /** --- */
0204   public String getAnnotationSetName() {
0205     return annotationSetName;
0206   }
0207 
0208   /** --- */
0209   public void setResolveIt(Boolean newValue) {
0210     this.resolveIt = newValue.booleanValue();
0211   }
0212 
0213   /** --- */
0214   public Boolean getResolveIt() {
0215     return new Boolean(this.resolveIt);
0216   }
0217 
0218 
0219   /**
0220    * This method runs the coreferencer. It assumes that all the needed parameters
0221    * are set. If they are not, an exception will be fired.
0222    */
0223   @SuppressWarnings("unchecked")
0224   @Override
0225   public void execute() throws ExecutionException{
0226 
0227     //0. preconditions
0228     if(null == this.document) {
0229       throw new ExecutionException("[coreference] Document is not set!");
0230     }
0231 
0232     //1. preprocess
0233     preprocess();
0234 /*
0235     //2. remove corefs from previous run
0236     String annSetName = this.annotationSetName == null ? "COREF"
0237                                                        : this.annotationSetName;
0238 
0239     AnnotationSet corefSet = this.document.getAnnotations(annSetName);
0240     if (false == corefSet.isEmpty()) {
0241       corefSet.clear();
0242     }
0243 */
0244     //3.get personal pronouns
0245     FeatureMap constraintPRP = new SimpleFeatureMapImpl();
0246     constraintPRP.put(TOKEN_CATEGORY_FEATURE_NAME,PRP_CATEGORY);
0247     AnnotationSet personalPronouns = this.defaultAnnotations.get(TOKEN_ANNOTATION_TYPE,constraintPRP);
0248 
0249     //4.get possesive pronouns
0250     FeatureMap constraintPRP$ = new SimpleFeatureMapImpl();
0251     constraintPRP$.put(TOKEN_CATEGORY_FEATURE_NAME,PRP$_CATEGORY);
0252     AnnotationSet possesivePronouns = this.defaultAnnotations.get(TOKEN_ANNOTATION_TYPE,constraintPRP$);
0253 
0254     //5.combine them
0255     List<Annotation> pronouns = new ArrayList<Annotation>();
0256     if (personalPronouns != null && !personalPronouns.isEmpty()) {
0257       pronouns.addAll(personalPronouns);
0258     }
0259 
0260     if (possesivePronouns != null && !possesivePronouns.isEmpty()) {
0261       pronouns.addAll(possesivePronouns);
0262     }
0263 
0264     //6.do we have pronouns at all?
0265     if (pronouns.isEmpty()) {
0266       //do nothing
0267       return;
0268     }
0269 
0270     //7.sort them according to offset
0271     Annotation[] arrPronouns = pronouns.toArray(new Annotation[pronouns.size()]);
0272     Arrays.sort(arrPronouns,ANNOTATION_OFFSET_COMPARATOR);
0273 
0274     //8.cleanup - ease the GC
0275     pronouns = null;
0276     personalPronouns = null;
0277     possesivePronouns = null;
0278 
0279     int prnSentIndex = 0;
0280 
0281 
0282     //10. process all pronouns
0283     for (int i=0; i< arrPronouns.length; i++) {
0284       Annotation currPronoun = arrPronouns[i];
0285       while (this.textSentences[prnSentIndex].getEndOffset().longValue() <
0286                                       currPronoun.getEndNode().getOffset().longValue()) {
0287         prnSentIndex++;
0288       }
0289 
0290       Sentence currSentence = this.textSentences[prnSentIndex];
0291       assert (currSentence.getStartOffset().longValue() <= currPronoun.getStartNode().getOffset().longValue());
0292       assert (currSentence.getEndOffset().longValue() >= currPronoun.getEndNode().getOffset().longValue());
0293 
0294       //11. find antecedent (if any) for pronoun
0295       Annotation antc = findAntecedent(currPronoun,prnSentIndex);
0296 
0297       //12. add to the ana2ant hashtable
0298       this.anaphor2antecedent.put(currPronoun,antc);
0299     }
0300 
0301     //done
0302   }
0303 
0304 
0305   /** --- */
0306   public Map<Annotation,Annotation> getResolvedAnaphora() {
0307     return this.anaphor2antecedent;
0308   }
0309 
0310   /** --- */
0311   private Annotation findAntecedent(Annotation currPronoun,int prnSentIndex) {
0312 
0313     //0. preconditions
0314     assert (null != currPronoun);
0315     assert (prnSentIndex >= 0);
0316     assert (currPronoun.getType().equals(TOKEN_ANNOTATION_TYPE));
0317     assert (currPronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0318                       currPronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0319 
0320     //1.
0321     String strPronoun = (String)currPronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0322 
0323     assert (null != strPronoun);
0324 
0325     //2. delegate processing to the appropriate methods
0326     if (strPronoun.equalsIgnoreCase("HE"||
0327         strPronoun.equalsIgnoreCase("HIM"||
0328         strPronoun.equalsIgnoreCase("HIS"||
0329         strPronoun.equalsIgnoreCase("HIMSELF")) {
0330       return _resolve$HE$HIM$HIS$HIMSELF$(currPronoun,prnSentIndex);
0331     }
0332     else if (strPronoun.equalsIgnoreCase("SHE"||
0333               strPronoun.equalsIgnoreCase("HER"||
0334               strPronoun.equalsIgnoreCase("HERS"||
0335               strPronoun.equalsIgnoreCase("HERSELF")) {
0336       return _resolve$SHE$HER$HERS$HERSELF$(currPronoun,prnSentIndex);
0337     }
0338     else if (strPronoun.equalsIgnoreCase("IT"||
0339               strPronoun.equalsIgnoreCase("ITS"||
0340               strPronoun.equalsIgnoreCase("ITSELF")) {
0341       return _resolve$IT$ITS$ITSELF$(currPronoun,prnSentIndex);
0342     }
0343     else if (strPronoun.equalsIgnoreCase("I"||
0344               strPronoun.equalsIgnoreCase("ME"||
0345               strPronoun.equalsIgnoreCase("MY"||
0346               strPronoun.equalsIgnoreCase("MINE"||
0347               strPronoun.equalsIgnoreCase("MYSELF")) {
0348       return _resolve$I$ME$MY$MINE$MYSELF$(currPronoun,prnSentIndex);
0349     }
0350     else {
0351       if (DEBUG) {
0352         gate.util.Err.println("["+strPronoun+"] is not handled yet...");
0353       }
0354       return null;
0355     }
0356   }
0357 
0358 
0359   boolean isPleonastic(Annotation pronoun) {
0360 
0361     //0. preconditions
0362     assert (null != pronoun);
0363     String str = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0364     assert (str.equalsIgnoreCase("IT"));
0365 
0366     //1. do we have pleonasms in this text?
0367     if (this.pleonasticIt.length == 0) {
0368       return false;
0369     }
0370 
0371     //2. find closest pleonasm index
0372     @SuppressWarnings("unchecked")
0373     int closestPleonasmIndex = Arrays.binarySearch(this.pleonasticIt,
0374                                                              pronoun,
0375                                                              ANNOTATION_OFFSET_COMPARATOR);
0376     //normalize index
0377     if (closestPleonasmIndex < 0) {
0378       closestPleonasmIndex = -closestPleonasmIndex --1;
0379     }
0380 
0381     //still not good?
0382     if (closestPleonasmIndex < 0) {
0383       closestPleonasmIndex = 0;
0384     }
0385 
0386     //get closest pleonasm
0387     Annotation pleonasm = this.pleonasticIt[closestPleonasmIndex];
0388 
0389 //System.out.println(pleonasm);
0390 //System.out.println(pronoun);
0391 
0392     //3. return true only if the proboun is contained in pleonastic fragment
0393     boolean result =  (pleonasm.getStartNode().getOffset().intValue() <= pronoun.getStartNode().getOffset().intValue()
0394             &&
0395             pleonasm.getEndNode().getOffset().intValue() >= pronoun.getEndNode().getOffset().intValue());
0396 //System.out.println("is pleon=["+result+"]");
0397     return result;
0398   }
0399 
0400 
0401   /** --- */
0402   private Annotation _resolve$HE$HIM$HIS$HIMSELF$(Annotation pronoun, int sentenceIndex) {
0403 
0404     //0. preconditions
0405     assert (pronoun.getType().equals(TOKEN_ANNOTATION_TYPE));
0406     assert (pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0407                       pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0408     String pronounString = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0409     assert (pronounString.equalsIgnoreCase("HE"||
0410                       pronounString.equalsIgnoreCase("HIM"||
0411                       pronounString.equalsIgnoreCase("HIS"||
0412                       pronounString.equalsIgnoreCase("HIMSELF"));
0413 
0414     //1.
0415     boolean antecedentFound = false;
0416     int scopeFirstIndex = sentenceIndex - SENTENCES_IN_SCOPE;
0417     if (scopeFirstIndex < scopeFirstIndex = 0;
0418 
0419     int currSentenceIndex = sentenceIndex;
0420     Annotation bestAntecedent = null;
0421 
0422     while (currSentenceIndex >= scopeFirstIndex || antecedentFound == false) {
0423       Sentence currSentence = this.textSentences[currSentenceIndex];
0424       AnnotationSet persons = currSentence.getPersons();
0425 
0426       Iterator<Annotation> it = persons.iterator();
0427       while (it.hasNext()) {
0428         Annotation currPerson = it.next();
0429         String gender = this.personGender.get(currPerson);
0430 
0431         if (null == gender ||
0432             gender.equalsIgnoreCase("MALE"||
0433             gender.equalsIgnoreCase("UNKNOWN")) {
0434           //hit
0435           antecedentFound = true;
0436 
0437           if (null == bestAntecedent) {
0438             bestAntecedent = currPerson;
0439           }
0440           else {
0441             bestAntecedent = _chooseAntecedent$HE$HIM$HIS$SHE$HER$HERS$HIMSELF$HERSELF$(bestAntecedent,currPerson,pronoun);
0442           }
0443         }
0444       }
0445 
0446       if (== currSentenceIndex--)
0447         break;
0448 
0449     }
0450 
0451     return bestAntecedent;
0452   }
0453 
0454 
0455   /** --- */
0456   private Annotation _resolve$SHE$HER$HERS$HERSELF$(Annotation pronoun, int sentenceIndex) {
0457 
0458     //0. preconditions
0459     assert (pronoun.getType().equals(TOKEN_ANNOTATION_TYPE));
0460     assert (pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0461                       pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0462     String pronounString = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0463     assert (pronounString.equalsIgnoreCase("SHE"||
0464                       pronounString.equalsIgnoreCase("HER"||
0465                       pronounString.equalsIgnoreCase("HERS"||
0466                       pronounString.equalsIgnoreCase("HERSELF"));
0467 
0468     //1.
0469     boolean antecedentFound = false;
0470     int scopeFirstIndex = sentenceIndex - SENTENCES_IN_SCOPE;
0471     if (scopeFirstIndex < scopeFirstIndex = 0;
0472     int currSentenceIndex = sentenceIndex;
0473     Annotation bestAntecedent = null;
0474 
0475     while (currSentenceIndex >= scopeFirstIndex || antecedentFound == false) {
0476       Sentence currSentence = this.textSentences[currSentenceIndex];
0477       AnnotationSet persons = currSentence.getPersons();
0478 
0479       Iterator<Annotation> it = persons.iterator();
0480       while (it.hasNext()) {
0481         Annotation currPerson = it.next();
0482         String gender = this.personGender.get(currPerson);
0483 
0484         if (null == gender ||
0485             gender.equalsIgnoreCase("FEMALE"||
0486             gender.equalsIgnoreCase("UNKNOWN")) {
0487           //hit
0488           antecedentFound = true;
0489 
0490           if (null == bestAntecedent) {
0491             bestAntecedent = currPerson;
0492           }
0493           else {
0494             bestAntecedent = _chooseAntecedent$HE$HIM$HIS$SHE$HER$HERS$HIMSELF$HERSELF$(bestAntecedent,currPerson,pronoun);
0495           }
0496         }
0497       }
0498 
0499       if (== currSentenceIndex--)
0500         break;
0501     }
0502 
0503     return bestAntecedent;
0504   }
0505 
0506 
0507   /** --- */
0508   private Annotation _resolve$IT$ITS$ITSELF$(Annotation pronoun, int sentenceIndex) {
0509     //do not resolve it pronouns if disabled by the user
0510     if (! resolveIt)
0511       return null;
0512 
0513     //0. preconditions
0514     assert (pronoun.getType().equals(TOKEN_ANNOTATION_TYPE));
0515     assert (pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0516                       pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0517     String pronounString = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0518     assert (pronounString.equalsIgnoreCase("IT"||
0519                       pronounString.equalsIgnoreCase("ITS"||
0520                       pronounString.equalsIgnoreCase("ITSELF"));
0521 
0522     //0.5 check if the IT is pleonastic
0523     if (pronounString.equalsIgnoreCase("IT"&&
0524         isPleonastic(pronoun)) {
0525 //System.out.println("PLEONASM...");
0526       return null;
0527     }
0528 
0529     //1.
0530     int scopeFirstIndex = sentenceIndex - 1;
0531     if (scopeFirstIndex < scopeFirstIndex = 0;
0532 
0533     int currSentenceIndex = sentenceIndex;
0534     Annotation bestAntecedent = null;
0535 
0536     while (currSentenceIndex >= scopeFirstIndex) {
0537 
0538       Sentence currSentence = this.textSentences[currSentenceIndex];
0539       Set<Annotation> org_loc = currSentence.getInanimated();
0540 
0541       Iterator<Annotation> it = org_loc.iterator();
0542       while (it.hasNext()) {
0543         Annotation currOrgLoc = it.next();
0544 
0545         if (null == bestAntecedent) {
0546           //discard cataphoric references
0547           if (currOrgLoc.getStartNode().getOffset().longValue() <
0548                                           pronoun.getStartNode().getOffset().longValue()) {
0549             bestAntecedent = currOrgLoc;
0550           }
0551         }
0552         else {
0553           bestAntecedent = this._chooseAntecedent$IT$ITS$ITSELF$(bestAntecedent,currOrgLoc,pronoun);
0554         }
0555       }
0556 
0557       if (== currSentenceIndex--)
0558         break;
0559     }
0560 
0561     return bestAntecedent;
0562   }
0563 
0564 
0565   /** --- */
0566   private Annotation _resolve$I$ME$MY$MINE$MYSELF$(Annotation pronoun, int sentenceIndex) {
0567 
0568     //0. preconditions
0569     assert (pronoun.getType().equals(TOKEN_ANNOTATION_TYPE));
0570     assert (pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0571                       pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0572     String pronounString = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0573     assert (pronounString.equalsIgnoreCase("I"||
0574                       pronounString.equalsIgnoreCase("MY"||
0575                       pronounString.equalsIgnoreCase("ME"||
0576                       pronounString.equalsIgnoreCase("MINE"||
0577                       pronounString.equalsIgnoreCase("MYSELF"));
0578 
0579     //0.5 sanity check
0580     //if there are not quotes at all in the text then exit
0581     if (== this.quotedText.length) {
0582 //System.out.println("TEXT WITH NO QUOTES ENCOUNTERED...");
0583       return null;
0584     }
0585 
0586 
0587     //1.
0588     Annotation bestAntecedent = null;
0589 
0590     @SuppressWarnings("unchecked")
0591     int closestQuoteIndex = Arrays.binarySearch(this.quotedText,pronoun,ANNOTATION_OFFSET_COMPARATOR);
0592     //normalize index
0593     if (closestQuoteIndex < 0) {
0594       closestQuoteIndex = -closestQuoteIndex --1;
0595     }
0596 
0597     //still not good?
0598     if (closestQuoteIndex < 0) {
0599       closestQuoteIndex = 0;
0600     }
0601 
0602     //get closest Quote
0603     Quote quoteContext = this.quotedText[closestQuoteIndex];
0604 
0605     //assure that the pronoun is contained in the quoted text fragment
0606     //otherwise exit
0607 
0608     if (pronoun.getStartNode().getOffset().intValue() > quoteContext.getEndOffset().intValue() ||
0609         pronoun.getEndNode().getOffset().intValue() < quoteContext.getStartOffset().intValue()) {
0610       //oops, probably incorrect text - I/My/Me is not part of quoted text fragment
0611       //exit
0612 //System.out.println("Oops! ["+pronounString+"] not part of quoted fragment...");
0613       return null;
0614     }
0615 
0616     //get the Persons that precede/succeed the quoted fragment
0617     //the order is:
0618     //
0619     //[1]. if there exists a Person or pronoun in {he, she} following the quoted fragment but
0620     //in the same sentence, then use it
0621     //i.e.  ["PRN1(x)...", said X ...A, B, C ....]
0622     //
0623     //[2]. if there is a Person (NOT a pronoun) in the same sentence,
0624     // preceding the quote, then use it
0625     //i.e. . [A, B, C...X ..."PRN1(x) ..."...]
0626     //
0627 
0628     //try [1]
0629     //get the succeeding Persons/pronouns
0630     Set<Annotation> succCandidates = quoteContext.getAntecedentCandidates(Quote.ANTEC_AFTER);
0631     if (false == succCandidates.isEmpty()) {
0632       //cool, we have candidates, pick up the one closest to the end quote
0633       Iterator<Annotation> it = succCandidates.iterator();
0634 
0635       while (it.hasNext()) {
0636         Annotation currCandidate = it.next();
0637         if (null == bestAntecedent || ANNOTATION_OFFSET_COMPARATOR.compare(bestAntecedent,currCandidate0) {
0638           //wow, we have a candidate that is closer to the quote
0639           bestAntecedent = currCandidate;
0640         }
0641       }
0642     }
0643 
0644     //try [2]
0645     //get the preceding Persons/pronouns
0646     if (null == bestAntecedent) {
0647       Set<Annotation> precCandidates = quoteContext.getAntecedentCandidates(Quote.ANTEC_BEFORE);
0648       if (false == precCandidates.isEmpty()) {
0649         //cool, we have candidates, pick up the one closest to the end quote
0650         Iterator<Annotation> it = precCandidates.iterator();
0651 
0652         while (it.hasNext()) {
0653           Annotation currCandidate = it.next();
0654           if (null == bestAntecedent || ANNOTATION_OFFSET_COMPARATOR.compare(bestAntecedent,currCandidate0) {
0655             //wow, we have a candidate that is closer to the quote
0656             bestAntecedent = currCandidate;
0657           }
0658         }
0659       }
0660     }
0661 
0662     //try [3]
0663     //get the Persons/pronouns back in context
0664     if (null == bestAntecedent) {
0665       Set<Annotation> precCandidates = quoteContext.getAntecedentCandidates(Quote.ANTEC_BACK);
0666       if (false == precCandidates.isEmpty()) {
0667         //cool, we have candidates, pick up the one closest to the end quote
0668         Iterator<Annotation> it = precCandidates.iterator();
0669 
0670         while (it.hasNext()) {
0671           Annotation currCandidate = it.next();
0672           if (null == bestAntecedent || ANNOTATION_OFFSET_COMPARATOR.compare(bestAntecedent,currCandidate0) {
0673             //wow, we have a candidate that is closer to the quote
0674             bestAntecedent = currCandidate;
0675           }
0676         }
0677       }
0678     }
0679 
0680     return bestAntecedent;
0681   }
0682 
0683 
0684   /** --- */
0685   @SuppressWarnings("unchecked")
0686   private void preprocess() throws ExecutionException {
0687 
0688     //0.5 cleanup
0689     this.personGender.clear();
0690     this.anaphor2antecedent.clear();
0691 
0692     //1.get all annotation in the input set
0693     if this.annotationSetName == null || this.annotationSetName.equals("")) {
0694       this.defaultAnnotations = this.document.getAnnotations();
0695     }
0696     else {
0697       this.defaultAnnotations = this.document.getAnnotations(annotationSetName);
0698     }
0699 
0700     //if none found, print warning and exit
0701     if (this.defaultAnnotations == null || this.defaultAnnotations.isEmpty()) {
0702       Err.prln("Coref Warning: No annotations found for processing!");
0703       return;
0704     }
0705 
0706     // get the list of inanimated entity types 
0707     if (inanimatedEntityTypes==null||inanimatedEntityTypes.equals(""))
0708       inanimatedEntityTypes="Organization;Location";
0709     
0710     String[] types = inanimatedEntityTypes.split(";");
0711     this.inanimatedSet.addAll(Arrays.asList(types));
0712         
0713     //2.1 remove QT annotations if left from previous execution
0714     AnnotationSet qtSet = this.defaultAnnotations.get(QUOTED_TEXT_TYPE);
0715     if (qtSet != null && !qtSet.isEmpty()) {
0716       this.defaultAnnotations.removeAll(qtSet);
0717     }
0718 
0719     //2.2. run quoted text transducer to generate "Quoted Text" annotations
0720     Benchmark.executeWithBenchmarking(this.qtTransducer,
0721             Benchmark.createBenchmarkId("qtTransducer",
0722                     getBenchmarkId()), this, null);
0723 
0724     //3.1 remove pleonastic annotations if left from previous execution
0725     AnnotationSet pleonSet = this.defaultAnnotations.get(PLEONASTIC_TYPE);
0726     if (pleonSet != null && !pleonSet.isEmpty()) {
0727       this.defaultAnnotations.removeAll(pleonSet);
0728     }
0729 
0730     //3.2 run quoted text transducer to generate "Pleonasm" annotations
0731     Benchmark.executeWithBenchmarking(pleonTransducer,
0732             Benchmark.createBenchmarkId("pleonTransducer",
0733                     getBenchmarkId()), this, null);
0734 
0735     //4.get all SENTENCE annotations
0736     AnnotationSet sentenceAnnotations = this.defaultAnnotations.get(SENTENCE_ANNOTATION_TYPE);
0737 
0738     this.textSentences = new Sentence[sentenceAnnotations.size()];
0739     
0740     Annotation[]  sentenceArray = sentenceAnnotations.toArray(new Annotation[sentenceAnnotations.size()]);
0741     Arrays.sort(sentenceArray,ANNOTATION_OFFSET_COMPARATOR);
0742 
0743     for (int i=0; i< sentenceArray.length; i++) {
0744 
0745       Annotation currSentence = sentenceArray[i];
0746       Long sentStartOffset = currSentence.getStartNode().getOffset();
0747       Long sentEndOffset = currSentence.getEndNode().getOffset();
0748       
0749       AnnotationSet tempASOffsets = this.defaultAnnotations.getContained(
0750               sentStartOffset,sentEndOffset);
0751 
0752       //4.1. get PERSONS in this sentence
0753       AnnotationSet sentPersons = tempASOffsets.get(PERSON_ANNOTATION_TYPE);
0754 
0755       //4.2. get inanimated entities (ORGANIZATIONS,LOCATION) in this sentence
0756      
0757       AnnotationSet sentInans = tempASOffsets.get(this.inanimatedSet);
0758 
0759       //4.5. create a Sentence for the SENTENCE annotation
0760       this.textSentences[inew Sentence(i,
0761                                             0,
0762                                             sentStartOffset,
0763                                             sentEndOffset,
0764                                             sentPersons,
0765                                             sentInans
0766                                   );
0767 
0768       //4.6. for all PERSONs in the sentence - find their gender using the
0769       //orthographic coreferences if the gender of some entity is unknown
0770       Iterator<Annotation> itPersons = sentPersons.iterator();
0771       while (itPersons.hasNext()) {
0772         Annotation currPerson = itPersons.next();
0773         String gender = this.findPersonGender(currPerson);
0774         this.personGender.put(currPerson,gender);
0775       }
0776     }
0777 
0778     //5. initialise the quoted text fragments
0779     AnnotationSet sentQuotes = this.defaultAnnotations.get(QUOTED_TEXT_TYPE);
0780 
0781     //if none then return
0782     if (null == sentQuotes) {
0783       this.quotedText = new Quote[0];
0784     }
0785     else {
0786       this.quotedText = new Quote[sentQuotes.size()];
0787 
0788       Annotation[] quotesArray = sentQuotes.toArray(new Annotation[sentQuotes.size()]);
0789       Arrays.sort(quotesArray,ANNOTATION_OFFSET_COMPARATOR);
0790 
0791       for (int i =0; i < quotesArray.length; i++) {
0792         this.quotedText[inew Quote(quotesArray[i],i);
0793       }
0794     }
0795 
0796     //6. initialuse the plonastic It annotations
0797     AnnotationSet plaonasticSet = this.defaultAnnotations.get(PLEONASTIC_TYPE);
0798 
0799     if (null == plaonasticSet) {
0800       this.pleonasticIt = new Annotation[0];
0801     }
0802     else {
0803       this.pleonasticIt = new Annotation[plaonasticSet.size()];
0804 
0805       Annotation[] quotesArray = plaonasticSet.toArray(new Annotation[plaonasticSet.size()]);
0806       Arrays.sort(quotesArray,ANNOTATION_OFFSET_COMPARATOR);
0807 
0808       for (int i=0; i< this.pleonasticIt.length; i++) {
0809         this.pleonasticIt[i= quotesArray[i];
0810       }
0811     }
0812 
0813   }
0814 
0815 
0816   /** --- */
0817   private String findPersonGender(Annotation person) {
0818 
0819     String result = (String)person.getFeatures().get(PERSON_GENDER_FEATURE_NAME);
0820 
0821     if (null==result) {
0822       //gender is unknown - try to find it from the ortho coreferences
0823       @SuppressWarnings("unchecked")
0824       List<Integer> orthoMatches  = (List<Integer>)person.getFeatures().get(ANNOTATION_COREF_FEATURE_NAME);
0825 
0826       if (null != orthoMatches) {
0827         Iterator<Integer> itMatches = orthoMatches.iterator();
0828 
0829         while (itMatches.hasNext()) {
0830           Integer correferringID = itMatches.next();
0831           Annotation coreferringEntity = this.defaultAnnotations.get(correferringID);
0832           if (coreferringEntity != null) {
0833             assert (coreferringEntity.getType().equalsIgnoreCase(PERSON_ANNOTATION_TYPE));
0834             String correferringGender = (String)coreferringEntity.getFeatures().get(PERSON_GENDER_FEATURE_NAME);
0835 
0836             if (null != correferringGender) {
0837               result = correferringGender;
0838               break;
0839             }
0840           }
0841         }
0842       }
0843     }
0844 
0845     return result;
0846   }
0847 
0848   @SuppressWarnings("rawtypes")
0849   private static class AnnotationOffsetComparator implements Comparator {
0850 
0851     private int _getOffset(Object o) {
0852 
0853       if (instanceof Annotation) {
0854         return ((Annotation)o).getEndNode().getOffset().intValue();
0855       }
0856       else if (instanceof Sentence) {
0857         return ((Sentence)o).getStartOffset().intValue();
0858       }
0859       else if (instanceof Quote) {
0860         return ((Quote)o).getStartOffset().intValue();
0861       }
0862       else if (instanceof Node) {
0863         return ((Node)o).getOffset().intValue();
0864       }
0865       else {
0866         throw new IllegalArgumentException();
0867       }
0868     }
0869 
0870     @Override
0871     public int compare(Object o1,Object o2) {
0872 
0873       //0. preconditions
0874       assert (null != o1);
0875       assert (null != o2);
0876       assert (o1 instanceof Annotation ||
0877                         o1 instanceof Sentence ||
0878                         o1 instanceof Quote ||
0879                         o1 instanceof Node);
0880       assert (o2 instanceof Annotation ||
0881                         o2 instanceof Sentence ||
0882                         o2 instanceof Quote ||
0883                         o2 instanceof Node);
0884 
0885       int offset1 = _getOffset(o1);
0886       int offset2 = _getOffset(o2);
0887 
0888       return offset1 - offset2;
0889     }
0890   }
0891 
0892 
0893   /** --- */
0894   private Annotation _chooseAntecedent$HE$HIM$HIS$SHE$HER$HERS$HIMSELF$HERSELF$(Annotation ant1, Annotation ant2, Annotation pronoun) {
0895 
0896     //0. preconditions
0897     assert (null != ant1);
0898     assert (null != ant2);
0899     assert (null != pronoun);
0900     assert (pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0901                       pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0902     String pronounString = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0903     assert (pronounString.equalsIgnoreCase("SHE"||
0904                       pronounString.equalsIgnoreCase("HER"||
0905                       pronounString.equalsIgnoreCase("HERS"||
0906                       pronounString.equalsIgnoreCase("HERSELF"||
0907                       pronounString.equalsIgnoreCase("HE"||
0908                       pronounString.equalsIgnoreCase("HIM"||
0909                       pronounString.equalsIgnoreCase("HIS"||
0910                       pronounString.equalsIgnoreCase("HIMSELF"));
0911 
0912     Long offset1 = ant1.getStartNode().getOffset();
0913     Long offset2 = ant2.getStartNode().getOffset();
0914     Long offsetPrn = pronoun.getStartNode().getOffset();
0915 
0916     long diff1 = offsetPrn.longValue() - offset1.longValue();
0917     long diff2 = offsetPrn.longValue() - offset2.longValue();
0918 //    assert (diff1 != 0 && diff2 != 0);
0919     //reject candidates that overlap with the pronoun
0920     if (diff1 == 0) {
0921       return ant2;
0922     }
0923     else if (diff2 == 0) {
0924       return ant1;
0925     }
0926 
0927     //get the one CLOSEST AND PRECEDING the pronoun
0928     if (diff1 > && diff2 > 0) {
0929       //we have [...antecedentA...AntecedentB....pronoun...] ==> choose B
0930       if (diff1 < diff2)
0931         return ant1;
0932       else
0933         return ant2;
0934     }
0935     else if (diff1 < && diff2 < 0) {
0936       //we have [...pronoun ...antecedentA...AntecedentB.......] ==> choose A
0937       if (Math.abs(diff1< Math.abs(diff2))
0938         return ant1;
0939       else
0940           return ant2;
0941     }
0942     else {
0943       assert (Math.abs(diff1 + diff2< Math.abs(diff1+ Math.abs(diff2));
0944       //we have [antecedentA...pronoun...AntecedentB] ==> choose A
0945       if (diff1 > 0)
0946         return ant1;
0947       else
0948         return ant2;
0949     }
0950   }
0951 
0952   /** --- */
0953   private Annotation _chooseAntecedent$IT$ITS$ITSELF$(Annotation ant1, Annotation ant2, Annotation pronoun) {
0954 
0955     //0. preconditions
0956     assert (null != ant1);
0957     assert (null != ant2);
0958     assert (null != pronoun);
0959     assert (pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP_CATEGORY||
0960                       pronoun.getFeatures().get(TOKEN_CATEGORY_FEATURE_NAME).equals(PRP$_CATEGORY));
0961     String pronounString = (String)pronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
0962 
0963     assert (pronounString.equalsIgnoreCase("IT"||
0964                       pronounString.equalsIgnoreCase("ITS"||
0965                       pronounString.equalsIgnoreCase("ITSELF"));
0966 
0967     Long offset1 = ant1.getStartNode().getOffset();
0968     Long offset2 = ant2.getStartNode().getOffset();
0969     Long offsetPrn = pronoun.getStartNode().getOffset();
0970     long diff1 = offsetPrn.longValue() - offset1.longValue();
0971     long diff2 = offsetPrn.longValue() - offset2.longValue();
0972 //    assert (diff1 != 0 && diff2 != 0);
0973     //reject candidates that overlap with the pronoun
0974     if (diff1 == 0) {
0975       return ant2;
0976     }
0977     else if (diff2 == 0) {
0978       return ant1;
0979     }
0980 
0981 
0982     //get the one CLOSEST AND PRECEDING the pronoun
0983     if (diff1 > && diff2 > 0) {
0984       //we have [...antecedentA...AntecedentB....pronoun...] ==> choose B
0985       if (diff1 < diff2)
0986         return ant1;
0987       else
0988         return ant2;
0989     }
0990     else if (diff1 > 0){
0991       assert (Math.abs(diff1 + diff2< Math.abs(diff1+ Math.abs(diff2));
0992       //we have [antecedentA...pronoun...AntecedentB] ==> choose A
0993       return ant1;
0994     }
0995     else if (diff2 > 0){
0996       assert (Math.abs(diff1 + diff2< Math.abs(diff1+ Math.abs(diff2));
0997       //we have [antecedentA...pronoun...AntecedentB] ==> choose A
0998       return ant2;
0999     }
1000     else {
1001       //both possible antecedents are BEHIND the anaophoric pronoun - i.e. we have either
1002       //cataphora, or nominal antecedent, or an antecedent that is further back in scope
1003       //in any case - discard the antecedents
1004       return null;
1005     }
1006   }
1007 
1008 
1009   /** --- */
1010   private class Quote {
1011 
1012     /** --- */
1013     public static final int ANTEC_AFTER = 1;
1014     /** --- */
1015     public static final int ANTEC_BEFORE = 2;
1016     /** --- */
1017     public static final int ANTEC_BACK = 3;
1018     /** --- */
1019     private Set<Annotation> antecedentsBefore;
1020     /** --- */
1021     private Set<Annotation> antecedentsAfter;
1022     /** --- */
1023     private Set<Annotation> antecedentsBackInContext;
1024     /** --- */
1025     private Annotation quoteAnnotation;
1026     /** --- */
1027     private int quoteIndex;
1028 
1029     /** --- */
1030     public Quote(Annotation quoteAnnotation, int index) {
1031 
1032       this.quoteAnnotation = quoteAnnotation;
1033       this.quoteIndex = index;
1034       init();
1035     }
1036 
1037     /** --- */
1038     private void init() {
1039 
1040       //0.preconditions
1041       assert (null != textSentences);
1042 
1043       //0.5 create a restriction for PRP pos tokens
1044       FeatureMap prpTokenRestriction = new SimpleFeatureMapImpl();
1045       prpTokenRestriction.put(TOKEN_CATEGORY_FEATURE_NAME,PRP_CATEGORY);
1046 
1047       //1. generate the precPersons set
1048 
1049       //1.1 locate the sentece containing the opening quote marks
1050       @SuppressWarnings("unchecked")
1051       int quoteStartPos = Arrays.binarySearch(textSentences,
1052                                                         this.quoteAnnotation.getStartNode(),
1053                                                         ANNOTATION_OFFSET_COMPARATOR);
1054 
1055       //normalize index
1056       int startSentenceIndex = quoteStartPos >= ? quoteStartPos
1057                                                   : -quoteStartPos --1// blame Sun, not me
1058       //still not good?
1059       if (startSentenceIndex < 0) {
1060         startSentenceIndex = 0;
1061       }
1062 
1063       //1.2. get the persons and restrict to these that precede the quote (i.e. not contained
1064       //in the quote)
1065       this.antecedentsBefore = generateAntecedentCandidates(startSentenceIndex,
1066                                                             this.quoteIndex,
1067                                                             ANTEC_BEFORE);
1068 
1069 
1070       //2. generate the precPersonsInCOntext set
1071       //2.1. get the persons from the sentence precedeing the sentence containing the quote start
1072       if (startSentenceIndex > 0) {
1073         this.antecedentsBackInContext = generateAntecedentCandidates(startSentenceIndex -1,
1074                                                                     this.quoteIndex,
1075                                                                     ANTEC_BACK);
1076       }
1077 
1078       //2. generate the succ  Persons set
1079       //2.1 locate the sentece containing the closing quote marks
1080       @SuppressWarnings("unchecked")
1081       int quoteEndPos = Arrays.binarySearch(textSentences,
1082                                                         this.quoteAnnotation.getEndNode(),
1083                                                         ANNOTATION_OFFSET_COMPARATOR);
1084 
1085       //normalize it
1086       int endSentenceIndex = quoteEndPos >= ? quoteEndPos
1087                                               : -quoteEndPos --1// blame Sun, not me
1088       //still not good?
1089       if (endSentenceIndex < 0) {
1090         endSentenceIndex = 0;
1091       }
1092 
1093       this.antecedentsAfter = generateAntecedentCandidates(endSentenceIndex,
1094                                                             this.quoteIndex,
1095                                                             ANTEC_AFTER);
1096       //generate t
1097     }
1098 
1099 
1100     /** --- */
1101     private Set<Annotation> generateAntecedentCandidates(int sentenceNumber,
1102                                                         int quoteNumber ,
1103                                                         int mode) {
1104 
1105       //0. preconditions
1106       assert (sentenceNumber >=0);
1107       assert (quoteNumber >=0);
1108       assert (mode == Quote.ANTEC_AFTER ||
1109                         mode == Quote.ANTEC_BEFORE ||
1110                         mode == Quote.ANTEC_BACK);
1111 
1112       //1. get sentence
1113      Sentence sentence = textSentences[sentenceNumber];
1114 
1115       //2. get the persons
1116       Set<Annotation> antecedents = new HashSet<Annotation>(sentence.getPersons());
1117 
1118       //4. now get the he/she pronouns in the relevant context
1119       AnnotationSet annotations = null;
1120 
1121       switch(mode) {
1122 
1123         case ANTEC_BEFORE:
1124           annotations = defaultAnnotations.getContained(sentence.getStartOffset(),
1125                                                       this.getStartOffset());
1126           break;
1127 
1128         case ANTEC_AFTER:
1129           annotations = defaultAnnotations.getContained(this.getEndOffset(),
1130                                                      sentence.getEndOffset());
1131           break;
1132 
1133         case ANTEC_BACK:
1134           annotations = defaultAnnotations.getContained(sentence.getStartOffset(),
1135                                                      sentence.getEndOffset());
1136           break;
1137       }
1138 
1139       //4. get the pronouns
1140       //restrict to he/she pronouns
1141       if (null != annotations) {
1142         AnnotationSet pronouns = annotations.get(TOKEN_ANNOTATION_TYPE,PRP_RESTRICTION);
1143 
1144         if (null != pronouns) {
1145 
1146           Iterator<Annotation> it = pronouns.iterator();
1147           while (it.hasNext()) {
1148             Annotation currPronoun = it.next();
1149             //add to succPersons only if HE/SHE
1150             String pronounString = (String)currPronoun.getFeatures().get(TOKEN_STRING_FEATURE_NAME);
1151 
1152             if (null != pronounString &&
1153                 (pronounString.equalsIgnoreCase("he"|| pronounString.equalsIgnoreCase("she"))
1154                 )
1155               antecedents.add(currPronoun);
1156           }//while
1157         }//if
1158       }//if
1159 
1160 
1161       //3. depending on the mode, may have to restrict persons to these that precede/succeed
1162       //the quoted fragment
1163       //
1164       //for ANTEC_BEFORE, get the ones #preceding# the quote, contained in the sentence where
1165       //the quote *starts*
1166       //
1167       //for ANTEC_AFTER, get the ones #succeeding# the quote, contained in the sentence where
1168       //the quote *ends*
1169       //
1170       //for ANTEC_BACK, we are operating in the context of the sentence previous to the
1171       //sentence where the quote starts. I.e. we're resolbinf a case like
1172       // [sss "q1q1q1q1" s1s1s1s1]["q2q2q2q2"]
1173       //...and we want to get the entities from the s1s1 part - they *succeed* the #previous# quote
1174       //Note that the cirrent sentence is the first one, not the second
1175       //
1176       Iterator<Annotation> itPersons = antecedents.iterator();
1177 
1178       while (itPersons.hasNext()) {
1179         Annotation currPerson = itPersons.next();
1180 
1181         //cut
1182         if (Quote.ANTEC_BEFORE == mode &&
1183             currPerson.getStartNode().getOffset().intValue() > getStartOffset().intValue()) {
1184           //restrict only to persosn preceding
1185           itPersons.remove();
1186         }
1187         else if (Quote.ANTEC_AFTER == mode &&
1188                 currPerson.getStartNode().getOffset().intValue() < getEndOffset().intValue()) {
1189           //restrict only to persons succeeding the quote
1190           itPersons.remove();
1191         }
1192         else if (Quote.ANTEC_BACK == mode) {
1193           //this one is tricky
1194           //locate the quote previous to the one we're resolving
1195           //(since we're operating in the sentence previous to the quote being resolved
1196           //wew try to find if any quote (prevQuote) exist in this sentence and get the
1197           //persons succeeding it)
1198 
1199           //get prev quote
1200           //is the curr quote the first one?
1201           if (quoteNumber >0) {
1202             Quote prevQuote = PronominalCoref.this.quotedText[quoteNumber-1];
1203 
1204             //restrict to the succeeding persons
1205             if (currPerson.getStartNode().getOffset().longValue() < prevQuote.getEndOffset().longValue()) {
1206               itPersons.remove();
1207             }
1208           }
1209         }
1210       }
1211 
1212       return antecedents;
1213     }
1214 
1215     /** --- */
1216     public Long getStartOffset() {
1217       return this.quoteAnnotation.getStartNode().getOffset();
1218     }
1219 
1220     /** --- */
1221     public Long getEndOffset() {
1222       return this.quoteAnnotation.getEndNode().getOffset();
1223     }
1224 
1225     /** --- */
1226     public Set<Annotation> getAntecedentCandidates(int type) {
1227 
1228       switch(type) {
1229 
1230         case ANTEC_AFTER:
1231           return null != this.antecedentsAfter ? 
1232                          this.antecedentsAfter : 
1233                          new HashSet<Annotation>();
1234 
1235         case ANTEC_BEFORE:
1236           return null != this.antecedentsBefore ? 
1237                          this.antecedentsBefore : 
1238                          new HashSet<Annotation>();
1239 
1240         case ANTEC_BACK:
1241           return null != this.antecedentsBackInContext ? 
1242                   this.antecedentsBackInContext : 
1243                   new HashSet<Annotation>();
1244 
1245         default:
1246           throw new IllegalArgumentException();
1247       }
1248     }
1249 
1250   }
1251 
1252 
1253   /** --- */
1254   private class Sentence {
1255 
1256     /** --- */
1257     @SuppressWarnings("unused")
1258     private int sentNumber;
1259     /** --- */
1260     @SuppressWarnings("unused")
1261     private int paraNumber;
1262     /** --- */
1263     private Long startOffset;
1264     /** --- */
1265     private Long endOffset;
1266     /** --- */
1267     private AnnotationSet persons;
1268     /** --- */
1269     private AnnotationSet inanimated;
1270 
1271     /** --- */
1272     public Sentence(int sentNumber,
1273                     int paraNumber,
1274                     Long startOffset,
1275                     Long endOffset,
1276                     AnnotationSet persons,
1277                     AnnotationSet inanimated) {
1278 
1279       this.sentNumber = sentNumber;
1280       this.paraNumber = paraNumber;
1281       this.startOffset = startOffset;
1282       this.endOffset = endOffset;
1283       this.persons = persons;
1284       this.inanimated = inanimated;
1285     }
1286 
1287     /** --- */
1288     public Long getStartOffset() {
1289       return this.startOffset;
1290     }
1291 
1292     /** --- */
1293     public Long getEndOffset() {
1294       return this.endOffset;
1295     }
1296 
1297     /** --- */
1298     public AnnotationSet getPersons() {
1299       return this.persons;
1300     }
1301 
1302     public AnnotationSet getInanimated() {
1303       return this.inanimated;
1304     }
1305     
1306   }
1307 
1308 
1309   public String getInanimatedEntityTypes() {
1310     return inanimatedEntityTypes;
1311   }
1312 
1313   public void setInanimatedEntityTypes(String inanimatedEntityTypes) {
1314     this.inanimatedEntityTypes = inanimatedEntityTypes;
1315   }
1316 
1317   /* (non-Javadoc)
1318    * @see gate.util.Benchmarkable#getBenchmarkId()
1319    */
1320   @Override
1321   public String getBenchmarkId() {
1322     if(benchmarkId == null) {
1323       return getName();
1324     }
1325     else {
1326       return benchmarkId;
1327     }
1328   }
1329 
1330   /* (non-Javadoc)
1331    * @see gate.util.Benchmarkable#setBenchmarkId(java.lang.String)
1332    */
1333   @Override
1334   public void setBenchmarkId(String benchmarkId) {
1335     this.benchmarkId = benchmarkId;
1336   }
1337 
1338 }