Re-structure LTTng sub-project as per the Linux Tools guidelines
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / statistics / TmfStatisticsView.java
CommitLineData
79e08fd0
BH
1/*******************************************************************************
2 * Copyright (c) 2011 Ericsson
3 *
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
8 *
9 * Contributors:
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 *******************************************************************************/
13
14package org.eclipse.linuxtools.tmf.ui.views.statistics;
15
16import java.util.Vector;
17
18import org.eclipse.jface.viewers.TreeViewer;
19import org.eclipse.jface.viewers.TreeViewerColumn;
20import org.eclipse.jface.viewers.Viewer;
21import org.eclipse.jface.viewers.ViewerComparator;
6c13869b
FC
22import org.eclipse.linuxtools.tmf.core.event.TmfEvent;
23import org.eclipse.linuxtools.tmf.core.event.TmfTimeRange;
24import org.eclipse.linuxtools.tmf.core.experiment.TmfExperiment;
25import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest;
26import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
27import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
28import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
29import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest.ExecutionType;
30import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentDisposedSignal;
31import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentRangeUpdatedSignal;
32import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentSelectedSignal;
33import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
34import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
79e08fd0
BH
35import org.eclipse.linuxtools.tmf.ui.views.TmfView;
36import org.eclipse.linuxtools.tmf.ui.views.statistics.model.AbsTmfStatisticsTree;
37import org.eclipse.linuxtools.tmf.ui.views.statistics.model.ITmfColumnDataProvider;
38import org.eclipse.linuxtools.tmf.ui.views.statistics.model.TmfBaseColumnData;
39import org.eclipse.linuxtools.tmf.ui.views.statistics.model.TmfBaseColumnDataProvider;
40import org.eclipse.linuxtools.tmf.ui.views.statistics.model.TmfBaseStatisticsTree;
41import org.eclipse.linuxtools.tmf.ui.views.statistics.model.TmfStatisticsTreeNode;
42import org.eclipse.linuxtools.tmf.ui.views.statistics.model.TmfStatisticsTreeRootFactory;
43import org.eclipse.linuxtools.tmf.ui.views.statistics.model.TmfTreeContentProvider;
44import org.eclipse.swt.SWT;
45import org.eclipse.swt.events.SelectionAdapter;
46import org.eclipse.swt.events.SelectionEvent;
47import org.eclipse.swt.graphics.Color;
48import org.eclipse.swt.graphics.Cursor;
49import org.eclipse.swt.layout.FillLayout;
50import org.eclipse.swt.widgets.Composite;
51import org.eclipse.swt.widgets.Display;
52import org.eclipse.swt.widgets.Event;
53import org.eclipse.swt.widgets.Listener;
54
55/**
56 * <b><u>TmfStatisticsView</u></b>
57 * <p>
58 * The generic Statistics View displays statistics for any kind of traces.
59 *
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.
62 * </p>
63 */
64public class TmfStatisticsView extends TmfView {
65 /**
66 * The ID correspond to the package in which this class is embedded
67 */
68 public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.statistics"; //$NON-NLS-1$
69
70 // view name
66711dc8 71 public static final String TMF_STATISTICS_VIEW = "StatisticsView"; //$NON-NLS-1$
79e08fd0
BH
72
73 // Refresh frequency
74 protected static final Long STATS_INPUT_CHANGED_REFRESH = 5000L;
75
76 // Default PAGE_SIZE
77 protected static final int PAGE_SIZE = 50000; // For background request
78
79 // The actual tree to display
80 protected TreeViewer fTreeViewer;
81 // Stores the request to the experiment
82 protected ITmfEventRequest<TmfEvent> fRequest = null;
83
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();
89
90 // Object to store the cursor while waiting for the experiment to load
91 private Cursor fWaitCursor = null;
92
93 // Stores the number of instance
94 private static int fCountInstance = 0;
95
96 // Number of this instance. Used as an instance ID
97 private int fInstanceNb;
98
99 /**
100 * Constructor of a statistics view.
101 *
102 * @param viewName
103 * The name to give to the view.
104 */
105 public TmfStatisticsView(String viewName) {
106 super(viewName);
107 fCountInstance++;
108 fInstanceNb = fCountInstance;
109 }
110
111 /**
112 * Default constructor.
113 */
114 public TmfStatisticsView() {
115 this(TMF_STATISTICS_VIEW);
116 }
117
118 /*
119 * (non-Javadoc)
120 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
121 */
122 @Override
123 public void createPartControl(Composite parent) {
124 final Vector<TmfBaseColumnData> columnDataList = getColumnDataProvider().getColumnData();
125 parent.setLayout(new FillLayout());
126
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);
131
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());
137
138 if (columnData.getComparator() != null) {
139 treeColumn.getColumn().addSelectionListener(new SelectionAdapter() {
140 @Override
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);
145 } else {
146 fTreeViewer.setComparator(new ViewerComparator() {
147 @Override
148 public int compare(Viewer viewer, Object e1, Object e2) {
149 return -1 * columnData.getComparator().compare(viewer, e1, e2);
150 }
151 });
152 fTreeViewer.getTree().setSortDirection(SWT.UP);
153 }
154 fTreeViewer.getTree().setSortColumn(treeColumn.getColumn());
155 }
156 });
157 }
158 treeColumn.setLabelProvider(columnData.getLabelProvider());
159 }
160
161 // Handler that will draw the bar charts.
162 fTreeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
163 @Override
164 public void handleEvent(Event event) {
165 if (columnDataList.get(event.index).getPercentageProvider() != null) {
166 TmfStatisticsTreeNode node = (TmfStatisticsTreeNode) event.item.getData();
167
168 double percentage = columnDataList.get(event.index).getPercentageProvider().getPercentage(node);
169 if (percentage == 0) {
170 return;
171 }
172
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;
179 }
180
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;
194 }
195 }
196 });
197
198 fTreeViewer.setComparator(columnDataList.get(0).getComparator());
199 fTreeViewer.getTree().setSortColumn(fTreeViewer.getTree().getColumn(0));
200 fTreeViewer.getTree().setSortDirection(SWT.DOWN);
201
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);
209 }
210 }
211
212 /*
213 * (non-Javadoc)
214 * @see org.eclipse.linuxtools.tmf.ui.views.TmfView#dispose()
215 */
216 @Override
217 public void dispose() {
218 super.dispose();
219 if (fWaitCursor != null) {
220 fWaitCursor.dispose();
221 }
222
223 // clean the model
224 TmfStatisticsTreeRootFactory.removeAll();
225 }
226
227 /*
228 * (non-Javadoc)
229 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
230 */
231 @Override
232 public void setFocus() {
233 fTreeViewer.getTree().setFocus();
234 }
235
236 /**
237 * Refresh the view.
238 */
239 public void modelInputChanged(boolean complete) {
240 // Ignore update if disposed
241 if (fTreeViewer.getTree().isDisposed())
242 return;
243
244 fTreeViewer.getTree().getDisplay().asyncExec(new Runnable() {
245 @Override
246 public void run() {
247 if (!fTreeViewer.getTree().isDisposed())
248 fTreeViewer.refresh();
249 }
250 });
251
252 if (complete) {
253 sendPendingUpdate();
254 }
255 }
256
257 /**
258 * Called when an experiment request has failed or has been canceled Remove the data retrieved from the experiment from the statistics tree.
259 *
260 * @param name
261 * the experiment name
262 */
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));
269
270 // Reset synchronization information
271 resetUpdateSynchronization();
272 modelInputChanged(false);
273 }
274 waitCursor(false);
275 }
276
277 /**
278 * If the user choose another experiment, the current must be disposed.
279 *
280 * @param signal
281 */
282 @TmfSignalHandler
283 public void experimentDisposed(TmfExperimentDisposedSignal<? extends TmfEvent> signal) {
284 cancelOngoingRequest();
285 }
286
287 /**
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
289 * cached.
290 *
291 * @param signal
292 * contains the information about the selection.
293 */
294 @TmfSignalHandler
295 public void experimentSelected(TmfExperimentSelectedSignal<? extends TmfEvent> signal) {
296 if (signal != null) {
297 TmfExperiment<?> experiment = signal.getExperiment();
298 String experimentName = experiment.getName();
299
300 if (TmfStatisticsTreeRootFactory.containsTreeRoot(getTreeID(experimentName))) {
301 // The experiment root is already present
302 TmfStatisticsTreeNode experimentTreeNode = TmfStatisticsTreeRootFactory.getStatTreeRoot(getTreeID(experimentName));
303
66711dc8 304 @SuppressWarnings("rawtypes")
79e08fd0
BH
305 ITmfTrace[] traces = experiment.getTraces();
306
307 // check if there is partial data loaded in the experiment
308 int numTraces = experiment.getTraces().length;
309 int numNodeTraces = experimentTreeNode.getNbChildren();
310
311 if (numTraces == numNodeTraces) {
312 boolean same = true;
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)) {
318 same = false;
319 break;
320 }
321 }
322
323 if (same) {
324 // no need to reload data, all traces are already loaded
325 fTreeViewer.setInput(experimentTreeNode);
326
327 resetUpdateSynchronization();
328
329 return;
330 }
331 experimentTreeNode.reset();
332 }
333 } else {
334 TmfStatisticsTreeRootFactory.addStatsTreeRoot(getTreeID(experimentName), getStatisticData());
335 }
336
337 resetUpdateSynchronization();
338
339 TmfStatisticsTreeNode treeModelRoot = TmfStatisticsTreeRootFactory.getStatTreeRoot(getTreeID(experiment.getName()));
340
341 // if the model has contents, clear to start over
342 if (treeModelRoot.hasChildren()) {
343 treeModelRoot.reset();
344 }
345
346 // set input to a clean data model
347 fTreeViewer.setInput(treeModelRoot);
348
349 // if the data is not available or has changed, reload it
350 requestData(experiment, experiment.getTimeRange());
351 }
352 }
353
354 /**
355 * @param signal
356 */
357 @SuppressWarnings("unchecked")
358 @TmfSignalHandler
359 public void experimentRangeUpdated(TmfExperimentRangeUpdatedSignal signal) {
360 TmfExperiment<TmfEvent> experiment = (TmfExperiment<TmfEvent>) signal.getExperiment();
361 // validate
362 if (! experiment.equals(TmfExperiment.getCurrentExperiment())) {
363 return;
364 }
365
366 requestData(experiment, signal.getRange());
367 }
368
369
370 /**
371 * Return the size of the request when performing background request.
372 *
373 * @return the block size for background request.
374 */
375 protected int getIndexPageSize() {
376 return PAGE_SIZE;
377 }
378
379 /**
380 *
381 * @return the quantity of data to retrieve before a refresh of the view is performed.
382 */
383 protected long getInputChangedRefresh() {
384 return STATS_INPUT_CHANGED_REFRESH;
385 }
386
387 /**
388 * This method can be overridden to implement another way to represent the statistics data and to retrieve the information for display.
389 *
390 * @return a TmfStatisticsData object.
391 */
392 protected AbsTmfStatisticsTree getStatisticData() {
393 return new TmfBaseStatisticsTree();
394 }
395
396 /**
397 * This method can be overridden to change the representation of the data in the columns.
398 *
399 * @return an object implementing ITmfBaseColumnDataProvider.
400 */
401 protected ITmfColumnDataProvider getColumnDataProvider() {
402 return new TmfBaseColumnDataProvider();
403 }
404
405 /**
406 * Construct the ID based on the experiment name
407 * @param experimentName the name of the trace name to show in the view
408 * @return a view ID
409 */
410 protected String getTreeID(String experimentName) {
411 return experimentName + fInstanceNb;
412 }
413
414 /**
415 * When the experiment is loading the cursor will be different so the user know the processing is not finished yet.
416 *
417 * @param waitInd
418 * indicates if we need to show the waiting cursor, or the default one
419 */
420 protected void waitCursor(final boolean waitInd) {
421 if ((fTreeViewer == null) || (fTreeViewer.getTree().isDisposed())) {
422 return;
423 }
424
425 Display display = fTreeViewer.getControl().getDisplay();
426 if (fWaitCursor == null) {
427 fWaitCursor = new Cursor(display, SWT.CURSOR_WAIT);
428 }
429
430 // Perform the updates on the UI thread
431 display.asyncExec(new Runnable() {
432 @Override
433 public void run() {
434 if ((fTreeViewer != null) && (!fTreeViewer.getTree().isDisposed())) {
435 Cursor cursor = null; /* indicates default */
436 if (waitInd) {
437 cursor = fWaitCursor;
438 }
439 fTreeViewer.getControl().setCursor(cursor);
440 }
441 }
442 });
443 }
444
445 /**
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
449 */
450 @SuppressWarnings("unchecked")
451 protected void requestData(final TmfExperiment<?> experiment, TmfTimeRange timeRange) {
452 if (experiment != null) {
453
454 // Check if update is already ongoing
455 if (checkUpdateBusy(timeRange)) {
456 return;
457 }
458
459 int index = 0;
460 for (TmfStatisticsTreeNode node : ((TmfStatisticsTreeNode) fTreeViewer.getInput()).getChildren()) {
461 index += (int) node.getValue().nbEvents;
462 }
463
464 // Preparation of the event request
465 fRequest = new TmfEventRequest<TmfEvent>(TmfEvent.class, timeRange, index, TmfDataRequest.ALL_DATA, getIndexPageSize(), ExecutionType.BACKGROUND) {
466
467 @Override
468 public void handleData(TmfEvent data) {
469 super.handleData(data);
470 if (data != null) {
471 AbsTmfStatisticsTree statisticsData = TmfStatisticsTreeRootFactory.getStatTree(getTreeID(experiment.getName()));
472
473 final String traceName = data.getParentTrace().getName();
474 ITmfExtraEventInfo extraInfo = new ITmfExtraEventInfo() {
475 @Override
476 public String getTraceName() {
477 if (traceName == null) {
478 return Messages.TmfStatisticsView_UnknownTraceName;
479 }
480 return traceName;
481 }
482 };
483 statisticsData.registerEvent(data, extraInfo);
484 statisticsData.increase(data, extraInfo, 1);
485 // Refresh View
486 if ((getNbRead() % getInputChangedRefresh()) == 0) {
487 modelInputChanged(false);
488 }
489 }
490 }
491
492 @Override
493 public void handleSuccess() {
494 super.handleSuccess();
495 modelInputChanged(true);
496 waitCursor(false);
497 }
498
499 @Override
500 public void handleFailure() {
501 super.handleFailure();
502 modelIncomplete(experiment.getName());
503 }
504
505 @Override
506 public void handleCancel() {
507 super.handleCancel();
508 modelIncomplete(experiment.getName());
509 }
510 };
511 ((TmfExperiment<TmfEvent>) experiment).sendRequest((ITmfDataRequest<TmfEvent>) fRequest);
512 waitCursor(true);
513 }
514 }
515
516 /**
517 * Cancels the current ongoing request
518 */
519 protected void cancelOngoingRequest() {
520 if (fRequest != null && !fRequest.isCompleted()) {
521 fRequest.cancel();
522 }
523 }
524
525 /**
526 * Reset update synchronization information
527 */
528 protected void resetUpdateSynchronization() {
529 synchronized (fStatisticsUpdateSyncObj) {
530 fStatisticsUpdateBusy = false;
531 fStatisticsUpdatePending = false;
532 }
533 }
534
535 /**
536 * Checks if statistic update is ongoing. If it is ongoing the new time range is stored as pending
537 *
538 * @param timeRange - new time range
539 * @return true if statistic update is ongoing else false
540 */
541 protected boolean checkUpdateBusy(TmfTimeRange timeRange) {
542 synchronized (fStatisticsUpdateSyncObj) {
543 if (fStatisticsUpdateBusy) {
544 fStatisticsUpdatePending = true;
545 fStatisticsUpdateRange = timeRange;
546 return true;
547 }
548 fStatisticsUpdateBusy = true;
549 return false;
550 }
551 }
552
553 /**
554 * Sends pending request (if any)
555 */
556 protected void sendPendingUpdate() {
557 synchronized (fStatisticsUpdateSyncObj) {
558 fStatisticsUpdateBusy = false;
559 if (fStatisticsUpdatePending) {
560 fStatisticsUpdatePending = false;
561 requestData(TmfExperiment.getCurrentExperiment(), fStatisticsUpdateRange);
562 }
563 }
564 }
565
566}
This page took 0.088321 seconds and 5 git commands to generate.