code: package caevo.sieves;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import caevo.Main;
  import caevo.SieveDocument;
  import caevo.SieveDocuments;
  import caevo.TextEvent;
  import caevo.Timex;
  import caevo.tlink.EventEventLink;
  import caevo.tlink.TLink;
  import caevo.tlink.TimeTimeLink;
  import caevo.util.CaevoProperties;
  import caevo.util.TreeOperator;
  import net.didion.jwnl.data.POS;
  import edu.stanford.nlp.trees.LabeledScoredTreeFactory;
  import edu.stanford.nlp.trees.Tree;
  import edu.stanford.nlp.trees.TreeFactory;
  
  /**
   * This Sieve labels event-event and time-time pairs based on whether they (1)
   * are possibly synonomous according to wordnet and/or (2) share the same lemma
   * 
   * Parameters
   * 
   * checkSiblings: label if the two words share the same synset checkLemmas:
   * label if the two words share the same lemma (NOTE: we expect pairs licensed
   * for checking by checkLemmas to be a subset of those licensed by
   * checkSiblings!) sentWindow: label pairs of words within this many sentences
   * ignoreReporting: ignore pair if either verb is a reporting verb
   * ignoreIAction: ignore pair if either verb is an intentional action
   * ignoreSameSentence: ignore pair if from the same sentence ttPairs: consider
   * timex-timex pairs eePairs: consider event-event pairs
   * 
   * 
   * Label timexes with the same wordnet lemma as simultaneous Label events with
   * the same wordnet lemma, or whose wordnet synsets overlap, as vague.
   * 
   * 
   * Some results (NOTE: I think there is a problem with my enumeration of event
   * pairs - more below)
   * 
   * checkSiblings(y)checkLemmas(y)sentWindow(0)ignoreReporting(n)ignoreIAction(n)
   * ignoreSameSentence(n)ttPairs(y)eePairs(y) WordNet209 p=0.52 13 of 25
   * Non-VAGUE: p=0.54 13 of 24
   * 
   * checkSiblings(y)checkLemmas(y)sentWindow(1)ignoreReporting(n)ignoreIAction(n)
   * ignoreSameSentence(n)ttPairs(y)eePairs(y) WordNet209 p=0.75 75 of 100
   * Non-VAGUE: p=0.76 75 of 99 WordNet209 p=0.77 69 of 90 Non-VAGUE: p=0.78 69 of
   * 89 (ignoreIaction(y)) WordNet209 p=0.65 34 of 52 Non-VAGUE: p=0.67 34 of 51
   * (ignoreIaction/reporting(y)) only check lemmas (
   * checkSiblings(n)checkLemmas(y) ) WordNet209 p=0.61 23 of 38 Non-VAGUE: p=0.62
   * 23 of 37
   * 
   * checkSiblings(y)checkLemmas(y)sentWindow(1)ignoreReporting(n)ignoreIAction(n)
   * ignoreSameSentence(y)ttPairs(y)eePairs(y) WordNet209 p=0.77 65 of 84
   * Non-VAGUE: p=0.77 65 of 84 only check lemmas ( checkSiblings(n)checkLemmas(y)
   * ) WordNet209 p=0.61 20 of 33 Non-VAGUE: p=0.62 23 of 37
   * 
   * ==> I think there is a bug - Shouldn't the denominators line up (first +
   * third = second)? (25 + 84 != 100). Since we are checking if two events are in
   * the same sentence before classifying, the mistake is probably that we are
   * missing 9 pairs during the enumeration of the 100 for the second case above.
   * Which 9?
   * 
   * 
   * @author cassidy
   */
  
  public class WordNet209 implements Sieve {
    public boolean debug = false;
    private static TreeFactory tf = new LabeledScoredTreeFactory();
    private int sentWindow = 1;
    private boolean checkSiblings = false;
    private boolean checkLemmas = false;
    private boolean ignoreReporting = false;
    private boolean ignoreIAction = false;
    private boolean ignoreSameSentence = false;
    private boolean eePairs = true;
    private boolean ttPairs = true;
  
    private static final Map<String, POS> postagSimpleToPOS;
    static {
      postagSimpleToPOS = new HashMap<String, POS>();
      postagSimpleToPOS.put("VB", POS.VERB);
      postagSimpleToPOS.put("NN", POS.NOUN);
      postagSimpleToPOS.put("JJ", POS.ADJECTIVE);
      postagSimpleToPOS.put("RB", POS.ADVERB);
    }
  
    /**
     * The main function. All sieves must have this.
     */
    /*
     * (non-Javadoc)
     * 
     * @see timesieve.sieves.Sieve#annotate(timesieve.InfoFile, java.lang.String,
     * java.util.List)
     */
    public List<TLink> annotate(SieveDocument doc, List<TLink> currentTLinks) {
      // Get property values from the config file
      try {
        sentWindow = CaevoProperties.getInt("Wordnet209.sentWindow", 1);
        checkSiblings = CaevoProperties.getBoolean("Wordnet209.checkSiblings",
            true);
        checkLemmas = CaevoProperties.getBoolean("Wordnet209.checkLemmas", true);
        ignoreReporting = CaevoProperties.getBoolean("Wordnet209.ignoreReporting",
            true);
        ignoreIAction = CaevoProperties.getBoolean("Wordnet209.ignoreIAction",
            true);
        ignoreSameSentence = CaevoProperties
            .getBoolean("Wordnet209.ignoreSameSentence", true);
        eePairs = CaevoProperties.getBoolean("Wordnet209.eePairs", true);
        ttPairs = CaevoProperties.getBoolean("Wordnet209.ttPairs", true);
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
  
      // proposed will hold all TLinks proposed by the sieve
      List<TLink> proposed = new ArrayList<TLink>();
  
      // get all events, timexes, by sentence
      List<List<TextEvent>> allEvents = doc.getEventsBySentence();
      List<List<Timex>> allTimexes = doc.getTimexesBySentence();
  
      // we need all trees in order to get pos tags
      List<Tree> trees = doc.getAllParseTrees();
  
      // hold all pairs to be classified (in accordance with sentWindow)
      // in array lists. No need for ET pairs here.
      ArrayList<TextEvent[]> eventPairs = getEventPairs(allEvents);
      ArrayList<Timex[]> TimexPairs = getTimexPairs(allTimexes);
  
      // classify each type of pair based on parameter settings
      if (eePairs == true) {
        for (TextEvent[] eventPair : eventPairs) {
          TLink tlink = getEELink(eventPair[0], eventPair[1], trees);
          if (tlink != null) {
            proposed.add(tlink);
          }
        }
      }
      if (ttPairs == true) {
        for (Timex[] timexPair : TimexPairs) {
          TLink tlink = getTTLink(timexPair[0], timexPair[1], trees);
          if (tlink != null) {
            proposed.add(tlink);
          }
        }
      }
      if (debug == true) {
        System.out.println("TLINKS: " + proposed);
      }
      return proposed;
    }
  
    // Get all pairs of events that need to be classified
    // TODO Note that if ignoreSameSentence is true, such pairs are excluded in
    // getLink() - this is not entirely efficient.
    private ArrayList<TextEvent[]> getEventPairs(
        List<List<TextEvent>> allEvents) {
      // for each event, compare is with all events in range, in accordance
      // with sentWindow.
      ArrayList<TextEvent[]> eventPairs = new ArrayList<TextEvent[]>();
      int numSents = allEvents.size();
      // iterate over each sentence
      for (int sid = 0; sid < numSents; sid++) {
        // iterate over events in the sent that corresponds with sid
        int numEvents = allEvents.get(sid).size();
        for (int i = 0; i < numEvents; i++) {
          // get the event in the list at index i
          TextEvent e1 = allEvents.get(sid).get(i);
          // iterate over remaining events in the sentence
          for (int j = i + 1; j < numEvents; j++) {
            TextEvent e2 = allEvents.get(sid).get(j);
            TextEvent[] eventPair = { e1, e2 };
            eventPairs.add(eventPair);
            // if the label is null, the mapping couldn't be applied.
            // otherwise, add (e1, e2, label) to proposed.
          }
          // iterate over other events in subsequent sentences in accordance
          // with sentWindow.
          // Note that if sentWindow == 0, the loop will never start since
          // sid2 <= sid + sentWindow will never be satisfied
          for (int sid2 = sid + 1; sid2 <= sid + sentWindow
              && sid2 < numSents; sid2++) {
            // iterate over each event in a given window sentence
            int numEvents2 = allEvents.get(sid2).size();
            // compare e1 with all events in the sentence with id sid2
            for (int k = 0; k < numEvents2; k++) {
              // label (e1, e2, label) only if label is not null, as in above
              TextEvent e2 = allEvents.get(sid2).get(k);
              TextEvent[] eventPair = { e1, e2 };
              eventPairs.add(eventPair);
            }
          }
        }
      }
      return eventPairs;
    }
  
    // Get all pairs of timexes that need to be classified
    // TODO Note that if ignoreSameSentence is true, such pairs are excluded in
    // getLink() - this is not entirely efficient.
    private ArrayList<Timex[]> getTimexPairs(List<List<Timex>> allTimexes) {
      // for each event, compare is with all events in range, in accordance
      // with sentWindow.
      ArrayList<Timex[]> timexPairs = new ArrayList<Timex[]>();
      int numSents = allTimexes.size();
      // iterate over each sentence
      for (int sid = 0; sid < numSents; sid++) {
        // iterate over events in the sent that corresponds with sid
        int numTimexes = allTimexes.get(sid).size();
        for (int i = 0; i < numTimexes; i++) {
          // get the event in the list at index i
          Timex e1 = allTimexes.get(sid).get(i);
          // iterate over remaining events in the sentence
          for (int j = i + 1; j < numTimexes; j++) {
            Timex e2 = allTimexes.get(sid).get(j);
            Timex[] timexPair = { e1, e2 };
            timexPairs.add(timexPair);
          }
          // iterate over other events in subsequent sentences in accordance
          // with sentWindow.
          // Note that if sentWindow == 0, the loop will never start since
          // sid2 <= sid + sentWindow will never be satisfied
          for (int sid2 = sid + 1; sid2 <= sid + sentWindow
              && sid2 < numSents; sid2++) {
            // iterate over each event in a given window sentence
            int numEvents2 = allTimexes.get(sid2).size();
            // compare e1 with all events in the sentence with id sid2
            for (int k = 0; k < numEvents2; k++) {
              // label (e1, e2, label) only if label is not null, as in above
              Timex e2 = allTimexes.get(sid2).get(k);
              Timex[] timexPair = { e1, e2 };
              timexPairs.add(timexPair);
            }
          }
        }
      }
      return timexPairs;
    }
  
    /**
     * all pairs of events that are siblings (i.e. their WordNet synsets overlap)
     * are labeled VAGUE. D&G use SIMULTANEOUS.
     */
    private TLink getEELink(TextEvent e1, TextEvent e2, List<Tree> trees) {
      // Check if any of the ignore criteria hold
      // If so, do not classify the pair
      boolean e1IsReporting = e1.getTheClass() == TextEvent.Class.REPORTING;
      boolean e2IsReporting = e2.getTheClass() == TextEvent.Class.REPORTING;
      if (ignoreReporting && (e1IsReporting || e2IsReporting)
          || ignoreIAction && e1.getTheClass() == TextEvent.Class.I_ACTION
          || ignoreSameSentence && e1.getSid() == e2.getSid()) {
        return null;
      }
      // Get the word, lemma, and pos tag
      // (the penn treebank string and the more basic wordnet POS via
      // postagSimpleToPOS)
      Tree sentParseTree1 = trees.get(e1.getSid());
      Tree sentParseTree2 = trees.get(e2.getSid());
      String postagStr1 = posTagFromTree(sentParseTree1, e1.getIndex());
      String postagStr2 = posTagFromTree(sentParseTree2, e2.getIndex());
  
      if (postagStr1.length() < 2 || postagStr2.length() < 2) {
        return null;
      }
  
      String postagSimple1 = postagStr1.substring(0, 2);
      String postagSimple2 = postagStr2.substring(0, 2);
      POS pos1 = postagSimpleToPOS.get(postagSimple1);
      POS pos2 = postagSimpleToPOS.get(postagSimple2);
      String lemma1 = Main.wordnet.lemmatizeTaggedWord(e1.getString(),
          postagStr1);
      String lemma2 = Main.wordnet.lemmatizeTaggedWord(e2.getString(),
          postagStr2);
      // Synset[] syns1 = Main.wordnet.synsetsOf(e1.getString(), pos1);
      // Synset[] syns2 = Main.wordnet.synsetsOf(e2.getString(), pos2);
      // Only proceed if pos1 and pos2 are non-null and equal,
      // and if so check if they are siblings.
      // Note that areSiblings appears be resistant to the case when
      // two words have the same lemma and (basic) pos but appear in
      // different morphological forms - presumably because the method ultimately
      // compares two synsets, which are fixed. D'Souza and Ng's rule says
      // to check if the headWordLemma of the event is within the synset of the
      // other;
      // I don't think it is necessary to check that here.
      if (checkSiblings && pos1 != null && pos2 != null && pos1 == pos2
          && Main.wordnet.areSiblings(e1.getString(), e2.getString(), pos1)) {
        return new EventEventLink(e1.getEiid(), e2.getEiid(), TLink.Type.VAGUE);
      }
      if (checkLemmas && lemma1.equals(lemma2)) {
        return new EventEventLink(e1.getEiid(), e2.getEiid(), TLink.Type.VAGUE);
      } else {
        return null;
      }
    }
  
    private TLink getTTLink(Timex e1, Timex e2, List<Tree> trees) {
      //
      if (ignoreSameSentence && e1.getSid() == e2.getSid())
        return null;
      // Get the word, lemma, and pos tag
      // (the penn treebank string and the more basic wordnet POS via
      // postagSimpleToPOS)
      Tree sentParseTree1 = trees.get(e1.getSid());
      Tree sentParseTree2 = trees.get(e2.getSid());
      String postagStr1 = posTagFromTree(sentParseTree1, e1.getTokenOffset());
      String postagStr2 = posTagFromTree(sentParseTree2, e2.getTokenOffset());
  
      if (postagStr1.length() < 2 || postagStr2.length() < 2) {
        return null;
      }
  
      String postagSimple1 = postagStr1.substring(0, 2);
      String postagSimple2 = postagStr2.substring(0, 2);
  
      POS pos1 = postagSimpleToPOS.get(postagSimple1);
      POS pos2 = postagSimpleToPOS.get(postagSimple2);
      String lemma1 = Main.wordnet.lemmatizeTaggedWord(e1.getText(), postagStr1);
      String lemma2 = Main.wordnet.lemmatizeTaggedWord(e2.getText(), postagStr2);
      // Synset[] syns1 = Main.wordnet.synsetsOf(e1.getString(), pos1);
      // Synset[] syns2 = Main.wordnet.synsetsOf(e2.getString(), pos2);
      // Only proceed if pos1 and pos2 are non-null and equal,
      // and if so check if they are siblings.
      // Note that areSiblings appears be resistant to the case when
      // two words have the same lemma and (basic) pos but appear in
      // different morphological forms - presumably because the method ultimately
      // compares two synsets, which are fixed. D'Souza and Ng's rule says
      // to check if the headWordLemma of the event is within the synset of the
      // other;
      // I don't think it is necessary to check that here.
      // if (checkSiblings && pos1 != null && pos2 != null && pos1 == pos2
      // && Main.wordnet.areSiblings(e1.getText(), e2.getText(), pos1)) {
      // return new TimeTimeLink(e1.getTid() , e2.getTid(),
      // TLink.Type.SIMULTANEOUS);
      // }
      if (checkLemmas && lemma1.equals(lemma2)) {
        return new TimeTimeLink(e1.getTid(), e2.getTid(),
            TLink.Type.SIMULTANEOUS);
      } else {
        return null;
      }
    }
  
    // Use posTagFromTree(sidToTree(*),i) to get pos tag for index i in a sentence
    private String posTagFromTree(Tree sentParseTree, int tokenIndex) {
      String posTag = TreeOperator.indexToPOSTag(sentParseTree, tokenIndex);
      return posTag;
    }
  
    private Tree sidToTree(int sid, List<String> allParseStrings) {
      String sentParseString = allParseStrings.get(sid);
      Tree sentParseTree = TreeOperator.stringToTree(sentParseString, tf);
      return sentParseTree;
    }
  
    /**
     * No training. Just rule-based.
     */
    public void train(SieveDocuments trainingInfo) {
      // no training
    }
  
  }
  