1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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 * Geneviève Bastien - Moved state system explorer to use the abstract tree viewer
15 * Patrick Tasse - Refactoring
16 *******************************************************************************/
18 package org
.eclipse
.tracecompass
.tmf
.ui
.views
.statesystem
;
20 import java
.util
.ArrayList
;
21 import java
.util
.List
;
23 import org
.eclipse
.jdt
.annotation
.NonNull
;
24 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
25 import org
.eclipse
.jface
.viewers
.Viewer
;
26 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
27 import org
.eclipse
.swt
.SWT
;
28 import org
.eclipse
.swt
.graphics
.Color
;
29 import org
.eclipse
.swt
.widgets
.Composite
;
30 import org
.eclipse
.swt
.widgets
.Display
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTimestampFormatUpdateSignal
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.ITmfAnalysisModuleWithStateSystems
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.TmfStateSystemAnalysisModule
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.ITmfTimestamp
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
45 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.AbstractTmfTreeViewer
;
46 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.ITmfTreeColumnDataProvider
;
47 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.ITmfTreeViewerEntry
;
48 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.TmfTreeColumnData
;
49 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.TmfTreeViewerEntry
;
52 * Displays the content of the state systems at the current time
54 * @author Florian Wininger
55 * @author Alexandre Montplaisir
56 * @author Geneviève Bastien
59 public class TmfStateSystemViewer
extends AbstractTmfTreeViewer
{
61 private static final String EMPTY_STRING
= ""; //$NON-NLS-1$
62 private static final int DEFAULT_AUTOEXPAND
= 2;
63 private boolean fFilterStatus
= false;
64 private long fSelection
= 0;
66 /* Order of columns */
67 private static final int ATTRIBUTE_NAME_COL
= 0;
68 private static final int QUARK_COL
= 1;
69 private static final int VALUE_COL
= 2;
70 private static final int TYPE_COL
= 3;
71 private static final int START_TIME_COL
= 4;
72 private static final int END_TIME_COL
= 5;
73 private static final int ATTRIBUTE_FULLPATH_COL
= 6;
76 * Base class to provide the labels for the tree viewer. Views extending
77 * this class typically need to override the getColumnText method if they
78 * have more than one column to display
80 protected static class StateSystemTreeLabelProvider
extends TreeLabelProvider
{
83 public String
getColumnText(Object element
, int columnIndex
) {
84 if (element
instanceof StateEntry
) {
85 StateEntry entry
= (StateEntry
) element
;
86 switch (columnIndex
) {
87 case ATTRIBUTE_NAME_COL
:
88 return entry
.getName();
90 return String
.valueOf(entry
.getQuark());
92 return entry
.getValue();
94 return entry
.getType();
96 return entry
.getStartTime();
98 return entry
.getEndTime();
99 case ATTRIBUTE_FULLPATH_COL
:
100 return entry
.getFullPath();
105 return super.getColumnText(element
, columnIndex
);
109 public Color
getBackground(Object element
, int columnIndex
) {
110 if (element
instanceof StateEntry
) {
111 if (((StateEntry
) element
).isModified()) {
112 return Display
.getCurrent().getSystemColor(SWT
.COLOR_YELLOW
);
115 return super.getBackground(element
, columnIndex
);
123 * The parent containing this viewer
125 public TmfStateSystemViewer(Composite parent
) {
126 super(parent
, false);
127 this.setLabelProvider(new StateSystemTreeLabelProvider());
128 getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND
);
132 protected ITmfTreeColumnDataProvider
getColumnDataProvider() {
133 return new ITmfTreeColumnDataProvider() {
136 public List
<TmfTreeColumnData
> getColumnData() {
137 List
<TmfTreeColumnData
> columns
= new ArrayList
<>();
138 TmfTreeColumnData column
= new TmfTreeColumnData(Messages
.TreeNodeColumnLabel
);
140 column
.setComparator(new ViewerComparator() {
142 public int compare(Viewer viewer
, Object e1
, Object e2
) {
143 TmfTreeViewerEntry n1
= (TmfTreeViewerEntry
) e1
;
144 TmfTreeViewerEntry n2
= (TmfTreeViewerEntry
) e2
;
146 return n1
.getName().compareTo(n2
.getName());
149 columns
.add(new TmfTreeColumnData(Messages
.QuarkColumnLabel
));
150 columns
.add(new TmfTreeColumnData(Messages
.ValueColumnLabel
));
151 columns
.add(new TmfTreeColumnData(Messages
.TypeColumnLabel
));
152 columns
.add(new TmfTreeColumnData(Messages
.StartTimeColumLabel
));
153 columns
.add(new TmfTreeColumnData(Messages
.EndTimeColumLabel
));
154 columns
.add(new TmfTreeColumnData(Messages
.AttributePathColumnLabel
));
161 // ------------------------------------------------------------------------
163 // ------------------------------------------------------------------------
166 protected ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean selection
) {
171 fSelection
= TmfTraceManager
.getInstance().getSelectionBeginTime().normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();
174 if (getTrace() == null) {
178 ITmfTreeViewerEntry root
= getInput();
182 } else if (fFilterStatus
) {
183 clearStateSystemEntries(root
);
187 * Update the values of the elements of the state systems at the
188 * selection start time
190 boolean changed
= updateStateSystemEntries(root
, fSelection
);
192 return selection
|| changed ? root
: null;
195 private ITmfTreeViewerEntry
createRoot() {
197 TmfTreeViewerEntry rootEntry
= new TmfTreeViewerEntry("root"); //$NON-NLS-1$
199 for (final ITmfTrace trace
: TmfTraceManager
.getTraceSetWithExperiment(getTrace())) {
201 rootEntry
.addChild(createTraceEntry(trace
));
207 private static TmfTreeViewerEntry
createTraceEntry(ITmfTrace trace
) {
208 TmfTreeViewerEntry traceEntry
= new TmfTreeViewerEntry(trace
.getName());
209 Iterable
<ITmfAnalysisModuleWithStateSystems
> modules
= trace
.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems
.class);
210 for (ITmfAnalysisModuleWithStateSystems module
: modules
) {
211 /* Just schedule the module, the data will be filled when available */
213 if (module
instanceof TmfStateSystemAnalysisModule
) {
214 // TODO: add this method to ITmfAnalysisModuleWithStateSystems
215 ((TmfStateSystemAnalysisModule
) module
).waitForInitialization();
217 for (ITmfStateSystem ss
: module
.getStateSystems()) {
219 traceEntry
.addChild(new StateSystemEntry(ss
));
226 private static void clearStateSystemEntries(ITmfTreeViewerEntry root
) {
227 for (ITmfTreeViewerEntry traceEntry
: root
.getChildren()) {
228 for (ITmfTreeViewerEntry ssEntry
: traceEntry
.getChildren()) {
229 ssEntry
.getChildren().clear();
234 private boolean updateStateSystemEntries(ITmfTreeViewerEntry root
, long timestamp
) {
235 boolean changed
= false;
236 for (ITmfTreeViewerEntry traceEntry
: root
.getChildren()) {
237 for (ITmfTreeViewerEntry ssEntry
: traceEntry
.getChildren()) {
238 StateSystemEntry stateSystemEntry
= (StateSystemEntry
) ssEntry
;
239 ITmfStateSystem ss
= stateSystemEntry
.getSS();
241 List
<ITmfStateInterval
> fullState
= ss
.queryFullState(timestamp
);
242 changed
|= updateStateEntries(ss
, fullState
, stateSystemEntry
, -1, timestamp
);
243 } catch (TimeRangeException e
) {
244 markOutOfRange(stateSystemEntry
);
246 } catch (StateSystemDisposedException e
) {
254 private boolean updateStateEntries(ITmfStateSystem ss
, List
<ITmfStateInterval
> fullState
, TmfTreeViewerEntry parent
, int parentQuark
, long timestamp
) {
255 boolean changed
= false;
257 for (int quark
: ss
.getSubAttributes(parentQuark
, false)) {
258 if (quark
>= fullState
.size()) {
259 // attribute was created after the full state query
262 ITmfStateInterval interval
= fullState
.get(quark
);
263 StateEntry stateEntry
= findStateEntry(parent
, quark
);
264 if (stateEntry
== null) {
265 boolean modified
= fFilterStatus ?
266 interval
.getStartTime() == timestamp
:
267 !interval
.getStateValue().isNull();
268 stateEntry
= new StateEntry(ss
.getAttributeName(quark
), quark
, ss
.getFullAttributePath(quark
),
269 interval
.getStateValue(),
270 new TmfTimestamp(interval
.getStartTime(), ITmfTimestamp
.NANOSECOND_SCALE
),
271 new TmfTimestamp(interval
.getEndTime(), ITmfTimestamp
.NANOSECOND_SCALE
),
274 // update children first to know if parent is really needed
275 updateStateEntries(ss
, fullState
, stateEntry
, quark
, timestamp
);
278 * Add this entry to parent if filtering is off, or
279 * if the entry has children to display, or
280 * if there is a state change at the current timestamp
282 if (!fFilterStatus
|| stateEntry
.hasChildren() || interval
.getStartTime() == timestamp
) {
283 parent
.addChild(stateEntry
);
287 stateEntry
.update(interval
.getStateValue(),
288 new TmfTimestamp(interval
.getStartTime(), ITmfTimestamp
.NANOSECOND_SCALE
),
289 new TmfTimestamp(interval
.getEndTime(), ITmfTimestamp
.NANOSECOND_SCALE
));
291 // update children recursively
292 updateStateEntries(ss
, fullState
, stateEntry
, quark
, timestamp
);
296 } catch (AttributeNotFoundException e
) {
297 /* Should not happen, we're iterating on known attributes */
302 private static StateEntry
findStateEntry(TmfTreeViewerEntry parent
, int quark
) {
303 for (ITmfTreeViewerEntry child
: parent
.getChildren()) {
304 StateEntry stateEntry
= (StateEntry
) child
;
305 if (stateEntry
.getQuark() == quark
) {
312 * Set the entries as out of range
314 private static void markOutOfRange(ITmfTreeViewerEntry parent
) {
315 for (ITmfTreeViewerEntry entry
: parent
.getChildren()) {
316 if (entry
instanceof StateEntry
) {
317 ((StateEntry
) entry
).setOutOfRange();
319 /* Update this node's children recursively */
320 markOutOfRange(entry
);
326 * Set the filter status of the viewer. By default, all entries of all state
327 * system are present, and the values that changed since last refresh are
328 * shown in yellow. When the filter status is true, only the entries with
329 * values modified at current time are displayed.
331 public void changeFilterStatus() {
332 fFilterStatus
= !fFilterStatus
;
334 getTreeViewer().setAutoExpandLevel(AbstractTreeViewer
.ALL_LEVELS
);
336 getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND
);
339 updateContent(getSelectionBeginTime(), getSelectionEndTime(), true);
343 * Update the display to use the updated timestamp format
346 * the incoming signal
349 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal
) {
350 updateContent(getSelectionBeginTime(), getSelectionEndTime(), true);
353 private static class StateSystemEntry
extends TmfTreeViewerEntry
{
354 private final @NonNull ITmfStateSystem fSS
;
356 public StateSystemEntry(@NonNull ITmfStateSystem ss
) {
361 public @NonNull ITmfStateSystem
getSS() {
366 private class StateEntry
extends TmfTreeViewerEntry
{
368 private final int fQuark
;
369 private final String fFullPath
;
370 private @NonNull TmfTimestamp fStart
;
371 private @NonNull TmfTimestamp fEnd
;
372 private ITmfStateValue fValue
;
373 private boolean fModified
;
374 private boolean fOutOfRange
= false;
376 public StateEntry(String name
, int quark
, String fullPath
, ITmfStateValue value
, @NonNull TmfTimestamp start
, @NonNull TmfTimestamp end
, boolean modified
) {
379 fFullPath
= fullPath
;
383 fModified
= modified
;
386 public int getQuark() {
390 public String
getFullPath() {
394 public String
getStartTime() {
398 return fStart
.toString();
401 public String
getEndTime() {
405 return fEnd
.toString();
408 public String
getValue() {
410 return Messages
.OutOfRangeMsg
;
412 switch (fValue
.getType()) {
417 return fValue
.toString();
424 public String
getType() {
428 switch (fValue
.getType()) {
430 return Messages
.TypeInteger
;
432 return Messages
.TypeLong
;
434 return Messages
.TypeDouble
;
436 return Messages
.TypeString
;
443 public boolean isModified() {
447 public void update(ITmfStateValue value
, @NonNull TmfTimestamp start
, @NonNull TmfTimestamp end
) {
450 if (!start
.equals(fStart
)) {
458 public void setOutOfRange() {