code: package caevo.tlink;
  
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.Map.Entry;
  import java.util.Set;
  
  import edu.stanford.nlp.ling.BasicDatum;
  import edu.stanford.nlp.ling.RVFDatum;
  import edu.stanford.nlp.stats.ClassicCounter;
  import edu.stanford.nlp.stats.Counter;
  import edu.stanford.nlp.util.CollectionUtils;
  
  /**
   * Class to represent a time expression's Class in relation to its document's
   * timestamp, and its features. Each feature can be seen more than once, these
   * aren't binary features.
   */
  public class TLinkDatum {
    public static enum TYPE {
      EESAMENODOMINATE, EESAMEDOMINATES, EEDIFF, EDCT, ETSAME, ETDIFF
    };
  
    TYPE _type = null;
    TLink.Type _relation;
    String _docSource = null;
    Counter<String> _featureCounts = null;
    public TLink _originalTLink = null; // for debugging
  
    public TLinkDatum() {
    }
  
    public TLinkDatum(TLink.Type yclass) {
      _relation = yclass;
    }
  
    /**
     * For debugging, keep the TimeBank object around...
     */
    public void setOriginalTLink(TLink link) {
      _originalTLink = link;
    }
  
    public void setDocSource(String doc) {
      _docSource = doc;
    }
  
    public void setType(TYPE type) {
      _type = type;
    }
  
    public void setLabel(TLink.Type label) {
      this._relation = label;
    }
  
    public Set<String> getFeatureKeys() {
      if (_featureCounts == null)
        return null;
      else
        return _featureCounts.keySet();
    }
  
    public Counter<String> getFeatures() {
      return _featureCounts;
    }
  
    public double getCount(String feat) {
      return _featureCounts.getCount(feat);
    }
  
    public double totalCount() {
      if (_featureCounts == null)
        return 0.0;
      else
        return _featureCounts.totalCount();
    }
  
    public void addFeature(String feat) {
      if (_featureCounts == null)
        _featureCounts = new ClassicCounter<String>();
      if (feat != null)
        _featureCounts.incrementCount(feat);
    }
  
    public void addFeature(String feat, double count) {
      if (_featureCounts == null)
        _featureCounts = new ClassicCounter<String>();
      if (feat != null)
        _featureCounts.incrementCount(feat, count);
    }
  
    public void addFeatures(Collection<String> feats) {
      if (_featureCounts == null)
        _featureCounts = new ClassicCounter<String>();
      if (feats != null) {
        for (String feat : feats)
          addFeature(feat);
      }
    }
  
    public void addFeatures(Counter<String> feats) {
      if (feats != null) {
        if (_featureCounts == null)
          _featureCounts = new ClassicCounter<String>();
        _featureCounts.addAll(feats);
      }
    }
  
    public void removeFeature(String feat) {
      if (_featureCounts != null)
        _featureCounts.remove(feat);
    }
  
    /**
     * Create an RVFDatum object from our current feature set.
     * 
     * @return An RVFDatum object for classification/training.
     */
    public RVFDatum<String, String> createRVFDatum() {
      if (_featureCounts == null)
        return new RVFDatum<String, String>(new ClassicCounter<String>(),
            (_relation == null ? "null" : _relation.toString()));
      else
        return new RVFDatum<String, String>(_featureCounts,
            (_relation == null ? "null" : _relation.toString()));
    }
  
    public BasicDatum<String, String> createBasicDatum() {
      if (_featureCounts == null)
        return new BasicDatum<String, String>(new HashSet<String>(),
            (_relation == null ? "null" : _relation.toString()));
      else
        return new BasicDatum<String, String>(_featureCounts.keySet(),
            (_relation == null ? "null" : _relation.toString()));
    }
  
    public String getLabelAsString() {
      return _relation.toString();
    }
  
    public TLink.Type getLabel() {
      return _relation;
    }
  
    public String getSourceDoc() {
      return _docSource;
    }
  
    public TYPE getType() {
      return _type;
    };
  
    /**
     * Creates an object from its string form, which should be on one line
     * tab-separated.
     */
    public TLinkDatum valueOf(String str) {
      return valueOf(str, null);
    }
  
    public TLinkDatum valueOf(String str, Set<String> keepFeats) {
      String[] parts = str.split("\t");
      if (parts.length > 1) {
        TLinkDatum datum = new TLinkDatum(TLink.Type.valueOf(parts[0]));
        for (int i = 1; i < parts.length; i += 2) {
          try {
            if (keepFeats == null || keepFeats.contains(parts[i]))
              datum.addFeature(parts[i], Double.valueOf(parts[i + 1]));
          } catch (Exception ex) {
            System.out.println("Error on " + parts[i] + " with " + parts[i + 1]);
            ex.printStackTrace();
            System.exit(-1);
          }
        }
        // for( int i = 1; i < parts.length; i++ )
        // datum.addFeature(parts[i]);
        return datum;
      }
      return null;
    }
  
    /**
     * Reinstantiate this instance as if it is brand new from a String
     * representation.
     * 
     * @param str
     *          The full datum in string form.
     * @param keepFeats
     *          Features to load. null means accept all
     */
    public void valueFrom(String str, Set<String> keepFeats) {
      String[] parts = str.split("\t");
      _relation = TLink.Type.valueOf(parts[0]);
      if (parts.length > 1) {
        _featureCounts = null;
        for (int i = 1; i < parts.length; i += 2) {
          try {
            if (keepFeats == null || keepFeats.contains(parts[i]))
              addFeature(parts[i], Double.valueOf(parts[i + 1]));
          } catch (Exception ex) {
            System.out.println("Error on " + parts[i] + " with " + parts[i + 1]);
            ex.printStackTrace();
            System.exit(-1);
          }
        }
      } else
        System.out.println("WARNING: no datum on the given line! *" + str + "*");
    }
  
    /**
     * Create an object from its string representation, features separated by
     * tabs. e.g., apw_eng_12323.4124 BEFORE sameSent dominates POS1-1-NN ...
     * 
     * @param str
     *          The string representation of the object from toString().
     * @return The new object from the given string.
     */
    public static TLinkDatum fromString(String str) {
      if (str == null)
        return null;
  
      try {
        String[] parts = str.split("\t");
        TLinkDatum datum = new TLinkDatum(TLink.Type.valueOf(parts[1]));
        datum.setDocSource(parts[0]);
        for (int ii = 2; ii < parts.length; ii++)
          datum.addFeature(parts[ii]);
  
        return datum;
      } catch (Exception ex) {
        System.out.println("ERROR parsing TLinkDatum from string: " + str);
        System.exit(-1);
      }
      return null;
    }
  
    /**
     * Write the features in text order.
     */
    public String toStringSorted() {
      StringBuffer sb = new StringBuffer(
          _docSource + "\t" + _relation.toString());
  
      for (String key : CollectionUtils.sorted(_featureCounts.keySet())) {
        sb.append("\t");
        sb.append(key);
        // sb.append("\t");
        // sb.append((int)_featureCounts.getCount(key));
      }
      // sb.append(Counters.toSortedByKeysString(_featureCounts, "%1$s %2$f",
      // "\t", "HAPPY"));
  
      return sb.toString();
    }
  
    public String toString() {
      StringBuffer sb = new StringBuffer(_docSource + "\t"
          + (_relation != null ? _relation.toString() : "null"));
      sb.append('\t');
      sb.append((_type != null ? _type.toString() : "null"));
      for (Entry<String, Double> entry : _featureCounts.entrySet()) {
        sb.append('\t');
        sb.append(entry.getKey());
        // sb.append('\t');
        // sb.append(entry.getValue().intValue());
      }
      return sb.toString();
    }
  }
  