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