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 *******************************************************************************/
15 package org
.eclipse
.linuxtools
.tmf
.ui
.views
.statesystem
;
17 import java
.util
.LinkedHashMap
;
18 import java
.util
.List
;
21 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalHandler
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTimeSynchSignal
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTraceClosedSignal
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystem
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.statevalue
.ITmfStateValue
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.timestamp
.ITmfTimestamp
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.timestamp
.TmfTimestamp
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.TmfTraceManager
;
37 import org
.eclipse
.linuxtools
.tmf
.ui
.views
.TmfView
;
38 import org
.eclipse
.swt
.SWT
;
39 import org
.eclipse
.swt
.widgets
.Composite
;
40 import org
.eclipse
.swt
.widgets
.Event
;
41 import org
.eclipse
.swt
.widgets
.Listener
;
42 import org
.eclipse
.swt
.widgets
.Tree
;
43 import org
.eclipse
.swt
.widgets
.TreeColumn
;
44 import org
.eclipse
.swt
.widgets
.TreeItem
;
47 * Displays the State System at a current time.
49 * @author Florian Wininger
50 * @author Alexandre Montplaisir
53 public class TmfStateSystemExplorer
extends TmfView
{
55 /** The Environment View's ID */
56 public static final String ID
= "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
58 private static final String emptyString
= ""; //$NON-NLS-1$
60 /* Order of columns */
61 private static final int ATTRIBUTE_NAME_COL
= 0;
62 private static final int QUARK_COL
= 1;
63 private static final int VALUE_COL
= 2;
64 private static final int START_TIME_COL
= 3;
65 private static final int END_TIME_COL
= 4;
66 private static final int ATTRIBUTE_FULLPATH_COL
= 5;
68 private ITmfTrace fTrace
;
70 private volatile long fCurrentTimestamp
= -1L;
75 public TmfStateSystemExplorer() {
79 // ------------------------------------------------------------------------
81 // ------------------------------------------------------------------------
84 public void createPartControl(Composite parent
) {
85 fTree
= new Tree(parent
, SWT
.NONE
);
86 TreeColumn nameCol
= new TreeColumn(fTree
, SWT
.NONE
, ATTRIBUTE_NAME_COL
);
87 TreeColumn quarkCol
= new TreeColumn(fTree
, SWT
.NONE
, QUARK_COL
);
88 TreeColumn valueCol
= new TreeColumn(fTree
, SWT
.NONE
, VALUE_COL
);
89 TreeColumn startCol
= new TreeColumn(fTree
, SWT
.NONE
, START_TIME_COL
);
90 TreeColumn endCol
= new TreeColumn(fTree
, SWT
.NONE
, END_TIME_COL
);
91 TreeColumn pathCol
= new TreeColumn(fTree
, SWT
.NONE
, ATTRIBUTE_FULLPATH_COL
);
93 nameCol
.setText(Messages
.TreeNodeColumnLabel
);
94 quarkCol
.setText(Messages
.QuarkColumnLabel
);
95 valueCol
.setText(Messages
.ValueColumnLabel
);
96 startCol
.setText(Messages
.StartTimeColumLabel
);
97 endCol
.setText(Messages
.EndTimeColumLabel
);
98 pathCol
.setText(Messages
.AttributePathColumnLabel
);
100 fTree
.setItemCount(0);
102 fTree
.setHeaderVisible(true);
106 fTree
.addListener(SWT
.Expand
, new Listener() {
108 public void handleEvent(Event e
) {
109 TreeItem item
= (TreeItem
) e
.item
;
110 item
.setExpanded(true);
115 ITmfTrace trace
= getActiveTrace();
117 traceSelected(new TmfTraceSelectedSignal(this, trace
));
121 // ------------------------------------------------------------------------
123 // ------------------------------------------------------------------------
126 * Create the initial tree from a trace.
128 private synchronized void createTable() {
129 if (fTrace
== null) {
133 /* Clear the table, in case a trace was previously using it */
134 fTree
.getDisplay().asyncExec(new Runnable() {
137 fTree
.setItemCount(0);
141 for (final ITmfTrace currentTrace
: TmfTraceManager
.getTraceSet(fTrace
)) {
143 * We will first do all the queries for this trace, then update that
144 * sub-tree in the UI thread.
146 final Map
<String
, ITmfStateSystem
> sss
= currentTrace
.getStateSystems();
147 final Map
<String
, List
<ITmfStateInterval
>> fullStates
=
148 new LinkedHashMap
<String
, List
<ITmfStateInterval
>>();
149 for (Map
.Entry
<String
, ITmfStateSystem
> entry
: sss
.entrySet()) {
150 String ssName
= entry
.getKey();
151 ITmfStateSystem ss
= entry
.getValue();
153 long startTime
= ss
.getStartTime();
155 fullStates
.put(ssName
, ss
.queryFullState(startTime
));
156 } catch (TimeRangeException e
) {
157 /* Should not happen since we're querying at start time */
158 throw new RuntimeException();
159 } catch (StateSystemDisposedException e
) {
160 /* Probably shutting down, cancel and return */
165 /* Update the table (in the UI thread) */
166 fTree
.getDisplay().asyncExec(new Runnable() {
169 TreeItem traceRoot
= new TreeItem(fTree
, SWT
.NONE
);
170 traceRoot
.setText(ATTRIBUTE_NAME_COL
, currentTrace
.getName());
172 for (Map
.Entry
<String
, ITmfStateSystem
> entry
: sss
.entrySet()) {
173 String ssName
= entry
.getKey();
174 ITmfStateSystem ss
= entry
.getValue();
175 List
<ITmfStateInterval
> fullState
= fullStates
.get(ssName
);
177 /* Root item of the current state system */
178 TreeItem item
= new TreeItem(traceRoot
, SWT
.NONE
);
180 /* Name of the SS goes in the first column */
181 item
.setText(ATTRIBUTE_NAME_COL
, ssName
);
184 * Calling with quark '-1' here to start with the root
185 * attribute, then it will be called recursively.
187 addChildren(ss
, fullState
, -1, item
);
190 /* Expand the first-level tree items */
191 for (TreeItem item
: fTree
.getItems()) {
192 item
.setExpanded(true);
201 * Add children node to a newly-created tree. Should only be called by the
204 private void addChildren(ITmfStateSystem ss
,
205 List
<ITmfStateInterval
> fullState
, int rootQuark
, TreeItem root
) {
207 for (int quark
: ss
.getSubAttributes(rootQuark
, false)) {
208 TreeItem subItem
= new TreeItem(root
, SWT
.NONE
);
210 /* Write the info we already know */
211 subItem
.setText(ATTRIBUTE_NAME_COL
, ss
.getAttributeName(quark
));
212 subItem
.setText(QUARK_COL
, String
.valueOf(quark
));
213 subItem
.setText(ATTRIBUTE_FULLPATH_COL
, ss
.getFullAttributePath(quark
));
215 /* Populate the other columns */
216 ITmfStateInterval interval
= fullState
.get(quark
);
217 populateColumns(subItem
, interval
);
219 /* Update this node's children recursively */
220 addChildren(ss
, fullState
, quark
, subItem
);
223 } catch (AttributeNotFoundException e
) {
224 /* Should not happen, we're iterating on known attributes */
225 throw new RuntimeException();
230 * Update the tree, which means keep the tree of attributes in the first
231 * column as-is, but update the values to the ones at a new timestamp.
233 private synchronized void updateTable() {
234 ITmfTrace
[] traces
= TmfTraceManager
.getTraceSet(fTrace
);
235 long ts
= fCurrentTimestamp
;
237 /* For each trace... */
238 for (int traceNb
= 0; traceNb
< traces
.length
; traceNb
++) {
239 Map
<String
, ITmfStateSystem
> sss
= traces
[traceNb
].getStateSystems();
241 /* For each state system associated with this trace... */
243 for (Map
.Entry
<String
, ITmfStateSystem
> entry
: sss
.entrySet()) {
245 * Even though we only use the value, it just feels safer to
246 * iterate the same way as before to keep the order the same.
248 final ITmfStateSystem ss
= entry
.getValue();
249 final int traceNb1
= traceNb
;
250 final int ssNb1
= ssNb
;
251 ts
= (ts
== -1 ? ss
.getStartTime() : ts
);
253 final List
<ITmfStateInterval
> fullState
= ss
.queryFullState(ts
);
254 fTree
.getDisplay().asyncExec(new Runnable() {
257 /* Get the tree item of the relevant state system */
258 TreeItem traceItem
= fTree
.getItem(traceNb1
);
259 TreeItem item
= traceItem
.getItem(ssNb1
);
260 /* Update it, then its children, recursively */
261 item
.setText(VALUE_COL
, emptyString
);
262 updateChildren(ss
, fullState
, -1, item
);
266 } catch (TimeRangeException e
) {
268 * This can happen in an experiment, if the user selects a
269 * range valid in the experiment, but this specific does not
270 * exist. Print "out-of-range" into all the values.
272 fTree
.getDisplay().asyncExec(new Runnable() {
275 TreeItem traceItem
= fTree
.getItem(traceNb1
);
276 TreeItem item
= traceItem
.getItem(ssNb1
);
277 markOutOfRange(item
);
280 } catch (StateSystemDisposedException e
) {
290 * Update the values shown by a child row when doing an update. Should only
291 * be called by the UI thread.
293 private void updateChildren(ITmfStateSystem ss
,
294 List
<ITmfStateInterval
> state
, int root_quark
, TreeItem root
) {
296 for (TreeItem item
: root
.getItems()) {
297 int quark
= ss
.getQuarkRelative(root_quark
, item
.getText(0));
298 ITmfStateInterval interval
= state
.get(quark
);
299 populateColumns(item
, interval
);
301 /* Update children recursively */
302 updateChildren(ss
, state
, quark
, item
);
305 } catch (AttributeNotFoundException e
) {
306 /* We're iterating on known attributes, should not happen */
307 throw new RuntimeException();
312 * Populate an 'item' (a row in the tree) with the information found in the
313 * interval. This method should only be called by the UI thread.
315 private static void populateColumns(TreeItem item
, ITmfStateInterval interval
) {
317 ITmfStateValue state
= interval
.getStateValue();
319 // add the value in the 2nd column
320 switch (state
.getType()) {
322 item
.setText(VALUE_COL
, String
.valueOf(state
.unboxInt()));
325 item
.setText(VALUE_COL
, String
.valueOf(state
.unboxLong()));
328 item
.setText(VALUE_COL
, state
.unboxStr());
332 item
.setText(VALUE_COL
, emptyString
);
336 TmfTimestamp startTime
= new TmfTimestamp(interval
.getStartTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
337 item
.setText(START_TIME_COL
, startTime
.toString());
339 TmfTimestamp endTime
= new TmfTimestamp(interval
.getEndTime(), ITmfTimestamp
.NANOSECOND_SCALE
);
340 item
.setText(END_TIME_COL
, endTime
.toString());
342 } catch (StateValueTypeException e
) {
343 /* Should not happen, we're case-switching on the specific types */
344 throw new RuntimeException();
349 * Same concept as {@link updateChildren}, but instead of printing actual
350 * values coming from the state system, we print "Out of range" into all
351 * values. This is to indicate that this specific state system is not
352 * currently defined at the selected timestamp.
354 * Guess by which thread this should be called? Hint: starts with a U, ends
357 private void markOutOfRange(TreeItem root
) {
358 root
.setText(VALUE_COL
, Messages
.OutOfRangeMsg
);
359 root
.setText(START_TIME_COL
, emptyString
);
360 root
.setText(END_TIME_COL
, emptyString
);
361 for (TreeItem item
: root
.getItems()) {
362 markOutOfRange(item
);
367 * Auto-pack all the columns in the display. Should only be called by the UI
370 private void packColumns() {
371 //FIXME should add a bit of padding
372 for (TreeColumn column
: fTree
.getColumns()) {
378 public void setFocus() {
382 // ------------------------------------------------------------------------
384 // ------------------------------------------------------------------------
386 * Handler for the trace opened signal.
388 * The incoming signal
392 public void traceOpened(TmfTraceOpenedSignal signal
) {
393 fTrace
= signal
.getTrace();
398 * Handler for the trace selected signal. This will make the view display
399 * the information for the newly-selected trace.
402 * The incoming signal
405 public void traceSelected(TmfTraceSelectedSignal signal
) {
406 ITmfTrace trace
= signal
.getTrace();
407 if (trace
!= fTrace
) {
414 * Handler for the trace closed signal. This will clear the view.
417 * the incoming signal
420 public void traceClosed(TmfTraceClosedSignal signal
) {
421 // delete the tree at the trace closed
422 if (signal
.getTrace() == fTrace
) {
424 fTree
.setItemCount(0);
429 * Handles the current time updated signal. This will update the view's
430 * values to the newly-selected timestamp.
433 * the signal to process
436 public void currentTimeUpdated(final TmfTimeSynchSignal signal
) {
437 Thread thread
= new Thread("State system visualizer update") { //$NON-NLS-1$
440 ITmfTimestamp currentTime
= signal
.getCurrentTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
);
441 fCurrentTimestamp
= currentTime
.getValue();
448 private void loadTrace() {
449 Thread thread
= new Thread("State system visualizer construction") { //$NON-NLS-1$