1 /*******************************************************************************
2 * Copyright (c) 2013 École Polytechnique de Montréal, Ericsson
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
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 *******************************************************************************/
16 package org
.eclipse
.linuxtools
.tmf
.ui
.views
.statesystem
;
19 import java
.util
.HashMap
;
20 import java
.util
.LinkedHashMap
;
21 import java
.util
.List
;
23 import java
.util
.Map
.Entry
;
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
;
62 * Displays the State System at a current time.
64 * @author Florian Wininger
65 * @author Alexandre Montplaisir
68 public class TmfStateSystemExplorer
extends TmfView
{
70 /** The Environment View's ID */
71 public static final String ID
= "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
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$
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;
87 private ITmfTrace fTrace
;
89 private volatile long fCurrentTimestamp
= -1L;
91 private boolean filterStatus
= false ;
96 public TmfStateSystemExplorer() {
100 // ------------------------------------------------------------------------
102 // ------------------------------------------------------------------------
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
);
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
);
123 fTree
.setItemCount(0);
125 fTree
.setHeaderVisible(true);
129 fTree
.addListener(SWT
.Expand
, new Listener() {
131 public void handleEvent(Event e
) {
132 TreeItem item
= (TreeItem
) e
.item
;
133 item
.setExpanded(true);
138 ITmfTrace trace
= getActiveTrace();
140 traceSelected(new TmfTraceSelectedSignal(this, trace
));
147 // ------------------------------------------------------------------------
149 // ------------------------------------------------------------------------
152 * Create the initial tree from a trace.
154 private synchronized void createTable() {
156 long ts
= fCurrentTimestamp
;
158 if (fTrace
== null) {
162 /* Clear the table, in case a trace was previously using it */
163 fTree
.getDisplay().asyncExec(new Runnable() {
166 fTree
.setItemCount(0);
170 for (final ITmfTrace currentTrace
: TmfTraceManager
.getTraceSet(fTrace
)) {
172 * We will first do all the queries for this trace, then update that
173 * sub-tree in the UI thread.
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()) {
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.
186 ITmfStateSystemAnalysisModule module
= entry
.getValue();
187 if (module
instanceof IAnalysisModule
) {
188 IAnalysisModule mod
= (IAnalysisModule
) module
;
190 mod
.waitForCompletion(new NullProgressMonitor());
192 for (Entry
<String
, ITmfStateSystem
> ssEntry
: module
.getStateSystems().entrySet()) {
193 String ssName
= ssEntry
.getKey();
194 ITmfStateSystem ss
= ssEntry
.getValue();
197 if (ts
== -1 || ts
< ss
.getStartTime() || ts
> ss
.getCurrentEndTime()) {
198 ts
= ss
.getStartTime();
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 */
213 /* Update the table (in the UI thread) */
214 fTree
.getDisplay().asyncExec(new Runnable() {
217 TreeItem traceRoot
= new TreeItem(fTree
, SWT
.NONE
);
218 traceRoot
.setText(ATTRIBUTE_NAME_COL
, currentTrace
.getName());
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
);
225 /* Root item of the current state system */
226 TreeItem item
= new TreeItem(traceRoot
, SWT
.NONE
);
228 /* Name of the SS goes in the first column */
229 item
.setText(ATTRIBUTE_NAME_COL
, ssName
);
232 * Calling with quark '-1' here to start with the root
233 * attribute, then it will be called recursively.
236 addChildren(ss
, fullState
, -1, item
);
240 /* Expand the first-level tree items */
241 for (TreeItem item
: fTree
.getItems()) {
242 item
.setExpanded(true);
247 filterChildren(traceRoot
);
255 * Add children node to a newly-created tree. Should only be called by the
258 private void addChildren(ITmfStateSystem ss
,
259 List
<ITmfStateInterval
> fullState
, int rootQuark
, TreeItem root
) {
261 for (int quark
: ss
.getSubAttributes(rootQuark
, false)) {
262 TreeItem subItem
= new TreeItem(root
, SWT
.NONE
);
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
));
269 /* Populate the other columns */
270 ITmfStateInterval interval
= fullState
.get(quark
);
271 populateColumns(subItem
, interval
);
273 /* Update this node's children recursively */
274 addChildren(ss
, fullState
, quark
, subItem
);
277 } catch (AttributeNotFoundException e
) {
278 /* Should not happen, we're iterating on known attributes */
279 throw new RuntimeException();
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.
287 private synchronized void updateTable() {
288 ITmfTrace
[] traces
= TmfTraceManager
.getTraceSet(fTrace
);
289 long ts
= fCurrentTimestamp
;
291 /* For each trace... */
292 for (int traceNb
= 0; traceNb
< traces
.length
; traceNb
++) {
293 Map
<String
, ITmfStateSystemAnalysisModule
> modules
= traces
[traceNb
].getAnalysisModules(ITmfStateSystemAnalysisModule
.class);
295 /* For each state system associated with this trace... */
297 for (Entry
<String
, ITmfStateSystemAnalysisModule
> module
: modules
.entrySet()) {
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.
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
;
308 ts
= (ts
== -1 ? ss
.getStartTime() : ts
);
310 final List
<ITmfStateInterval
> fullState
= ss
.queryFullState(ts
);
311 fTree
.getDisplay().asyncExec(new Runnable() {
315 * Get the tree item of the relevant state
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
);
326 } catch (TimeRangeException e
) {
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.
333 fTree
.getDisplay().asyncExec(new Runnable() {
336 TreeItem traceItem
= fTree
.getItem(traceNb1
);
337 TreeItem item
= traceItem
.getItem(ssNb1
);
338 markOutOfRange(item
);
341 } catch (StateSystemDisposedException e
) {
353 * Update the values shown by a child row when doing an update. Should only
354 * be called by the UI thread.
356 private void updateChildren(ITmfStateSystem ss
,
357 List
<ITmfStateInterval
> state
, int root_quark
, TreeItem root
) {
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
);
364 /* Update children recursively */
365 updateChildren(ss
, state
, quark
, item
);
368 } catch (AttributeNotFoundException e
) {
369 /* We're iterating on known attributes, should not happen */
370 throw new RuntimeException();
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.
378 private void populateColumns(TreeItem item
, ITmfStateInterval interval
) {
380 ITmfStateValue state
= interval
.getStateValue();
383 // add the value in the 2nd column
384 switch (state
.getType()) {
386 value
= String
.valueOf(state
.unboxInt());
387 item
.setText(TYPE_COL
, Messages
.TypeInteger
);
390 value
= String
.valueOf(state
.unboxLong());
391 item
.setText(TYPE_COL
, Messages
.TypeLong
);
394 value
= String
.valueOf(state
.unboxDouble());
395 item
.setText(TYPE_COL
, Messages
.TypeDouble
);
398 value
= state
.unboxStr();
399 item
.setText(TYPE_COL
, Messages
.TypeString
);
403 value
= emptyString
;
404 item
.setText(TYPE_COL
, emptyString
);
408 TmfTimestamp startTime
= new TmfTimestamp(interval
.getStartTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
409 item
.setText(START_TIME_COL
, startTime
.toString());
411 TmfTimestamp endTime
= new TmfTimestamp(interval
.getEndTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
412 item
.setText(END_TIME_COL
, endTime
.toString());
414 item
.setBackground(Display
.getDefault().getSystemColor(SWT
.COLOR_WIDGET_BACKGROUND
)) ;
416 if (!filterStatus
&& (!value
.equals(item
.getText(VALUE_COL
)) || fCurrentTimestamp
== startTime
.getValue())) {
417 item
.setBackground(Display
.getDefault().getSystemColor(SWT
.COLOR_YELLOW
));
420 item
.setText(VALUE_COL
, value
) ;
422 } catch (StateValueTypeException e
) {
423 /* Should not happen, we're case-switching on the specific types */
424 throw new RuntimeException();
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.
434 * Guess by which thread this should be called? Hint: starts with a U, ends
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
);
448 * Auto-pack all the columns in the display. Should only be called by the UI
451 private void packColumns() {
452 //FIXME should add a bit of padding
453 for (TreeColumn column
: fTree
.getColumns()) {
459 public void setFocus() {
463 // ------------------------------------------------------------------------
465 // ------------------------------------------------------------------------
467 * Handler for the trace opened signal.
469 * The incoming signal
473 public void traceOpened(TmfTraceOpenedSignal signal
) {
474 fTrace
= signal
.getTrace();
479 * Handler for the trace selected signal. This will make the view display
480 * the information for the newly-selected trace.
483 * The incoming signal
486 public void traceSelected(TmfTraceSelectedSignal signal
) {
487 ITmfTrace trace
= signal
.getTrace();
488 if (trace
!= fTrace
) {
495 * Handler for the trace closed signal. This will clear the view.
498 * the incoming signal
501 public void traceClosed(TmfTraceClosedSignal signal
) {
502 // delete the tree at the trace closed
503 if (signal
.getTrace() == fTrace
) {
505 fTree
.setItemCount(0);
510 * Handles the current time updated signal. This will update the view's
511 * values to the newly-selected timestamp.
514 * the signal to process
517 public void currentTimeUpdated(final TmfTimeSynchSignal signal
) {
518 Thread thread
= new Thread("State system visualizer update") { //$NON-NLS-1$
521 ITmfTimestamp currentTime
= signal
.getBeginTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
);
522 fCurrentTimestamp
= currentTime
.getValue();
534 private void loadTrace() {
535 Thread thread
= new Thread("State system visualizer construction") { //$NON-NLS-1$
545 * Update the display to use the updated timestamp format
547 * @param signal the incoming signal
551 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal
) {
552 Thread thread
= new Thread("State system visualizer update") { //$NON-NLS-1$
562 * Function for the delete TreeItem
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);
570 for (TreeItem item
: root
.getItems()) {
571 /* Update children recursively */
572 valid
= filterChildren(item
) || valid
;
581 // ------------------------------------------------------------------------
582 // Part For Button Action
583 // ------------------------------------------------------------------------
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);
593 IActionBars bars
= getViewSite().getActionBars();
594 IToolBarManager manager
= bars
.getToolBarManager();
595 manager
.add(fFilterAction
);
598 private class FilterAction
extends Action
{
601 filterStatus
= !filterStatus
;