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
.LinkedHashMap
;
20 import java
.util
.List
;
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
;
57 * Displays the State System at a current time.
59 * @author Florian Wininger
60 * @author Alexandre Montplaisir
63 public class TmfStateSystemExplorer
extends TmfView
{
65 /** The Environment View's ID */
66 public static final String ID
= "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
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$
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;
82 private ITmfTrace fTrace
;
84 private volatile long fCurrentTimestamp
= -1L;
86 private boolean filterStatus
= false ;
91 public TmfStateSystemExplorer() {
95 // ------------------------------------------------------------------------
97 // ------------------------------------------------------------------------
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
);
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
);
118 fTree
.setItemCount(0);
120 fTree
.setHeaderVisible(true);
124 fTree
.addListener(SWT
.Expand
, new Listener() {
126 public void handleEvent(Event e
) {
127 TreeItem item
= (TreeItem
) e
.item
;
128 item
.setExpanded(true);
133 ITmfTrace trace
= getActiveTrace();
135 traceSelected(new TmfTraceSelectedSignal(this, trace
));
142 // ------------------------------------------------------------------------
144 // ------------------------------------------------------------------------
147 * Create the initial tree from a trace.
149 private synchronized void createTable() {
151 long ts
= fCurrentTimestamp
;
153 if (fTrace
== null) {
157 /* Clear the table, in case a trace was previously using it */
158 fTree
.getDisplay().asyncExec(new Runnable() {
161 fTree
.setItemCount(0);
165 for (final ITmfTrace currentTrace
: TmfTraceManager
.getTraceSet(fTrace
)) {
167 * We will first do all the queries for this trace, then update that
168 * sub-tree in the UI thread.
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();
177 if (ts
== -1 || ts
< ss
.getStartTime() || ts
> ss
.getCurrentEndTime()) {
178 ts
= ss
.getStartTime();
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 */
191 /* Update the table (in the UI thread) */
192 fTree
.getDisplay().asyncExec(new Runnable() {
195 TreeItem traceRoot
= new TreeItem(fTree
, SWT
.NONE
);
196 traceRoot
.setText(ATTRIBUTE_NAME_COL
, currentTrace
.getName());
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
);
203 /* Root item of the current state system */
204 TreeItem item
= new TreeItem(traceRoot
, SWT
.NONE
);
206 /* Name of the SS goes in the first column */
207 item
.setText(ATTRIBUTE_NAME_COL
, ssName
);
210 * Calling with quark '-1' here to start with the root
211 * attribute, then it will be called recursively.
213 addChildren(ss
, fullState
, -1, item
);
216 /* Expand the first-level tree items */
217 for (TreeItem item
: fTree
.getItems()) {
218 item
.setExpanded(true);
223 filterChildren(traceRoot
);
231 * Add children node to a newly-created tree. Should only be called by the
234 private void addChildren(ITmfStateSystem ss
,
235 List
<ITmfStateInterval
> fullState
, int rootQuark
, TreeItem root
) {
237 for (int quark
: ss
.getSubAttributes(rootQuark
, false)) {
238 TreeItem subItem
= new TreeItem(root
, SWT
.NONE
);
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
));
245 /* Populate the other columns */
246 ITmfStateInterval interval
= fullState
.get(quark
);
247 populateColumns(subItem
, interval
);
249 /* Update this node's children recursively */
250 addChildren(ss
, fullState
, quark
, subItem
);
253 } catch (AttributeNotFoundException e
) {
254 /* Should not happen, we're iterating on known attributes */
255 throw new RuntimeException();
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.
263 private synchronized void updateTable() {
264 ITmfTrace
[] traces
= TmfTraceManager
.getTraceSet(fTrace
);
265 long ts
= fCurrentTimestamp
;
267 /* For each trace... */
268 for (int traceNb
= 0; traceNb
< traces
.length
; traceNb
++) {
269 Map
<String
, ITmfStateSystem
> sss
= traces
[traceNb
].getStateSystems();
271 /* For each state system associated with this trace... */
273 for (Map
.Entry
<String
, ITmfStateSystem
> entry
: sss
.entrySet()) {
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.
278 final ITmfStateSystem ss
= entry
.getValue();
279 final int traceNb1
= traceNb
;
280 final int ssNb1
= ssNb
;
281 ts
= (ts
== -1 ? ss
.getStartTime() : ts
);
283 final List
<ITmfStateInterval
> fullState
= ss
.queryFullState(ts
);
284 fTree
.getDisplay().asyncExec(new Runnable() {
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
);
296 } catch (TimeRangeException e
) {
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.
302 fTree
.getDisplay().asyncExec(new Runnable() {
305 TreeItem traceItem
= fTree
.getItem(traceNb1
);
306 TreeItem item
= traceItem
.getItem(ssNb1
);
307 markOutOfRange(item
);
310 } catch (StateSystemDisposedException e
) {
320 * Update the values shown by a child row when doing an update. Should only
321 * be called by the UI thread.
323 private void updateChildren(ITmfStateSystem ss
,
324 List
<ITmfStateInterval
> state
, int root_quark
, TreeItem root
) {
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
);
331 /* Update children recursively */
332 updateChildren(ss
, state
, quark
, item
);
335 } catch (AttributeNotFoundException e
) {
336 /* We're iterating on known attributes, should not happen */
337 throw new RuntimeException();
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.
345 private void populateColumns(TreeItem item
, ITmfStateInterval interval
) {
347 ITmfStateValue state
= interval
.getStateValue();
350 // add the value in the 2nd column
351 switch (state
.getType()) {
353 value
= String
.valueOf(state
.unboxInt());
354 item
.setText(TYPE_COL
, Messages
.TypeInteger
);
357 value
= String
.valueOf(state
.unboxLong());
358 item
.setText(TYPE_COL
, Messages
.TypeLong
);
361 value
= state
.unboxStr();
362 item
.setText(TYPE_COL
, Messages
.TypeString
);
366 value
= emptyString
;
367 item
.setText(TYPE_COL
, emptyString
);
371 TmfTimestamp startTime
= new TmfTimestamp(interval
.getStartTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
372 item
.setText(START_TIME_COL
, startTime
.toString());
374 TmfTimestamp endTime
= new TmfTimestamp(interval
.getEndTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
375 item
.setText(END_TIME_COL
, endTime
.toString());
377 item
.setBackground(Display
.getDefault().getSystemColor(SWT
.COLOR_WIDGET_BACKGROUND
)) ;
379 if (!filterStatus
&& (!value
.equals(item
.getText(VALUE_COL
)) || fCurrentTimestamp
== startTime
.getValue())) {
380 item
.setBackground(Display
.getDefault().getSystemColor(SWT
.COLOR_YELLOW
));
383 item
.setText(VALUE_COL
, value
) ;
385 } catch (StateValueTypeException e
) {
386 /* Should not happen, we're case-switching on the specific types */
387 throw new RuntimeException();
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.
397 * Guess by which thread this should be called? Hint: starts with a U, ends
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
);
411 * Auto-pack all the columns in the display. Should only be called by the UI
414 private void packColumns() {
415 //FIXME should add a bit of padding
416 for (TreeColumn column
: fTree
.getColumns()) {
422 public void setFocus() {
426 // ------------------------------------------------------------------------
428 // ------------------------------------------------------------------------
430 * Handler for the trace opened signal.
432 * The incoming signal
436 public void traceOpened(TmfTraceOpenedSignal signal
) {
437 fTrace
= signal
.getTrace();
442 * Handler for the trace selected signal. This will make the view display
443 * the information for the newly-selected trace.
446 * The incoming signal
449 public void traceSelected(TmfTraceSelectedSignal signal
) {
450 ITmfTrace trace
= signal
.getTrace();
451 if (trace
!= fTrace
) {
458 * Handler for the trace closed signal. This will clear the view.
461 * the incoming signal
464 public void traceClosed(TmfTraceClosedSignal signal
) {
465 // delete the tree at the trace closed
466 if (signal
.getTrace() == fTrace
) {
468 fTree
.setItemCount(0);
473 * Handles the current time updated signal. This will update the view's
474 * values to the newly-selected timestamp.
477 * the signal to process
480 public void currentTimeUpdated(final TmfTimeSynchSignal signal
) {
481 Thread thread
= new Thread("State system visualizer update") { //$NON-NLS-1$
484 ITmfTimestamp currentTime
= signal
.getBeginTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
);
485 fCurrentTimestamp
= currentTime
.getValue();
497 private void loadTrace() {
498 Thread thread
= new Thread("State system visualizer construction") { //$NON-NLS-1$
508 * Update the display to use the updated timestamp format
510 * @param signal the incoming signal
514 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal
) {
515 Thread thread
= new Thread("State system visualizer update") { //$NON-NLS-1$
525 * Function for the delete TreeItem
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);
533 for (TreeItem item
: root
.getItems()) {
534 /* Update children recursively */
535 valid
= filterChildren(item
) || valid
;
544 // ------------------------------------------------------------------------
545 // Part For Button Action
546 // ------------------------------------------------------------------------
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);
556 IActionBars bars
= getViewSite().getActionBars();
557 IToolBarManager manager
= bars
.getToolBarManager();
558 manager
.add(fFilterAction
);
561 private class FilterAction
extends Action
{
564 filterStatus
= !filterStatus
;