tmf: Switch tmf.ui to Java 7 + fix warnings
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / statesystem / TmfStateSystemExplorer.java
1 /*******************************************************************************
2 * Copyright (c) 2013 École Polytechnique de Montréal, 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 * Florian Wininger - Initial API and implementation
11 * Alexandre Montplaisir - Refactoring, performance tweaks
12 * Bernd Hufmann - Updated signal handling
13 * Marc-Andre Laperle - Add time zone preference
14 *******************************************************************************/
15
16 package org.eclipse.linuxtools.tmf.ui.views.statesystem;
17
18 import java.io.File;
19 import java.util.HashMap;
20 import java.util.LinkedHashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24
25 import org.eclipse.core.runtime.NullProgressMonitor;
26 import org.eclipse.jface.action.Action;
27 import org.eclipse.jface.action.IToolBarManager;
28 import org.eclipse.jface.resource.ImageDescriptor;
29 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
30 import org.eclipse.linuxtools.tmf.core.analysis.IAnalysisModule;
31 import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
32 import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
33 import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
34 import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
35 import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
36 import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
37 import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
38 import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
39 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceClosedSignal;
40 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceOpenedSignal;
41 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal;
42 import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
43 import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystemAnalysisModule;
44 import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
45 import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
46 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
47 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
48 import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
49 import org.eclipse.linuxtools.tmf.ui.views.TmfView;
50 import org.eclipse.swt.SWT;
51 import org.eclipse.swt.graphics.Image;
52 import org.eclipse.swt.widgets.Composite;
53 import org.eclipse.swt.widgets.Display;
54 import org.eclipse.swt.widgets.Event;
55 import org.eclipse.swt.widgets.Listener;
56 import org.eclipse.swt.widgets.Tree;
57 import org.eclipse.swt.widgets.TreeColumn;
58 import org.eclipse.swt.widgets.TreeItem;
59 import org.eclipse.ui.IActionBars;
60
61 /**
62 * Displays the State System at a current time.
63 *
64 * @author Florian Wininger
65 * @author Alexandre Montplaisir
66 * @since 2.0
67 */
68 public class TmfStateSystemExplorer extends TmfView {
69
70 /** The Environment View's ID */
71 public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
72
73 private static final String emptyString = ""; //$NON-NLS-1$
74 private static final String FS = File.separator;
75 private static final Image FILTER_IMAGE =
76 Activator.getDefault().getImageFromPath(FS + "icons" + FS + "elcl16" + FS + "filter_items.gif"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
77
78 /* Order of columns */
79 private static final int ATTRIBUTE_NAME_COL = 0;
80 private static final int QUARK_COL = 1;
81 private static final int VALUE_COL = 2;
82 private static final int TYPE_COL = 3;
83 private static final int START_TIME_COL = 4;
84 private static final int END_TIME_COL = 5;
85 private static final int ATTRIBUTE_FULLPATH_COL = 6;
86
87 private ITmfTrace fTrace;
88 private Tree fTree;
89 private volatile long fCurrentTimestamp = -1L;
90
91 private boolean filterStatus = false ;
92
93 /**
94 * Default constructor
95 */
96 public TmfStateSystemExplorer() {
97 super(ID);
98 }
99
100 // ------------------------------------------------------------------------
101 // ViewPart
102 // ------------------------------------------------------------------------
103
104 @Override
105 public void createPartControl(Composite parent) {
106 fTree = new Tree(parent, SWT.NONE);
107 TreeColumn nameCol = new TreeColumn(fTree, SWT.NONE, ATTRIBUTE_NAME_COL);
108 TreeColumn quarkCol = new TreeColumn(fTree, SWT.NONE, QUARK_COL);
109 TreeColumn valueCol = new TreeColumn(fTree, SWT.NONE, VALUE_COL);
110 TreeColumn typeCol = new TreeColumn(fTree, SWT.NONE, TYPE_COL);
111 TreeColumn startCol = new TreeColumn(fTree, SWT.NONE, START_TIME_COL);
112 TreeColumn endCol = new TreeColumn(fTree, SWT.NONE, END_TIME_COL);
113 TreeColumn pathCol = new TreeColumn(fTree, SWT.NONE, ATTRIBUTE_FULLPATH_COL);
114
115 nameCol.setText(Messages.TreeNodeColumnLabel);
116 quarkCol.setText(Messages.QuarkColumnLabel);
117 valueCol.setText(Messages.ValueColumnLabel);
118 typeCol.setText(Messages.TypeColumnLabel);
119 startCol.setText(Messages.StartTimeColumLabel);
120 endCol.setText(Messages.EndTimeColumLabel);
121 pathCol.setText(Messages.AttributePathColumnLabel);
122
123 fTree.setItemCount(0);
124
125 fTree.setHeaderVisible(true);
126 nameCol.pack();
127 valueCol.pack();
128
129 fTree.addListener(SWT.Expand, new Listener() {
130 @Override
131 public void handleEvent(Event e) {
132 TreeItem item = (TreeItem) e.item;
133 item.setExpanded(true);
134 updateTable();
135 }
136 });
137
138 ITmfTrace trace = getActiveTrace();
139 if (trace != null) {
140 traceSelected(new TmfTraceSelectedSignal(this, trace));
141 }
142
143 fillToolBar() ;
144
145 }
146
147 // ------------------------------------------------------------------------
148 // Operations
149 // ------------------------------------------------------------------------
150
151 /**
152 * Create the initial tree from a trace.
153 */
154 private synchronized void createTable() {
155
156 long ts = fCurrentTimestamp;
157
158 if (fTrace == null) {
159 return;
160 }
161
162 /* Clear the table, in case a trace was previously using it */
163 fTree.getDisplay().asyncExec(new Runnable() {
164 @Override
165 public void run() {
166 fTree.setItemCount(0);
167 }
168 });
169
170 for (final ITmfTrace currentTrace : TmfTraceManager.getTraceSet(fTrace)) {
171 /*
172 * We will first do all the queries for this trace, then update that
173 * sub-tree in the UI thread.
174 */
175 Map<String, ITmfStateSystemAnalysisModule> modules = currentTrace.getAnalysisModules(ITmfStateSystemAnalysisModule.class);
176 final Map<String, ITmfStateSystem> sss = new HashMap<>();
177 final Map<String, List<ITmfStateInterval>> fullStates =
178 new LinkedHashMap<>();
179 for (Entry<String, ITmfStateSystemAnalysisModule> entry : modules.entrySet()) {
180 /*
181 * FIXME: For now, this view is a way to execute and display
182 * state system. But with phase 2 of analysis API, we won't want
183 * to run state system that have not been requested. We will
184 * leave the title, but there won't be anything underneath.
185 */
186 ITmfStateSystemAnalysisModule module = entry.getValue();
187 if (module instanceof IAnalysisModule) {
188 IAnalysisModule mod = (IAnalysisModule) module;
189 mod.schedule();
190 mod.waitForCompletion(new NullProgressMonitor());
191 }
192 for (Entry<String, ITmfStateSystem> ssEntry : module.getStateSystems().entrySet()) {
193 String ssName = ssEntry.getKey();
194 ITmfStateSystem ss = ssEntry.getValue();
195 sss.put(ssName, ss);
196 if (ss != null) {
197 if (ts == -1 || ts < ss.getStartTime() || ts > ss.getCurrentEndTime()) {
198 ts = ss.getStartTime();
199 }
200 try {
201 fullStates.put(ssName, ss.queryFullState(ts));
202 } catch (TimeRangeException e) {
203 /* We already checked the limits ourselves */
204 throw new IllegalStateException();
205 } catch (StateSystemDisposedException e) {
206 /* Probably shutting down, cancel and return */
207 return;
208 }
209 }
210 }
211 }
212
213 /* Update the table (in the UI thread) */
214 fTree.getDisplay().asyncExec(new Runnable() {
215 @Override
216 public void run() {
217 TreeItem traceRoot = new TreeItem(fTree, SWT.NONE);
218 traceRoot.setText(ATTRIBUTE_NAME_COL, currentTrace.getName());
219
220 for (Map.Entry<String, ITmfStateSystem> entry : sss.entrySet()) {
221 String ssName = entry.getKey();
222 ITmfStateSystem ss = entry.getValue();
223 List<ITmfStateInterval> fullState = fullStates.get(ssName);
224
225 /* Root item of the current state system */
226 TreeItem item = new TreeItem(traceRoot, SWT.NONE);
227
228 /* Name of the SS goes in the first column */
229 item.setText(ATTRIBUTE_NAME_COL, ssName);
230
231 /*
232 * Calling with quark '-1' here to start with the root
233 * attribute, then it will be called recursively.
234 */
235 if (ss != null) {
236 addChildren(ss, fullState, -1, item);
237 }
238 }
239
240 /* Expand the first-level tree items */
241 for (TreeItem item : fTree.getItems()) {
242 item.setExpanded(true);
243 }
244 packColumns();
245
246 if (filterStatus) {
247 filterChildren(traceRoot);
248 }
249 }
250 });
251 }
252 }
253
254 /**
255 * Add children node to a newly-created tree. Should only be called by the
256 * UI thread.
257 */
258 private void addChildren(ITmfStateSystem ss,
259 List<ITmfStateInterval> fullState, int rootQuark, TreeItem root) {
260 try {
261 for (int quark : ss.getSubAttributes(rootQuark, false)) {
262 TreeItem subItem = new TreeItem(root, SWT.NONE);
263
264 /* Write the info we already know */
265 subItem.setText(ATTRIBUTE_NAME_COL, ss.getAttributeName(quark));
266 subItem.setText(QUARK_COL, String.valueOf(quark));
267 subItem.setText(ATTRIBUTE_FULLPATH_COL, ss.getFullAttributePath(quark));
268
269 /* Populate the other columns */
270 ITmfStateInterval interval = fullState.get(quark);
271 populateColumns(subItem, interval);
272
273 /* Update this node's children recursively */
274 addChildren(ss, fullState, quark, subItem);
275 }
276
277 } catch (AttributeNotFoundException e) {
278 /* Should not happen, we're iterating on known attributes */
279 throw new RuntimeException();
280 }
281 }
282
283 /**
284 * Update the tree, which means keep the tree of attributes in the first
285 * column as-is, but update the values to the ones at a new timestamp.
286 */
287 private synchronized void updateTable() {
288 ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
289 long ts = fCurrentTimestamp;
290
291 /* For each trace... */
292 for (int traceNb = 0; traceNb < traces.length; traceNb++) {
293 Map<String, ITmfStateSystemAnalysisModule> modules = traces[traceNb].getAnalysisModules(ITmfStateSystemAnalysisModule.class);
294
295 /* For each state system associated with this trace... */
296 int ssNb = 0;
297 for (Entry<String, ITmfStateSystemAnalysisModule> module : modules.entrySet()) {
298
299 /*
300 * Even though we only use the value, it just feels safer to
301 * iterate the same way as before to keep the order the same.
302 */
303 for (Entry<String, ITmfStateSystem> ssEntry : module.getValue().getStateSystems().entrySet()) {
304 final ITmfStateSystem ss = ssEntry.getValue();
305 final int traceNb1 = traceNb;
306 final int ssNb1 = ssNb;
307 if (ss != null) {
308 ts = (ts == -1 ? ss.getStartTime() : ts);
309 try {
310 final List<ITmfStateInterval> fullState = ss.queryFullState(ts);
311 fTree.getDisplay().asyncExec(new Runnable() {
312 @Override
313 public void run() {
314 /*
315 * Get the tree item of the relevant state
316 * system
317 */
318 TreeItem traceItem = fTree.getItem(traceNb1);
319 TreeItem item = traceItem.getItem(ssNb1);
320 /* Update it, then its children, recursively */
321 item.setText(VALUE_COL, emptyString);
322 updateChildren(ss, fullState, -1, item);
323 }
324 });
325
326 } catch (TimeRangeException e) {
327 /*
328 * This can happen in an experiment, if the user
329 * selects a range valid in the experiment, but this
330 * specific does not exist. Print "out-of-range"
331 * into all the values.
332 */
333 fTree.getDisplay().asyncExec(new Runnable() {
334 @Override
335 public void run() {
336 TreeItem traceItem = fTree.getItem(traceNb1);
337 TreeItem item = traceItem.getItem(ssNb1);
338 markOutOfRange(item);
339 }
340 });
341 } catch (StateSystemDisposedException e) {
342 return;
343 }
344 }
345
346 ssNb++;
347 }
348 }
349 }
350 }
351
352 /**
353 * Update the values shown by a child row when doing an update. Should only
354 * be called by the UI thread.
355 */
356 private void updateChildren(ITmfStateSystem ss,
357 List<ITmfStateInterval> state, int root_quark, TreeItem root) {
358 try {
359 for (TreeItem item : root.getItems()) {
360 int quark = ss.getQuarkRelative(root_quark, item.getText(0));
361 ITmfStateInterval interval = state.get(quark);
362 populateColumns(item, interval);
363
364 /* Update children recursively */
365 updateChildren(ss, state, quark, item);
366 }
367
368 } catch (AttributeNotFoundException e) {
369 /* We're iterating on known attributes, should not happen */
370 throw new RuntimeException();
371 }
372 }
373
374 /**
375 * Populate an 'item' (a row in the tree) with the information found in the
376 * interval. This method should only be called by the UI thread.
377 */
378 private void populateColumns(TreeItem item, ITmfStateInterval interval) {
379 try {
380 ITmfStateValue state = interval.getStateValue();
381 String value ;
382
383 // add the value in the 2nd column
384 switch (state.getType()) {
385 case INTEGER:
386 value = String.valueOf(state.unboxInt());
387 item.setText(TYPE_COL, Messages.TypeInteger);
388 break;
389 case LONG:
390 value = String.valueOf(state.unboxLong());
391 item.setText(TYPE_COL, Messages.TypeLong);
392 break;
393 case DOUBLE:
394 value = String.valueOf(state.unboxDouble());
395 item.setText(TYPE_COL, Messages.TypeDouble);
396 break;
397 case STRING:
398 value = state.unboxStr();
399 item.setText(TYPE_COL, Messages.TypeString);
400 break;
401 case NULL:
402 default:
403 value = emptyString ;
404 item.setText(TYPE_COL, emptyString);
405 break;
406 }
407
408 TmfTimestamp startTime = new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE);
409 item.setText(START_TIME_COL, startTime.toString());
410
411 TmfTimestamp endTime = new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE);
412 item.setText(END_TIME_COL, endTime.toString());
413
414 item.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)) ;
415
416 if (!filterStatus && (!value.equals(item.getText(VALUE_COL)) || fCurrentTimestamp == startTime.getValue())) {
417 item.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_YELLOW));
418 }
419
420 item.setText(VALUE_COL, value) ;
421
422 } catch (StateValueTypeException e) {
423 /* Should not happen, we're case-switching on the specific types */
424 throw new RuntimeException();
425 }
426 }
427
428 /**
429 * Same concept as {@link updateChildren}, but instead of printing actual
430 * values coming from the state system, we print "Out of range" into all
431 * values. This is to indicate that this specific state system is not
432 * currently defined at the selected timestamp.
433 *
434 * Guess by which thread this should be called? Hint: starts with a U, ends
435 * with an I.
436 */
437 private void markOutOfRange(TreeItem root) {
438 root.setText(VALUE_COL, Messages.OutOfRangeMsg);
439 root.setText(TYPE_COL, emptyString);
440 root.setText(START_TIME_COL, emptyString);
441 root.setText(END_TIME_COL, emptyString);
442 for (TreeItem item : root.getItems()) {
443 markOutOfRange(item);
444 }
445 }
446
447 /**
448 * Auto-pack all the columns in the display. Should only be called by the UI
449 * thread.
450 */
451 private void packColumns() {
452 //FIXME should add a bit of padding
453 for (TreeColumn column : fTree.getColumns()) {
454 column.pack();
455 }
456 }
457
458 @Override
459 public void setFocus() {
460 fTree.setFocus();
461 }
462
463 // ------------------------------------------------------------------------
464 // Signal handlers
465 // ------------------------------------------------------------------------
466 /**
467 * Handler for the trace opened signal.
468 * @param signal
469 * The incoming signal
470 * @since 2.0
471 */
472 @TmfSignalHandler
473 public void traceOpened(TmfTraceOpenedSignal signal) {
474 fTrace = signal.getTrace();
475 loadTrace();
476 }
477
478 /**
479 * Handler for the trace selected signal. This will make the view display
480 * the information for the newly-selected trace.
481 *
482 * @param signal
483 * The incoming signal
484 */
485 @TmfSignalHandler
486 public void traceSelected(TmfTraceSelectedSignal signal) {
487 ITmfTrace trace = signal.getTrace();
488 if (trace != fTrace) {
489 fTrace = trace;
490 loadTrace();
491 }
492 }
493
494 /**
495 * Handler for the trace closed signal. This will clear the view.
496 *
497 * @param signal
498 * the incoming signal
499 */
500 @TmfSignalHandler
501 public void traceClosed(TmfTraceClosedSignal signal) {
502 // delete the tree at the trace closed
503 if (signal.getTrace() == fTrace) {
504 fTrace = null;
505 fTree.setItemCount(0);
506 }
507 }
508
509 /**
510 * Handles the current time updated signal. This will update the view's
511 * values to the newly-selected timestamp.
512 *
513 * @param signal
514 * the signal to process
515 */
516 @TmfSignalHandler
517 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
518 Thread thread = new Thread("State system visualizer update") { //$NON-NLS-1$
519 @Override
520 public void run() {
521 ITmfTimestamp currentTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE);
522 fCurrentTimestamp = currentTime.getValue();
523
524 if (filterStatus) {
525 createTable();
526 } else {
527 updateTable();
528 }
529 }
530 };
531 thread.start();
532 }
533
534 private void loadTrace() {
535 Thread thread = new Thread("State system visualizer construction") { //$NON-NLS-1$
536 @Override
537 public void run() {
538 createTable();
539 }
540 };
541 thread.start();
542 }
543
544 /**
545 * Update the display to use the updated timestamp format
546 *
547 * @param signal the incoming signal
548 * @since 2.1
549 */
550 @TmfSignalHandler
551 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
552 Thread thread = new Thread("State system visualizer update") { //$NON-NLS-1$
553 @Override
554 public void run() {
555 updateTable();
556 }
557 };
558 thread.start();
559 }
560
561 /**
562 * Function for the delete TreeItem
563 */
564 private boolean filterChildren(TreeItem root) {
565 boolean valid = false ;
566 TmfTimestamp startTime = new TmfTimestamp(fCurrentTimestamp, ITmfTimestamp.NANOSECOND_SCALE);
567 valid = root.getText(START_TIME_COL).equals(startTime.toString());
568 root.setExpanded(true);
569
570 for (TreeItem item : root.getItems()) {
571 /* Update children recursively */
572 valid = filterChildren(item) || valid;
573 }
574
575 if (!valid) {
576 root.dispose();
577 }
578 return valid;
579 }
580
581 // ------------------------------------------------------------------------
582 // Part For Button Action
583 // ------------------------------------------------------------------------
584
585
586
587 private void fillToolBar() {
588 Action fFilterAction = new FilterAction();
589 fFilterAction.setImageDescriptor(ImageDescriptor.createFromImage(FILTER_IMAGE));
590 fFilterAction.setToolTipText(Messages.FilterButton) ;
591 fFilterAction.setChecked(false);
592
593 IActionBars bars = getViewSite().getActionBars();
594 IToolBarManager manager = bars.getToolBarManager();
595 manager.add(fFilterAction);
596 }
597
598 private class FilterAction extends Action {
599 @Override
600 public void run() {
601 filterStatus = !filterStatus;
602 if (!filterStatus) {
603 createTable();
604 }
605 }
606 }
607 }
This page took 0.053709 seconds and 5 git commands to generate.