1 /*******************************************************************************
2 * Copyright (c) 2011 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) - Generalized version based on LTTng
11 * Bernd Hufmann - Updated to use trace reference in TmfEvent and streaming
12 *******************************************************************************/
14 package org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
;
16 import java
.util
.Vector
;
18 import org
.eclipse
.jface
.viewers
.TreeViewer
;
19 import org
.eclipse
.jface
.viewers
.TreeViewerColumn
;
20 import org
.eclipse
.jface
.viewers
.Viewer
;
21 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.event
.TmfEvent
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.event
.TmfTimeRange
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.experiment
.TmfExperiment
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfDataRequest
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfEventRequest
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.request
.TmfDataRequest
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.request
.TmfEventRequest
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfDataRequest
.ExecutionType
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentDisposedSignal
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentRangeUpdatedSignal
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfExperimentSelectedSignal
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalHandler
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
35 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.TmfView
;
36 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.AbsTmfStatisticsTree
;
37 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.ITmfColumnDataProvider
;
38 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.TmfBaseColumnData
;
39 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.TmfBaseColumnDataProvider
;
40 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.TmfBaseStatisticsTree
;
41 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.TmfStatisticsTreeNode
;
42 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.TmfStatisticsTreeRootFactory
;
43 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.statistics
.model
.TmfTreeContentProvider
;
44 import org
.eclipse
.swt
.SWT
;
45 import org
.eclipse
.swt
.events
.SelectionAdapter
;
46 import org
.eclipse
.swt
.events
.SelectionEvent
;
47 import org
.eclipse
.swt
.graphics
.Color
;
48 import org
.eclipse
.swt
.graphics
.Cursor
;
49 import org
.eclipse
.swt
.layout
.FillLayout
;
50 import org
.eclipse
.swt
.widgets
.Composite
;
51 import org
.eclipse
.swt
.widgets
.Display
;
52 import org
.eclipse
.swt
.widgets
.Event
;
53 import org
.eclipse
.swt
.widgets
.Listener
;
56 * <b><u>TmfStatisticsView</u></b>
58 * The generic Statistics View displays statistics for any kind of traces.
60 * It is implemented according to the MVC pattern. - The model is a TmfStatisticsTreeNode built by the State Manager. - The view is built with a
61 * TreeViewer. - The controller that keeps model and view synchronized is an observer of the model.
64 public class TmfStatisticsView
extends TmfView
{
66 * The ID correspond to the package in which this class is embedded
68 public static final String ID
= "org.eclipse.linuxtools.tmf.ui.views.statistics"; //$NON-NLS-1$
71 public static final String TMF_STATISTICS_VIEW
= "StatisticsView"; //$NON-NLS-1$
74 protected static final Long STATS_INPUT_CHANGED_REFRESH
= 5000L;
77 protected static final int PAGE_SIZE
= 50000; // For background request
79 // The actual tree to display
80 protected TreeViewer fTreeViewer
;
81 // Stores the request to the experiment
82 protected ITmfEventRequest
<TmfEvent
> fRequest
= null;
84 // Update synchronization parameters (used for streaming)
85 protected boolean fStatisticsUpdateBusy
= false;
86 protected boolean fStatisticsUpdatePending
= false;
87 protected TmfTimeRange fStatisticsUpdateRange
= null;
88 protected final Object fStatisticsUpdateSyncObj
= new Object();
90 // Object to store the cursor while waiting for the experiment to load
91 private Cursor fWaitCursor
= null;
93 // Stores the number of instance
94 private static int fCountInstance
= 0;
96 // Number of this instance. Used as an instance ID
97 private int fInstanceNb
;
100 * Constructor of a statistics view.
103 * The name to give to the view.
105 public TmfStatisticsView(String viewName
) {
108 fInstanceNb
= fCountInstance
;
112 * Default constructor.
114 public TmfStatisticsView() {
115 this(TMF_STATISTICS_VIEW
);
120 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
123 public void createPartControl(Composite parent
) {
124 final Vector
<TmfBaseColumnData
> columnDataList
= getColumnDataProvider().getColumnData();
125 parent
.setLayout(new FillLayout());
127 fTreeViewer
= new TreeViewer(parent
, SWT
.BORDER
| SWT
.H_SCROLL
| SWT
.V_SCROLL
);
128 fTreeViewer
.setContentProvider(new TmfTreeContentProvider());
129 fTreeViewer
.getTree().setHeaderVisible(true);
130 fTreeViewer
.setUseHashlookup(true);
132 for (final TmfBaseColumnData columnData
: columnDataList
) {
133 final TreeViewerColumn treeColumn
= new TreeViewerColumn(fTreeViewer
, columnData
.getAlignment());
134 treeColumn
.getColumn().setText(columnData
.getHeader());
135 treeColumn
.getColumn().setWidth(columnData
.getWidth());
136 treeColumn
.getColumn().setToolTipText(columnData
.getTooltip());
138 if (columnData
.getComparator() != null) {
139 treeColumn
.getColumn().addSelectionListener(new SelectionAdapter() {
141 public void widgetSelected(SelectionEvent e
) {
142 if (fTreeViewer
.getTree().getSortDirection() == SWT
.UP
|| fTreeViewer
.getTree().getSortColumn() != treeColumn
.getColumn()) {
143 fTreeViewer
.setComparator(columnData
.getComparator());
144 fTreeViewer
.getTree().setSortDirection(SWT
.DOWN
);
146 fTreeViewer
.setComparator(new ViewerComparator() {
148 public int compare(Viewer viewer
, Object e1
, Object e2
) {
149 return -1 * columnData
.getComparator().compare(viewer
, e1
, e2
);
152 fTreeViewer
.getTree().setSortDirection(SWT
.UP
);
154 fTreeViewer
.getTree().setSortColumn(treeColumn
.getColumn());
158 treeColumn
.setLabelProvider(columnData
.getLabelProvider());
161 // Handler that will draw the bar charts.
162 fTreeViewer
.getTree().addListener(SWT
.EraseItem
, new Listener() {
164 public void handleEvent(Event event
) {
165 if (columnDataList
.get(event
.index
).getPercentageProvider() != null) {
166 TmfStatisticsTreeNode node
= (TmfStatisticsTreeNode
) event
.item
.getData();
168 double percentage
= columnDataList
.get(event
.index
).getPercentageProvider().getPercentage(node
);
169 if (percentage
== 0) {
173 if ((event
.detail
& SWT
.SELECTED
) > 0) {
174 Color oldForeground
= event
.gc
.getForeground();
175 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_SELECTION
));
176 event
.gc
.fillRectangle(event
.x
, event
.y
, event
.width
, event
.height
);
177 event
.gc
.setForeground(oldForeground
);
178 event
.detail
&= ~SWT
.SELECTED
;
181 int barWidth
= (int) ((fTreeViewer
.getTree().getColumn(1).getWidth() - 8) * percentage
);
182 int oldAlpha
= event
.gc
.getAlpha();
183 Color oldForeground
= event
.gc
.getForeground();
184 Color oldBackground
= event
.gc
.getBackground();
185 event
.gc
.setAlpha(64);
186 event
.gc
.setForeground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_BLUE
));
187 event
.gc
.setBackground(event
.item
.getDisplay().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
188 event
.gc
.fillGradientRectangle(event
.x
, event
.y
, barWidth
, event
.height
, true);
189 event
.gc
.drawRectangle(event
.x
, event
.y
, barWidth
, event
.height
);
190 event
.gc
.setForeground(oldForeground
);
191 event
.gc
.setBackground(oldBackground
);
192 event
.gc
.setAlpha(oldAlpha
);
193 event
.detail
&= ~SWT
.BACKGROUND
;
198 fTreeViewer
.setComparator(columnDataList
.get(0).getComparator());
199 fTreeViewer
.getTree().setSortColumn(fTreeViewer
.getTree().getColumn(0));
200 fTreeViewer
.getTree().setSortDirection(SWT
.DOWN
);
202 // Read current data if any available
203 TmfExperiment
<?
> experiment
= TmfExperiment
.getCurrentExperiment();
204 if (experiment
!= null) {
205 // Insert the statistics data into the tree
206 @SuppressWarnings({ "rawtypes", "unchecked" })
207 TmfExperimentSelectedSignal
<?
> signal
= new TmfExperimentSelectedSignal(this, experiment
);
208 experimentSelected(signal
);
214 * @see org.eclipse.linuxtools.tmf.ui.views.TmfView#dispose()
217 public void dispose() {
219 if (fWaitCursor
!= null) {
220 fWaitCursor
.dispose();
224 TmfStatisticsTreeRootFactory
.removeAll();
229 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
232 public void setFocus() {
233 fTreeViewer
.getTree().setFocus();
239 public void modelInputChanged(boolean complete
) {
240 // Ignore update if disposed
241 if (fTreeViewer
.getTree().isDisposed())
244 fTreeViewer
.getTree().getDisplay().asyncExec(new Runnable() {
247 if (!fTreeViewer
.getTree().isDisposed())
248 fTreeViewer
.refresh();
258 * Called when an experiment request has failed or has been canceled Remove the data retrieved from the experiment from the statistics tree.
261 * the experiment name
263 public void modelIncomplete(String name
) {
264 Object input
= fTreeViewer
.getInput();
265 if (input
!= null && input
instanceof TmfStatisticsTreeNode
) {
266 // The data from this experiment is invalid and shall be removed to
267 // refresh upon next selection
268 TmfStatisticsTreeRootFactory
.removeStatTreeRoot(getTreeID(name
));
270 // Reset synchronization information
271 resetUpdateSynchronization();
272 modelInputChanged(false);
278 * If the user choose another experiment, the current must be disposed.
283 public void experimentDisposed(TmfExperimentDisposedSignal
<?
extends TmfEvent
> signal
) {
284 cancelOngoingRequest();
288 * Handler called when an experiment is selected. Checks if the experiment has changed and requests the selected experiment if it has not yet been
292 * contains the information about the selection.
295 public void experimentSelected(TmfExperimentSelectedSignal
<?
extends TmfEvent
> signal
) {
296 if (signal
!= null) {
297 TmfExperiment
<?
> experiment
= signal
.getExperiment();
298 String experimentName
= experiment
.getName();
300 if (TmfStatisticsTreeRootFactory
.containsTreeRoot(getTreeID(experimentName
))) {
301 // The experiment root is already present
302 TmfStatisticsTreeNode experimentTreeNode
= TmfStatisticsTreeRootFactory
.getStatTreeRoot(getTreeID(experimentName
));
304 @SuppressWarnings("rawtypes")
305 ITmfTrace
[] traces
= experiment
.getTraces();
307 // check if there is partial data loaded in the experiment
308 int numTraces
= experiment
.getTraces().length
;
309 int numNodeTraces
= experimentTreeNode
.getNbChildren();
311 if (numTraces
== numNodeTraces
) {
313 // Detect if the experiment contains the same traces as when
314 // previously selected
315 for (int i
= 0; i
< numTraces
; i
++) {
316 String traceName
= traces
[i
].getName();
317 if (!experimentTreeNode
.containsChild(traceName
)) {
324 // no need to reload data, all traces are already loaded
325 fTreeViewer
.setInput(experimentTreeNode
);
327 resetUpdateSynchronization();
331 experimentTreeNode
.reset();
334 TmfStatisticsTreeRootFactory
.addStatsTreeRoot(getTreeID(experimentName
), getStatisticData());
337 resetUpdateSynchronization();
339 TmfStatisticsTreeNode treeModelRoot
= TmfStatisticsTreeRootFactory
.getStatTreeRoot(getTreeID(experiment
.getName()));
341 // if the model has contents, clear to start over
342 if (treeModelRoot
.hasChildren()) {
343 treeModelRoot
.reset();
346 // set input to a clean data model
347 fTreeViewer
.setInput(treeModelRoot
);
349 // if the data is not available or has changed, reload it
350 requestData(experiment
, experiment
.getTimeRange());
357 @SuppressWarnings("unchecked")
359 public void experimentRangeUpdated(TmfExperimentRangeUpdatedSignal signal
) {
360 TmfExperiment
<TmfEvent
> experiment
= (TmfExperiment
<TmfEvent
>) signal
.getExperiment();
362 if (! experiment
.equals(TmfExperiment
.getCurrentExperiment())) {
366 requestData(experiment
, signal
.getRange());
371 * Return the size of the request when performing background request.
373 * @return the block size for background request.
375 protected int getIndexPageSize() {
381 * @return the quantity of data to retrieve before a refresh of the view is performed.
383 protected long getInputChangedRefresh() {
384 return STATS_INPUT_CHANGED_REFRESH
;
388 * This method can be overridden to implement another way to represent the statistics data and to retrieve the information for display.
390 * @return a TmfStatisticsData object.
392 protected AbsTmfStatisticsTree
getStatisticData() {
393 return new TmfBaseStatisticsTree();
397 * This method can be overridden to change the representation of the data in the columns.
399 * @return an object implementing ITmfBaseColumnDataProvider.
401 protected ITmfColumnDataProvider
getColumnDataProvider() {
402 return new TmfBaseColumnDataProvider();
406 * Construct the ID based on the experiment name
407 * @param experimentName the name of the trace name to show in the view
410 protected String
getTreeID(String experimentName
) {
411 return experimentName
+ fInstanceNb
;
415 * When the experiment is loading the cursor will be different so the user know the processing is not finished yet.
418 * indicates if we need to show the waiting cursor, or the default one
420 protected void waitCursor(final boolean waitInd
) {
421 if ((fTreeViewer
== null) || (fTreeViewer
.getTree().isDisposed())) {
425 Display display
= fTreeViewer
.getControl().getDisplay();
426 if (fWaitCursor
== null) {
427 fWaitCursor
= new Cursor(display
, SWT
.CURSOR_WAIT
);
430 // Perform the updates on the UI thread
431 display
.asyncExec(new Runnable() {
434 if ((fTreeViewer
!= null) && (!fTreeViewer
.getTree().isDisposed())) {
435 Cursor cursor
= null; /* indicates default */
437 cursor
= fWaitCursor
;
439 fTreeViewer
.getControl().setCursor(cursor
);
446 * Perform the request for an experiment and populates the statistics tree with event.
447 * @param experiment experiment for which we need the statistics data.
448 * @param time range to request
450 @SuppressWarnings("unchecked")
451 protected void requestData(final TmfExperiment
<?
> experiment
, TmfTimeRange timeRange
) {
452 if (experiment
!= null) {
454 // Check if update is already ongoing
455 if (checkUpdateBusy(timeRange
)) {
460 for (TmfStatisticsTreeNode node
: ((TmfStatisticsTreeNode
) fTreeViewer
.getInput()).getChildren()) {
461 index
+= (int) node
.getValue().nbEvents
;
464 // Preparation of the event request
465 fRequest
= new TmfEventRequest
<TmfEvent
>(TmfEvent
.class, timeRange
, index
, TmfDataRequest
.ALL_DATA
, getIndexPageSize(), ExecutionType
.BACKGROUND
) {
468 public void handleData(TmfEvent data
) {
469 super.handleData(data
);
471 AbsTmfStatisticsTree statisticsData
= TmfStatisticsTreeRootFactory
.getStatTree(getTreeID(experiment
.getName()));
473 final String traceName
= data
.getParentTrace().getName();
474 ITmfExtraEventInfo extraInfo
= new ITmfExtraEventInfo() {
476 public String
getTraceName() {
477 if (traceName
== null) {
478 return Messages
.TmfStatisticsView_UnknownTraceName
;
483 statisticsData
.registerEvent(data
, extraInfo
);
484 statisticsData
.increase(data
, extraInfo
, 1);
486 if ((getNbRead() % getInputChangedRefresh()) == 0) {
487 modelInputChanged(false);
493 public void handleSuccess() {
494 super.handleSuccess();
495 modelInputChanged(true);
500 public void handleFailure() {
501 super.handleFailure();
502 modelIncomplete(experiment
.getName());
506 public void handleCancel() {
507 super.handleCancel();
508 modelIncomplete(experiment
.getName());
511 ((TmfExperiment
<TmfEvent
>) experiment
).sendRequest((ITmfDataRequest
<TmfEvent
>) fRequest
);
517 * Cancels the current ongoing request
519 protected void cancelOngoingRequest() {
520 if (fRequest
!= null && !fRequest
.isCompleted()) {
526 * Reset update synchronization information
528 protected void resetUpdateSynchronization() {
529 synchronized (fStatisticsUpdateSyncObj
) {
530 fStatisticsUpdateBusy
= false;
531 fStatisticsUpdatePending
= false;
536 * Checks if statistic update is ongoing. If it is ongoing the new time range is stored as pending
538 * @param timeRange - new time range
539 * @return true if statistic update is ongoing else false
541 protected boolean checkUpdateBusy(TmfTimeRange timeRange
) {
542 synchronized (fStatisticsUpdateSyncObj
) {
543 if (fStatisticsUpdateBusy
) {
544 fStatisticsUpdatePending
= true;
545 fStatisticsUpdateRange
= timeRange
;
548 fStatisticsUpdateBusy
= true;
554 * Sends pending request (if any)
556 protected void sendPendingUpdate() {
557 synchronized (fStatisticsUpdateSyncObj
) {
558 fStatisticsUpdateBusy
= false;
559 if (fStatisticsUpdatePending
) {
560 fStatisticsUpdatePending
= false;
561 requestData(TmfExperiment
.getCurrentExperiment(), fStatisticsUpdateRange
);