fr.inria.linuxtools.tmf.ui.views.statesystem.TmfStateSystemViewer.java Source code

Java tutorial

Introduction

Here is the source code for fr.inria.linuxtools.tmf.ui.views.statesystem.TmfStateSystemViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2014 cole Polytechnique de Montral
 *
 * 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
 *   Genevive Bastien - Moved state system explorer to use the abstract tree viewer
 *******************************************************************************/

package fr.inria.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.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import fr.inria.linuxtools.statesystem.core.ITmfStateSystem;
import fr.inria.linuxtools.statesystem.core.exceptions.AttributeNotFoundException;
import fr.inria.linuxtools.statesystem.core.exceptions.StateSystemDisposedException;
import fr.inria.linuxtools.statesystem.core.exceptions.TimeRangeException;
import fr.inria.linuxtools.statesystem.core.interval.ITmfStateInterval;
import fr.inria.linuxtools.statesystem.core.statevalue.ITmfStateValue;
import fr.inria.linuxtools.tmf.core.signal.TmfSignalHandler;
import fr.inria.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
import fr.inria.linuxtools.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
import fr.inria.linuxtools.tmf.core.timestamp.ITmfTimestamp;
import fr.inria.linuxtools.tmf.core.timestamp.TmfTimestamp;
import fr.inria.linuxtools.tmf.core.trace.ITmfTrace;
import fr.inria.linuxtools.tmf.core.trace.TmfTraceManager;
import fr.inria.linuxtools.tmf.ui.viewers.tree.AbstractTmfTreeViewer;
import fr.inria.linuxtools.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
import fr.inria.linuxtools.tmf.ui.viewers.tree.ITmfTreeViewerEntry;
import fr.inria.linuxtools.tmf.ui.viewers.tree.TmfTreeColumnData;
import fr.inria.linuxtools.tmf.ui.viewers.tree.TmfTreeViewerEntry;

/**
 * Displays the content of the state systems at the current time
 *
 * @author Florian Wininger
 * @author Alexandre Montplaisir
 * @author Genevive 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 ITmfTreeViewerEntry updateElements(long start, long end, boolean selection) {
        if (getTrace() == null) {
            return null;
        }

        ITmfTreeViewerEntry root = getInput();

        if ((!selection) && (root != null)) {
            return null;
        }

        /*
         * Build the entries if it is the first time or to show only modified
         * values
         */
        if (root == null || fFilterStatus) {
            root = buildEntries(start);
        } else if (root instanceof TmfTreeViewerEntry) {
            /*
             * Update the values of the elements of the state systems at time
             * 'start'
             */
            updateEntriesList(((TmfTreeViewerEntry) root).getChildren(), start);
        }

        return root;
    }

    private ITmfTreeViewerEntry buildEntries(long timestamp) {
        // 'Fake' root node
        TmfTreeViewerEntry rootEntry = new TmfTreeViewerEntry(""); //$NON-NLS-1$

        List<ITmfTreeViewerEntry> children = rootEntry.getChildren();
        for (final ITmfTrace currentTrace : TmfTraceManager.getTraceSet(getTrace())) {
            if (currentTrace == null) {
                continue;
            }
            buildEntriesForTrace(currentTrace, timestamp, children);
        }
        return rootEntry;
    }

    /*
     * Update the values of the entries. It will also create trace and state
     * system entries if they do not exist yet.
     */
    private void 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);
                    }
                }
            }
        }
    }

    @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;
        }
    }
}