Fix for empty views that extend TmfView after restart (Bug 409345)
[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 *******************************************************************************/
14
15 package org.eclipse.linuxtools.tmf.ui.views.statesystem;
16
17 import java.util.LinkedHashMap;
18 import java.util.List;
19 import java.util.Map;
20
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;
45
46 /**
47 * Displays the State System at a current time.
48 *
49 * @author Florian Wininger
50 * @author Alexandre Montplaisir
51 * @since 2.0
52 */
53 public class TmfStateSystemExplorer extends TmfView {
54
55 /** The Environment View's ID */
56 public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
57
58 private static final String emptyString = ""; //$NON-NLS-1$
59
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;
67
68 private ITmfTrace fTrace;
69 private Tree fTree;
70 private volatile long fCurrentTimestamp = -1L;
71
72 /**
73 * Default constructor
74 */
75 public TmfStateSystemExplorer() {
76 super(ID);
77 }
78
79 // ------------------------------------------------------------------------
80 // ViewPart
81 // ------------------------------------------------------------------------
82
83 @Override
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);
92
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);
99
100 fTree.setItemCount(0);
101
102 fTree.setHeaderVisible(true);
103 nameCol.pack();
104 valueCol.pack();
105
106 fTree.addListener(SWT.Expand, new Listener() {
107 @Override
108 public void handleEvent(Event e) {
109 TreeItem item = (TreeItem) e.item;
110 item.setExpanded(true);
111 updateTable();
112 }
113 });
114
115 ITmfTrace trace = getActiveTrace();
116 if (trace != null) {
117 traceSelected(new TmfTraceSelectedSignal(this, trace));
118 }
119 }
120
121 // ------------------------------------------------------------------------
122 // Operations
123 // ------------------------------------------------------------------------
124
125 /**
126 * Create the initial tree from a trace.
127 */
128 private synchronized void createTable() {
129 if (fTrace == null) {
130 return;
131 }
132
133 /* Clear the table, in case a trace was previously using it */
134 fTree.getDisplay().asyncExec(new Runnable() {
135 @Override
136 public void run() {
137 fTree.setItemCount(0);
138 }
139 });
140
141 for (final ITmfTrace currentTrace : TmfTraceManager.getTraceSet(fTrace)) {
142 /*
143 * We will first do all the queries for this trace, then update that
144 * sub-tree in the UI thread.
145 */
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();
152 ss.waitUntilBuilt();
153 long startTime = ss.getStartTime();
154 try {
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 */
161 return;
162 }
163 }
164
165 /* Update the table (in the UI thread) */
166 fTree.getDisplay().asyncExec(new Runnable() {
167 @Override
168 public void run() {
169 TreeItem traceRoot = new TreeItem(fTree, SWT.NONE);
170 traceRoot.setText(ATTRIBUTE_NAME_COL, currentTrace.getName());
171
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);
176
177 /* Root item of the current state system */
178 TreeItem item = new TreeItem(traceRoot, SWT.NONE);
179
180 /* Name of the SS goes in the first column */
181 item.setText(ATTRIBUTE_NAME_COL, ssName);
182
183 /*
184 * Calling with quark '-1' here to start with the root
185 * attribute, then it will be called recursively.
186 */
187 addChildren(ss, fullState, -1, item);
188 }
189
190 /* Expand the first-level tree items */
191 for (TreeItem item : fTree.getItems()) {
192 item.setExpanded(true);
193 }
194 packColumns();
195 }
196 });
197 }
198 }
199
200 /**
201 * Add children node to a newly-created tree. Should only be called by the
202 * UI thread.
203 */
204 private void addChildren(ITmfStateSystem ss,
205 List<ITmfStateInterval> fullState, int rootQuark, TreeItem root) {
206 try {
207 for (int quark : ss.getSubAttributes(rootQuark, false)) {
208 TreeItem subItem = new TreeItem(root, SWT.NONE);
209
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));
214
215 /* Populate the other columns */
216 ITmfStateInterval interval = fullState.get(quark);
217 populateColumns(subItem, interval);
218
219 /* Update this node's children recursively */
220 addChildren(ss, fullState, quark, subItem);
221 }
222
223 } catch (AttributeNotFoundException e) {
224 /* Should not happen, we're iterating on known attributes */
225 throw new RuntimeException();
226 }
227 }
228
229 /**
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.
232 */
233 private synchronized void updateTable() {
234 ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
235 long ts = fCurrentTimestamp;
236
237 /* For each trace... */
238 for (int traceNb = 0; traceNb < traces.length; traceNb++) {
239 Map<String, ITmfStateSystem> sss = traces[traceNb].getStateSystems();
240
241 /* For each state system associated with this trace... */
242 int ssNb = 0;
243 for (Map.Entry<String, ITmfStateSystem> entry : sss.entrySet()) {
244 /*
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.
247 */
248 final ITmfStateSystem ss = entry.getValue();
249 final int traceNb1 = traceNb;
250 final int ssNb1 = ssNb;
251 ts = (ts == -1 ? ss.getStartTime() : ts);
252 try {
253 final List<ITmfStateInterval> fullState = ss.queryFullState(ts);
254 fTree.getDisplay().asyncExec(new Runnable() {
255 @Override
256 public void run() {
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);
263 }
264 });
265
266 } catch (TimeRangeException e) {
267 /*
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.
271 */
272 fTree.getDisplay().asyncExec(new Runnable() {
273 @Override
274 public void run() {
275 TreeItem traceItem = fTree.getItem(traceNb1);
276 TreeItem item = traceItem.getItem(ssNb1);
277 markOutOfRange(item);
278 }
279 });
280 } catch (StateSystemDisposedException e) {
281 return;
282 }
283
284 ssNb++;
285 }
286 }
287 }
288
289 /**
290 * Update the values shown by a child row when doing an update. Should only
291 * be called by the UI thread.
292 */
293 private void updateChildren(ITmfStateSystem ss,
294 List<ITmfStateInterval> state, int root_quark, TreeItem root) {
295 try {
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);
300
301 /* Update children recursively */
302 updateChildren(ss, state, quark, item);
303 }
304
305 } catch (AttributeNotFoundException e) {
306 /* We're iterating on known attributes, should not happen */
307 throw new RuntimeException();
308 }
309 }
310
311 /**
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.
314 */
315 private static void populateColumns(TreeItem item, ITmfStateInterval interval) {
316 try {
317 ITmfStateValue state = interval.getStateValue();
318
319 // add the value in the 2nd column
320 switch (state.getType()) {
321 case INTEGER:
322 item.setText(VALUE_COL, String.valueOf(state.unboxInt()));
323 break;
324 case LONG:
325 item.setText(VALUE_COL, String.valueOf(state.unboxLong()));
326 break;
327 case STRING:
328 item.setText(VALUE_COL, state.unboxStr());
329 break;
330 case NULL:
331 default:
332 item.setText(VALUE_COL, emptyString);
333 break;
334 }
335
336 TmfTimestamp startTime = new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE);
337 item.setText(START_TIME_COL, startTime.toString());
338
339 TmfTimestamp endTime = new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE);
340 item.setText(END_TIME_COL, endTime.toString());
341
342 } catch (StateValueTypeException e) {
343 /* Should not happen, we're case-switching on the specific types */
344 throw new RuntimeException();
345 }
346 }
347
348 /**
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.
353 *
354 * Guess by which thread this should be called? Hint: starts with a U, ends
355 * with an I.
356 */
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);
363 }
364 }
365
366 /**
367 * Auto-pack all the columns in the display. Should only be called by the UI
368 * thread.
369 */
370 private void packColumns() {
371 //FIXME should add a bit of padding
372 for (TreeColumn column : fTree.getColumns()) {
373 column.pack();
374 }
375 }
376
377 @Override
378 public void setFocus() {
379 fTree.setFocus();
380 }
381
382 // ------------------------------------------------------------------------
383 // Signal handlers
384 // ------------------------------------------------------------------------
385 /**
386 * Handler for the trace opened signal.
387 * @param signal
388 * The incoming signal
389 * @since 2.0
390 */
391 @TmfSignalHandler
392 public void traceOpened(TmfTraceOpenedSignal signal) {
393 fTrace = signal.getTrace();
394 loadTrace();
395 }
396
397 /**
398 * Handler for the trace selected signal. This will make the view display
399 * the information for the newly-selected trace.
400 *
401 * @param signal
402 * The incoming signal
403 */
404 @TmfSignalHandler
405 public void traceSelected(TmfTraceSelectedSignal signal) {
406 ITmfTrace trace = signal.getTrace();
407 if (trace != fTrace) {
408 fTrace = trace;
409 loadTrace();
410 }
411 }
412
413 /**
414 * Handler for the trace closed signal. This will clear the view.
415 *
416 * @param signal
417 * the incoming signal
418 */
419 @TmfSignalHandler
420 public void traceClosed(TmfTraceClosedSignal signal) {
421 // delete the tree at the trace closed
422 if (signal.getTrace() == fTrace) {
423 fTrace = null;
424 fTree.setItemCount(0);
425 }
426 }
427
428 /**
429 * Handles the current time updated signal. This will update the view's
430 * values to the newly-selected timestamp.
431 *
432 * @param signal
433 * the signal to process
434 */
435 @TmfSignalHandler
436 public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
437 Thread thread = new Thread("State system visualizer update") { //$NON-NLS-1$
438 @Override
439 public void run() {
440 ITmfTimestamp currentTime = signal.getCurrentTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE);
441 fCurrentTimestamp = currentTime.getValue();
442 updateTable();
443 }
444 };
445 thread.start();
446 }
447
448 private void loadTrace() {
449 Thread thread = new Thread("State system visualizer construction") { //$NON-NLS-1$
450 @Override
451 public void run() {
452 createTable();
453 }
454 };
455 thread.start();
456 }
457
458 }
This page took 0.042724 seconds and 5 git commands to generate.