001    package org.hackystat.telemetry.analyzer.model;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.Comparator;
007    import java.util.List;
008    import java.util.TreeMap;
009    
010    import org.hackystat.utilities.time.period.TimePeriod;
011    
012    /**
013     * Represents a single telemetry stream, which contains an ordered series of TelemetryDataPoint
014     * instances.
015     * 
016     * @author (Cedric) Qin Zhang
017     */
018    public class TelemetryStream {
019    
020      private static final TelemetryDataPointTimePeriodComparator DATAPOINT_TIMEPERIOD_COMPARATOR 
021          = new TelemetryDataPointTimePeriodComparator();
022    
023      private Object tag;
024    
025      private TreeMap<TimePeriod, TelemetryDataPoint> dataPoints = 
026        new TreeMap<TimePeriod, TelemetryDataPoint>();
027    
028      /**
029       * Constructs this instance.
030       * 
031       * @param tag An object that helps to recognize this telemetry stream. Null is
032       *        a valid value.
033       */
034      public TelemetryStream(Object tag) {
035        this.tag = tag;
036      }
037    
038      /**
039       * Gets the tag associated with this telemetry stream.
040       * 
041       * @return The tag object.
042       */
043      public Object getTag() {
044        return this.tag;
045      }
046    
047      /**
048       * Sets the tag associated with this telemetry stream.
049       * 
050       * @param tag The new tag value
051       */
052      public void setTag(final Object tag) {
053        this.tag = tag;
054      }
055    
056      /**
057       * Adds a data point to this telemetry stream. Note that the time period in
058       * all data points must be of the same type (either day, week, or month).
059       * Otherwise, an exception will be raised.
060       * 
061       * @param dataPoint The data point to be added.
062       * 
063       * @throws TelemetryDataModelException If the data for the time period already exists.
064       */
065      public void addDataPoint(TelemetryDataPoint dataPoint) throws TelemetryDataModelException {
066        try {
067          TimePeriod period = dataPoint.getPeriod();
068          if (this.dataPoints.containsKey(period)) {
069            throw new TelemetryDataModelException("Duplicated period: " + period.toString());
070          }
071          this.dataPoints.put(period, dataPoint);
072        }
073        catch (ClassCastException ex) {
074          // this exception is raised when the types of time period in data points are different.
075          throw new 
076          TelemetryDataModelException("All data points must have the same time period type.", ex);
077        }
078      }
079    
080      /**
081       * Gets the value associated with the specified time period. Note that if this
082       * telemetry stream does not contain the data point associated the time
083       * period, an exception is raised. If a null is return, it means that this
084       * streams contains the data point, but the value in that data point is null.
085       * 
086       * @param timePeriod The time period.
087       * @return The value.
088       * 
089       * @throws TelemetryDataModelException If there is no value associated with the
090       *         time period.
091       */
092      public Number getDataPointValue(TimePeriod timePeriod) throws TelemetryDataModelException {
093        TelemetryDataPoint dataPoint = this.dataPoints.get(timePeriod);
094        if (dataPoint == null) {
095          throw new TelemetryDataModelException("No data for the period " + timePeriod.toString());
096        }
097        return dataPoint.getValue();
098      }
099    
100      /**
101       * Gets a list of data points in this telemetry stream, ordered by time
102       * period.
103       * 
104       * @return A collection of <code>TelemetryDataPoint</code> objects.
105       */
106      public List<TelemetryDataPoint> getDataPoints() {
107        ArrayList<TelemetryDataPoint> list = 
108          new ArrayList<TelemetryDataPoint>(this.dataPoints.values());
109        Collections.sort(list, DATAPOINT_TIMEPERIOD_COMPARATOR);
110        return list;
111      }
112    
113      /**
114       * Comparator for <code>TelemetryDataPoint</code> instances based on their
115       * time period property.
116       * 
117       * @author (Cedric) Qin Zhang
118       */
119      private static class TelemetryDataPointTimePeriodComparator 
120      implements Serializable, Comparator<TelemetryDataPoint> {
121    
122        private static final long serialVersionUID = 1L;
123    
124        /**
125         * Compares two instances.
126         * 
127         * @param o1 Data point 1.
128         * @param o2 Data point 2.
129         * 
130         * @return 0 indicates they are equal, positive value indicates data point 1
131         *         has later time stamp than data point 2, negative value indicates
132         *         data point 1 has earlier time stamp than data point 2.
133         */
134        public int compare(TelemetryDataPoint o1, TelemetryDataPoint o2) {
135          TimePeriod t1 = o1.getPeriod();
136          TimePeriod t2 = o2.getPeriod();
137          return t1.compareTo(t2);
138        }
139      }
140    }