3e2622af9ffeb93f5e2f2fb94631fff7b9834235
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / views / statesystem / TmfStateSystemViewer.java
1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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 * 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 *******************************************************************************/
17
18 package org.eclipse.tracecompass.tmf.ui.views.statesystem;
19
20 import java.util.ArrayList;
21 import java.util.List;
22
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.TmfTraceContext;
45 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
46 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
47 import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractTmfTreeViewer;
48 import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
49 import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeViewerEntry;
50 import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData;
51 import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeViewerEntry;
52
53 /**
54 * Displays the content of the state systems at the current time
55 *
56 * @author Florian Wininger
57 * @author Alexandre Montplaisir
58 * @author Geneviève Bastien
59 */
60 public class TmfStateSystemViewer extends AbstractTmfTreeViewer {
61
62 private static final String EMPTY_STRING = ""; //$NON-NLS-1$
63 private static final int DEFAULT_AUTOEXPAND = 2;
64 private boolean fFilterStatus = false;
65 private long fSelection = 0;
66
67 /* Order of columns */
68 private static final int ATTRIBUTE_NAME_COL = 0;
69 private static final int QUARK_COL = 1;
70 private static final int VALUE_COL = 2;
71 private static final int TYPE_COL = 3;
72 private static final int START_TIME_COL = 4;
73 private static final int END_TIME_COL = 5;
74 private static final int ATTRIBUTE_FULLPATH_COL = 6;
75
76 /**
77 * Base class to provide the labels for the tree viewer. Views extending
78 * this class typically need to override the getColumnText method if they
79 * have more than one column to display
80 */
81 protected static class StateSystemTreeLabelProvider extends TreeLabelProvider {
82
83 @Override
84 public String getColumnText(Object element, int columnIndex) {
85 if (element instanceof StateEntry) {
86 StateEntry entry = (StateEntry) element;
87 switch (columnIndex) {
88 case ATTRIBUTE_NAME_COL:
89 return entry.getName();
90 case QUARK_COL:
91 return String.valueOf(entry.getQuark());
92 case VALUE_COL:
93 return entry.getValue();
94 case TYPE_COL:
95 return entry.getType();
96 case START_TIME_COL:
97 return entry.getStartTime();
98 case END_TIME_COL:
99 return entry.getEndTime();
100 case ATTRIBUTE_FULLPATH_COL:
101 return entry.getFullPath();
102 default:
103 return EMPTY_STRING;
104 }
105 }
106 return super.getColumnText(element, columnIndex);
107 }
108
109 @Override
110 public Color getBackground(Object element, int columnIndex) {
111 if (element instanceof StateEntry) {
112 if (((StateEntry) element).isModified()) {
113 return Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW);
114 }
115 }
116 return super.getBackground(element, columnIndex);
117 }
118 }
119
120 /**
121 * Constructor
122 *
123 * @param parent
124 * The parent containing this viewer
125 */
126 public TmfStateSystemViewer(Composite parent) {
127 super(parent, false);
128 this.setLabelProvider(new StateSystemTreeLabelProvider());
129 getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND);
130 }
131
132 @Override
133 protected ITmfTreeColumnDataProvider getColumnDataProvider() {
134 return new ITmfTreeColumnDataProvider() {
135
136 @Override
137 public List<TmfTreeColumnData> getColumnData() {
138 List<TmfTreeColumnData> columns = new ArrayList<>();
139 TmfTreeColumnData column = new TmfTreeColumnData(Messages.TreeNodeColumnLabel);
140 columns.add(column);
141 column.setComparator(new ViewerComparator() {
142 @Override
143 public int compare(Viewer viewer, Object e1, Object e2) {
144 TmfTreeViewerEntry n1 = (TmfTreeViewerEntry) e1;
145 TmfTreeViewerEntry n2 = (TmfTreeViewerEntry) e2;
146
147 return n1.getName().compareTo(n2.getName());
148 }
149 });
150 columns.add(new TmfTreeColumnData(Messages.QuarkColumnLabel));
151 columns.add(new TmfTreeColumnData(Messages.ValueColumnLabel));
152 columns.add(new TmfTreeColumnData(Messages.TypeColumnLabel));
153 columns.add(new TmfTreeColumnData(Messages.StartTimeColumLabel));
154 columns.add(new TmfTreeColumnData(Messages.EndTimeColumLabel));
155 columns.add(new TmfTreeColumnData(Messages.AttributePathColumnLabel));
156 return columns;
157 }
158
159 };
160 }
161
162 // ------------------------------------------------------------------------
163 // Operations
164 // ------------------------------------------------------------------------
165
166 @Override
167 protected ITmfTreeViewerEntry updateElements(long start, long end, boolean selection) {
168
169 if (selection) {
170 fSelection = start;
171 } else {
172 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
173 fSelection = ctx.getSelectionRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
174 }
175
176 if (getTrace() == null) {
177 return null;
178 }
179
180 ITmfTreeViewerEntry root = getInput();
181
182 if (root == null) {
183 root = createRoot();
184 } else if (fFilterStatus) {
185 clearStateSystemEntries(root);
186 }
187
188 /*
189 * Update the values of the elements of the state systems at the
190 * selection start time
191 */
192 boolean changed = updateStateSystemEntries(root, fSelection);
193
194 return selection || changed ? root : null;
195 }
196
197 private ITmfTreeViewerEntry createRoot() {
198 // 'Fake' root node
199 TmfTreeViewerEntry rootEntry = new TmfTreeViewerEntry("root"); //$NON-NLS-1$
200
201 for (final ITmfTrace trace : TmfTraceManager.getTraceSetWithExperiment(getTrace())) {
202 if (trace != null) {
203 rootEntry.addChild(createTraceEntry(trace));
204 }
205 }
206 return rootEntry;
207 }
208
209 private static TmfTreeViewerEntry createTraceEntry(ITmfTrace trace) {
210 TmfTreeViewerEntry traceEntry = new TmfTreeViewerEntry(trace.getName());
211 Iterable<ITmfAnalysisModuleWithStateSystems> modules = TmfTraceUtils.getAnalysisModulesOfClass(trace, ITmfAnalysisModuleWithStateSystems.class);
212 for (ITmfAnalysisModuleWithStateSystems module : modules) {
213 /* Just schedule the module, the data will be filled when available */
214 module.schedule();
215 if (module instanceof TmfStateSystemAnalysisModule) {
216 // TODO: add this method to ITmfAnalysisModuleWithStateSystems
217 ((TmfStateSystemAnalysisModule) module).waitForInitialization();
218 }
219 for (ITmfStateSystem ss : module.getStateSystems()) {
220 if (ss != null) {
221 traceEntry.addChild(new StateSystemEntry(ss));
222 }
223 }
224 }
225 return traceEntry;
226 }
227
228 private static void clearStateSystemEntries(ITmfTreeViewerEntry root) {
229 for (ITmfTreeViewerEntry traceEntry : root.getChildren()) {
230 for (ITmfTreeViewerEntry ssEntry : traceEntry.getChildren()) {
231 ssEntry.getChildren().clear();
232 }
233 }
234 }
235
236 private boolean updateStateSystemEntries(ITmfTreeViewerEntry root, long timestamp) {
237 boolean changed = false;
238 for (ITmfTreeViewerEntry traceEntry : root.getChildren()) {
239 for (ITmfTreeViewerEntry ssEntry : traceEntry.getChildren()) {
240 StateSystemEntry stateSystemEntry = (StateSystemEntry) ssEntry;
241 ITmfStateSystem ss = stateSystemEntry.getSS();
242 try {
243 List<ITmfStateInterval> fullState = ss.queryFullState(timestamp);
244 changed |= updateStateEntries(ss, fullState, stateSystemEntry, -1, timestamp);
245 } catch (TimeRangeException e) {
246 markOutOfRange(stateSystemEntry);
247 changed = true;
248 } catch (StateSystemDisposedException e) {
249 /* Ignored */
250 }
251 }
252 }
253 return changed;
254 }
255
256 private boolean updateStateEntries(ITmfStateSystem ss, List<ITmfStateInterval> fullState, TmfTreeViewerEntry parent, int parentQuark, long timestamp) {
257 boolean changed = false;
258 try {
259 for (int quark : ss.getSubAttributes(parentQuark, false)) {
260 if (quark >= fullState.size()) {
261 // attribute was created after the full state query
262 continue;
263 }
264 ITmfStateInterval interval = fullState.get(quark);
265 StateEntry stateEntry = findStateEntry(parent, quark);
266 if (stateEntry == null) {
267 boolean modified = fFilterStatus ?
268 interval.getStartTime() == timestamp :
269 !interval.getStateValue().isNull();
270 stateEntry = new StateEntry(ss.getAttributeName(quark), quark, ss.getFullAttributePath(quark),
271 interval.getStateValue(),
272 new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE),
273 new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE),
274 modified);
275
276 // update children first to know if parent is really needed
277 updateStateEntries(ss, fullState, stateEntry, quark, timestamp);
278
279 /*
280 * Add this entry to parent if filtering is off, or
281 * if the entry has children to display, or
282 * if there is a state change at the current timestamp
283 */
284 if (!fFilterStatus || stateEntry.hasChildren() || interval.getStartTime() == timestamp) {
285 parent.addChild(stateEntry);
286 changed = true;
287 }
288 } else {
289 stateEntry.update(interval.getStateValue(),
290 new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE),
291 new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE));
292
293 // update children recursively
294 updateStateEntries(ss, fullState, stateEntry, quark, timestamp);
295 }
296
297 }
298 } catch (AttributeNotFoundException e) {
299 /* Should not happen, we're iterating on known attributes */
300 }
301 return changed;
302 }
303
304 private static StateEntry findStateEntry(TmfTreeViewerEntry parent, int quark) {
305 for (ITmfTreeViewerEntry child : parent.getChildren()) {
306 StateEntry stateEntry = (StateEntry) child;
307 if (stateEntry.getQuark() == quark) {
308 return stateEntry;
309 }
310 }
311 return null;
312 }
313 /**
314 * Set the entries as out of range
315 */
316 private static void markOutOfRange(ITmfTreeViewerEntry parent) {
317 for (ITmfTreeViewerEntry entry : parent.getChildren()) {
318 if (entry instanceof StateEntry) {
319 ((StateEntry) entry).setOutOfRange();
320
321 /* Update this node's children recursively */
322 markOutOfRange(entry);
323 }
324 }
325 }
326
327 /**
328 * Set the filter status of the viewer. By default, all entries of all state
329 * system are present, and the values that changed since last refresh are
330 * shown in yellow. When the filter status is true, only the entries with
331 * values modified at current time are displayed.
332 */
333 public void changeFilterStatus() {
334 fFilterStatus = !fFilterStatus;
335 if (fFilterStatus) {
336 getTreeViewer().setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
337 } else {
338 getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND);
339 clearContent();
340 }
341 updateContent(getSelectionBeginTime(), getSelectionEndTime(), true);
342 }
343
344 /**
345 * Update the display to use the updated timestamp format
346 *
347 * @param signal
348 * the incoming signal
349 */
350 @TmfSignalHandler
351 public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
352 updateContent(getSelectionBeginTime(), getSelectionEndTime(), true);
353 }
354
355 private static class StateSystemEntry extends TmfTreeViewerEntry {
356 private final @NonNull ITmfStateSystem fSS;
357
358 public StateSystemEntry(@NonNull ITmfStateSystem ss) {
359 super(ss.getSSID());
360 fSS = ss;
361 }
362
363 public @NonNull ITmfStateSystem getSS() {
364 return fSS;
365 }
366 }
367
368 private class StateEntry extends TmfTreeViewerEntry {
369
370 private final int fQuark;
371 private final String fFullPath;
372 private @NonNull TmfTimestamp fStart;
373 private @NonNull TmfTimestamp fEnd;
374 private ITmfStateValue fValue;
375 private boolean fModified;
376 private boolean fOutOfRange = false;
377
378 public StateEntry(String name, int quark, String fullPath, ITmfStateValue value, @NonNull TmfTimestamp start, @NonNull TmfTimestamp end, boolean modified) {
379 super(name);
380 fQuark = quark;
381 fFullPath = fullPath;
382 fStart = start;
383 fEnd = end;
384 fValue = value;
385 fModified = modified;
386 }
387
388 public int getQuark() {
389 return fQuark;
390 }
391
392 public String getFullPath() {
393 return fFullPath;
394 }
395
396 public String getStartTime() {
397 if (fOutOfRange) {
398 return EMPTY_STRING;
399 }
400 return fStart.toString();
401 }
402
403 public String getEndTime() {
404 if (fOutOfRange) {
405 return EMPTY_STRING;
406 }
407 return fEnd.toString();
408 }
409
410 public String getValue() {
411 if (fOutOfRange) {
412 return Messages.OutOfRangeMsg;
413 }
414 switch (fValue.getType()) {
415 case INTEGER:
416 case LONG:
417 case DOUBLE:
418 case STRING:
419 return fValue.toString();
420 case NULL:
421 default:
422 return EMPTY_STRING;
423 }
424 }
425
426 public String getType() {
427 if (fOutOfRange) {
428 return EMPTY_STRING;
429 }
430 switch (fValue.getType()) {
431 case INTEGER:
432 return Messages.TypeInteger;
433 case LONG:
434 return Messages.TypeLong;
435 case DOUBLE:
436 return Messages.TypeDouble;
437 case STRING:
438 return Messages.TypeString;
439 case NULL:
440 default:
441 return EMPTY_STRING;
442 }
443 }
444
445 public boolean isModified() {
446 return fModified;
447 }
448
449 public void update(ITmfStateValue value, @NonNull TmfTimestamp start, @NonNull TmfTimestamp end) {
450 fModified = false;
451 fOutOfRange = false;
452 if (!start.equals(fStart)) {
453 fModified = true;
454 fStart = start;
455 fEnd = end;
456 fValue = value;
457 }
458 }
459
460 public void setOutOfRange() {
461 fModified = false;
462 fOutOfRange = true;
463 }
464 }
465 }
This page took 0.060272 seconds and 5 git commands to generate.