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