ss: Move plugins to Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / histogram / HistogramDataModel.java
index a2394a04781c73fc3007b17e0995303799d85df2..f708d5ecde55d56d52f3b2d75237ae77d75bebe3 100644 (file)
@@ -1,28 +1,37 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2012 Ericsson
- * 
+ * Copyright (c) 2011, 2014 Ericsson
+ *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License v1.0 which
  * accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
- * 
+ *
  * Contributors:
  *   Francois Chouinard - Initial API and implementation
- *   Bernd Hufmann - Implementation of new interfaces/listeners and support for 
+ *   Bernd Hufmann - Implementation of new interfaces/listeners and support for
  *                   time stamp in any order
  *   Francois Chouinard - Moved from LTTng to TMF
+ *   Francois Chouinard - Added support for empty initial buckets
+ *   Patrick Tasse - Support selection range
+ *   Jean-Christian Kouamé, Simon Delisle - Added support to manage lost events
+ *   Xavier Raynaud - Support multi-trace coloring
  *******************************************************************************/
 
 package org.eclipse.linuxtools.tmf.ui.views.histogram;
 
 import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
 
 /**
- * <b><u>HistogramDataModel</u></b>
- * <p>
- * Histogram-independent data model with the following characteristics:
+ * Histogram-independent data model.
+ *
+ * It has the following characteristics:
  * <ul>
  * <li>The <i>basetime</i> is the timestamp of the first event
  * <li>There is a fixed number (<i>n</i>) of buckets of uniform duration
@@ -44,23 +53,22 @@ import org.eclipse.core.runtime.ListenerList;
  * 2<i>d</i>). This compaction happens as needed as the trace is read.
  * <p>
  * The model allows for timestamps in not increasing order. The timestamps can
- * be fed to the model in any order. If an event has a timestamp less than the 
+ * be fed to the model in any order. If an event has a timestamp less than the
  * <i>basetime</i>, the buckets will be moved to the right to account for the
- * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket 
- * duration smaller then the previous <i>basetime</i>. Note that the <i>basetime</i>
- * might not be anymore a timestamp of an event. If necessary, the buckets will
- * be compacted before moving to the right. This might be necessary to not 
- * loose any event counts at the end of the buckets array.
+ * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket
+ * duration smaller then the previous <i>basetime</i>. Note that the
+ * <i>basetime</i> might no longer be the timestamp of an event. If necessary,
+ * the buckets will be compacted before moving to the right. This might be
+ * necessary to not lose any event counts at the end of the buckets array.
  * <p>
  * The mapping from the model to the UI is performed by the <i>scaleTo()</i>
  * method. By keeping the number of buckets <i>n</i> relatively large with
  * respect to to the number of pixels in the actual histogram, we should achieve
  * a nice result when visualizing the histogram.
  * <p>
- * TODO: Add filter support for more refined event counting (e.g. by trace,
- * event type, etc.)
- * <p>
- * TODO: Cut-off eccentric values? TODO: Support for going back in time?
+ *
+ * @version 2.0
+ * @author Francois Chouinard
  */
 public class HistogramDataModel implements IHistogramDataModel {
 
@@ -68,18 +76,28 @@ public class HistogramDataModel implements IHistogramDataModel {
     // Constants
     // ------------------------------------------------------------------------
 
-    // The default number of buckets
+    /**
+     * The default number of buckets
+     */
     public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000;
 
+    /**
+     * Number of events after which listeners will be notified.
+     */
     public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS;
-    
+
     // ------------------------------------------------------------------------
     // Attributes
     // ------------------------------------------------------------------------
 
+    // Trace management
+    private ITmfTrace fTrace = null;
+    private final Map<ITmfTrace, Integer> fTraceMap = new LinkedHashMap<>();
+
     // Bucket management
     private final int fNbBuckets;
-    private final long[] fBuckets;
+    private final HistogramBucket[] fBuckets;
+    private final long[] fLostEventsBuckets;
     private long fBucketDuration;
     private long fNbEvents;
     private int fLastBucket;
@@ -87,38 +105,85 @@ public class HistogramDataModel implements IHistogramDataModel {
     // Timestamps
     private long fFirstBucketTime; // could be negative when analyzing events with descending order!!!
     private long fFirstEventTime;
-    private long fLastEventTime;
-    private long fCurrentEventTime;
+    private long fEndTime;
+    private long fSelectionBegin;
+    private long fSelectionEnd;
     private long fTimeLimit;
-    
+
     // Private listener lists
     private final ListenerList fModelListeners;
-    
+
     // ------------------------------------------------------------------------
     // Constructors
     // ------------------------------------------------------------------------
 
+    /**
+     * Default constructor with default number of buckets.
+     */
     public HistogramDataModel() {
-        this(DEFAULT_NUMBER_OF_BUCKETS);
+        this(0, DEFAULT_NUMBER_OF_BUCKETS);
+    }
+
+    /**
+     * Default constructor with default number of buckets.
+     *
+     * @param startTime
+     *            The histogram start time
+     * @since 2.0
+     */
+    public HistogramDataModel(long startTime) {
+        this(startTime, DEFAULT_NUMBER_OF_BUCKETS);
     }
 
+    /**
+     * Constructor with non-default number of buckets.
+     *
+     * @param nbBuckets
+     *            A number of buckets.
+     */
     public HistogramDataModel(int nbBuckets) {
+        this(0, nbBuckets);
+    }
+
+    /**
+     * Constructor with non-default number of buckets.
+     *
+     * @param startTime
+     *            the histogram start time
+     * @param nbBuckets
+     *            A number of buckets.
+     * @since 2.0
+     */
+    public HistogramDataModel(long startTime, int nbBuckets) {
+        fFirstBucketTime = fFirstEventTime = fEndTime = startTime;
         fNbBuckets = nbBuckets;
-        fBuckets = new long[nbBuckets];
+        fBuckets = new HistogramBucket[nbBuckets];
+        fLostEventsBuckets = new long[nbBuckets];
         fModelListeners = new ListenerList();
         clear();
     }
 
+    /**
+     * Copy constructor.
+     *
+     * @param other
+     *            A model to copy.
+     */
     public HistogramDataModel(HistogramDataModel other) {
         fNbBuckets = other.fNbBuckets;
-        fBuckets = Arrays.copyOf(other.fBuckets, fNbBuckets);
-        fBucketDuration = other.fBucketDuration;
+        fBuckets = new HistogramBucket[fNbBuckets];
+        for (int i = 0; i < fNbBuckets; i++) {
+            fBuckets[i] = new HistogramBucket(other.fBuckets[i]);
+        }
+        fLostEventsBuckets = Arrays.copyOf(other.fLostEventsBuckets, fNbBuckets);
+        fBucketDuration = Math.max(other.fBucketDuration, 1);
         fNbEvents = other.fNbEvents;
         fLastBucket = other.fLastBucket;
         fFirstBucketTime = other.fFirstBucketTime;
         fFirstEventTime = other.fFirstEventTime;
-        fLastEventTime = other.fLastEventTime;
-        fCurrentEventTime = other.fCurrentEventTime;
+        fEndTime = other.fEndTime;
+        fSelectionBegin = other.fSelectionBegin;
+        fSelectionEnd = other.fSelectionEnd;
         fTimeLimit = other.fTimeLimit;
         fModelListeners = new ListenerList();
         Object[] listeners = other.fModelListeners.getListeners();
@@ -127,68 +192,233 @@ public class HistogramDataModel implements IHistogramDataModel {
         }
     }
 
+
+    /**
+     * Disposes the data model
+     * @since 3.0
+     */
+    public void dispose() {
+        fTraceMap.clear();
+        fTrace = null;
+    }
+
     // ------------------------------------------------------------------------
     // Accessors
     // ------------------------------------------------------------------------
 
+    /**
+     * Returns the number of events in the data model.
+     *
+     * @return number of events.
+     */
     public long getNbEvents() {
         return fNbEvents;
     }
 
+    /**
+     * Returns the number of buckets in the model.
+     *
+     * @return number of buckets.
+     */
     public int getNbBuckets() {
         return fNbBuckets;
     }
 
+    /**
+     * Returns the current bucket duration.
+     *
+     * @return bucket duration
+     */
     public long getBucketDuration() {
         return fBucketDuration;
     }
-    
+
+    /**
+     * Returns the time value of the first bucket in the model.
+     *
+     * @return time of first bucket.
+     */
     public long getFirstBucketTime() {
         return fFirstBucketTime;
     }
 
+    /**
+     * Returns the time of the first event in the model.
+     *
+     * @return time of first event.
+     */
     public long getStartTime() {
         return fFirstEventTime;
     }
-    
+
+    /**
+     * Sets the trace of this model.
+     * @param trace - a {@link ITmfTrace}
+     * @since 3.0
+     */
+    public void setTrace(ITmfTrace trace) {
+        this.fTrace = trace;
+        fTraceMap.clear();
+        ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
+        if (traces != null) {
+            int i = 0;
+            for (ITmfTrace tr : traces) {
+                fTraceMap.put(tr, i);
+                i++;
+            }
+        }
+    }
+
+    /**
+     * Gets the trace of this model.
+     * @return a {@link ITmfTrace}
+     * @since 3.0
+     */
+    public ITmfTrace getTrace() {
+        return this.fTrace;
+    }
+
+    /**
+     * Gets the traces names of this model.
+     * @return an array of trace names
+     * @since 3.0
+     */
+    public String[] getTraceNames() {
+        ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
+        if (traces == null) {
+            return new String[0];
+        }
+        String[] traceNames = new String[traces.length];
+        int i = 0;
+        for (ITmfTrace tr : traces) {
+            traceNames[i] = tr.getName();
+            i++;
+        }
+        return traceNames;
+    }
+
+    /**
+     * Gets the number of traces of this model.
+     * @return the number of traces of this model.
+     * @since 3.0
+     */
+    public int getNbTraces() {
+        ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
+        if (traces == null) {
+            return 1; //
+        }
+        return traces.length;
+    }
+
+    /**
+     * Sets the model start time
+     *
+     * @param startTime
+     *            the histogram range start time
+     * @param endTime
+     *            the histogram range end time
+     * @since 2.0
+     */
+    public void setTimeRange(long startTime, long endTime) {
+        fFirstBucketTime = fFirstEventTime = fEndTime = startTime;
+        fBucketDuration = 1;
+        updateEndTime();
+        while (endTime >= fTimeLimit) {
+            mergeBuckets();
+        }
+    }
+
+    /**
+     * Set the end time. Setting this ensures that the corresponding bucket is
+     * displayed regardless of the event counts.
+     *
+     * @param endTime
+     *            the time of the last used bucket
+     * @since 2.2
+     */
+    public void setEndTime(long endTime) {
+        fEndTime = endTime;
+        fLastBucket = (int) ((endTime - fFirstBucketTime) / fBucketDuration);
+    }
+
+    /**
+     * Returns the end time.
+     *
+     * @return the time of the last used bucket
+     */
     public long getEndTime() {
-        return fLastEventTime;
+        return fEndTime;
     }
 
-    public long getCurrentEventTime() {
-        return fCurrentEventTime;
+    /**
+     * Returns the begin time of the current selection in the model.
+     *
+     * @return the begin time of the current selection.
+     * @since 2.1
+     */
+    public long getSelectionBegin() {
+        return fSelectionBegin;
     }
 
+    /**
+     * Returns the end time of the current selection in the model.
+     *
+     * @return the end time of the current selection.
+     * @since 2.1
+     */
+    public long getSelectionEnd() {
+        return fSelectionEnd;
+    }
+
+    /**
+     * Returns the time limit with is: start time + nbBuckets * bucketDuration
+     *
+     * @return the time limit.
+     */
     public long getTimeLimit() {
         return fTimeLimit;
     }
-    
+
     // ------------------------------------------------------------------------
     // Listener handling
     // ------------------------------------------------------------------------
-    
+
+    /**
+     * Add a listener to the model to be informed about model changes.
+     *
+     * @param listener
+     *            A listener to add.
+     */
     public void addHistogramListener(IHistogramModelListener listener) {
-        fModelListeners.add(listener);        
+        fModelListeners.add(listener);
     }
-    
+
+    /**
+     * Remove a given model listener.
+     *
+     * @param listener
+     *            A listener to remove.
+     */
     public void removeHistogramListener(IHistogramModelListener listener) {
         fModelListeners.remove(listener);
     }
 
+    // Notify listeners (always)
     private void fireModelUpdateNotification() {
         fireModelUpdateNotification(0);
     }
-    
+
+    // Notify listener on boundary
     private void fireModelUpdateNotification(long count) {
-        if (count % REFRESH_FREQUENCY == 0) {
+        if ((count % REFRESH_FREQUENCY) == 0) {
             Object[] listeners = fModelListeners.getListeners();
-            for (int i = 0; i < listeners.length; i++) {
-                IHistogramModelListener listener = (IHistogramModelListener) listeners[i];
+            for (Object listener2 : listeners) {
+                IHistogramModelListener listener = (IHistogramModelListener) listener2;
                 listener.modelUpdated();
             }
         }
     }
-    
+
     // ------------------------------------------------------------------------
     // Operations
     // ------------------------------------------------------------------------
@@ -200,67 +430,87 @@ public class HistogramDataModel implements IHistogramDataModel {
 
     /**
      * Clear the histogram model.
+     *
+     * @see org.eclipse.linuxtools.tmf.ui.views.distribution.model.IBaseDistributionModel#clear()
      */
     @Override
     public void clear() {
-        Arrays.fill(fBuckets, 0);
+        Arrays.fill(fBuckets, null);
+        Arrays.fill(fLostEventsBuckets, 0);
         fNbEvents = 0;
         fFirstBucketTime = 0;
-        fLastEventTime = 0;
-        fCurrentEventTime = 0;
+        fEndTime = 0;
+        fSelectionBegin = 0;
+        fSelectionEnd = 0;
         fLastBucket = 0;
-        fBucketDuration = 1; // 1ns
+        fBucketDuration = 1;
         updateEndTime();
         fireModelUpdateNotification();
     }
 
     /**
-     * Sets the current event time
-     * 
-     * @param timestamp
+     * Sets the current selection time range (no notification of listeners)
+     *
+     * @param beginTime
+     *            The selection begin time.
+     * @param endTime
+     *            The selection end time.
+     * @since 2.1
      */
-    public void setCurrentEvent(long timestamp) {
-        fCurrentEventTime = timestamp;
+    public void setSelection(long beginTime, long endTime) {
+        fSelectionBegin = beginTime;
+        fSelectionEnd = endTime;
     }
 
     /**
-     * Sets the current event time
-     * 
-     * @param timestamp
+     * Sets the current selection time range with notification of listeners
+     *
+     * @param beginTime
+     *            The selection begin time.
+     * @param endTime
+     *            The selection end time.
+     * @since 2.1
      */
-    public void setCurrentEventNotifyListeners(long timestamp) {
-        fCurrentEventTime = timestamp;
+    public void setSelectionNotifyListeners(long beginTime, long endTime) {
+        fSelectionBegin = beginTime;
+        fSelectionEnd = endTime;
         fireModelUpdateNotification();
     }
-    
+
     /**
      * Add event to the correct bucket, compacting the if needed.
-     * 
-     * @param timestamp the timestamp of the event to count
+     *
+     * @param eventCount
+     *            The current event Count (for notification purposes)
+     * @param timestamp
+     *            The timestamp of the event to count
+     * @param trace
+     *            The event trace
+     * @since 3.0
      */
     @Override
-    public void countEvent(long eventCount, long timestamp) {
-        
+    public void countEvent(long eventCount, long timestamp, ITmfTrace trace) {
+
         // Validate
         if (timestamp < 0) {
             return;
         }
-        
+
         // Set the start/end time if not already done
-        if (fLastBucket == 0 && fBuckets[0] == 0 && timestamp > 0) {
+        if ((fFirstBucketTime == 0) && (fLastBucket == 0) && (fBuckets[0] == null) && (timestamp > 0)) {
             fFirstBucketTime = timestamp;
             fFirstEventTime = timestamp;
             updateEndTime();
         }
-        
+
         if (timestamp < fFirstEventTime) {
             fFirstEventTime = timestamp;
         }
-        
-        if (fLastEventTime < timestamp) {
-            fLastEventTime = timestamp;
+
+        if (fEndTime < timestamp) {
+            fEndTime = timestamp;
         }
-        
+
         if (timestamp >= fFirstBucketTime) {
 
             // Compact as needed
@@ -269,81 +519,156 @@ public class HistogramDataModel implements IHistogramDataModel {
             }
 
         } else {
-            
+
             // get offset for adjustment
             int offset = getOffset(timestamp);
 
             // Compact as needed
-            while(fLastBucket + offset >= fNbBuckets) {
+            while ((fLastBucket + offset) >= fNbBuckets) {
                 mergeBuckets();
                 offset = getOffset(timestamp);
             }
-            
+
             moveBuckets(offset);
 
             fLastBucket = fLastBucket + offset;
 
-            fFirstBucketTime = fFirstBucketTime - offset*fBucketDuration;
+            fFirstBucketTime = fFirstBucketTime - (offset * fBucketDuration);
             updateEndTime();
         }
-        
+
         // Increment the right bucket
         int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration);
-        fBuckets[index]++;
+        if (fBuckets[index] == null) {
+            fBuckets[index] = new HistogramBucket(getNbTraces());
+        }
+        Integer traceIndex = fTraceMap.get(trace);
+        if (traceIndex == null) {
+            traceIndex = 0;
+        }
+        fBuckets[index].addEvent(traceIndex);
         fNbEvents++;
-        if (fLastBucket < index)
+        if (fLastBucket < index) {
             fLastBucket = index;
-        
+        }
+
         fireModelUpdateNotification(eventCount);
     }
 
+    /**
+     * Add lost event to the correct bucket, compacting the if needed.
+     *
+     * @param timeRange
+     *            time range of a lost event
+     * @param nbLostEvents
+     *            the number of lost events
+     * @param fullRange
+     *            Full range or time range for histogram request
+     * @since 2.2
+     */
+    public void countLostEvent(TmfTimeRange timeRange, long nbLostEvents, boolean fullRange) {
+
+        // Validate
+        if (timeRange.getStartTime().getValue() < 0 || timeRange.getEndTime().getValue() < 0) {
+            return;
+        }
+
+        // Compact as needed
+        if (fullRange) {
+            while (timeRange.getEndTime().getValue() >= fTimeLimit) {
+                mergeBuckets();
+            }
+        }
+
+        int indexStart = (int) ((timeRange.getStartTime().getValue() - fFirstBucketTime) / fBucketDuration);
+        int indexEnd = (int) ((timeRange.getEndTime().getValue() - fFirstBucketTime) / fBucketDuration);
+        int nbBucketRange = (indexEnd - indexStart) + 1;
+
+        int lostEventPerBucket = (int) Math.ceil((double) nbLostEvents / nbBucketRange);
+        long lastLostCol = Math.max(1, nbLostEvents - lostEventPerBucket * (nbBucketRange - 1));
+
+        // Increment the right bucket, bear in mind that ranges make it almost certain that some lost events are out of range
+        for (int index = indexStart; index <= indexEnd && index < fLostEventsBuckets.length; index++) {
+            if (index == (indexStart + nbBucketRange - 1)) {
+                fLostEventsBuckets[index] += lastLostCol;
+            } else {
+                fLostEventsBuckets[index] += lostEventPerBucket;
+            }
+        }
+
+        fNbEvents++;
+
+        fireModelUpdateNotification(nbLostEvents);
+    }
+
     /**
      * Scale the model data to the width, height and bar width requested.
-     * 
+     *
      * @param width
+     *            A width of the histogram canvas
      * @param height
+     *            A height of the histogram canvas
      * @param barWidth
+     *            A width (in pixel) of a histogram bar
      * @return the result array of size [width] and where the highest value
      *         doesn't exceed [height]
+     *
+     * @see org.eclipse.linuxtools.tmf.ui.views.histogram.IHistogramDataModel#scaleTo(int,
+     *      int, int)
      */
     @Override
     public HistogramScaledData scaleTo(int width, int height, int barWidth) {
         // Basic validation
-        if (width <= 0 ||  height <= 0 || barWidth <= 0)
+        if ((width <= 0) || (height <= 0) || (barWidth <= 0))
+        {
             throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+        }
 
         // The result structure
         HistogramScaledData result = new HistogramScaledData(width, height, barWidth);
 
         // Scale horizontally
         result.fMaxValue = 0;
-        
+
         int nbBars = width / barWidth;
-        int bucketsPerBar = fLastBucket / nbBars + 1;
-        result.fBucketDuration = bucketsPerBar * fBucketDuration;
+        int bucketsPerBar = (fLastBucket / nbBars) + 1;
+        result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration, 1);
         for (int i = 0; i < nbBars; i++) {
             int count = 0;
-            for (int j = i * bucketsPerBar; j < (i + 1) * bucketsPerBar; j++) {
-                if (fNbBuckets <= j)
+            int countLostEvent = 0;
+            result.fData[i] = new HistogramBucket(getNbTraces());
+            for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) {
+                if (fNbBuckets <= j) {
                     break;
-                count += fBuckets[j];
+                }
+                if (fBuckets[j] != null) {
+                    count += fBuckets[j].getNbEvents();
+                    result.fData[i].add(fBuckets[j]);
+                }
+                countLostEvent += fLostEventsBuckets[j];
             }
-            result.fData[i] = count;
+            result.fLostEventsData[i] = countLostEvent;
             result.fLastBucket = i;
-            if (result.fMaxValue < count)
+            if (result.fMaxValue < count) {
                 result.fMaxValue = count;
+            }
+            if (result.fMaxCombinedValue < count + countLostEvent) {
+                result.fMaxCombinedValue = count + countLostEvent;
+            }
         }
 
         // Scale vertically
         if (result.fMaxValue > 0) {
             result.fScalingFactor = (double) height / result.fMaxValue;
         }
+        if (result.fMaxCombinedValue > 0) {
+            result.fScalingFactorCombined = (double) height / result.fMaxCombinedValue;
+        }
 
-        // Set the current event index in the scaled histogram
-        if (fCurrentEventTime >= fFirstBucketTime && fCurrentEventTime <= fLastEventTime)
-            result.fCurrentBucket = (int) ((fCurrentEventTime - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
-        else
-            result.fCurrentBucket = HistogramScaledData.OUT_OF_RANGE_BUCKET;
+        fBucketDuration = Math.max(fBucketDuration, 1);
+        // Set selection begin and end index in the scaled histogram
+        result.fSelectionBeginBucket = (int) ((fSelectionBegin - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
+        result.fSelectionEndBucket = (int) ((fSelectionEnd - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
 
         result.fFirstBucketTime = fFirstBucketTime;
         result.fFirstEventTime = fFirstEventTime;
@@ -355,35 +680,38 @@ public class HistogramDataModel implements IHistogramDataModel {
     // ------------------------------------------------------------------------
 
     private void updateEndTime() {
-        fTimeLimit = fFirstBucketTime + fNbBuckets * fBucketDuration;
+        fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration);
     }
 
     private void mergeBuckets() {
-        for (int i = 0; i < fNbBuckets / 2; i++) {
-            fBuckets[i] = fBuckets[2 * i] + fBuckets[2 * i + 1];
+        for (int i = 0; i < (fNbBuckets / 2); i++) {
+            fBuckets[i] = new HistogramBucket(fBuckets[2 * i], fBuckets[(2 * i) + 1]);
+            fLostEventsBuckets[i] = fLostEventsBuckets[2 * i] + fLostEventsBuckets[(2 * i) + 1];
         }
-        Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, 0);
+        Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, null);
+        Arrays.fill(fLostEventsBuckets, fNbBuckets / 2, fNbBuckets, 0);
         fBucketDuration *= 2;
         updateEndTime();
-        fLastBucket = fNbBuckets / 2 - 1;
+        fLastBucket = (fNbBuckets / 2) - 1;
     }
-    
+
     private void moveBuckets(int offset) {
-        for(int i = fNbBuckets - 1; i >= offset; i--) {
-            fBuckets[i] = fBuckets[i-offset]; 
+        for (int i = fNbBuckets - 1; i >= offset; i--) {
+            fBuckets[i] = new HistogramBucket(fBuckets[i - offset]);
+            fLostEventsBuckets[i] = fLostEventsBuckets[i - offset];
         }
 
         for (int i = 0; i < offset; i++) {
-            fBuckets[i] = 0;
+            fBuckets[i] = null;
+            fLostEventsBuckets[i] = 0;
         }
     }
 
     private int getOffset(long timestamp) {
         int offset = (int) ((fFirstBucketTime - timestamp) / fBucketDuration);
-        if ((fFirstBucketTime - timestamp) % fBucketDuration != 0) {
+        if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) {
             offset++;
         }
         return offset;
     }
-
 }
This page took 0.033411 seconds and 5 git commands to generate.