/*******************************************************************************
- * Copyright (c) 2013 École Polytechnique de Montréal, Ericsson
+ * Copyright (c) 2013, 2014 École Polytechnique de Montréal, Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* Alexandre Montplaisir - Refactoring, performance tweaks
* Bernd Hufmann - Updated signal handling
* Marc-Andre Laperle - Add time zone preference
+ * Geneviève Bastien - Use a tree viewer instead of a tree
*******************************************************************************/
package org.eclipse.linuxtools.tmf.ui.views.statesystem;
import java.io.File;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.linuxtools.internal.tmf.ui.Activator;
-import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
-import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
-import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
-import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
-import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
-import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
-import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
-import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
-import org.eclipse.linuxtools.tmf.core.signal.TmfTraceClosedSignal;
-import org.eclipse.linuxtools.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal;
-import org.eclipse.linuxtools.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
-import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
-import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
-import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
-import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
-import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
import org.eclipse.linuxtools.tmf.ui.views.TmfView;
-import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeColumn;
-import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
/**
/** The Environment View's ID */
public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.ssview"; //$NON-NLS-1$
- private static final String emptyString = ""; //$NON-NLS-1$
- private static final String FS = File.separator;
private static final Image FILTER_IMAGE =
- Activator.getDefault().getImageFromPath(FS + "icons" + FS + "elcl16" + FS + "filter_items.gif"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ Activator.getDefault().getImageFromPath( File.separator + "icons" + File.separator + "elcl16" + File.separator + "filter_items.gif"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- /* Order of columns */
- private static final int ATTRIBUTE_NAME_COL = 0;
- private static final int QUARK_COL = 1;
- private static final int VALUE_COL = 2;
- private static final int TYPE_COL = 3;
- private static final int START_TIME_COL = 4;
- private static final int END_TIME_COL = 5;
- private static final int ATTRIBUTE_FULLPATH_COL = 6;
-
- private ITmfTrace fTrace;
- private Tree fTree;
- private volatile long fCurrentTimestamp = -1L;
-
- private boolean filterStatus = false ;
+ private TmfStateSystemViewer fViewer;
/**
* Default constructor
@Override
public void createPartControl(Composite parent) {
- fTree = new Tree(parent, SWT.NONE);
- TreeColumn nameCol = new TreeColumn(fTree, SWT.NONE, ATTRIBUTE_NAME_COL);
- TreeColumn quarkCol = new TreeColumn(fTree, SWT.NONE, QUARK_COL);
- TreeColumn valueCol = new TreeColumn(fTree, SWT.NONE, VALUE_COL);
- TreeColumn typeCol = new TreeColumn(fTree, SWT.NONE, TYPE_COL);
- TreeColumn startCol = new TreeColumn(fTree, SWT.NONE, START_TIME_COL);
- TreeColumn endCol = new TreeColumn(fTree, SWT.NONE, END_TIME_COL);
- TreeColumn pathCol = new TreeColumn(fTree, SWT.NONE, ATTRIBUTE_FULLPATH_COL);
-
- nameCol.setText(Messages.TreeNodeColumnLabel);
- quarkCol.setText(Messages.QuarkColumnLabel);
- valueCol.setText(Messages.ValueColumnLabel);
- typeCol.setText(Messages.TypeColumnLabel);
- startCol.setText(Messages.StartTimeColumLabel);
- endCol.setText(Messages.EndTimeColumLabel);
- pathCol.setText(Messages.AttributePathColumnLabel);
-
- fTree.setItemCount(0);
- fTree.setHeaderVisible(true);
- nameCol.pack();
- valueCol.pack();
+ fViewer = new TmfStateSystemViewer(parent);
- fTree.addListener(SWT.Expand, new Listener() {
- @Override
- public void handleEvent(Event e) {
- TreeItem item = (TreeItem) e.item;
- item.setExpanded(true);
- updateTable();
- }
- });
+ fillToolBar() ;
ITmfTrace trace = getActiveTrace();
if (trace != null) {
- traceSelected(new TmfTraceSelectedSignal(this, trace));
- }
-
- fillToolBar() ;
-
- }
-
- // ------------------------------------------------------------------------
- // Operations
- // ------------------------------------------------------------------------
-
- /**
- * Create the initial tree from a trace.
- */
- private synchronized void createTable() {
-
- long ts = fCurrentTimestamp;
-
- if (fTrace == null) {
- return;
- }
-
- /* Clear the table, in case a trace was previously using it */
- fTree.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- fTree.setItemCount(0);
- }
- });
-
- for (final ITmfTrace currentTrace : TmfTraceManager.getTraceSet(fTrace)) {
- /*
- * We will first do all the queries for this trace, then update that
- * sub-tree in the UI thread.
- */
- Iterable<ITmfAnalysisModuleWithStateSystems> modules = currentTrace.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class);
- final Map<String, ITmfStateSystem> sss = new HashMap<>();
- final Map<String, List<ITmfStateInterval>> fullStates = new LinkedHashMap<>();
- for (ITmfAnalysisModuleWithStateSystems module : modules) {
-
- /*
- * FIXME: For now, this view is a way to execute and display
- * state system. But with phase 2 of analysis API, we won't want
- * to run state system that have not been requested. We will
- * leave the title, but there won't be anything underneath.
- */
- module.schedule();
- module.waitForCompletion();
- for (ITmfStateSystem ss : module.getStateSystems()) {
- if (ss == null) {
- continue;
- }
- String ssName = ss.getSSID();
- sss.put(ssName, ss);
-
- if (ts == -1 || ts < ss.getStartTime() || ts > ss.getCurrentEndTime()) {
- ts = ss.getStartTime();
- }
- try {
- fullStates.put(ssName, ss.queryFullState(ts));
- } catch (TimeRangeException e) {
- /* We already checked the limits ourselves */
- throw new IllegalStateException();
- } catch (StateSystemDisposedException e) {
- /* Probably shutting down, cancel and return */
- return;
- }
- }
- }
-
- /* Update the table (in the UI thread) */
- fTree.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- TreeItem traceRoot = new TreeItem(fTree, SWT.NONE);
- traceRoot.setText(ATTRIBUTE_NAME_COL, currentTrace.getName());
-
- for (Map.Entry<String, ITmfStateSystem> entry : sss.entrySet()) {
- String ssName = entry.getKey();
- ITmfStateSystem ss = entry.getValue();
- List<ITmfStateInterval> fullState = fullStates.get(ssName);
-
- /* Root item of the current state system */
- TreeItem item = new TreeItem(traceRoot, SWT.NONE);
-
- /* Name of the SS goes in the first column */
- item.setText(ATTRIBUTE_NAME_COL, ssName);
-
- /*
- * Calling with quark '-1' here to start with the root
- * attribute, then it will be called recursively.
- */
- if (ss != null) {
- addChildren(ss, fullState, -1, item);
- }
- }
-
- /* Expand the first-level tree items */
- for (TreeItem item : fTree.getItems()) {
- item.setExpanded(true);
- }
- packColumns();
-
- if (filterStatus) {
- filterChildren(traceRoot);
- }
- }
- });
- }
- }
-
- /**
- * Add children node to a newly-created tree. Should only be called by the
- * UI thread.
- */
- private void addChildren(ITmfStateSystem ss,
- List<ITmfStateInterval> fullState, int rootQuark, TreeItem root) {
- try {
- for (int quark : ss.getSubAttributes(rootQuark, false)) {
- TreeItem subItem = new TreeItem(root, SWT.NONE);
-
- /* Write the info we already know */
- subItem.setText(ATTRIBUTE_NAME_COL, ss.getAttributeName(quark));
- subItem.setText(QUARK_COL, String.valueOf(quark));
- subItem.setText(ATTRIBUTE_FULLPATH_COL, ss.getFullAttributePath(quark));
-
- /* Populate the other columns */
- ITmfStateInterval interval = fullState.get(quark);
- populateColumns(subItem, interval);
-
- /* Update this node's children recursively */
- addChildren(ss, fullState, quark, subItem);
- }
-
- } catch (AttributeNotFoundException e) {
- /* Should not happen, we're iterating on known attributes */
- throw new RuntimeException();
- }
- }
-
- /**
- * Update the tree, which means keep the tree of attributes in the first
- * column as-is, but update the values to the ones at a new timestamp.
- */
- private synchronized void updateTable() {
- ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
- long ts = fCurrentTimestamp;
-
- /* For each trace... */
- for (int traceNb = 0; traceNb < traces.length; traceNb++) {
- Iterable<ITmfAnalysisModuleWithStateSystems> modules = traces[traceNb].getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class);
-
- /* For each state system associated with this trace... */
- int ssNb = 0;
- for (ITmfAnalysisModuleWithStateSystems module : modules) {
-
- /*
- * Even though we only use the value, it just feels safer to
- * iterate the same way as before to keep the order the same.
- */
- for (final ITmfStateSystem ss : module.getStateSystems()) {
- final int traceNb1 = traceNb;
- final int ssNb1 = ssNb;
- if (ss != null) {
- final String ssName = ss.getSSID();
- ts = (ts == -1 ? ss.getStartTime() : ts);
- try {
- final List<ITmfStateInterval> fullState = ss.queryFullState(ts);
- fTree.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- /*
- * Get the tree item of the relevant state
- * system
- */
- TreeItem traceItem = fTree.getItem(traceNb1);
- /* Find the item corresponding to this state system */
- TreeItem item = null;
- for (TreeItem ssItem : traceItem.getItems()) {
- if (ssItem.getText(ATTRIBUTE_NAME_COL).equals(ssName)) {
- item = ssItem;
- break;
- }
- }
- if (item != null) {
- /* Update it, then its children, recursively */
- item.setText(VALUE_COL, emptyString);
- updateChildren(ss, fullState, -1, item);
- }
- }
- });
-
- } catch (TimeRangeException e) {
- /*
- * This can happen in an experiment, if the user
- * selects a range valid in the experiment, but this
- * specific does not exist. Print "out-of-range"
- * into all the values.
- */
- fTree.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- TreeItem traceItem = fTree.getItem(traceNb1);
- TreeItem item = traceItem.getItem(ssNb1);
- markOutOfRange(item);
- }
- });
- } catch (StateSystemDisposedException e) {
- return;
- }
- }
-
- ssNb++;
- }
- }
- }
- }
-
- /**
- * Update the values shown by a child row when doing an update. Should only
- * be called by the UI thread.
- */
- private void updateChildren(ITmfStateSystem ss,
- List<ITmfStateInterval> state, int root_quark, TreeItem root) {
- try {
- for (TreeItem item : root.getItems()) {
- int quark = ss.getQuarkRelative(root_quark, item.getText(0));
- ITmfStateInterval interval = state.get(quark);
- populateColumns(item, interval);
-
- /* Update children recursively */
- updateChildren(ss, state, quark, item);
- }
-
- } catch (AttributeNotFoundException e) {
- /* We're iterating on known attributes, should not happen */
- throw new RuntimeException();
- }
- }
-
- /**
- * Populate an 'item' (a row in the tree) with the information found in the
- * interval. This method should only be called by the UI thread.
- */
- private void populateColumns(TreeItem item, ITmfStateInterval interval) {
- try {
- ITmfStateValue state = interval.getStateValue();
- String value ;
-
- // add the value in the 2nd column
- switch (state.getType()) {
- case INTEGER:
- value = String.valueOf(state.unboxInt());
- item.setText(TYPE_COL, Messages.TypeInteger);
- break;
- case LONG:
- value = String.valueOf(state.unboxLong());
- item.setText(TYPE_COL, Messages.TypeLong);
- break;
- case DOUBLE:
- value = String.valueOf(state.unboxDouble());
- item.setText(TYPE_COL, Messages.TypeDouble);
- break;
- case STRING:
- value = state.unboxStr();
- item.setText(TYPE_COL, Messages.TypeString);
- break;
- case NULL:
- default:
- value = emptyString ;
- item.setText(TYPE_COL, emptyString);
- break;
- }
-
- TmfTimestamp startTime = new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE);
- item.setText(START_TIME_COL, startTime.toString());
-
- TmfTimestamp endTime = new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE);
- item.setText(END_TIME_COL, endTime.toString());
-
- item.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)) ;
-
- if (!filterStatus && (!value.equals(item.getText(VALUE_COL)) || fCurrentTimestamp == startTime.getValue())) {
- item.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_YELLOW));
- }
-
- item.setText(VALUE_COL, value) ;
-
- } catch (StateValueTypeException e) {
- /* Should not happen, we're case-switching on the specific types */
- throw new RuntimeException();
- }
- }
-
- /**
- * Same concept as {@link updateChildren}, but instead of printing actual
- * values coming from the state system, we print "Out of range" into all
- * values. This is to indicate that this specific state system is not
- * currently defined at the selected timestamp.
- *
- * Guess by which thread this should be called? Hint: starts with a U, ends
- * with an I.
- */
- private void markOutOfRange(TreeItem root) {
- root.setText(VALUE_COL, Messages.OutOfRangeMsg);
- root.setText(TYPE_COL, emptyString);
- root.setText(START_TIME_COL, emptyString);
- root.setText(END_TIME_COL, emptyString);
- for (TreeItem item : root.getItems()) {
- markOutOfRange(item);
+ fViewer.traceSelected(new TmfTraceSelectedSignal(this, trace));
}
- }
- /**
- * Auto-pack all the columns in the display. Should only be called by the UI
- * thread.
- */
- private void packColumns() {
- //FIXME should add a bit of padding
- for (TreeColumn column : fTree.getColumns()) {
- column.pack();
- }
- }
-
- @Override
- public void setFocus() {
- fTree.setFocus();
- }
-
- // ------------------------------------------------------------------------
- // Signal handlers
- // ------------------------------------------------------------------------
- /**
- * Handler for the trace opened signal.
- * @param signal
- * The incoming signal
- * @since 2.0
- */
- @TmfSignalHandler
- public void traceOpened(TmfTraceOpenedSignal signal) {
- fTrace = signal.getTrace();
- loadTrace();
- }
-
- /**
- * Handler for the trace selected signal. This will make the view display
- * the information for the newly-selected trace.
- *
- * @param signal
- * The incoming signal
- */
- @TmfSignalHandler
- public void traceSelected(TmfTraceSelectedSignal signal) {
- ITmfTrace trace = signal.getTrace();
- if (trace != fTrace) {
- fTrace = trace;
- loadTrace();
- }
- }
-
- /**
- * Handler for the trace closed signal. This will clear the view.
- *
- * @param signal
- * the incoming signal
- */
- @TmfSignalHandler
- public void traceClosed(TmfTraceClosedSignal signal) {
- // delete the tree at the trace closed
- if (signal.getTrace() == fTrace) {
- fTrace = null;
- fTree.setItemCount(0);
- }
- }
-
- /**
- * Handles the current time updated signal. This will update the view's
- * values to the newly-selected timestamp.
- *
- * @param signal
- * the signal to process
- */
- @TmfSignalHandler
- public void currentTimeUpdated(final TmfTimeSynchSignal signal) {
- Thread thread = new Thread("State system visualizer update") { //$NON-NLS-1$
- @Override
- public void run() {
- ITmfTimestamp currentTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE);
- fCurrentTimestamp = currentTime.getValue();
-
- if (filterStatus) {
- createTable();
- } else {
- updateTable();
- }
- }
- };
- thread.start();
- }
-
- private void loadTrace() {
- Thread thread = new Thread("State system visualizer construction") { //$NON-NLS-1$
- @Override
- public void run() {
- createTable();
- }
- };
- thread.start();
- }
-
- /**
- * Update the display to use the updated timestamp format
- *
- * @param signal the incoming signal
- * @since 2.1
- */
- @TmfSignalHandler
- public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
- Thread thread = new Thread("State system visualizer update") { //$NON-NLS-1$
- @Override
- public void run() {
- updateTable();
- }
- };
- thread.start();
- }
-
- /**
- * Function for the delete TreeItem
- */
- private boolean filterChildren(TreeItem root) {
- boolean valid = false ;
- TmfTimestamp startTime = new TmfTimestamp(fCurrentTimestamp, ITmfTimestamp.NANOSECOND_SCALE);
- valid = root.getText(START_TIME_COL).equals(startTime.toString());
- root.setExpanded(true);
-
- for (TreeItem item : root.getItems()) {
- /* Update children recursively */
- valid = filterChildren(item) || valid;
- }
-
- if (!valid) {
- root.dispose();
- }
- return valid;
}
// ------------------------------------------------------------------------
// Part For Button Action
// ------------------------------------------------------------------------
-
-
private void fillToolBar() {
Action fFilterAction = new FilterAction();
fFilterAction.setImageDescriptor(ImageDescriptor.createFromImage(FILTER_IMAGE));
private class FilterAction extends Action {
@Override
public void run() {
- filterStatus = !filterStatus;
- if (!filterStatus) {
- createTable();
- }
+ fViewer.changeFilterStatus();
}
}
+
+ @Override
+ public void setFocus() {
+ }
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2014 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Florian Wininger - Initial API and implementation
+ * Alexandre Montplaisir - Refactoring, performance tweaks
+ * Bernd Hufmann - Updated signal handling
+ * Marc-Andre Laperle - Add time zone preference
+ * Geneviève Bastien - Moved state system explorer to use the abstract tree viewer
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.views.statesystem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
+import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
+import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
+import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
+import org.eclipse.linuxtools.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
+import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
+import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
+import org.eclipse.linuxtools.tmf.ui.viewers.tree.AbstractTmfTreeViewer;
+import org.eclipse.linuxtools.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
+import org.eclipse.linuxtools.tmf.ui.viewers.tree.ITmfTreeViewerEntry;
+import org.eclipse.linuxtools.tmf.ui.viewers.tree.TmfTreeColumnData;
+import org.eclipse.linuxtools.tmf.ui.viewers.tree.TmfTreeViewerEntry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Displays the content of the state systems at the current time
+ *
+ * @author Florian Wininger
+ * @author Alexandre Montplaisir
+ * @author Geneviève Bastien
+ * @since 3.0
+ */
+public class TmfStateSystemViewer extends AbstractTmfTreeViewer {
+
+ private static final String EMPTY_STRING = ""; //$NON-NLS-1$
+ private boolean fFilterStatus = false;
+ private static final int DEFAULT_AUTOEXPAND = 2;
+
+ /* Order of columns */
+ private static final int ATTRIBUTE_NAME_COL = 0;
+ private static final int QUARK_COL = 1;
+ private static final int VALUE_COL = 2;
+ private static final int TYPE_COL = 3;
+ private static final int START_TIME_COL = 4;
+ private static final int END_TIME_COL = 5;
+ private static final int ATTRIBUTE_FULLPATH_COL = 6;
+
+ /**
+ * Base class to provide the labels for the tree viewer. Views extending
+ * this class typically need to override the getColumnText method if they
+ * have more than one column to display
+ */
+ protected static class StateSystemTreeLabelProvider extends TreeLabelProvider {
+
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ if (element instanceof StateSystemEntry) {
+ StateSystemEntry entry = (StateSystemEntry) element;
+ switch (columnIndex) {
+ case ATTRIBUTE_NAME_COL:
+ return entry.getName();
+ case QUARK_COL:
+ return String.valueOf(entry.getQuark());
+ case VALUE_COL:
+ return entry.getValue();
+ case TYPE_COL:
+ return entry.getType();
+ case START_TIME_COL:
+ return entry.getStartTime();
+ case END_TIME_COL:
+ return entry.getEndTime();
+ case ATTRIBUTE_FULLPATH_COL:
+ return entry.getFullPath();
+ default:
+ return EMPTY_STRING;
+ }
+ }
+ return super.getColumnText(element, columnIndex);
+ }
+
+ @Override
+ public Color getBackground(Object element, int columnIndex) {
+ if (element instanceof StateSystemEntry) {
+ if (((StateSystemEntry) element).isModified()) {
+ return Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW);
+ }
+ }
+ return super.getBackground(element, columnIndex);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * The parent containing this viewer
+ */
+ public TmfStateSystemViewer(Composite parent) {
+ super(parent, false);
+ this.setLabelProvider(new StateSystemTreeLabelProvider());
+ getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND);
+ }
+
+ @Override
+ protected ITmfTreeColumnDataProvider getColumnDataProvider() {
+ return new ITmfTreeColumnDataProvider() {
+
+ @Override
+ public List<TmfTreeColumnData> getColumnData() {
+ List<TmfTreeColumnData> columns = new ArrayList<>();
+ TmfTreeColumnData column = new TmfTreeColumnData(Messages.TreeNodeColumnLabel);
+ columns.add(column);
+ column.setComparator(new ViewerComparator() {
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ TmfTreeViewerEntry n1 = (TmfTreeViewerEntry) e1;
+ TmfTreeViewerEntry n2 = (TmfTreeViewerEntry) e2;
+
+ return n1.getName().compareTo(n2.getName());
+ }
+ });
+ columns.add(new TmfTreeColumnData(Messages.QuarkColumnLabel));
+ columns.add(new TmfTreeColumnData(Messages.ValueColumnLabel));
+ columns.add(new TmfTreeColumnData(Messages.TypeColumnLabel));
+ columns.add(new TmfTreeColumnData(Messages.StartTimeColumLabel));
+ columns.add(new TmfTreeColumnData(Messages.EndTimeColumLabel));
+ columns.add(new TmfTreeColumnData(Messages.AttributePathColumnLabel));
+ return columns;
+ }
+
+ };
+ }
+
+ // ------------------------------------------------------------------------
+ // Operations
+ // ------------------------------------------------------------------------
+
+ @Override
+ protected List<ITmfTreeViewerEntry> updateElements(long start, long end, boolean selection) {
+ if (getTrace() == null) {
+ return null;
+ }
+
+ List<ITmfTreeViewerEntry> entries = (List<ITmfTreeViewerEntry>) getInput();
+
+ if ((!selection) && (entries != null)) {
+ return null;
+ }
+
+ /*
+ * Build the entries if it is the first time or to show only modified
+ * values
+ */
+ if (entries == null || fFilterStatus) {
+ entries = buildEntriesList(start);
+ } else {
+ /*
+ * Update the values of the elements of the state systems at time
+ * 'start'
+ */
+ entries = updateEntriesList(entries, start);
+ }
+
+ return entries;
+ }
+
+ private List<ITmfTreeViewerEntry> buildEntriesList(long timestamp) {
+ List<ITmfTreeViewerEntry> rootEntries = new ArrayList<>();
+ for (final ITmfTrace currentTrace : TmfTraceManager.getTraceSet(getTrace())) {
+ if (currentTrace == null) {
+ continue;
+ }
+ buildEntriesForTrace(currentTrace, timestamp, rootEntries);
+ }
+ return rootEntries;
+ }
+
+ /*
+ * Update the values of the entries. It will also create trace and state
+ * system entries if they do not exist yet.
+ */
+ private List<ITmfTreeViewerEntry> updateEntriesList(List<ITmfTreeViewerEntry> entries, long timestamp) {
+ for (final ITmfTrace trace : TmfTraceManager.getTraceSet(getTrace())) {
+ if (trace == null) {
+ continue;
+ }
+ ITmfTreeViewerEntry traceEntry = null;
+ for (ITmfTreeViewerEntry entry : entries) {
+ if (entry.getName().equals(trace.getName())) {
+ traceEntry = entry;
+ }
+ }
+ if (traceEntry == null) {
+ traceEntry = buildEntriesForTrace(trace, timestamp, entries);
+ }
+
+ /* Find the state system entries for this trace */
+ Iterable<ITmfAnalysisModuleWithStateSystems> modules = trace.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class);
+ for (ITmfAnalysisModuleWithStateSystems module : modules) {
+ module.schedule();
+ for (ITmfStateSystem ss : module.getStateSystems()) {
+ if (ss == null) {
+ continue;
+ }
+ ITmfTreeViewerEntry ssEntry = null;
+ for (ITmfTreeViewerEntry entry : traceEntry.getChildren()) {
+ if (entry.getName().equals(ss.getSSID())) {
+ ssEntry = entry;
+ }
+ }
+
+ if (ssEntry == null) {
+ /* The state system entry has not been built yet */
+ buildEntriesForStateSystem(ss, timestamp, (TmfTreeViewerEntry) traceEntry);
+ } else if (ssEntry.hasChildren()) {
+ /*
+ * Typical case at this point, update the data from the
+ * state system
+ */
+ updateEntriesForStateSystem(ss, timestamp, (TmfTreeViewerEntry) ssEntry);
+ } else {
+ /*
+ * The state system existed but entries were not filled,
+ * that would occur if for instance the values were out
+ * of range at the first query.
+ */
+ fillEntriesForStateSystem(ss, timestamp, (TmfTreeViewerEntry) ssEntry);
+ }
+ }
+ }
+ }
+ return entries;
+ }
+
+ @NonNull
+ private ITmfTreeViewerEntry buildEntriesForTrace(@NonNull ITmfTrace trace, long timestamp, @NonNull List<ITmfTreeViewerEntry> rootEntries) {
+ TmfTreeViewerEntry traceEntry = new TmfTreeViewerEntry(trace.getName());
+ rootEntries.add(traceEntry);
+
+ Iterable<ITmfAnalysisModuleWithStateSystems> modules = trace.getAnalysisModulesOfClass(ITmfAnalysisModuleWithStateSystems.class);
+ for (ITmfAnalysisModuleWithStateSystems module : modules) {
+ /* Just schedule the module, the data will be filled when available */
+ module.schedule();
+ for (ITmfStateSystem ss : module.getStateSystems()) {
+ if (ss == null) {
+ continue;
+ }
+ buildEntriesForStateSystem(ss, timestamp, traceEntry);
+ }
+ }
+ return traceEntry;
+ }
+
+ private void buildEntriesForStateSystem(ITmfStateSystem ss, long timestamp, TmfTreeViewerEntry traceEntry) {
+ TmfTreeViewerEntry ssEntry = new TmfTreeViewerEntry(ss.getSSID());
+ traceEntry.addChild(ssEntry);
+ fillEntriesForStateSystem(ss, timestamp, ssEntry);
+ }
+
+ private void fillEntriesForStateSystem(ITmfStateSystem ss, long timestamp, TmfTreeViewerEntry ssEntry) {
+ try {
+ addChildren(ss, ss.queryFullState(timestamp), -1, ssEntry, timestamp);
+ } catch (StateSystemDisposedException | TimeRangeException e) {
+ /* Nothing to do */
+ }
+ }
+
+ /**
+ * Add children node to an entry. It will create all necessary entries.
+ */
+ private void addChildren(ITmfStateSystem ss, List<ITmfStateInterval> fullState, int rootQuark, TmfTreeViewerEntry root, long timestamp) {
+ try {
+ for (int quark : ss.getSubAttributes(rootQuark, false)) {
+
+ ITmfStateInterval interval = fullState.get(quark);
+
+ StateSystemEntry entry = new StateSystemEntry(ss.getAttributeName(quark), quark, ss.getFullAttributePath(quark),
+ interval.getStateValue(),
+ new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE),
+ new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE));
+
+ /* Add this node's children recursively */
+ addChildren(ss, fullState, quark, entry, timestamp);
+
+ /**
+ * <pre>
+ * Do not add this entry to root if
+ * 1- the filter status is ON
+ * AND
+ * 2- the entry has no children
+ * AND
+ * 3- the start time is not the current timestamp
+ * </pre>
+ */
+ if (!(fFilterStatus && !entry.hasChildren() && (interval.getStartTime() != timestamp))) {
+ root.addChild(entry);
+ }
+ }
+
+ } catch (AttributeNotFoundException e) {
+ /* Should not happen, we're iterating on known attributes */
+ throw new RuntimeException();
+ }
+ }
+
+ private void updateEntriesForStateSystem(ITmfStateSystem ss, long timestamp, TmfTreeViewerEntry ssEntry) {
+ try {
+ updateChildren(ss, ss.queryFullState(timestamp), ssEntry);
+ } catch (StateSystemDisposedException e) {
+ } catch (TimeRangeException e) {
+ /* Mark all entries out of range */
+ markOutOfRange(ssEntry);
+ }
+ }
+
+ /**
+ * Update the values of existing entries.
+ */
+ private void updateChildren(ITmfStateSystem ss, List<ITmfStateInterval> fullState, ITmfTreeViewerEntry root) {
+ for (ITmfTreeViewerEntry entry : root.getChildren()) {
+ if (entry instanceof StateSystemEntry) {
+ /*
+ * FIXME: if new sub attributes were added since the element was
+ * built, then then will not be added
+ */
+ StateSystemEntry ssEntry = (StateSystemEntry) entry;
+ ITmfStateInterval interval = fullState.get(ssEntry.getQuark());
+ if (interval != null) {
+ ssEntry.update(interval.getStateValue(), new TmfTimestamp(interval.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE),
+ new TmfTimestamp(interval.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE));
+ }
+
+ /* Update this node's children recursively */
+ updateChildren(ss, fullState, ssEntry);
+ }
+ }
+ }
+
+ /**
+ * Set the entries as out of range
+ */
+ private void markOutOfRange(ITmfTreeViewerEntry root) {
+ for (ITmfTreeViewerEntry entry : root.getChildren()) {
+ if (entry instanceof StateSystemEntry) {
+ ((StateSystemEntry) entry).setOutOfRange();
+
+ /* Update this node's children recursively */
+ markOutOfRange(entry);
+ }
+ }
+ }
+
+ /**
+ * Set the filter status of the viewer. By default, all entries of all state
+ * system are present, and the values that changed since last refresh are
+ * shown in yellow. When the filter status is true, only the entries with
+ * values modified at current time are displayed.
+ */
+ public void changeFilterStatus() {
+ fFilterStatus = !fFilterStatus;
+ if (fFilterStatus) {
+ getTreeViewer().setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
+ } else {
+ getTreeViewer().setAutoExpandLevel(DEFAULT_AUTOEXPAND);
+ clearContent();
+ }
+ updateContent(getSelectionBeginTime(), getSelectionEndTime(), true);
+ }
+
+ /**
+ * Update the display to use the updated timestamp format
+ *
+ * @param signal
+ * the incoming signal
+ */
+ @TmfSignalHandler
+ public void timestampFormatUpdated(TmfTimestampFormatUpdateSignal signal) {
+ updateContent(getSelectionBeginTime(), getSelectionEndTime(), true);
+ }
+
+ private class StateSystemEntry extends TmfTreeViewerEntry {
+
+ private final int fQuark;
+ private final String fFullPath;
+ private @NonNull TmfTimestamp fStart;
+ private @NonNull TmfTimestamp fEnd;
+ private ITmfStateValue fValue;
+ private boolean fModified = false;
+ private boolean fOutOfRange = false;
+
+ public StateSystemEntry(String name, int quark, String fullPath, ITmfStateValue value, @NonNull TmfTimestamp start, @NonNull TmfTimestamp end) {
+ super(name);
+ fQuark = quark;
+ fFullPath = fullPath;
+ fStart = start;
+ fEnd = end;
+ fValue = value;
+ }
+
+ public int getQuark() {
+ return fQuark;
+ }
+
+ public String getFullPath() {
+ return fFullPath;
+ }
+
+ public String getStartTime() {
+ if (fOutOfRange) {
+ return EMPTY_STRING;
+ }
+ return fStart.toString();
+ }
+
+ public String getEndTime() {
+ if (fOutOfRange) {
+ return EMPTY_STRING;
+ }
+ return fEnd.toString();
+ }
+
+ public String getValue() {
+ if (fOutOfRange) {
+ return Messages.OutOfRangeMsg;
+ }
+ switch (fValue.getType()) {
+ case INTEGER:
+ case LONG:
+ case DOUBLE:
+ case STRING:
+ return fValue.toString();
+ case NULL:
+ default:
+ return EMPTY_STRING;
+ }
+ }
+
+ public String getType() {
+ if (fOutOfRange) {
+ return EMPTY_STRING;
+ }
+ switch (fValue.getType()) {
+ case INTEGER:
+ return Messages.TypeInteger;
+ case LONG:
+ return Messages.TypeLong;
+ case DOUBLE:
+ return Messages.TypeDouble;
+ case STRING:
+ return Messages.TypeString;
+ case NULL:
+ default:
+ return EMPTY_STRING;
+ }
+ }
+
+ public boolean isModified() {
+ return fModified;
+ }
+
+ public void update(ITmfStateValue value, @NonNull TmfTimestamp start, @NonNull TmfTimestamp end) {
+ fModified = false;
+ fOutOfRange = false;
+ if (!start.equals(fStart)) {
+ fModified = true;
+ fStart = start;
+ fEnd = end;
+ fValue = value;
+ }
+ }
+
+ public void setOutOfRange() {
+ fModified = false;
+ fOutOfRange = true;
+ }
+ }
+}