Fix javadoc warnings
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / statistics / TmfStatisticsView.java
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
14 package org.eclipse.linuxtools.tmf.ui.views.statistics;
15
16 import java.util.Vector;
17
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.ITmfEvent;
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.ITmfDataRequest.ExecutionType;
27 import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
28 import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
29 import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
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;
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 */
64 public 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
71 public static final String TMF_STATISTICS_VIEW = "StatisticsView"; //$NON-NLS-1$
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<ITmfEvent> 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 // Flag to force request the data from trace
91 protected boolean fRequestData = false;
92
93
94 // Object to store the cursor while waiting for the experiment to load
95 private Cursor fWaitCursor = null;
96
97 // Stores the number of instance
98 private static int fCountInstance = 0;
99
100 // Number of this instance. Used as an instance ID
101 private int fInstanceNb;
102
103
104 /**
105 * Constructor of a statistics view.
106 *
107 * @param viewName
108 * The name to give to the view.
109 */
110 public TmfStatisticsView(String viewName) {
111 super(viewName);
112 fCountInstance++;
113 fInstanceNb = fCountInstance;
114 }
115
116 /**
117 * Default constructor.
118 */
119 public TmfStatisticsView() {
120 this(TMF_STATISTICS_VIEW);
121 }
122
123 /*
124 * (non-Javadoc)
125 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
126 */
127 @Override
128 public void createPartControl(Composite parent) {
129 final Vector<TmfBaseColumnData> columnDataList = getColumnDataProvider().getColumnData();
130 parent.setLayout(new FillLayout());
131
132 fTreeViewer = new TreeViewer(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
133 fTreeViewer.setContentProvider(new TmfTreeContentProvider());
134 fTreeViewer.getTree().setHeaderVisible(true);
135 fTreeViewer.setUseHashlookup(true);
136
137 for (final TmfBaseColumnData columnData : columnDataList) {
138 final TreeViewerColumn treeColumn = new TreeViewerColumn(fTreeViewer, columnData.getAlignment());
139 treeColumn.getColumn().setText(columnData.getHeader());
140 treeColumn.getColumn().setWidth(columnData.getWidth());
141 treeColumn.getColumn().setToolTipText(columnData.getTooltip());
142
143 if (columnData.getComparator() != null) {
144 treeColumn.getColumn().addSelectionListener(new SelectionAdapter() {
145 @Override
146 public void widgetSelected(SelectionEvent e) {
147 if (fTreeViewer.getTree().getSortDirection() == SWT.UP || fTreeViewer.getTree().getSortColumn() != treeColumn.getColumn()) {
148 fTreeViewer.setComparator(columnData.getComparator());
149 fTreeViewer.getTree().setSortDirection(SWT.DOWN);
150 } else {
151 fTreeViewer.setComparator(new ViewerComparator() {
152 @Override
153 public int compare(Viewer viewer, Object e1, Object e2) {
154 return -1 * columnData.getComparator().compare(viewer, e1, e2);
155 }
156 });
157 fTreeViewer.getTree().setSortDirection(SWT.UP);
158 }
159 fTreeViewer.getTree().setSortColumn(treeColumn.getColumn());
160 }
161 });
162 }
163 treeColumn.setLabelProvider(columnData.getLabelProvider());
164 }
165
166 // Handler that will draw the bar charts.
167 fTreeViewer.getTree().addListener(SWT.EraseItem, new Listener() {
168 @Override
169 public void handleEvent(Event event) {
170 if (columnDataList.get(event.index).getPercentageProvider() != null) {
171 TmfStatisticsTreeNode node = (TmfStatisticsTreeNode) event.item.getData();
172
173 double percentage = columnDataList.get(event.index).getPercentageProvider().getPercentage(node);
174 if (percentage == 0) {
175 return;
176 }
177
178 if ((event.detail & SWT.SELECTED) > 0) {
179 Color oldForeground = event.gc.getForeground();
180 event.gc.setForeground(event.item.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION));
181 event.gc.fillRectangle(event.x, event.y, event.width, event.height);
182 event.gc.setForeground(oldForeground);
183 event.detail &= ~SWT.SELECTED;
184 }
185
186 int barWidth = (int) ((fTreeViewer.getTree().getColumn(1).getWidth() - 8) * percentage);
187 int oldAlpha = event.gc.getAlpha();
188 Color oldForeground = event.gc.getForeground();
189 Color oldBackground = event.gc.getBackground();
190 event.gc.setAlpha(64);
191 event.gc.setForeground(event.item.getDisplay().getSystemColor(SWT.COLOR_BLUE));
192 event.gc.setBackground(event.item.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
193 event.gc.fillGradientRectangle(event.x, event.y, barWidth, event.height, true);
194 event.gc.drawRectangle(event.x, event.y, barWidth, event.height);
195 event.gc.setForeground(oldForeground);
196 event.gc.setBackground(oldBackground);
197 event.gc.setAlpha(oldAlpha);
198 event.detail &= ~SWT.BACKGROUND;
199 }
200 }
201 });
202
203 fTreeViewer.setComparator(columnDataList.get(0).getComparator());
204 fTreeViewer.getTree().setSortColumn(fTreeViewer.getTree().getColumn(0));
205 fTreeViewer.getTree().setSortDirection(SWT.DOWN);
206
207 // Read current data if any available
208 TmfExperiment<?> experiment = TmfExperiment.getCurrentExperiment();
209 if (experiment != null) {
210 fRequestData = true;
211 // Insert the statistics data into the tree
212 @SuppressWarnings({ "rawtypes", "unchecked" })
213 TmfExperimentSelectedSignal<?> signal = new TmfExperimentSelectedSignal(this, experiment);
214 experimentSelected(signal);
215 }
216 }
217
218 /*
219 * (non-Javadoc)
220 * @see org.eclipse.linuxtools.tmf.ui.views.TmfView#dispose()
221 */
222 @Override
223 public void dispose() {
224 super.dispose();
225 if (fWaitCursor != null) {
226 fWaitCursor.dispose();
227 }
228
229 // clean the model
230 TmfStatisticsTreeRootFactory.removeAll();
231 }
232
233 /*
234 * (non-Javadoc)
235 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
236 */
237 @Override
238 public void setFocus() {
239 fTreeViewer.getTree().setFocus();
240 }
241
242 /**
243 * Refresh the view.
244 */
245 public void modelInputChanged(boolean complete) {
246 // Ignore update if disposed
247 if (fTreeViewer.getTree().isDisposed())
248 return;
249
250 fTreeViewer.getTree().getDisplay().asyncExec(new Runnable() {
251 @Override
252 public void run() {
253 if (!fTreeViewer.getTree().isDisposed())
254 fTreeViewer.refresh();
255 }
256 });
257
258 if (complete) {
259 sendPendingUpdate();
260 }
261 }
262
263 /**
264 * Called when an experiment request has failed or has been canceled Remove the data retrieved from the experiment from the statistics tree.
265 *
266 * @param name
267 * the experiment name
268 */
269 public void modelIncomplete(String name) {
270 Object input = fTreeViewer.getInput();
271 if (input != null && input instanceof TmfStatisticsTreeNode) {
272 // The data from this experiment is invalid and shall be removed to
273 // refresh upon next selection
274 TmfStatisticsTreeRootFactory.removeStatTreeRoot(getTreeID(name));
275
276 // Reset synchronization information
277 resetUpdateSynchronization();
278 modelInputChanged(false);
279 }
280 waitCursor(false);
281 }
282
283 /**
284 * If the user choose another experiment, the current must be disposed.
285 *
286 * @param signal
287 */
288 @TmfSignalHandler
289 public void experimentDisposed(TmfExperimentDisposedSignal<? extends ITmfEvent> signal) {
290 if (signal.getExperiment() != TmfExperiment.getCurrentExperiment()) {
291 return;
292 }
293 cancelOngoingRequest();
294 }
295
296 /**
297 * Handler called when an experiment is selected. Checks if the experiment has changed and requests the selected experiment if it has not yet been
298 * cached.
299 *
300 * @param signal
301 * contains the information about the selection.
302 */
303 @TmfSignalHandler
304 public void experimentSelected(TmfExperimentSelectedSignal<? extends ITmfEvent> signal) {
305 if (signal != null) {
306 TmfExperiment<?> experiment = signal.getExperiment();
307 String experimentName = experiment.getName();
308
309 if (TmfStatisticsTreeRootFactory.containsTreeRoot(getTreeID(experimentName))) {
310 // The experiment root is already present
311 TmfStatisticsTreeNode experimentTreeNode = TmfStatisticsTreeRootFactory.getStatTreeRoot(getTreeID(experimentName));
312
313 @SuppressWarnings("rawtypes")
314 ITmfTrace[] traces = experiment.getTraces();
315
316 // check if there is partial data loaded in the experiment
317 int numTraces = experiment.getTraces().length;
318 int numNodeTraces = experimentTreeNode.getNbChildren();
319
320 if (numTraces == numNodeTraces) {
321 boolean same = true;
322 // Detect if the experiment contains the same traces as when
323 // previously selected
324 for (int i = 0; i < numTraces; i++) {
325 String traceName = traces[i].getName();
326 if (!experimentTreeNode.containsChild(traceName)) {
327 same = false;
328 break;
329 }
330 }
331
332 if (same) {
333 // no need to reload data, all traces are already loaded
334 fTreeViewer.setInput(experimentTreeNode);
335
336 resetUpdateSynchronization();
337
338 return;
339 }
340 experimentTreeNode.reset();
341 }
342 } else {
343 TmfStatisticsTreeRootFactory.addStatsTreeRoot(getTreeID(experimentName), getStatisticData());
344 }
345
346 resetUpdateSynchronization();
347
348 TmfStatisticsTreeNode treeModelRoot = TmfStatisticsTreeRootFactory.getStatTreeRoot(getTreeID(experiment.getName()));
349
350 // if the model has contents, clear to start over
351 if (treeModelRoot.hasChildren()) {
352 treeModelRoot.reset();
353 }
354
355 // set input to a clean data model
356 fTreeViewer.setInput(treeModelRoot);
357
358 if (fRequestData) {
359 requestData(experiment, experiment.getTimeRange());
360 fRequestData = false;
361 }
362 }
363 }
364
365 /**
366 * @param signal
367 */
368 @SuppressWarnings("unchecked")
369 @TmfSignalHandler
370 public void experimentRangeUpdated(TmfExperimentRangeUpdatedSignal signal) {
371 TmfExperiment<ITmfEvent> experiment = (TmfExperiment<ITmfEvent>) signal.getExperiment();
372 // validate
373 if (! experiment.equals(TmfExperiment.getCurrentExperiment())) {
374 return;
375 }
376
377 requestData(experiment, signal.getRange());
378 }
379
380
381 /**
382 * Return the size of the request when performing background request.
383 *
384 * @return the block size for background request.
385 */
386 protected int getIndexPageSize() {
387 return PAGE_SIZE;
388 }
389
390 /**
391 *
392 * @return the quantity of data to retrieve before a refresh of the view is performed.
393 */
394 protected long getInputChangedRefresh() {
395 return STATS_INPUT_CHANGED_REFRESH;
396 }
397
398 /**
399 * This method can be overridden to implement another way to represent the statistics data and to retrieve the information for display.
400 *
401 * @return a TmfStatisticsData object.
402 */
403 protected AbsTmfStatisticsTree getStatisticData() {
404 return new TmfBaseStatisticsTree();
405 }
406
407 /**
408 * This method can be overridden to change the representation of the data in the columns.
409 *
410 * @return an object implementing ITmfBaseColumnDataProvider.
411 */
412 protected ITmfColumnDataProvider getColumnDataProvider() {
413 return new TmfBaseColumnDataProvider();
414 }
415
416 /**
417 * Construct the ID based on the experiment name
418 * @param experimentName the name of the trace name to show in the view
419 * @return a view ID
420 */
421 protected String getTreeID(String experimentName) {
422 return experimentName + fInstanceNb;
423 }
424
425 /**
426 * When the experiment is loading the cursor will be different so the user know the processing is not finished yet.
427 *
428 * @param waitInd
429 * indicates if we need to show the waiting cursor, or the default one
430 */
431 protected void waitCursor(final boolean waitInd) {
432 if ((fTreeViewer == null) || (fTreeViewer.getTree().isDisposed())) {
433 return;
434 }
435
436 Display display = fTreeViewer.getControl().getDisplay();
437 if (fWaitCursor == null) {
438 fWaitCursor = new Cursor(display, SWT.CURSOR_WAIT);
439 }
440
441 // Perform the updates on the UI thread
442 display.asyncExec(new Runnable() {
443 @Override
444 public void run() {
445 if ((fTreeViewer != null) && (!fTreeViewer.getTree().isDisposed())) {
446 Cursor cursor = null; /* indicates default */
447 if (waitInd) {
448 cursor = fWaitCursor;
449 }
450 fTreeViewer.getControl().setCursor(cursor);
451 }
452 }
453 });
454 }
455
456 /**
457 * Perform the request for an experiment and populates the statistics tree with event.
458 *
459 * @param experiment experiment for which we need the statistics data.
460 * @param timeRange to request
461 */
462 @SuppressWarnings("unchecked")
463 protected void requestData(final TmfExperiment<?> experiment, TmfTimeRange timeRange) {
464 if (experiment != null) {
465
466 // Check if update is already ongoing
467 if (checkUpdateBusy(timeRange)) {
468 return;
469 }
470
471 int index = 0;
472 for (TmfStatisticsTreeNode node : ((TmfStatisticsTreeNode) fTreeViewer.getInput()).getChildren()) {
473 index += (int) node.getValue().nbEvents;
474 }
475
476 // Preparation of the event request
477 fRequest = new TmfEventRequest<ITmfEvent>(ITmfEvent.class, timeRange, index, TmfDataRequest.ALL_DATA, getIndexPageSize(), ExecutionType.BACKGROUND) {
478
479 @Override
480 public void handleData(ITmfEvent data) {
481 super.handleData(data);
482 if (data != null) {
483 AbsTmfStatisticsTree statisticsData = TmfStatisticsTreeRootFactory.getStatTree(getTreeID(experiment.getName()));
484
485 final String traceName = data.getTrace().getName();
486 ITmfExtraEventInfo extraInfo = new ITmfExtraEventInfo() {
487 @Override
488 public String getTraceName() {
489 if (traceName == null) {
490 return Messages.TmfStatisticsView_UnknownTraceName;
491 }
492 return traceName;
493 }
494 };
495 statisticsData.registerEvent(data, extraInfo);
496 statisticsData.increase(data, extraInfo, 1);
497 // Refresh View
498 if ((getNbRead() % getInputChangedRefresh()) == 0) {
499 modelInputChanged(false);
500 }
501 }
502 }
503
504 @Override
505 public void handleSuccess() {
506 super.handleSuccess();
507 modelInputChanged(true);
508 waitCursor(false);
509 }
510
511 @Override
512 public void handleFailure() {
513 super.handleFailure();
514 modelIncomplete(experiment.getName());
515 }
516
517 @Override
518 public void handleCancel() {
519 super.handleCancel();
520 modelIncomplete(experiment.getName());
521 }
522 };
523 ((TmfExperiment<ITmfEvent>) experiment).sendRequest((ITmfDataRequest<ITmfEvent>) fRequest);
524 waitCursor(true);
525 }
526 }
527
528 /**
529 * Cancels the current ongoing request
530 */
531 protected void cancelOngoingRequest() {
532 if (fRequest != null && !fRequest.isCompleted()) {
533 fRequest.cancel();
534 }
535 }
536
537 /**
538 * Reset update synchronization information
539 */
540 protected void resetUpdateSynchronization() {
541 synchronized (fStatisticsUpdateSyncObj) {
542 fStatisticsUpdateBusy = false;
543 fStatisticsUpdatePending = false;
544 }
545 }
546
547 /**
548 * Checks if statistic update is ongoing. If it is ongoing the new time range is stored as pending
549 *
550 * @param timeRange - new time range
551 * @return true if statistic update is ongoing else false
552 */
553 protected boolean checkUpdateBusy(TmfTimeRange timeRange) {
554 synchronized (fStatisticsUpdateSyncObj) {
555 if (fStatisticsUpdateBusy) {
556 fStatisticsUpdatePending = true;
557 fStatisticsUpdateRange = timeRange;
558 return true;
559 }
560 fStatisticsUpdateBusy = true;
561 return false;
562 }
563 }
564
565 /**
566 * Sends pending request (if any)
567 */
568 protected void sendPendingUpdate() {
569 synchronized (fStatisticsUpdateSyncObj) {
570 fStatisticsUpdateBusy = false;
571 if (fStatisticsUpdatePending) {
572 fStatisticsUpdatePending = false;
573 requestData(TmfExperiment.getCurrentExperiment(), fStatisticsUpdateRange);
574 }
575 }
576 }
577
578 }
This page took 0.061844 seconds and 5 git commands to generate.