tmf: Add a state system explorer view
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / statesystem / TmfStateSystemExplorer.java
CommitLineData
7692e512
AM
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 *******************************************************************************/
13
14package org.eclipse.linuxtools.tmf.ui.views.statesystem;
15
16import java.util.LinkedHashMap;
17import java.util.List;
18import java.util.Map;
19
20import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
21import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
22import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
23import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
24import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
25import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
26import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
27import org.eclipse.linuxtools.tmf.core.signal.TmfTraceClosedSignal;
28import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal;
29import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
30import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
31import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
32import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
33import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
34import org.eclipse.linuxtools.tmf.ui.views.TmfView;
35import org.eclipse.swt.SWT;
36import org.eclipse.swt.widgets.Composite;
37import org.eclipse.swt.widgets.Event;
38import org.eclipse.swt.widgets.Listener;
39import org.eclipse.swt.widgets.Tree;
40import org.eclipse.swt.widgets.TreeColumn;
41import org.eclipse.swt.widgets.TreeItem;
42
43/**
44 * Displays the State System at a current time.
45 *
46 * @author Florian Wininger
47 * @author Alexandre Montplaisir
48 * @since 2.0
49 */
50public class TmfStateSystemExplorer extends TmfView {
51
52 /** The Environment View's ID */
53 public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
54
55 private static final String emptyString = ""; //$NON-NLS-1$
56
57 /* Order of columns */
58 private static final int ATTRIBUTE_NAME_COL = 0;
59 private static final int QUARK_COL = 1;
60 private static final int VALUE_COL = 2;
61 private static final int START_TIME_COL = 3;
62 private static final int END_TIME_COL = 4;
63 private static final int ATTRIBUTE_FULLPATH_COL = 5;
64
65 private ITmfTrace fTrace;
66 private Tree fTree;
67 private volatile long fCurrentTimestamp = -1L;
68
69 /**
70 * Default constructor
71 */
72 public TmfStateSystemExplorer() {
73 super(ID);
74 }
75
76 // ------------------------------------------------------------------------
77 // ViewPart
78 // ------------------------------------------------------------------------
79
80 @Override
81 public void createPartControl(Composite parent) {
82 fTree = new Tree(parent, SWT.NONE);
83 TreeColumn nameCol = new TreeColumn(fTree, SWT.NONE, ATTRIBUTE_NAME_COL);
84 TreeColumn quarkCol = new TreeColumn(fTree, SWT.NONE, QUARK_COL);
85 TreeColumn valueCol = new TreeColumn(fTree, SWT.NONE, VALUE_COL);
86 TreeColumn startCol = new TreeColumn(fTree, SWT.NONE, START_TIME_COL);
87 TreeColumn endCol = new TreeColumn(fTree, SWT.NONE, END_TIME_COL);
88 TreeColumn pathCol = new TreeColumn(fTree, SWT.NONE, ATTRIBUTE_FULLPATH_COL);
89
90 nameCol.setText(Messages.TreeNodeColumnLabel);
91 quarkCol.setText(Messages.QuarkColumnLabel);
92 valueCol.setText(Messages.ValueColumnLabel);
93 startCol.setText(Messages.StartTimeColumLabel);
94 endCol.setText(Messages.EndTimeColumLabel);
95 pathCol.setText(Messages.AttributePathColumnLabel);
96
97 fTree.setItemCount(0);
98
99 fTree.setHeaderVisible(true);
100 nameCol.pack();
101 valueCol.pack();
102
103 fTree.addListener(SWT.Expand, new Listener() {
104 @Override
105 public void handleEvent(Event e) {
106 TreeItem item = (TreeItem) e.item;
107 item.setExpanded(true);
108 updateTable();
109 }
110 });
111
112 ITmfTrace trace = getActiveTrace();
113 if (trace != null) {
114 traceSelected(new TmfTraceSelectedSignal(this, trace));
115 }
116 }
117
118 // ------------------------------------------------------------------------
119 // Operations
120 // ------------------------------------------------------------------------
121
122 /**
123 * Create the initial tree from a trace.
124 */
125 private synchronized void createTable() {
126 if (fTrace == null) {
127 return;
128 }
129
130 /* Clear the table, in case a trace was previously using it */
131 fTree.getDisplay().asyncExec(new Runnable() {
132 @Override
133 public void run() {
134 fTree.setItemCount(0);
135 }
136 });
137
138 for (final ITmfTrace currentTrace : fTrace.getTraces()) {
139 /*
140 * We will first do all the queries for this trace, then update that
141 * sub-tree in the UI thread.
142 */
143 final Map<String, ITmfStateSystem> sss = currentTrace.getStateSystems();
144 final Map<String, List<ITmfStateInterval>> fullStates =
145 new LinkedHashMap<String, List<ITmfStateInterval>>();
146 for (Map.Entry<String, ITmfStateSystem> entry : sss.entrySet()) {
147 String ssName = entry.getKey();
148 ITmfStateSystem ss = entry.getValue();
149 ss.waitUntilBuilt();
150 long startTime = ss.getStartTime();
151 try {
152 fullStates.put(ssName, ss.queryFullState(startTime));
153 } catch (TimeRangeException e) {
154 /* Should not happen since we're querying at start time */
155 throw new RuntimeException();
156 } catch (StateSystemDisposedException e) {
157 /* Probably shutting down, cancel and return */
158 return;
159 }
160 }
161
162 /* Update the table (in the UI thread) */
163 fTree.getDisplay().asyncExec(new Runnable() {
164 @Override
165 public void run() {
166 TreeItem traceRoot = new TreeItem(fTree, SWT.NONE);
167 traceRoot.setText(ATTRIBUTE_NAME_COL, currentTrace.getName());
168
169 for (Map.Entry<String, ITmfStateSystem> entry : sss.entrySet()) {
170 String ssName = entry.getKey();
171 ITmfStateSystem ss = entry.getValue();
172 List<ITmfStateInterval> fullState = fullStates.get(ssName);
173
174 /* Root item of the current state system */
175 TreeItem item = new TreeItem(traceRoot, SWT.NONE);
176
177 /* Name of the SS goes in the first column */
178 item.setText(ATTRIBUTE_NAME_COL, ssName);
179
180 /*
181 * Calling with quark '-1' here to start with the root
182 * attribute, then it will be called recursively.
183 */
184 addChildren(ss, fullState, -1, item);
185 }
186
187 /* Expand the first-level tree items */
188 for (TreeItem item : fTree.getItems()) {
189 item.setExpanded(true);
190 }
191 packColumns();
192 }
193 });
194 }
195 }
196
197 /**
198 * Add children node to a newly-created tree. Should only be called by the
199 * UI thread.
200 */
201 private void addChildren(ITmfStateSystem ss,
202 List<ITmfStateInterval> fullState, int rootQuark, TreeItem root) {
203 try {
204 for (int quark : ss.getSubAttributes(rootQuark, false)) {
205 TreeItem subItem = new TreeItem(root, SWT.NONE);
206
207 /* Write the info we already know */
208 subItem.setText(ATTRIBUTE_NAME_COL, ss.getAttributeName(quark));
209 subItem.setText(QUARK_COL, String.valueOf(quark));
210 subItem.setText(ATTRIBUTE_FULLPATH_COL, ss.getFullAttributePath(quark));
211
212 /* Populate the other columns */
213 ITmfStateInterval interval = fullState.get(quark);
214 populateColumns(subItem, interval);
215
216 /* Update this node's children recursively */
217 addChildren(ss, fullState, quark, subItem);
218 }
219
220 } catch (AttributeNotFoundException e) {
221 /* Should not happen, we're iterating on known attributes */
222 throw new RuntimeException();
223 }
224 }
225
226 /**
227 * Update the tree, which means keep the tree of attributes in the first
228 * column as-is, but update the values to the ones at a new timestamp.
229 */
230 private synchronized void updateTable() {
231 ITmfTrace[] traces = fTrace.getTraces();
232 long ts = fCurrentTimestamp;
233
234 /* For each trace... */
235 for (int traceNb = 0; traceNb < traces.length; traceNb++) {
236 Map<String, ITmfStateSystem> sss = traces[traceNb].getStateSystems();
237
238 /* For each state system associated with this trace... */
239 int ssNb = 0;
240 for (Map.Entry<String, ITmfStateSystem> entry : sss.entrySet()) {
241 /*
242 * Even though we only use the value, it just feels safer to
243 * iterate the same way as before to keep the order the same.
244 */
245 final ITmfStateSystem ss = entry.getValue();
246 final int traceNb1 = traceNb;
247 final int ssNb1 = ssNb;
248 ts = (ts == -1 ? ss.getStartTime() : ts);
249 try {
250 final List<ITmfStateInterval> fullState = ss.queryFullState(ts);
251 fTree.getDisplay().asyncExec(new Runnable() {
252 @Override
253 public void run() {
254 /* Get the tree item of the relevant state system */
255 TreeItem traceItem = fTree.getItem(traceNb1);
256 TreeItem item = traceItem.getItem(ssNb1);
257 /* Update it, then its children, recursively */
258 item.setText(VALUE_COL, emptyString);
259 updateChildren(ss, fullState, -1, item);
260 }
261 });
262
263 } catch (TimeRangeException e) {
264 /*
265 * This can happen in an experiment, if the user selects a
266 * range valid in the experiment, but this specific does not
267 * exist. Print "out-of-range" into all the values.
268 */
269 fTree.getDisplay().asyncExec(new Runnable() {
270 @Override
271 public void run() {
272 TreeItem traceItem = fTree.getItem(traceNb1);
273 TreeItem item = traceItem.getItem(ssNb1);
274 markOutOfRange(item);
275 }
276 });
277 } catch (StateSystemDisposedException e) {
278 return;
279 }
280
281 ssNb++;
282 }
283 }
284 }
285
286 /**
287 * Update the values shown by a child row when doing an update. Should only
288 * be called by the UI thread.
289 */
290 private void updateChildren(ITmfStateSystem ss,
291 List<ITmfStateInterval> state, int root_quark, TreeItem root) {
292 try {
293 for (TreeItem item : root.getItems()) {
294 int quark = ss.getQuarkRelative(root_quark, item.getText(0));
295 ITmfStateInterval interval = state.get(quark);
296 populateColumns(item, interval);
297
298 /* Update children recursively */
299 updateChildren(ss, state, quark, item);
300 }
301
302 } catch (AttributeNotFoundException e) {
303 /* We're iterating on known attributes, should not happen */
304 throw new RuntimeException();
305 }
306 }
307
308 /**
309 * Populate an 'item' (a row in the tree) with the information found in the
310 * interval. This method should only be called by the UI thread.
311 */
312 private static void populateColumns(TreeItem item, ITmfStateInterval interval) {
313 try {
314 ITmfStateValue state = interval.getStateValue();
315
316 // add the value in the 2nd column
317 switch (state.getType()) {
318 case INTEGER:
319 item.setText(VALUE_COL, String.valueOf(state.unboxInt()));
320 break;
321 case LONG:
322 item.setText(VALUE_COL, String.valueOf(state.unboxLong()));
323 break;
324 case STRING:
325 item.setText(VALUE_COL, state.unboxStr());
326 break;
327 case NULL:
328 default:
329 item.setText(VALUE_COL, emptyString);
330 break;
331 }
332
333 TmfTimestamp startTime = new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE);
334 item.setText(START_TIME_COL, startTime.toString());
335
336 TmfTimestamp endTime = new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE);
337 item.setText(END_TIME_COL, endTime.toString());
338
339 } catch (StateValueTypeException e) {
340 /* Should not happen, we're case-switching on the specific types */
341 throw new RuntimeException();
342 }
343 }
344
345 /**
346 * Same concept as {@link updateChildren}, but instead of printing actual
347 * values coming from the state system, we print "Out of range" into all
348 * values. This is to indicate that this specific state system is not
349 * currently defined at the selected timestamp.
350 *
351 * Guess by which thread this should be called? Hint: starts with a U, ends
352 * with an I.
353 */
354 private void markOutOfRange(TreeItem root) {
355 root.setText(VALUE_COL, Messages.OutOfRangeMsg);
356 root.setText(START_TIME_COL, emptyString);
357 root.setText(END_TIME_COL, emptyString);
358 for (TreeItem item : root.getItems()) {
359 markOutOfRange(item);
360 }
361 }
362
363 /**
364 * Auto-pack all the columns in the display. Should only be called by the UI
365 * thread.
366 */
367 private void packColumns() {
368 //FIXME should add a bit of padding
369 for (TreeColumn column : fTree.getColumns()) {
370 column.pack();
371 }
372 }
373
374 @Override
375 public void setFocus() {
376 fTree.setFocus();
377 }
378
379 // ------------------------------------------------------------------------
380 // Signal handlers
381 // ------------------------------------------------------------------------
382
383 /**
384 * Handler for the trace selected signal. This will make the view display
385 * the information for the newly-selected trace.
386 *
387 * @param signal
388 * The incoming signal
389 */
390 @TmfSignalHandler
391 public void traceSelected(TmfTraceSelectedSignal signal) {
392 ITmfTrace trace = signal.getTrace();
393 if (trace != fTrace) {
394 fTrace = trace;
395 Thread thread = new Thread("State system visualizer construction") { //$NON-NLS-1$
396 @Override
397 public void run() {
398 createTable();
399 }
400 };
401 thread.start();
402 }
403 }
404
405 /**
406 * Handler for the trace closed signal. This will clear the view.
407 *
408 * @param signal
409 * the incoming signal
410 */
411 @TmfSignalHandler
412 public void traceClosed(TmfTraceClosedSignal signal) {
413 // delete the tree at the trace closed
414 if (signal.getTrace() == fTrace) {
415 fTrace = null;
416 fTree.setItemCount(0);
417 }
418 }
419
420 /**
421 * Handles the current time updated signal. This will update the view's
422 * values to the newly-selected timestamp.
423 *
424 * @param signal
425 * the signal to process
426 */
427 @TmfSignalHandler
428 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
429 Thread thread = new Thread("State system visualizer update") { //$NON-NLS-1$
430 @Override
431 public void run() {
432 ITmfTimestamp currentTime = signal.getCurrentTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE);
433 fCurrentTimestamp = currentTime.getValue();
434 updateTable();
435 }
436 };
437 thread.start();
438 }
439}
This page took 0.042642 seconds and 5 git commands to generate.