1 /*******************************************************************************
2 * Copyright (c) 2012 Ericsson
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
10 * Mathieu Denis <mathieu.denis@polymtl.ca> - Initial API and implementation
11 * Alexandre Montplaisir - Port to ITmfStatistics provider
12 *******************************************************************************/
14 package org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
;
16 import java
.util
.List
;
19 import org
.eclipse
.jface
.viewers
.TreeViewer
;
20 import org
.eclipse
.jface
.viewers
.TreeViewerColumn
;
21 import org
.eclipse
.jface
.viewers
.Viewer
;
22 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.component
.TmfComponent
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.event
.ITmfTimestamp
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.event
.TmfTimeRange
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.event
.TmfTimestamp
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfDataRequest
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentRangeUpdatedSignal
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfRangeSynchSignal
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalHandler
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfStateSystemBuildCompleted
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfStatsUpdatedSignal
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.statistics
.ITmfStatistics
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.statistics
.TmfStateStatistics
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.TmfExperiment
;
37 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.TmfViewer
;
38 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.ITmfColumnDataProvider
;
39 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfBaseColumnData
;
40 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfBaseColumnDataProvider
;
41 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfStatisticsTree
;
42 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfStatisticsTreeManager
;
43 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfStatisticsTreeNode
;
44 import org
.eclipse
.linuxtools
.tmf
.ui
.viewers
.statistics
.model
.TmfTreeContentProvider
;
45 import org
.eclipse
.swt
.SWT
;
46 import org
.eclipse
.swt
.events
.SelectionAdapter
;
47 import org
.eclipse
.swt
.events
.SelectionEvent
;
48 import org
.eclipse
.swt
.graphics
.Color
;
49 import org
.eclipse
.swt
.graphics
.Cursor
;
50 import org
.eclipse
.swt
.widgets
.Composite
;
51 import org
.eclipse
.swt
.widgets
.Control
;
52 import org
.eclipse
.swt
.widgets
.Display
;
53 import org
.eclipse
.swt
.widgets
.Event
;
54 import org
.eclipse
.swt
.widgets
.Listener
;
57 * A basic viewer to display statistics in the statistics view.
59 * It is linked to a single ITmfTrace until its disposal.
61 * @author Mathieu Denis
65 public class TmfStatisticsViewer
extends TmfViewer
{
68 * The initial window span (in nanoseconds)
70 public static final long INITIAL_WINDOW_SPAN
= (1L * 100 * 1000 * 1000); // .1sec
73 * Timestamp scale (nanosecond)
75 public static final byte TIME_SCALE
= -9;
78 * Default PAGE_SIZE for background requests.
80 protected static final int PAGE_SIZE
= 50000;
85 protected final Long STATS_INPUT_CHANGED_REFRESH
= 5000L;
88 * The actual tree viewer to display
90 protected TreeViewer fTreeViewer
;
93 * The statistics tree linked to this viewer
95 protected TmfStatisticsTree fStatisticsData
;
98 * Update synchronization parameter (used for streaming): Update busy
101 protected boolean fStatisticsUpdateBusy
= false;
104 * Update synchronization parameter (used for streaming): Update pending
107 protected boolean fStatisticsUpdatePending
= false;
110 * Update synchronization parameter (used for streaming): Pending Update
113 protected TmfTimeRange fStatisticsUpdateRange
= null;
116 * Update synchronization object.
118 protected final Object fStatisticsUpdateSyncObj
= new Object();
121 * Update range synchronization object.
123 protected final Object fStatisticsRangeUpdateSyncObj
= new Object();
126 * The trace that is displayed by this viewer
128 protected ITmfTrace fTrace
;
131 * Stores the requested time range.
133 protected TmfTimeRange fRequestedTimerange
;
136 * Indicates to process all events
138 private boolean fProcessAll
;
141 * View instance counter (for multiple statistics views)
143 private static int fCountInstance
= 0;
146 * Number of this instance. Used as an instance ID.
148 private int fInstanceNb
;
151 * Object to store the cursor while waiting for the experiment to load
153 private Cursor fWaitCursor
= null;
156 * Counts the number of times waitCursor() has been called. It avoids
157 * removing the waiting cursor, since there may be multiple requests running
160 private int fWaitCursorCount
= 0;
163 * Tells to send a time range request when the experiment gets updated.
165 private boolean fSendRangeRequest
= true;
168 * Empty constructor. To be used in conjunction with
169 * {@link TmfStatisticsViewer#init(Composite, String, ITmfTrace)}
171 public TmfStatisticsViewer() {
176 * Create a basic statistics viewer. To be used in conjunction with
177 * {@link TmfStatisticsViewer#init(Composite, String, ITmfTrace)}
180 * The parent composite that will hold the viewer
182 * The name that will be assigned to this viewer
184 * The trace that is displayed by this viewer
187 public TmfStatisticsViewer(Composite parent
, String viewerName
, ITmfTrace trace
) {
188 init(parent
, viewerName
, trace
);
192 * Initialize the statistics viewer.
195 * The parent component of the viewer.
197 * The name to give to the viewer.
199 * The trace that will be displayed by the viewer.
201 public void init(Composite parent
, String viewerName
, ITmfTrace trace
) {
202 super.init(parent
, viewerName
);
203 // Increment a counter to make sure the tree ID is unique.
205 fInstanceNb
= fCountInstance
;
208 // The viewer will process all events if he is assigned to the experiment
209 fProcessAll
= (trace
instanceof TmfExperiment
);
218 * @see org.eclipse.linuxtools.tmf.core.component.TmfComponent#dispose()
221 public void dispose() {
223 if (fWaitCursor
!= null) {
224 fWaitCursor
.dispose();
227 // Clean the model for this viewer
228 TmfStatisticsTreeManager
.removeStatTreeRoot(getTreeID());
231 // ------------------------------------------------------------------------
233 // ------------------------------------------------------------------------
236 * Handles the signal about new experiment range.
239 * The experiment range updated signal
242 public void experimentRangeUpdated(TmfExperimentRangeUpdatedSignal signal
) {
243 TmfExperiment experiment
= signal
.getExperiment();
245 if (!experiment
.equals(TmfExperiment
.getCurrentExperiment())) {
249 synchronized (fStatisticsRangeUpdateSyncObj
) {
250 // Sends the time range request only once from this method.
251 if (fSendRangeRequest
) {
252 fSendRangeRequest
= false;
253 // Calculate the selected time range to request
254 long startTime
= signal
.getRange().getStartTime().normalize(0, TIME_SCALE
).getValue();
255 TmfTimestamp startTS
= new TmfTimestamp(startTime
, TIME_SCALE
);
256 TmfTimestamp endTS
= new TmfTimestamp(startTime
+ INITIAL_WINDOW_SPAN
, TIME_SCALE
);
257 TmfTimeRange timeRange
= new TmfTimeRange(startTS
, endTS
);
259 requestTimeRangeData(experiment
, timeRange
);
262 requestData(experiment
, signal
.getRange());
266 * Handles the time range updated signal. It updates the time range
270 * Contains the information about the new selected time range.
273 public void timeRangeUpdated(TmfRangeSynchSignal signal
) {
274 requestTimeRangeData(TmfExperiment
.getCurrentExperiment(), signal
.getCurrentRange());
278 * Whenever a trace's statistics back-end finishes computing the statistics
279 * for a given interval, it will send the StatsUpdated signal. This method
280 * will receive this signal and update the statistics view accordingly.
283 * The signal that is received
286 public void statsUpdated(TmfStatsUpdatedSignal sig
) {
287 /* Only handle this signal if it's about the trace we represent. */
288 if (!isListeningTo(sig
.getTrace())) {
292 final TmfStatisticsTree statsData
= TmfStatisticsTreeManager
.getStatTree(getTreeID());
293 Map
<String
, Long
> map
= sig
.getEventsPerType();
294 String name
= sig
.getTrace().getName();
295 boolean isGlobal
= sig
.isGlobal();
298 * "Global", "partial", "total", etc., it's all very confusing...
300 * The base view shows the total count for the trace and for
301 * each even types, organized in columns like this:
303 * | Global | Time range |
304 * trace name | A | B |
306 * <event 1> | C | D |
307 * <event 2> | ... | ... |
310 * Here, we called the cells like this:
313 * C : GlobalTypeCount(s)
314 * D : TimeRangeTypeCount(s)
317 /* Fill in an the event counts (either cells C or D) */
318 for (Map
.Entry
<String
, Long
> entry
: map
.entrySet()) {
319 statsData
.setTypeCount(name
, entry
.getKey(), isGlobal
, entry
.getValue());
323 * Calculate the totals (cell A or B, depending if isGlobal). We will
324 * use the results of the previous request instead of sending another
327 long globalTotal
= 0;
328 for (long val
: map
.values()) {
331 statsData
.setTotal(name
, isGlobal
, globalTotal
);
333 modelComplete(isGlobal
);
336 // ------------------------------------------------------------------------
338 // ------------------------------------------------------------------------
341 * Returns the primary control associated with this viewer.
343 * @return the SWT control which displays this viewer's content
346 public Control
getControl() {
347 return fTreeViewer
.getControl();
351 * Get the input of the viewer.
353 * @return an object representing the input of the statistics viewer.
355 public Object
getInput() {
356 return fTreeViewer
.getInput();
360 * Return the size of the request when performing background request.
362 * @return the block size for background request.
364 public int getPageSize() {
369 * Return the number of events to receive before a refresh of the viewer is
372 * @return the input refresh rate
374 public long getRefreshRate() {
375 return STATS_INPUT_CHANGED_REFRESH
;
379 * This method can be overridden to implement another way of representing
380 * the statistics data and to retrieve the information for display.
382 * @return a TmfStatisticsData object.
384 public TmfStatisticsTree
getStatisticData() {
385 if (fStatisticsData
== null) {
386 fStatisticsData
= new TmfStatisticsTree();
388 return fStatisticsData
;
392 * Returns a unique ID based on name to be associated with the statistics
393 * tree for this viewer. For a same name, it will always return the same ID.
395 * @return a unique statistics tree ID.
397 public String
getTreeID() {
398 return getName() + fInstanceNb
;
402 public void refresh() {
403 final Control viewerControl
= getControl();
404 // Ignore update if disposed
405 if (viewerControl
.isDisposed()) {
409 viewerControl
.getDisplay().asyncExec(new Runnable() {
412 if (!viewerControl
.isDisposed()) {
413 fTreeViewer
.refresh();
420 * Will force a request on the partial event count if one is needed.
422 public void sendPartialRequestOnNextUpdate() {
423 synchronized (fStatisticsRangeUpdateSyncObj
) {
424 fSendRangeRequest
= true;
429 * Focus on the statistics tree of the viewer
431 public void setFocus() {
432 fTreeViewer
.getTree().setFocus();
436 * Cancels the request if it is not already completed
439 * The request to be canceled
441 protected void cancelOngoingRequest(ITmfDataRequest request
) {
442 if (request
!= null && !request
.isCompleted()) {
448 * This method can be overridden to change the representation of the data in
451 * @return an object implementing ITmfBaseColumnDataProvider.
453 protected ITmfColumnDataProvider
getColumnDataProvider() {
454 return new TmfBaseColumnDataProvider();
458 * Initialize the content that will be drawn in this viewer
461 * The parent of the control to create
463 protected void initContent(Composite parent
) {
464 final List
<TmfBaseColumnData
> columnDataList
= getColumnDataProvider().getColumnData();
466 fTreeViewer
= new TreeViewer(parent
, SWT
.BORDER
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
467 fTreeViewer
.setContentProvider(new TmfTreeContentProvider());
468 fTreeViewer
.getTree().setHeaderVisible(true);
469 fTreeViewer
.setUseHashlookup(true);
471 // Creates the columns defined by the column data provider
472 for (final TmfBaseColumnData columnData
: columnDataList
) {
473 final TreeViewerColumn treeColumn
= new TreeViewerColumn(fTreeViewer
, columnData
.getAlignment());
474 treeColumn
.getColumn().setText(columnData
.getHeader());
475 treeColumn
.getColumn().setWidth(columnData
.getWidth());
476 treeColumn
.getColumn().setToolTipText(columnData
.getTooltip());
478 if (columnData
.getComparator() != null) { // A comparator is defined.
479 // Adds a listener on the columns header for sorting purpose.
480 treeColumn
.getColumn().addSelectionListener(new SelectionAdapter() {
482 private ViewerComparator reverseComparator
;
485 public void widgetSelected(SelectionEvent e
) {
486 // Initializes the reverse comparator once.
487 if (reverseComparator
== null) {
488 reverseComparator
= new ViewerComparator() {
490 public int compare(Viewer viewer
, Object e1
, Object e2
) {
491 return -1 * columnData
.getComparator().compare(viewer
, e1
, e2
);
496 if (fTreeViewer
.getTree().getSortDirection() == SWT
.UP
497 || fTreeViewer
.getTree().getSortColumn() != treeColumn
.getColumn()) {
499 * Puts the descendant order if the old order was
500 * up or if the selected column has changed.
502 fTreeViewer
.setComparator(columnData
.getComparator());
503 fTreeViewer
.getTree().setSortDirection(SWT
.DOWN
);
506 * Puts the ascendant ordering if the selected
507 * column hasn't changed.
509 fTreeViewer
.setComparator(reverseComparator
);
510 fTreeViewer
.getTree().setSortDirection(SWT
.UP
);
512 fTreeViewer
.getTree().setSortColumn(treeColumn
.getColumn());
516 treeColumn
.setLabelProvider(columnData
.getLabelProvider());
519 // Handler that will draw the bar charts.
520 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
522 public void handleEvent(Event event
) {
523 if (columnDataList
.get(event
.index
).getPercentageProvider() != null) {
524 TmfStatisticsTreeNode node
= (TmfStatisticsTreeNode
) event
.item
.getData();
526 double percentage
= columnDataList
.get(event
.index
).getPercentageProvider().getPercentage(node
);
527 if (percentage
== 0) { // No bar to draw
531 if ((event
.detail
& SWT
.SELECTED
) > 0) { // The item is selected.
532 // Draws our own background to avoid overwritten the bar.
533 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
534 event
.detail
&= ~SWT
.SELECTED
;
537 int barWidth
= (int) ((fTreeViewer
.getTree().getColumn(event
.index
).getWidth() - 8) * percentage
);
538 int oldAlpha
= event
.gc
.getAlpha();
539 Color oldForeground
= event
.gc
.getForeground();
540 Color oldBackground
= event
.gc
.getBackground();
542 * Draws a transparent gradient rectangle from the color of
543 * foreground and background.
545 event
.gc
.setAlpha(64);
546 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_BLUE
));
547 event
.gc
.setBackground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
548 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
, event
.height
, true);
549 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
, event
.height
);
550 // Restores old values
551 event
.gc
.setForeground(oldForeground
);
552 event
.gc
.setBackground(oldBackground
);
553 event
.gc
.setAlpha(oldAlpha
);
554 event
.detail
&= ~SWT
.BACKGROUND
;
559 // Initializes the comparator parameters
560 fTreeViewer
.setComparator(columnDataList
.get(0).getComparator());
561 fTreeViewer
.getTree().setSortColumn(fTreeViewer
.getTree().getColumn(0));
562 fTreeViewer
.getTree().setSortDirection(SWT
.DOWN
);
566 * Initializes the input for the tree viewer.
569 * The input of this viewer, or <code>null</code> if none
571 protected void initInput() {
572 String treeID
= getTreeID();
573 TmfStatisticsTreeNode experimentTreeNode
;
574 if (TmfStatisticsTreeManager
.containsTreeRoot(treeID
)) {
575 // The experiment root is already present
576 experimentTreeNode
= TmfStatisticsTreeManager
.getStatTreeRoot(treeID
);
578 // Checks if the trace is already in the statistics tree.
579 int numNodeTraces
= experimentTreeNode
.getNbChildren();
582 ITmfTrace
[] trace
= { fTrace
};
583 // For experiment, gets all the traces within it
584 if (fTrace
instanceof TmfExperiment
) {
585 TmfExperiment experiment
= (TmfExperiment
) fTrace
;
586 numTraces
= experiment
.getTraces().length
;
587 trace
= experiment
.getTraces();
590 if (numTraces
== numNodeTraces
) {
593 * Checks if the experiment contains the same traces as when
594 * previously selected.
596 for (int i
= 0; i
< numTraces
; i
++) {
597 String traceName
= trace
[i
].getName();
598 if (!experimentTreeNode
.containsChild(traceName
)) {
605 // No need to reload data, all traces are already loaded
606 fTreeViewer
.setInput(experimentTreeNode
);
609 // Clears the old content to start over
610 experimentTreeNode
.reset();
613 // Creates a new tree
614 experimentTreeNode
= TmfStatisticsTreeManager
.addStatsTreeRoot(treeID
, getStatisticData());
617 // Sets the input to a clean data model
618 fTreeViewer
.setInput(experimentTreeNode
);
619 resetUpdateSynchronization();
623 * Tells if the viewer is listening to a trace from the selected experiment.
626 * The trace that the viewer may be listening
627 * @return true if the viewer is listening to the trace, false otherwise
629 protected boolean isListeningTo(ITmfTrace trace
) {
630 if (fProcessAll
|| trace
== fTrace
) {
637 * Called when an experiment request has been completed successfully.
640 * Tells if the request is a global or time range (partial)
643 protected void modelComplete(boolean global
) {
652 * Called when an experiment request has failed or has been cancelled.
654 * @param isGlobalRequest
655 * Tells if the request is a global or time range (partial)
658 protected void modelIncomplete(boolean isGlobalRequest
) {
659 if (isGlobalRequest
) { // Clean the global statistics
661 * No need to reset the global number of events, since the index of
662 * the last requested event is known.
664 resetUpdateSynchronization();
666 } else { // Clean the partial statistics
667 resetTimeRangeValue();
674 * Sends the request to the experiment for the whole trace
677 * The experiment used to send the request
679 * The range to request to the experiment
681 protected void requestData(final TmfExperiment experiment
, final TmfTimeRange timeRange
) {
682 buildStatisticsTree(experiment
, timeRange
, true);
686 * Sends the time range request from the experiment
689 * The experiment used to send the request
691 * The range to request to the experiment
693 protected void requestTimeRangeData(final TmfExperiment experiment
, final TmfTimeRange timeRange
) {
694 fRequestedTimerange
= timeRange
;
695 buildStatisticsTree(experiment
, timeRange
, false);
699 * Requests all the data of the experiment to the state system which
700 * contains information about the statistics.
702 * Since the viewer may be listening to multiple traces, it have to receive
703 * the experiment rather than a single trace. The filtering is done with the
704 * method {@link #isListeningTo(String trace)}.
707 * The experiment for which a request must be done
709 * The time range that will be requested to the state system
711 * Tells if the request is for the global event count or the
714 private void buildStatisticsTree(final TmfExperiment experiment
, TmfTimeRange timeRange
, boolean isGlobal
) {
715 final TmfStatisticsTreeNode statTree
= TmfStatisticsTreeManager
.getStatTreeRoot(getTreeID());
716 final TmfStatisticsTree statsData
= TmfStatisticsTreeManager
.getStatTree(getTreeID());
717 if (statsData
== null) {
721 synchronized (statsData
) {
723 statTree
.resetGlobalValue();
725 statTree
.resetTimeRangeValue();
729 * Checks each trace in the experiment, since the viewer may be
730 * listening to multiple traces.
732 for (final ITmfTrace trace
: experiment
.getTraces()) {
733 if (!isListeningTo(trace
)) {
737 /* Retrieves the statistics object */
738 final ITmfStatistics stats
= trace
.getStatistics();
741 * The statistics provider for this trace is not accessible
742 * (yet?). Try the next one.
747 /* The generic statistics are stored in nanoseconds, so we must make
748 * sure the time range is scaled correctly. */
749 ITmfTimestamp start
= timeRange
.getStartTime().normalize(0, TIME_SCALE
);
750 ITmfTimestamp end
= timeRange
.getEndTime().normalize(0, TIME_SCALE
);
753 * Send a request to update the statistics view. The result will
754 * be sent through a {@link TmfStatsUpdatedSignal}, and will be
755 * processed by the signal handler.
757 trace
.getStatistics().updateStats(isGlobal
, start
, end
);
763 * Resets the number of events within the time range
765 protected void resetTimeRangeValue() {
766 TmfStatisticsTreeNode treeModelRoot
= TmfStatisticsTreeManager
.getStatTreeRoot(getTreeID());
767 if (treeModelRoot
!= null && treeModelRoot
.hasChildren()) {
768 treeModelRoot
.resetTimeRangeValue();
773 * When the experiment is loading the cursor will be different so the user
774 * knows that the processing is not finished yet.
776 * Calls to this method are stacked.
778 * @param waitRequested
779 * Indicates if we need to show the waiting cursor, or the
782 protected void waitCursor(final boolean waitRequested
) {
783 if ((fTreeViewer
== null) || (fTreeViewer
.getTree().isDisposed())) {
787 boolean needsUpdate
= false;
788 Display display
= fTreeViewer
.getControl().getDisplay();
791 if (fWaitCursor
== null) { // The cursor hasn't been initialized yet
792 fWaitCursor
= new Cursor(display
, SWT
.CURSOR_WAIT
);
794 if (fWaitCursorCount
== 1) { // The cursor is not in waiting mode
798 if (fWaitCursorCount
> 0) { // The cursor is in waiting mode
800 if (fWaitCursorCount
== 0) { // No more reason to wait
801 // Put back the default cursor
808 // Performs the updates on the UI thread
809 display
.asyncExec(new Runnable() {
812 if ((fTreeViewer
!= null)
813 && (!fTreeViewer
.getTree().isDisposed())) {
814 Cursor cursor
= null; // indicates default
816 cursor
= fWaitCursor
;
818 fTreeViewer
.getControl().setCursor(cursor
);
825 // ------------------------------------------------------------------------
826 // Methods reserved for the streaming functionality
827 // ------------------------------------------------------------------------
830 * Resets update synchronization information
832 protected void resetUpdateSynchronization() {
833 synchronized (fStatisticsUpdateSyncObj
) {
834 fStatisticsUpdateBusy
= false;
835 fStatisticsUpdatePending
= false;
836 fStatisticsUpdateRange
= null;
841 * Checks if statistics update is ongoing. If it is ongoing, the new time
842 * range is stored as pending
846 * @return true if statistic update is ongoing else false
848 protected boolean checkUpdateBusy(TmfTimeRange timeRange
) {
849 synchronized (fStatisticsUpdateSyncObj
) {
850 if (fStatisticsUpdateBusy
) {
851 fStatisticsUpdatePending
= true;
852 if (fStatisticsUpdateRange
== null
853 || timeRange
.getEndTime().compareTo(fStatisticsUpdateRange
.getEndTime()) > 0) {
854 fStatisticsUpdateRange
= timeRange
;
858 fStatisticsUpdateBusy
= true;
864 * Sends pending request (if any)
866 protected void sendPendingUpdate() {
867 synchronized (fStatisticsUpdateSyncObj
) {
868 fStatisticsUpdateBusy
= false;
869 if (fStatisticsUpdatePending
) {
870 fStatisticsUpdatePending
= false;
871 requestData(TmfExperiment
.getCurrentExperiment(), fStatisticsUpdateRange
);
872 fStatisticsUpdateRange
= null;