TMF: Changed the state system explorer to use an abstract tree viewer
authorGeneviève Bastien <gbastien+lttng@versatic.net>
Mon, 17 Feb 2014 20:11:58 +0000 (15:11 -0500)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Tue, 25 Feb 2014 21:13:05 +0000 (16:13 -0500)
This adds the possibility to sort columns. And we don't need to worry about
which thread runs the code.

Change-Id: Iee53523f05eb3a3bdc6bf4d4226cc22a93bf9864
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/22185
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
IP-Clean: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Hudson CI
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/views/statesystem/TmfStateSystemExplorer.java
org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/views/statesystem/TmfStateSystemViewer.java [new file with mode: 0644]

index ea6bd1cf07341ea5fd8f45cef068ab31a8443ea7..e9f4e96a675baec278bbb609cd9811d01697724d 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * 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;
 
 /**
@@ -67,25 +41,10 @@ public class TmfStateSystemExplorer extends TmfView {
     /** 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
@@ -100,493 +59,22 @@ public class TmfStateSystemExplorer extends TmfView {
 
     @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));
@@ -601,10 +89,11 @@ public class TmfStateSystemExplorer extends TmfView {
     private class FilterAction extends Action {
         @Override
         public void run() {
-            filterStatus = !filterStatus;
-            if (!filterStatus) {
-                createTable();
-            }
+            fViewer.changeFilterStatus();
         }
     }
+
+    @Override
+    public void setFocus() {
+    }
 }
diff --git a/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/views/statesystem/TmfStateSystemViewer.java b/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/views/statesystem/TmfStateSystemViewer.java
new file mode 100644 (file)
index 0000000..34f8fbe
--- /dev/null
@@ -0,0 +1,501 @@
+/*******************************************************************************
+ * 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;
+        }
+    }
+}
This page took 0.036217 seconds and 5 git commands to generate.