code: package caevo;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import caevo.util.TreeOperator;
  import edu.stanford.nlp.trees.TypedDependency;
  
  /**
   * A TemporalContext contains partitions the TextEvents in a SieveDocument that
   * share the same temporal context.
   * 
   * 
   * @author tcassidy
   *
   */
  
  public class TemporalContext {
    private SieveDocument doc;
    private HashMap<String, List<TextEvent>> contextMap;
    private static String creationTimeRegex = "(\\d{4}-\\d{2}-\\d{2}).*";
    private static Pattern creationTimePattern = Pattern
        .compile(creationTimeRegex);
    private boolean debug = true;
  
    // Return a new TemporalContext
    public TemporalContext(SieveDocument doc) {
      this.doc = doc;
      this.contextMap = new HashMap<String, List<TextEvent>>();
    }
  
    // Add a single TextEvent to a context by contextId
    private void addEventToContext(String contextIdString, TextEvent event) {
      List<TextEvent> context = this.contextMap.get(contextIdString);
      if (context != null)
        context.add(event);
      else {
        this.createContext(contextIdString);
        this.addEventToContext(contextIdString, event);
      }
    }
  
    // Add a list of TextEvents to a context by contextId
    private void addEventsToContext(String contextIdString,
        List<TextEvent> eventList) {
      List<TextEvent> context = this.contextMap.get(contextIdString);
      if (context != null)
        context.addAll(eventList);
      else {
        this.createContext(contextIdString);
        this.addEventsToContext(contextIdString, eventList); // infinite recursion
                                                             // possibility?
      }
    }
  
    // Create a new context with id contextId
    private void createContext(String contextIdString) {
      this.contextMap.put(contextIdString, new ArrayList<TextEvent>());
    }
  
    // Return the context with id contextId
    public List<TextEvent> getContext(String contextIdString) {
      return this.contextMap.get(contextIdString);
    }
  
    // Return the entire context hashmap
    public HashMap<String, List<TextEvent>> getContextMap() {
      return this.contextMap;
    }
  
    // This context puts any two events in the same sentence in the same context
    public void addContextSameSent() {
      // Iterate over lists of events in our doc by sent;
      // add each list as a single temporal context
      List<List<TextEvent>> eventsBySent = doc.getEventsBySentence();
      Integer c = 0;
      for (List<TextEvent> eventList : eventsBySent) {
        this.addEventsToContext(c.toString(), eventList);
        c++;
      }
    }
  
    //
    // public void addContextMostRecentTimex() {
  
    // }
  
    private String getDocCreationDate(Timex dctTimex) {
      String value = dctTimex.getValue();
      Matcher matcher = creationTimePattern.matcher(value);
      if (matcher.matches()) {
        String dcd = matcher.group(1);
        return dcd;
      }
      return null;
    }
  
    public void addContextSimpleDep() {
      // First get the dct Timex
      // It's value (up to the day granularity)
      // will serve as the default contextId, for events that don't govern a time
      // expression
      List<Timex> dctTimexList = doc.getDocstamp();
      if (dctTimexList == null)
        return;
      Timex dct = dctTimexList.get(0);
      if (dct == null)
        return;
  
      // Now we want to map each event to either dctDayValue, or the timex it
      // governs.
      // If it governs more than one timex, ???
  
      List<List<Timex>> timexesBySentId = doc.getTimexesBySentence();
      List<List<TextEvent>> eventsBySentId = doc.getEventsBySentence();
      List<List<TypedDependency>> depsBySentId = doc.getAllDependencies();
      List<SieveSentence> sentsById = null;
      if (this.debug) {
        sentsById = doc.getSentences();
      }
      int numSents = depsBySentId.size();
  
      for (int sid = 0; sid < numSents; sid++) {
        for (TextEvent event : eventsBySentId.get(sid)) {
          // First check if the event governs a timex.
          // put them all into a list called timexesGovernedByEvent
          List<Timex> timexesGovernedByEvent = new ArrayList<Timex>();
          for (Timex timex : timexesBySentId.get(sid)) {
            // First try to add timexes in the sentences that event governs...
            for (TypedDependency td : depsBySentId.get(sid)) {
              if (event.getIndex() == td.gov().index()
                  && timex.getTokenOffset() == td.dep().index()) {
                timexesGovernedByEvent.add(timex);
                if (this.debug) {
                  System.out.printf("%s(%s) governs %s(%s)\n%s\n",
                      event.getString(), event.getId(), timex.getText(),
                      timex.getTid(), sentsById.get(sid).sentence());
                }
              }
            }
          }
  
          // If no timexes were added to timexesGovernedByEvent, then add the
          // default dct
          if (timexesGovernedByEvent.size() == 0) {
            timexesGovernedByEvent.add(dct);
          }
          // Now, add the current event to the appropriate temporal context(s).
          // Note that if the event governed more than one timex it will be added
          // to more than one context!
          for (Timex timex : timexesGovernedByEvent) {
            this.addEventToContext(this.getValueFromTimex(timex), event);
          }
        }
      }
  
    }
  
    /**
     * For each event, obtain the shortest dependency path between the event each
     * timex in its sentence. If no such paths exist, assign the event to the DCT
     * Else, choose the shortest path. If same length, choose one with most
     * gov-to-dep pointing arrows If still more than one, choose the first one in
     * the list
     */
    public void addContextNearestTimexInPath() {
      // First get the DCT Timex
      List<Timex> dctTimexList = doc.getDocstamp();
      Timex dct = dctTimexList.get(0);
      if (dct == null) {
        return;
      }
  
      // Get timexes, events, and dependencies (by sid) for the document.
      List<List<Timex>> timexesBySentId = doc.getTimexesBySentence();
      List<List<TextEvent>> eventsBySentId = doc.getEventsBySentence();
      List<List<TypedDependency>> depsBySentId = doc.getAllDependencies();
  
      // Loop
      int numSents = depsBySentId.size();
      for (int sid = 0; sid < numSents; sid++) {
        for (TextEvent event : eventsBySentId.get(sid)) {
          // We want to map each Timex t to an array of ints representing
          // (1) the length of the path between event and t, and
          // (2) the number of right arrows
          HashMap<Timex, int[]> timexToCounts = new HashMap<Timex, int[]>();
          for (Timex timex : timexesBySentId.get(sid)) {
            // Get the shortest path from event to timex:
            List<TypedDependency> deps = depsBySentId.get(sid);
            int start = event.getIndex();
            int end = timex.getTokenOffset();
            String shortestPath = TreeOperator.dependencyPath(start, end, deps);
            // to be continued?
          }
        }
  
      }
  
    }
  
    /**
     * Note that getValueFromTimex returns the value attribute of a timex except
     * the case of dct, in which case a truncated value is returned.
     **/
    public String getValueFromTimex(Timex timex) {
      // Helper function to deal with the fact that there is no Timex with
      // document creation day.
      if (timex.isDCT()) {
        return this.getDocCreationDate(timex);
      } else {
        return timex.getValue();
      }
    }
  
    /**
     * 
     * @param path
     * @return the length of a dependency path (provided as a String)
     */
    public int getDepPathLength(String path) {
      int count = path.split("->").length;
      count += path.split("<-").length;
      return count;
    }
  
  }
  