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