Add support for selected event in Properties view.
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / histogram / HistogramDataModel.java
CommitLineData
c392540b 1/*******************************************************************************
e0752744 2 * Copyright (c) 2011, 2012 Ericsson
bfe038ff 3 *
c392540b
FC
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
bfe038ff 8 *
c392540b
FC
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
bfe038ff 11 * Bernd Hufmann - Implementation of new interfaces/listeners and support for
fbd124dd 12 * time stamp in any order
e0752744 13 * Francois Chouinard - Moved from LTTng to TMF
c392540b
FC
14 *******************************************************************************/
15
e0752744 16package org.eclipse.linuxtools.tmf.ui.views.histogram;
c392540b
FC
17
18import java.util.Arrays;
19
fbd124dd 20import org.eclipse.core.runtime.ListenerList;
c392540b
FC
21
22/**
b544077e
BH
23 * Histogram-independent data model.
24 *
25 * It has the following characteristics:
c392540b
FC
26 * <ul>
27 * <li>The <i>basetime</i> is the timestamp of the first event
28 * <li>There is a fixed number (<i>n</i>) of buckets of uniform duration
29 * (<i>d</i>)
30 * <li>The <i>timespan</i> of the model is thus: <i>n</i> * <i>d</i> time units
31 * <li>Bucket <i>i</i> holds the number of events that occurred in time range:
32 * [<i>basetime</i> + <i>i</i> * <i>d</i>, <i>basetime</i> + (<i>i</i> + 1) *
33 * <i>d</i>)
34 * </ul>
35 * Initially, the bucket durations is set to 1ns. As the events are read, they
36 * are tallied (using <i>countEvent()</i>) in the appropriate bucket (relative
37 * to the <i>basetime</i>).
38 * <p>
39 * Eventually, an event will have a timestamp that exceeds the <i>timespan</i>
40 * high end (determined by <i>n</i>, the number of buckets, and <i>d</i>, the
41 * bucket duration). At this point, the histogram needs to be compacted. This is
42 * done by simply merging adjacent buckets by pair, in effect doubling the
43 * <i>timespan</i> (<i>timespan'</i> = <i>n</i> * <i>d'</i>, where <i>d'</i> =
44 * 2<i>d</i>). This compaction happens as needed as the trace is read.
45 * <p>
fbd124dd 46 * The model allows for timestamps in not increasing order. The timestamps can
bfe038ff 47 * be fed to the model in any order. If an event has a timestamp less than the
fbd124dd 48 * <i>basetime</i>, the buckets will be moved to the right to account for the
bfe038ff 49 * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket
fbd124dd
BH
50 * duration smaller then the previous <i>basetime</i>. Note that the <i>basetime</i>
51 * might not be anymore a timestamp of an event. If necessary, the buckets will
bfe038ff 52 * be compacted before moving to the right. This might be necessary to not
fbd124dd
BH
53 * loose any event counts at the end of the buckets array.
54 * <p>
c392540b
FC
55 * The mapping from the model to the UI is performed by the <i>scaleTo()</i>
56 * method. By keeping the number of buckets <i>n</i> relatively large with
57 * respect to to the number of pixels in the actual histogram, we should achieve
58 * a nice result when visualizing the histogram.
59 * <p>
b544077e
BH
60 *
61 * @version 1.0
62 * @author Francois Chouinard
c392540b 63 */
fbd124dd 64public class HistogramDataModel implements IHistogramDataModel {
c392540b
FC
65
66 // ------------------------------------------------------------------------
67 // Constants
68 // ------------------------------------------------------------------------
69
b544077e
BH
70 /**
71 * The default number of buckets
72 */
c392540b
FC
73 public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000;
74
b544077e
BH
75 /**
76 * Number of events after which listeners will be notified.
77 */
fbd124dd 78 public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS;
bfe038ff 79
c392540b
FC
80 // ------------------------------------------------------------------------
81 // Attributes
82 // ------------------------------------------------------------------------
83
84 // Bucket management
85 private final int fNbBuckets;
86 private final long[] fBuckets;
87 private long fBucketDuration;
88 private long fNbEvents;
89 private int fLastBucket;
90
91 // Timestamps
fbd124dd 92 private long fFirstBucketTime; // could be negative when analyzing events with descending order!!!
c392540b
FC
93 private long fFirstEventTime;
94 private long fLastEventTime;
95 private long fCurrentEventTime;
96 private long fTimeLimit;
bfe038ff 97
e0752744 98 // Private listener lists
fbd124dd 99 private final ListenerList fModelListeners;
bfe038ff 100
e0752744
FC
101 // ------------------------------------------------------------------------
102 // Constructors
103 // ------------------------------------------------------------------------
104
b544077e
BH
105 /**
106 * Default constructor with default number of buckets.
107 */
c392540b
FC
108 public HistogramDataModel() {
109 this(DEFAULT_NUMBER_OF_BUCKETS);
110 }
111
b544077e
BH
112 /**
113 * Constructor with non-default number of buckets.
114 * @param nbBuckets A number of buckets.
115 */
c392540b
FC
116 public HistogramDataModel(int nbBuckets) {
117 fNbBuckets = nbBuckets;
118 fBuckets = new long[nbBuckets];
fbd124dd 119 fModelListeners = new ListenerList();
c392540b
FC
120 clear();
121 }
122
b544077e
BH
123 /**
124 * Copy constructor.
125 * @param other A model to copy.
126 */
c392540b
FC
127 public HistogramDataModel(HistogramDataModel other) {
128 fNbBuckets = other.fNbBuckets;
129 fBuckets = Arrays.copyOf(other.fBuckets, fNbBuckets);
bfe038ff 130 fBucketDuration = Math.max(other.fBucketDuration,1);
c392540b
FC
131 fNbEvents = other.fNbEvents;
132 fLastBucket = other.fLastBucket;
fbd124dd 133 fFirstBucketTime = other.fFirstBucketTime;
c392540b
FC
134 fFirstEventTime = other.fFirstEventTime;
135 fLastEventTime = other.fLastEventTime;
136 fCurrentEventTime = other.fCurrentEventTime;
137 fTimeLimit = other.fTimeLimit;
fbd124dd
BH
138 fModelListeners = new ListenerList();
139 Object[] listeners = other.fModelListeners.getListeners();
140 for (Object listener : listeners) {
141 fModelListeners.add(listener);
142 }
c392540b
FC
143 }
144
145 // ------------------------------------------------------------------------
146 // Accessors
147 // ------------------------------------------------------------------------
148
b544077e
BH
149 /**
150 * Returns the number of events in the data model.
151 * @return number of events.
152 */
c392540b
FC
153 public long getNbEvents() {
154 return fNbEvents;
155 }
156
b544077e
BH
157 /**
158 * Returns the number of buckets in the model.
159 * @return number of buckets.
160 */
c392540b
FC
161 public int getNbBuckets() {
162 return fNbBuckets;
163 }
164
b544077e
BH
165 /**
166 * Returns the current bucket duration.
167 * @return bucket duration
168 */
c392540b
FC
169 public long getBucketDuration() {
170 return fBucketDuration;
171 }
bfe038ff 172
b544077e
BH
173 /**
174 * Returns the time value of the first bucket in the model.
175 * @return time of first bucket.
176 */
fbd124dd
BH
177 public long getFirstBucketTime() {
178 return fFirstBucketTime;
179 }
c392540b 180
b544077e
BH
181 /**
182 * Returns the time of the first event in the model.
183 * @return time of first event.
184 */
c392540b
FC
185 public long getStartTime() {
186 return fFirstEventTime;
187 }
bfe038ff 188
b544077e
BH
189 /**
190 * Returns the time of the last event in the model.
191 * @return the time of last event.
192 */
c392540b
FC
193 public long getEndTime() {
194 return fLastEventTime;
195 }
196
b544077e
BH
197 /**
198 * Returns the time of the current event in the model.
199 * @return the time of the current event.
200 */
c392540b
FC
201 public long getCurrentEventTime() {
202 return fCurrentEventTime;
203 }
204
b544077e
BH
205 /**
206 * Returns the time limit with is: start time + nbBuckets * bucketDuration
207 * @return the time limit.
208 */
c392540b
FC
209 public long getTimeLimit() {
210 return fTimeLimit;
211 }
bfe038ff 212
fbd124dd
BH
213 // ------------------------------------------------------------------------
214 // Listener handling
215 // ------------------------------------------------------------------------
bfe038ff 216
b544077e
BH
217 /**
218 * Add a listener to the model to be informed about model changes.
219 * @param listener A listener to add.
220 */
fbd124dd 221 public void addHistogramListener(IHistogramModelListener listener) {
bfe038ff 222 fModelListeners.add(listener);
fbd124dd 223 }
bfe038ff 224
b544077e
BH
225 /**
226 * Remove a given model listener.
227 * @param listener A listener to remove.
228 */
fbd124dd
BH
229 public void removeHistogramListener(IHistogramModelListener listener) {
230 fModelListeners.remove(listener);
231 }
c392540b 232
b544077e 233 // Notify listeners (always)
fbd124dd
BH
234 private void fireModelUpdateNotification() {
235 fireModelUpdateNotification(0);
236 }
bfe038ff 237
b544077e 238 // Notify listener on boundary
fbd124dd 239 private void fireModelUpdateNotification(long count) {
bfe038ff 240 if ((count % REFRESH_FREQUENCY) == 0) {
fbd124dd 241 Object[] listeners = fModelListeners.getListeners();
bfe038ff
MK
242 for (Object listener2 : listeners) {
243 IHistogramModelListener listener = (IHistogramModelListener) listener2;
fbd124dd
BH
244 listener.modelUpdated();
245 }
246 }
247 }
bfe038ff 248
c392540b
FC
249 // ------------------------------------------------------------------------
250 // Operations
251 // ------------------------------------------------------------------------
e0752744 252
b544077e
BH
253 /*
254 * (non-Javadoc)
255 * @see org.eclipse.linuxtools.tmf.ui.views.distribution.model.IBaseDistributionModel#complete()
256 */
fbd124dd
BH
257 @Override
258 public void complete() {
259 fireModelUpdateNotification();
260 }
c392540b
FC
261
262 /**
263 * Clear the histogram model.
b544077e 264 * @see org.eclipse.linuxtools.tmf.ui.views.distribution.model.IBaseDistributionModel#clear()
c392540b 265 */
fbd124dd 266 @Override
c392540b
FC
267 public void clear() {
268 Arrays.fill(fBuckets, 0);
269 fNbEvents = 0;
fbd124dd 270 fFirstBucketTime = 0;
c392540b
FC
271 fLastEventTime = 0;
272 fCurrentEventTime = 0;
273 fLastBucket = 0;
274 fBucketDuration = 1; // 1ns
275 updateEndTime();
fbd124dd 276 fireModelUpdateNotification();
c392540b
FC
277 }
278
279 /**
b544077e 280 * Sets the current event time (no notification of listeners)
bfe038ff 281 *
b544077e 282 * @param timestamp A time stamp to set.
c392540b
FC
283 */
284 public void setCurrentEvent(long timestamp) {
285 fCurrentEventTime = timestamp;
286 }
287
fbd124dd 288 /**
b544077e 289 * Sets the current event time with notification of listeners
bfe038ff 290 *
b544077e 291 * @param timestamp A time stamp to set.
fbd124dd
BH
292 */
293 public void setCurrentEventNotifyListeners(long timestamp) {
294 fCurrentEventTime = timestamp;
295 fireModelUpdateNotification();
296 }
bfe038ff 297
c392540b
FC
298 /**
299 * Add event to the correct bucket, compacting the if needed.
bfe038ff 300 *
b544077e
BH
301 * @param eventCount The current event Count (for notification purposes)
302 * @param timestamp The timestamp of the event to count
303 *
c392540b 304 */
fbd124dd
BH
305 @Override
306 public void countEvent(long eventCount, long timestamp) {
bfe038ff 307
fbd124dd
BH
308 // Validate
309 if (timestamp < 0) {
fbd124dd
BH
310 return;
311 }
bfe038ff 312
c392540b 313 // Set the start/end time if not already done
bfe038ff 314 if ((fLastBucket == 0) && (fBuckets[0] == 0) && (timestamp > 0)) {
fbd124dd 315 fFirstBucketTime = timestamp;
c392540b
FC
316 fFirstEventTime = timestamp;
317 updateEndTime();
318 }
bfe038ff 319
fbd124dd
BH
320 if (timestamp < fFirstEventTime) {
321 fFirstEventTime = timestamp;
322 }
bfe038ff 323
c392540b
FC
324 if (fLastEventTime < timestamp) {
325 fLastEventTime = timestamp;
326 }
bfe038ff 327
fbd124dd 328 if (timestamp >= fFirstBucketTime) {
c392540b 329
fbd124dd
BH
330 // Compact as needed
331 while (timestamp >= fTimeLimit) {
332 mergeBuckets();
333 }
c392540b 334
fbd124dd 335 } else {
bfe038ff 336
fbd124dd
BH
337 // get offset for adjustment
338 int offset = getOffset(timestamp);
339
340 // Compact as needed
bfe038ff 341 while((fLastBucket + offset) >= fNbBuckets) {
fbd124dd
BH
342 mergeBuckets();
343 offset = getOffset(timestamp);
344 }
bfe038ff 345
fbd124dd
BH
346 moveBuckets(offset);
347
348 fLastBucket = fLastBucket + offset;
c392540b 349
bfe038ff 350 fFirstBucketTime = fFirstBucketTime - (offset*fBucketDuration);
fbd124dd
BH
351 updateEndTime();
352 }
bfe038ff 353
c392540b 354 // Increment the right bucket
fbd124dd 355 int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration);
c392540b
FC
356 fBuckets[index]++;
357 fNbEvents++;
bfe038ff 358 if (fLastBucket < index) {
c392540b 359 fLastBucket = index;
bfe038ff
MK
360 }
361
fbd124dd 362 fireModelUpdateNotification(eventCount);
c392540b
FC
363 }
364
365 /**
fbd124dd 366 * Scale the model data to the width, height and bar width requested.
bfe038ff 367 *
b544077e
BH
368 * @param width A width of the histogram canvas
369 * @param height A height of the histogram canvas
370 * @param barWidth A width (in pixel) of a histogram bar
371 * @return the result array of size [width] and where the highest value doesn't exceed [height]
372 *
373 * @see org.eclipse.linuxtools.tmf.ui.views.histogram.IHistogramDataModel#scaleTo(int, int, int)
c392540b 374 */
fbd124dd
BH
375 @Override
376 public HistogramScaledData scaleTo(int width, int height, int barWidth) {
c392540b 377 // Basic validation
bfe038ff
MK
378 if ((width <= 0) || (height <= 0) || (barWidth <= 0))
379 {
fbd124dd 380 throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
bfe038ff 381 }
c392540b
FC
382
383 // The result structure
fbd124dd 384 HistogramScaledData result = new HistogramScaledData(width, height, barWidth);
c392540b
FC
385
386 // Scale horizontally
74237cc3 387 result.fMaxValue = 0;
bfe038ff 388
fbd124dd 389 int nbBars = width / barWidth;
bfe038ff
MK
390 int bucketsPerBar = (fLastBucket / nbBars) + 1;
391 result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration,1);
fbd124dd 392 for (int i = 0; i < nbBars; i++) {
c392540b 393 int count = 0;
bfe038ff
MK
394 for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) {
395 if (fNbBuckets <= j) {
c392540b 396 break;
bfe038ff 397 }
c392540b
FC
398 count += fBuckets[j];
399 }
400 result.fData[i] = count;
401 result.fLastBucket = i;
bfe038ff 402 if (result.fMaxValue < count) {
c392540b 403 result.fMaxValue = count;
bfe038ff 404 }
c392540b
FC
405 }
406
407 // Scale vertically
408 if (result.fMaxValue > 0) {
409 result.fScalingFactor = (double) height / result.fMaxValue;
410 }
411
bfe038ff 412 fBucketDuration = Math.max(fBucketDuration, 1);
c392540b 413 // Set the current event index in the scaled histogram
bfe038ff 414 if ((fCurrentEventTime >= fFirstBucketTime) && (fCurrentEventTime <= fLastEventTime)) {
fbd124dd 415 result.fCurrentBucket = (int) ((fCurrentEventTime - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
bfe038ff 416 } else {
c392540b 417 result.fCurrentBucket = HistogramScaledData.OUT_OF_RANGE_BUCKET;
bfe038ff 418 }
c392540b 419
fbd124dd
BH
420 result.fFirstBucketTime = fFirstBucketTime;
421 result.fFirstEventTime = fFirstEventTime;
c392540b
FC
422 return result;
423 }
424
425 // ------------------------------------------------------------------------
426 // Helper functions
427 // ------------------------------------------------------------------------
428
429 private void updateEndTime() {
bfe038ff 430 fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration);
c392540b
FC
431 }
432
433 private void mergeBuckets() {
bfe038ff
MK
434 for (int i = 0; i < (fNbBuckets / 2); i++) {
435 fBuckets[i] = fBuckets[2 * i] + fBuckets[(2 * i) + 1];
c392540b
FC
436 }
437 Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, 0);
438 fBucketDuration *= 2;
439 updateEndTime();
bfe038ff 440 fLastBucket = (fNbBuckets / 2) - 1;
c392540b 441 }
bfe038ff 442
fbd124dd
BH
443 private void moveBuckets(int offset) {
444 for(int i = fNbBuckets - 1; i >= offset; i--) {
bfe038ff 445 fBuckets[i] = fBuckets[i-offset];
fbd124dd
BH
446 }
447
448 for (int i = 0; i < offset; i++) {
449 fBuckets[i] = 0;
450 }
451 }
452
453 private int getOffset(long timestamp) {
454 int offset = (int) ((fFirstBucketTime - timestamp) / fBucketDuration);
bfe038ff 455 if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) {
fbd124dd
BH
456 offset++;
457 }
458 return offset;
459 }
c392540b
FC
460
461}
This page took 0.057129 seconds and 5 git commands to generate.