doc: Update data provider developer documentation

bug 560728

Add more example code to the documentation, for data providers and the
corresponding views.

Also add example classes in the o.e.t.examples plugin

[Fixed] Added example and documentation for data providers

Change-Id: I72c9c6c4e568ef46c31e871243e479a0f404ae6e
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/158763
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
diff --git a/doc/org.eclipse.tracecompass.doc.dev/doc/Developer-Guide.mediawiki b/doc/org.eclipse.tracecompass.doc.dev/doc/Developer-Guide.mediawiki
index e04c3af..c56e1ec 100644
--- a/doc/org.eclipse.tracecompass.doc.dev/doc/Developer-Guide.mediawiki
+++ b/doc/org.eclipse.tracecompass.doc.dev/doc/Developer-Guide.mediawiki
@@ -1802,7 +1802,7 @@
  */
 public class ExampleStateProvider extends AbstractTmfStateProvider {
 
-    private static final @NonNull String PROVIDER_ID = "example.state.provider.id"; //$NON-NLS-1$
+    private static final @NonNull String PROVIDER_ID = "org.eclipse.tracecompass.examples.state.provider"; //$NON-NLS-1$
     private static final int VERSION = 0;
 
     /**
@@ -1877,7 +1877,7 @@
     /**
      * Module ID
      */
-    public static final String ID = "example.state.system.module"; //$NON-NLS-1$
+    public static final String ID = "org.eclipse.tracecompass.examples.state.system.module"; //$NON-NLS-1$
 
     @Override
     protected @NonNull ITmfStateProvider createStateProvider() {
@@ -4763,101 +4763,743 @@
 * CANCELLED if the query was cancelled by the progress monitor
 * FAILED if an error occurred inside the data provider
 * RUNNING if the response was returned before the underlying analysis was completed, and querying the provider again with the same parameters can return a different model.
-* COMPLETED if the underlying analysis is finished and we do not expect a different model for the query parameters.
+* COMPLETED if the underlying analysis is finished and we do not expect a different response for the query parameters.
+
+''Note that a complete example of analysis, data provider and views can be found in the [https://git.eclipse.org/c/tracecompass/org.eclipse.tracecompass.git/tree/doc/org.eclipse.tracecompass.examples org.eclipse.tracecompass.examples plugin sources].''
 
 == Data provider types ==
-The base data provider returns a tree, as a list of '''TmfTreeDataModel''', with a name, ID and parent ID. The ID is unique to a provider type and the parent ID indicates which element from the list is the entry's parent to rebuild the tree from the list of models.
 
-The base '''TimeGraphEntryModel''' class extends this with a start time and end time. Concrete classes are free to add other required fields, as long as the model is serializable.
+The base data provider returns a tree, as a list of '''TmfTreeDataModel''', with a name, ID and parent ID. The ID is unique to a provider type and the parent ID indicates which element from the list is the entry's parent to rebuild the tree hierarchy from the list of models.
 
-=== Tree XY ===
-The tree XY data provider type is used to associate XY series to an entry from the tree. The data provider is queried with a filter that also contains a Collection of the IDs of the entries for which we want XY series. The response contains a map of the series for the desired IDs.
+The base '''TimeGraphEntryModel''' class extends this with a start time and end time. Concrete classes are free to add other fields, as long as the model is serializable.
+
+=== XY ===
+
+The XY data provider type is used to associate an XY series to an entry from the tree. The data provider is queried with a filter that also contains a Collection of the IDs of the entries for which we want XY series. The response contains a map of the series for the desired IDs.
 
 Each XY series can have its own x axis ('''ISeriesModel''' / '''SeriesModel''' - encapsulated in an '''ITmfXyModel''' / '''TmfXyModel''') or they can be shared by all models ('''IYModel''' / '''YModel''' encapsulated in an '''ITmfCommonXAxisModel''' / '''TmfCommonXAxisModel'''). The X axis is an array of longs, which makes it useful for a time axis or time buckets, but it can be used for any XY content.
 
 The interface to implement is '''ITmfTreeXYDataProvider'''.
 
+Here is a simple example of XY data provider, retrieving data from a simple state system displaying the child attributes of the root attributes.
+
+<pre>
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.internal.tmf.core.model.tree.AbstractTreeDataProvider;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
+import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.tmf.core.model.TmfCommonXAxisModel;
+import org.eclipse.tracecompass.tmf.core.model.YModel;
+import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfTreeXYDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXyModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+/**
+ * An example of an XY data provider.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+@NonNullByDefault
+public class ExampleXYDataProvider extends AbstractTreeDataProvider<ExampleStateSystemAnalysisModule, TmfTreeDataModel> implements ITmfTreeXYDataProvider<TmfTreeDataModel> {
+
+    /**
+     * Provider unique ID.
+     */
+    public static final String ID = "org.eclipse.tracecompass.examples.xy.dataprovider"; //$NON-NLS-1$
+    private static final AtomicLong sfAtomicId = new AtomicLong();
+
+    private final BiMap<Long, Integer> fIDToDisplayQuark = HashBiMap.create();
+    private final Map<Integer, String> fQuarkToString = new HashMap<>();
+
+    /**
+     * Constructor
+     *
+     * @param trace
+     *            The trace this data provider is for
+     * @param analysisModule
+     *            The analysis module
+     */
+    public ExampleXYDataProvider(ITmfTrace trace, ExampleStateSystemAnalysisModule analysisModule) {
+        super(trace, analysisModule);
+    }
+
+    /**
+     * Create the time graph data provider
+     *
+     * @param trace
+     *            The trace for which is the data provider
+     * @return The data provider
+     */
+    public static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? new ExampleXYDataProvider(trace, module) : null;
+    }
+
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    protected boolean isCacheable() {
+        return true;
+    }
+
+    @Override
+    protected TmfTreeModel<TmfTreeDataModel> getTree(ITmfStateSystem ss, Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
+        // Make an entry for each base quark
+        List<TmfTreeDataModel> entryList = new ArrayList<>();
+        for (Integer quark : ss.getQuarks("CPUs", "*")) { //$NON-NLS-1$ //$NON-NLS-2$
+            int statusQuark = ss.optQuarkRelative(quark, "Status"); //$NON-NLS-1$
+            if (statusQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
+                Long id = fIDToDisplayQuark.inverse().computeIfAbsent(statusQuark, q -> sfAtomicId.getAndIncrement());
+                entryList.add(new TmfTreeDataModel(id, -1, ss.getAttributeName(quark)));
+                fQuarkToString.put(statusQuark, ss.getAttributeName(quark));
+            }
+        }
+        return new TmfTreeModel<>(Collections.emptyList(), entryList);
+    }
+
+    @Override
+    public TmfModelResponse<ITmfXyModel> fetchXY(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        ITmfStateSystem ss = getAnalysisModule().getStateSystem();
+        if (ss == null) {
+            return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+        }
+
+        Map<Integer, double[]> quarkToValues = new HashMap<>();
+        // Prepare the quarks to display
+        Collection<Long> selectedItems = DataProviderParameterUtils.extractSelectedItems(fetchParameters);
+        if (selectedItems == null) {
+            // No selected items, take them all
+            selectedItems = fIDToDisplayQuark.keySet();
+        }
+        List<Long> times = getTimes(ss, DataProviderParameterUtils.extractTimeRequested(fetchParameters));
+        for (Long id : selectedItems) {
+            Integer quark = fIDToDisplayQuark.get(id);
+            if (quark != null) {
+                quarkToValues.put(quark, new double[times.size()]);
+            }
+        }
+        long[] nativeTimes = new long[times.size()];
+        for (int i = 0; i < times.size(); i++) {
+            nativeTimes[i] = times.get(i);
+        }
+
+        // Query the state system to fill the array of values
+        try {
+            for (ITmfStateInterval interval : ss.query2D(quarkToValues.keySet(), times)) {
+                if (monitor != null && monitor.isCanceled()) {
+                    return new TmfModelResponse<>(null, Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
+                }
+                double[] row = quarkToValues.get(interval.getAttribute());
+                Object value = interval.getValue();
+                if (row != null && (value instanceof Number)) {
+                    Double dblValue = ((Number) value).doubleValue();
+                    for (int i = 0; i < times.size(); i++) {
+                        Long time = times.get(i);
+                        if (interval.getStartTime() <= time && interval.getEndTime() >= time) {
+                            row[i] = dblValue;
+                        }
+                    }
+                }
+            }
+        } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
+            return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
+        }
+        Map<String, IYModel> models = new HashMap<>();
+        for (Entry<Integer, double[]> values : quarkToValues.entrySet()) {
+            models.put(String.valueOf(fQuarkToString.get(values.getKey())), new YModel(fIDToDisplayQuark.inverse().getOrDefault(values.getKey(), -1L), String.valueOf(fQuarkToString.get(values.getKey())), values.getValue()));
+        }
+
+        return new TmfModelResponse<>(new TmfCommonXAxisModel("Example XY data provider", nativeTimes, models), Status.COMPLETED, CommonStatusMessage.COMPLETED); //$NON-NLS-1$
+    }
+
+    private static List<Long> getTimes(ITmfStateSystem key, @Nullable List<Long> list) {
+        if (list == null) {
+            return Collections.emptyList();
+        }
+        List<@NonNull Long> times = new ArrayList<>();
+        for (long t : list) {
+            if (key.getStartTime() <= t && t <= key.getCurrentEndTime()) {
+                times.add(t);
+            }
+        }
+        Collections.sort(times);
+        return times;
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<ITmfXyModel> fetchXY(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
+        return fetchXY(parameters, monitor);
+    }
+}
+</pre>
+
 === Time Graph ===
+
 The Time Graph data provider is used to associate states to tree entries, i.e. a sampled list of states, with a start time, duration, integer value and optional label. The time graph states ('''ITimeGraphState''' / '''TimeGraphState''') are encapsulated in an '''ITimeGraphRowModel''' which also provides the ID of the entry they map to.
 
 The time graph data provider can also supply arrows to link entries one to another with a start time and start ID as well as a duration and target ID. The interface to implement is '''ITimeGraphArrow''', else '''TimeGraphArrow''' can be extended.
 
-Additional information can be added to the states with tooltips, which are maps of tooltip entry names to tooltip entry values.
+Additional information can be added to the states with tooltips, which are maps of tooltip entry names to tooltip entry values. The data provider may also suggest styles for the states by implementing the '''IOutputStyleProvider''' interface.
 
 The interface to implement is '''ITimeGraphDataProvider'''.
 
+Also, if the data provider wants to provide some styling information, for example, colors, height and opacity, etc, it can implement the '''IOutputStyleProvider''' interface who will add a method to fetch styles. The '''TimeGraphState''' objects can then be constructed with a style and the view will automatically use this style information.
+
+Here is a simple example of a time graph data provider, retrieving data from a simple state system where each root attribute is to be displayed. It also provides simple styling.
+
+<pre>
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Predicate;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.AbstractTmfTraceDataProvider;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
+import org.eclipse.tracecompass.tmf.core.dataprovider.X11ColorUtils;
+import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider;
+import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
+import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel;
+import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+
+/**
+ * An example of a time graph data provider.
+ *
+ * This class is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+@NonNullByDefault
+public class ExampleTimeGraphDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel>, IOutputStyleProvider {
+
+    /**
+     * Provider unique ID.
+     */
+    public static final String ID = "org.eclipse.tracecompass.examples.timegraph.dataprovider"; //$NON-NLS-1$
+    private static final AtomicLong sfAtomicId = new AtomicLong();
+    private static final String STYLE0_NAME = "style0"; //$NON-NLS-1$
+    private static final String STYLE1_NAME = "style1"; //$NON-NLS-1$
+    private static final String STYLE2_NAME = "style2"; //$NON-NLS-1$
+
+    /* The map of basic styles */
+    private static final Map<String, OutputElementStyle> STATE_MAP;
+    /*
+     * A map of styles names to a style that has the basic style as parent, to
+     * avoid returning complete styles for each state
+     */
+    private static final Map<String, OutputElementStyle> STYLE_MAP;
+
+    static {
+        /* Build three different styles to use as examples */
+        ImmutableMap.Builder<String, OutputElementStyle> builder = new ImmutableMap.Builder<>();
+
+        builder.put(STYLE0_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE0_NAME,
+                StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("blue")), //$NON-NLS-1$
+                StyleProperties.HEIGHT, 0.5f,
+                StyleProperties.OPACITY, 0.75f)));
+        builder.put(STYLE1_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE1_NAME,
+                StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("yellow")), //$NON-NLS-1$
+                StyleProperties.HEIGHT, 1.0f,
+                StyleProperties.OPACITY, 1.0f)));
+        builder.put(STYLE2_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE2_NAME,
+                StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("green")), //$NON-NLS-1$
+                StyleProperties.HEIGHT, 0.75f,
+                StyleProperties.OPACITY, 0.5f)));
+        STATE_MAP = builder.build();
+
+        /* build the style map too */
+        builder = new ImmutableMap.Builder<>();
+        builder.put(STYLE0_NAME, new OutputElementStyle(STYLE0_NAME));
+        builder.put(STYLE1_NAME, new OutputElementStyle(STYLE1_NAME));
+        builder.put(STYLE2_NAME, new OutputElementStyle(STYLE2_NAME));
+        STYLE_MAP = builder.build();
+    }
+
+    private final BiMap<Long, Integer> fIDToDisplayQuark = HashBiMap.create();
+    private ExampleStateSystemAnalysisModule fModule;
+
+    /**
+     * Constructor
+     *
+     * @param trace
+     *            The trace this analysis is for
+     * @param module
+     *            The scripted analysis for this data provider
+     */
+    public ExampleTimeGraphDataProvider(ITmfTrace trace, ExampleStateSystemAnalysisModule module) {
+        super(trace);
+        fModule = module;
+    }
+
+    /**
+     * Create the time graph data provider
+     *
+     * @param trace
+     *            The trace for which is the data provider
+     * @return The data provider
+     */
+    public static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? new ExampleTimeGraphDataProvider(trace, module) : null;
+    }
+
+    @Override
+    public TmfModelResponse<TmfTreeModel<@NonNull ITimeGraphEntryModel>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        fModule.waitForInitialization();
+        ITmfStateSystem ss = fModule.getStateSystem();
+        if (ss == null) {
+            return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+        }
+
+        boolean isComplete = ss.waitUntilBuilt(0);
+        long endTime = ss.getCurrentEndTime();
+
+        // Make an entry for each base quark
+        List<ITimeGraphEntryModel> entryList = new ArrayList<>();
+        for (Integer quark : ss.getQuarks("CPUs", "*")) { //$NON-NLS-1$ //$NON-NLS-2$
+            Long id = fIDToDisplayQuark.inverse().computeIfAbsent(quark, q -> sfAtomicId.getAndIncrement());
+            entryList.add(new TimeGraphEntryModel(id, -1, ss.getAttributeName(quark), ss.getStartTime(), endTime));
+        }
+
+        Status status = isComplete ? Status.COMPLETED : Status.RUNNING;
+        String msg = isComplete ? CommonStatusMessage.COMPLETED : CommonStatusMessage.RUNNING;
+        return new TmfModelResponse<>(new TmfTreeModel<>(Collections.emptyList(), entryList), status, msg);
+    }
+
+    @Override
+    public @NonNull String getId() {
+        return ID;
+    }
+
+    @Override
+    public @NonNull TmfModelResponse<TimeGraphModel> fetchRowModel(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        ITmfStateSystem ss = fModule.getStateSystem();
+        if (ss == null) {
+            return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+        }
+
+        try {
+            List<@NonNull ITimeGraphRowModel> rowModels = getDefaultRowModels(fetchParameters, ss, monitor);
+            if (rowModels == null) {
+                rowModels = Collections.emptyList();
+            }
+            return new TmfModelResponse<>(new TimeGraphModel(rowModels), Status.COMPLETED, CommonStatusMessage.COMPLETED);
+        } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
+            return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
+        }
+    }
+
+    private @Nullable List<ITimeGraphRowModel> getDefaultRowModels(Map<String, Object> fetchParameters, ITmfStateSystem ss, @Nullable IProgressMonitor monitor) throws IndexOutOfBoundsException, TimeRangeException, StateSystemDisposedException {
+        Map<Integer, ITimeGraphRowModel> quarkToRow = new HashMap<>();
+        // Prepare the quarks to display
+        Collection<Long> selectedItems = DataProviderParameterUtils.extractSelectedItems(fetchParameters);
+        if (selectedItems == null) {
+            // No selected items, take them all
+            selectedItems = fIDToDisplayQuark.keySet();
+        }
+        for (Long id : selectedItems) {
+            Integer quark = fIDToDisplayQuark.get(id);
+            if (quark != null) {
+                quarkToRow.put(quark, new TimeGraphRowModel(id, new ArrayList<>()));
+            }
+        }
+
+        // This regex map automatically filters or highlights the entry
+        // according to the global filter entered by the user
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull Object>>> predicates = new HashMap<>();
+        Multimap<@NonNull Integer, @NonNull String> regexesMap = DataProviderParameterUtils.extractRegexFilter(fetchParameters);
+        if (regexesMap != null) {
+            predicates.putAll(computeRegexPredicate(regexesMap));
+        }
+
+        // Query the state system to fill the states
+        long currentEndTime = ss.getCurrentEndTime();
+        for (ITmfStateInterval interval : ss.query2D(quarkToRow.keySet(), getTimes(ss, DataProviderParameterUtils.extractTimeRequested(fetchParameters)))) {
+            if (monitor != null && monitor.isCanceled()) {
+                return Collections.emptyList();
+            }
+            ITimeGraphRowModel row = quarkToRow.get(interval.getAttribute());
+            if (row != null) {
+                List<@NonNull ITimeGraphState> states = row.getStates();
+                ITimeGraphState timeGraphState = getStateFromInterval(interval, currentEndTime);
+                // This call will compare the state with the filter predicate
+                applyFilterAndAddState(states, timeGraphState, row.getEntryID(), predicates, monitor);
+            }
+        }
+        for (ITimeGraphRowModel model : quarkToRow.values()) {
+            model.getStates().sort(Comparator.comparingLong(ITimeGraphState::getStartTime));
+        }
+
+        return new ArrayList<>(quarkToRow.values());
+    }
+
+    private static TimeGraphState getStateFromInterval(ITmfStateInterval statusInterval, long currentEndTime) {
+        long time = statusInterval.getStartTime();
+        long duration = Math.min(currentEndTime, statusInterval.getEndTime() + 1) - time;
+        Object o = statusInterval.getValue();
+        if (!(o instanceof Long)) {
+            // Add a null state
+            return new TimeGraphState(time, duration, Integer.MIN_VALUE);
+        }
+        String styleName = "style" + ((Long) o) % 3; //$NON-NLS-1$
+        return new TimeGraphState(time, duration, String.valueOf(o), STYLE_MAP.get(styleName));
+    }
+
+    private static Set<Long> getTimes(ITmfStateSystem key, @Nullable List<Long> list) {
+        if (list == null) {
+            return Collections.emptySet();
+        }
+        Set<@NonNull Long> times = new HashSet<>();
+        for (long t : list) {
+            if (key.getStartTime() <= t && t <= key.getCurrentEndTime()) {
+                times.add(t);
+            }
+        }
+        return times;
+    }
+
+    @Override
+    public @NonNull TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        /**
+         * If there were arrows to be drawn, this is where they would be defined
+         */
+        return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    @Override
+    public @NonNull TmfModelResponse<Map<String, String>> fetchTooltip(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        /**
+         * If there were tooltips to be drawn, this is where they would be
+         * defined
+         */
+        return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    @Override
+    public TmfModelResponse<OutputStyleModel> fetchStyle(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        return new TmfModelResponse<>(new OutputStyleModel(STATE_MAP), Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<List<ITimeGraphEntryModel>> fetchTree(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
+        TmfModelResponse<TmfTreeModel<ITimeGraphEntryModel>> modelResponse = fetchTree(parameters, monitor);
+        TmfTreeModel<ITimeGraphEntryModel> model = modelResponse.getModel();
+        return new TmfModelResponse<>(model == null ? null : model.getEntries(), modelResponse.getStatus(), modelResponse.getStatusMessage());
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<List<ITimeGraphRowModel>> fetchRowModel(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
+        TmfModelResponse<TimeGraphModel> modelResponse = fetchRowModel(parameters, monitor);
+        TimeGraphModel model = modelResponse.getModel();
+        return new TmfModelResponse<>(model == null ? null : model.getRows(), modelResponse.getStatus(), modelResponse.getStatusMessage());
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
+        return fetchArrows(parameters, monitor);
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<Map<String, String>> fetchTooltip(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
+        return fetchTooltip(parameters, monitor);
+    }
+
+}
+</pre>
+
 == Data provider management ==
+
 Data providers can be handled by the '''DataProviderManager''' class, which uses an extension point and factories for data providers.
+
 This manager associates a unique data provider per trace and extension point ID, ensuring that data providers can be reused and that each entry for a trace reuses the same unique entry ID.
 
+=== Data Provider Factories ===
+
+The data provider manager requires a factory for the various data providers, to create the data provider instances for a trace. Here is an example of factory class to create the time graph data provider of the previous section.
+
+<pre>
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.DataProviderDescriptor;
+import org.eclipse.tracecompass.internal.tmf.core.model.xy.TmfTreeXYCompositeDataProvider;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+/**
+ * An example of a time graph data provider factory.
+ *
+ * This factory is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class ExampleTimeGraphProviderFactory implements IDataProviderFactory {
+
+    private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
+            .setId(ExampleTimeGraphDataProvider.ID)
+            .setName("Example time graph data provider") //$NON-NLS-1$
+            .setDescription("This is an example of a time graph data provider using a state system analysis as a source of data") //$NON-NLS-1$
+            .setProviderType(ProviderType.TIME_GRAPH)
+            .build();
+
+    @Override
+    public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+        Collection<@NonNull ITmfTrace> traces = TmfTraceManager.getTraceSet(trace);
+        if (traces.size() == 1) {
+            return ExampleTimeGraphDataProvider.create(trace);
+        }
+        return TmfTreeXYCompositeDataProvider.create(traces, "Example time graph data provider", ExampleTimeGraphDataProvider.ID); //$NON-NLS-1$
+    }
+
+    @Override
+    public Collection<IDataProviderDescriptor> getDescriptors(@NonNull ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
+    }
+
+}
+</pre>
+
 === Extension point ===
-example:
+This extension needs to be added to the plugin's plugin.xml file:
 <pre>
 <extension point="org.eclipse.tracecompass.tmf.core.dataprovider">
     <dataProviderFactory
-        class="org.eclipse.tracecompass.analysis.graph.core.dataprovider.CriticalPathDataProviderFactory"
-        id="org.eclipse.tracecompass.analysis.graph.core.dataprovider.CriticalPathDataProvider">
+         class="org.eclipse.tracecompass.examples.core.data.provider.ExampleTimeGraphProviderFactory"
+         id="org.eclipse.tracecompass.examples.timegraph.dataprovider">
+    </dataProviderFactory>
+    <dataProviderFactory
+         class="org.eclipse.tracecompass.examples.core.data.provider.ExampleXYDataProviderFactory"
+         id="org.eclipse.tracecompass.examples.xy.dataprovider">
     </dataProviderFactory>
 </extension>
 </pre>
 
 === Experiments ===
+
 In the data provider manager, experiments also get a unique instance of a data provider, which can be specific or encapsulate the data providers from the child traces. For example, an experiment can have its own concrete data provider when required (an analysis that runs only on experiments), or the factory would create a '''CompositeDataProvider''' (using '''TmfTreeXYCompositeDataProvider''' or '''TmfTimeGraphCompositeDataProvider''') encapsulating the providers from its traces. The benefit of encapsulating the providers from child traces is that their entries/IDs can be reused, limiting the number of created objects and ensuring consistency in views. These composite data providers dispatch the request to all the encapsulated providers and aggregates the results into the expected data structure.
 
 == Utilities ==
+
 Abstract base classes are provided for TreeXY and time graph data providers based on '''TmfStateSystemAnalysisModule'''s ('''AbstractTreeCommonXDataProvider''' and '''AbstractTimeGraphDataProvider''', respectively). They handle concurrency, mapping of state system attributes to unique IDs, exceptions, caching and encapsulating the model in a response with the correct status.
 
-On the view side, we recommend building TreeXY views from '''AbstractSelectTreeViewer''' and '''TmfFilteredXYChartViewer''' and building Time Graph Views by extending  '''BaseDataProviderTimeGraphView'''.
+== Views ==
 
-=== Example Tree XY code ===
+Data providers are used to populate views. When the data provider is well implemented and if the view does not require any additional behavior, it should be straightforward to implement.
 
-Below is an example of the minimum implementation required to display the data from a tree XY data provider.
+=== XY views ===
+
+XY data providers can be visualized with a view extending '''TmfChartView'''. Here's an example of the minimal implementation required to display its data.
+
+The tree viewer (left part) can be an extension of the '''AbstractSelectTreeViewer''' class, while the chart part, showing the XY chart itself can extend '''AbstractSelectTreeViewer'''.
 
 Out of the box, it supports experiments, updating during the trace analysis, Pin & Clone and a number of chart viewer features.
 
 <pre>
-public class TreeXyView extends TmfChartView {
+import java.util.Comparator;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.examples.core.data.provider.ExampleXYDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractSelectTreeViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeViewerEntry;
+import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfFilteredXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfXYChartSettings;
+import org.eclipse.tracecompass.tmf.ui.views.TmfChartView;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * An example of a data provider XY view
+ *
+ * @author Geneviève Bastien
+ */
+public class ExampleXYDataProviderView extends TmfChartView {
+
+    /** View ID. */
+    public static final String ID = "org.eclipse.tracecompass.examples.dataprovider.xyview"; //$NON-NLS-1$
 
     /**
      * Constructor
      */
-    public TreeXyView() {
-        super("Tree XY View");
+    public ExampleXYDataProviderView() {
+        super("Example Tree XY View"); //$NON-NLS-1$
     }
 
     @Override
-    protected TmfXYChartViewer createChartViewer(Composite parent) {
+    protected TmfXYChartViewer createChartViewer(@Nullable Composite parent) {
         TmfXYChartSettings settings = new TmfXYChartSettings(null, null, null, 1);
-        return new TmfFilteredXYChartViewer(parent, settings, DataProvider.ID);
+        return new TmfFilteredXYChartViewer(parent, settings, ExampleXYDataProvider.ID);
     }
 
     private static final class TreeXyViewer extends AbstractSelectTreeViewer {
 
-        private final class TreeXyLabelProvider extends TreeLabelProvider {
+        private final class TreeXyLabelProvider extends AbstractSelectTreeViewer.TreeLabelProvider {
             @Override
-            public Image getColumnImage(Object element, int columnIndex) {
+            public @Nullable Image getColumnImage(@Nullable Object element, int columnIndex) {
                 if (columnIndex == 1 && element instanceof TmfTreeViewerEntry && isChecked(element)) {
-                    return getLegendImage(((TmfTreeViewerEntry) element).getName());
+                    return getLegendImage(String.valueOf(((TmfTreeViewerEntry) element).getName()));
                 }
                 return null;
             }
         }
 
         public TreeXyViewer(Composite parent) {
-            super(parent, 1, DataProvider.ID);
+            super(parent, 1, ExampleXYDataProvider.ID);
             setLabelProvider(new TreeXyLabelProvider());
         }
 
         @Override
         protected ITmfTreeColumnDataProvider getColumnDataProvider() {
-            return () -> ImmutableList.of(createColumn("Name", Comparator.comparing(TmfTreeViewerEntry::getName)),
-                    new TmfTreeColumnData("Legend"));
+            return () -> ImmutableList.of(createColumn("Name", Comparator.comparing(TmfTreeViewerEntry::getName)), //$NON-NLS-1$
+                    new TmfTreeColumnData("Legend")); //$NON-NLS-1$
         }
     }
 
     @Override
-    protected @NonNull TmfViewer createLeftChildViewer(Composite parent) {
-        return new TreeXyViewer(parent);
+    protected @NonNull TmfViewer createLeftChildViewer(@Nullable Composite parent) {
+        return new TreeXyViewer(Objects.requireNonNull(parent));
     }
 }
+</pre>
+
+=== Time Graph Views ===
+
+For time graph view populated by a data provider, the base class to extend would be the '''BaseDataProviderTimeGraphView'''. The default implementation is fairly straightforward and if the the data provider also provides styling through the '''IOutputStyleProvider''', an instance of the '''BaseDataProviderTimeGraphPresentationProvider''' can be used directly. 
+
+If there is no styling though, all states would be drawn as black states. In that case, a presentation provider will need to be added to the view. It can extend '''TimeGraphPresentationProvider'''.
+
+Here is the simplest implementation of the time graph view using a data provider:
+
+<pre>
+import org.eclipse.tracecompass.examples.core.data.provider.ExampleTimeGraphDataProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.ui.widgets.timegraph.BaseDataProviderTimeGraphPresentationProvider;
+import org.eclipse.tracecompass.tmf.ui.views.timegraph.BaseDataProviderTimeGraphView;
+
+/**
+ * An example of a data provider time graph view
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class ExampleTimeGraphDataProviderView extends BaseDataProviderTimeGraphView {
+
+    /** View ID. */
+    public static final String ID = "org.eclipse.tracecompass.examples.dataprovider.tgview"; //$NON-NLS-1$
+
+    /**
+     * Default constructor
+     */
+    public ExampleTimeGraphDataProviderView() {
+       super(ID, new BaseDataProviderTimeGraphPresentationProvider(), ExampleTimeGraphDataProvider.ID);
+    }
+
+}
 </pre>
\ No newline at end of file
diff --git a/doc/org.eclipse.tracecompass.examples/META-INF/MANIFEST.MF b/doc/org.eclipse.tracecompass.examples/META-INF/MANIFEST.MF
index 0dc17c7..af21da2 100644
--- a/doc/org.eclipse.tracecompass.examples/META-INF/MANIFEST.MF
+++ b/doc/org.eclipse.tracecompass.examples/META-INF/MANIFEST.MF
@@ -10,12 +10,15 @@
  org.eclipse.tracecompass.common.core,
  org.eclipse.tracecompass.tmf.core,
  org.eclipse.tracecompass.tmf.ui,
+ org.eclipse.core.resources,
  org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Export-Package: org.eclipse.tracecompass.examples.core.analysis;x-internal:=true,
+ org.eclipse.tracecompass.examples.core.data.provider;x-internal:=true,
  org.eclipse.tracecompass.examples.ui;x-internal:=true,
  org.eclipse.tracecompass.examples.ui.viewers.histogram;x-internal:=true,
+ org.eclipse.tracecompass.examples.ui.views.data.provider,
  org.eclipse.tracecompass.examples.ui.views.histogram;x-internal:=true
 Bundle-Vendor: %Bundle-Vendor
 Import-Package: com.google.common.collect,
diff --git a/doc/org.eclipse.tracecompass.examples/plugin.properties b/doc/org.eclipse.tracecompass.examples/plugin.properties
index b70e05b..7244efb 100644
--- a/doc/org.eclipse.tracecompass.examples/plugin.properties
+++ b/doc/org.eclipse.tracecompass.examples/plugin.properties
@@ -17,3 +17,5 @@
 
 tracing.examples.histogram.view.name=New Histogram
 tracing.examples.histogram.view.description=New Histogram Implementation Based on State System
+tracing.examples.dataprovider.timegraph.view.name=Example Data Provider Time Graph View
+tracing.examples.dataprovider.xy.view.name=Example Data Provider XY View
diff --git a/doc/org.eclipse.tracecompass.examples/plugin.xml b/doc/org.eclipse.tracecompass.examples/plugin.xml
index f9a7d2e..42b17a1 100644
--- a/doc/org.eclipse.tracecompass.examples/plugin.xml
+++ b/doc/org.eclipse.tracecompass.examples/plugin.xml
@@ -10,11 +10,25 @@
             name="%tracing.examples.histogram.view.name"
             restorable="true">
       </view>
+      <view
+            category="org.eclipse.linuxtools.tmf.ui.views.category"
+            class="org.eclipse.tracecompass.examples.ui.views.data.provider.ExampleTimeGraphDataProviderView"
+            id="org.eclipse.tracecompass.examples.dataprovider.tgview"
+            name="%tracing.examples.dataprovider.timegraph.view.name"
+            restorable="true">
+      </view>
+      <view
+            category="org.eclipse.linuxtools.tmf.ui.views.category"
+            class="org.eclipse.tracecompass.examples.ui.views.data.provider.ExampleXYDataProviderView"
+            id="org.eclipse.tracecompass.examples.dataprovider.xyview"
+            name="%tracing.examples.dataprovider.xy.view.name"
+            restorable="true">
+      </view>
    </extension>
    <extension
          point="org.eclipse.linuxtools.tmf.core.analysis">
       <module
-         id="example.state.system.module"
+         id="org.eclipse.tracecompass.examples.state.system.module"
          name="Example State System Module"
          analysis_module="org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule"
          automatic="true">
@@ -22,6 +36,30 @@
                class="org.eclipse.tracecompass.tmf.core.trace.TmfTrace">
          </tracetype>
       </module>
+      <output
+            class="org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput"
+            id="org.eclipse.tracecompass.examples.dataprovider.tgview">
+         <analysisId
+               id="org.eclipse.tracecompass.examples.state.system.module">
+         </analysisId>
+      </output>
+      <output
+            class="org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput"
+            id="org.eclipse.tracecompass.examples.dataprovider.xyview">
+         <analysisId
+               id="org.eclipse.tracecompass.examples.state.system.module">
+         </analysisId>
+      </output>
+   </extension>
+   <extension point="org.eclipse.tracecompass.tmf.core.dataprovider">
+      <dataProviderFactory
+         class="org.eclipse.tracecompass.examples.core.data.provider.ExampleTimeGraphProviderFactory"
+         id="org.eclipse.tracecompass.examples.timegraph.dataprovider">
+      </dataProviderFactory>
+      <dataProviderFactory
+         class="org.eclipse.tracecompass.examples.core.data.provider.ExampleXYDataProviderFactory"
+         id="org.eclipse.tracecompass.examples.xy.dataprovider">
+      </dataProviderFactory>
    </extension>
 
 </plugin>
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateProvider.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateProvider.java
index 793f037..0667000 100644
--- a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateProvider.java
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateProvider.java
@@ -33,7 +33,7 @@
  */
 public class ExampleStateProvider extends AbstractTmfStateProvider {
 
-    private static final @NonNull String PROVIDER_ID = "example.state.provider.id"; //$NON-NLS-1$
+    private static final @NonNull String PROVIDER_ID = "org.eclipse.tracecompass.examples.state.provider"; //$NON-NLS-1$
     private static final int VERSION = 0;
 
     /**
@@ -74,14 +74,13 @@
 
             ITmfStateSystemBuilder ss = Objects.requireNonNull(getStateSystemBuilder());
             int quark = ss.getQuarkAbsoluteAndAdd("CPUs", String.valueOf(cpu)); //$NON-NLS-1$
+            // The main quark contains the tid of the running thread
+            ss.modifyAttribute(ts, nextTid, quark);
 
             // The status attribute has an integer value
             int statusQuark = ss.getQuarkRelativeAndAdd(quark, "Status"); //$NON-NLS-1$
             Integer value = (nextTid > 0 ? 1 : 0);
             ss.modifyAttribute(ts, value, statusQuark);
-
-            // The main quark contains the tid of the running thread
-            ss.modifyAttribute(ts, nextTid, quark);
         }
     }
 
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateSystemAnalysisModule.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateSystemAnalysisModule.java
index 64333e0..a5d26b8 100644
--- a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateSystemAnalysisModule.java
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/analysis/ExampleStateSystemAnalysisModule.java
@@ -30,7 +30,7 @@
     /**
      * Module ID
      */
-    public static final String ID = "example.state.system.module"; //$NON-NLS-1$
+    public static final String ID = "org.eclipse.tracecompass.examples.state.system.module"; //$NON-NLS-1$
 
     @Override
     protected @NonNull ITmfStateProvider createStateProvider() {
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleTimeGraphDataProvider.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleTimeGraphDataProvider.java
new file mode 100644
index 0000000..ec64f3b
--- /dev/null
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleTimeGraphDataProvider.java
@@ -0,0 +1,321 @@
+/**********************************************************************
+ * Copyright (c) 2020 É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 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ **********************************************************************/
+
+package org.eclipse.tracecompass.examples.core.data.provider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Predicate;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.AbstractTmfTraceDataProvider;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
+import org.eclipse.tracecompass.tmf.core.dataprovider.X11ColorUtils;
+import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider;
+import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
+import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel;
+import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
+import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+
+/**
+ * An example of a time graph data provider.
+ *
+ * This class is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+@NonNullByDefault
+public class ExampleTimeGraphDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel>, IOutputStyleProvider {
+
+    /**
+     * Provider unique ID.
+     */
+    public static final String ID = "org.eclipse.tracecompass.examples.timegraph.dataprovider"; //$NON-NLS-1$
+    private static final AtomicLong sfAtomicId = new AtomicLong();
+    private static final String STYLE0_NAME = "style0"; //$NON-NLS-1$
+    private static final String STYLE1_NAME = "style1"; //$NON-NLS-1$
+    private static final String STYLE2_NAME = "style2"; //$NON-NLS-1$
+
+    /* The map of basic styles */
+    private static final Map<String, OutputElementStyle> STATE_MAP;
+    /*
+     * A map of styles names to a style that has the basic style as parent, to
+     * avoid returning complete styles for each state
+     */
+    private static final Map<String, OutputElementStyle> STYLE_MAP;
+
+    static {
+        /* Build three different styles to use as examples */
+        ImmutableMap.Builder<String, OutputElementStyle> builder = new ImmutableMap.Builder<>();
+
+        builder.put(STYLE0_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE0_NAME,
+                StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("blue")), //$NON-NLS-1$
+                StyleProperties.HEIGHT, 0.5f,
+                StyleProperties.OPACITY, 0.75f)));
+        builder.put(STYLE1_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE1_NAME,
+                StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("yellow")), //$NON-NLS-1$
+                StyleProperties.HEIGHT, 1.0f,
+                StyleProperties.OPACITY, 1.0f)));
+        builder.put(STYLE2_NAME, new OutputElementStyle(null, ImmutableMap.of(StyleProperties.STYLE_NAME, STYLE2_NAME,
+                StyleProperties.BACKGROUND_COLOR, String.valueOf(X11ColorUtils.toHexColor("green")), //$NON-NLS-1$
+                StyleProperties.HEIGHT, 0.75f,
+                StyleProperties.OPACITY, 0.5f)));
+        STATE_MAP = builder.build();
+
+        /* build the style map too */
+        builder = new ImmutableMap.Builder<>();
+        builder.put(STYLE0_NAME, new OutputElementStyle(STYLE0_NAME));
+        builder.put(STYLE1_NAME, new OutputElementStyle(STYLE1_NAME));
+        builder.put(STYLE2_NAME, new OutputElementStyle(STYLE2_NAME));
+        STYLE_MAP = builder.build();
+    }
+
+    private final BiMap<Long, Integer> fIDToDisplayQuark = HashBiMap.create();
+    private ExampleStateSystemAnalysisModule fModule;
+
+    /**
+     * Constructor
+     *
+     * @param trace
+     *            The trace this analysis is for
+     * @param module
+     *            The scripted analysis for this data provider
+     */
+    public ExampleTimeGraphDataProvider(ITmfTrace trace, ExampleStateSystemAnalysisModule module) {
+        super(trace);
+        fModule = module;
+    }
+
+    /**
+     * Create the time graph data provider
+     *
+     * @param trace
+     *            The trace for which is the data provider
+     * @return The data provider
+     */
+    public static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? new ExampleTimeGraphDataProvider(trace, module) : null;
+    }
+
+    @Override
+    public TmfModelResponse<TmfTreeModel<@NonNull ITimeGraphEntryModel>> fetchTree(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        fModule.waitForInitialization();
+        ITmfStateSystem ss = fModule.getStateSystem();
+        if (ss == null) {
+            return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+        }
+
+        boolean isComplete = ss.waitUntilBuilt(0);
+        long endTime = ss.getCurrentEndTime();
+
+        // Make an entry for each base quark
+        List<ITimeGraphEntryModel> entryList = new ArrayList<>();
+        for (Integer quark : ss.getQuarks("CPUs", "*")) { //$NON-NLS-1$ //$NON-NLS-2$
+            Long id = fIDToDisplayQuark.inverse().computeIfAbsent(quark, q -> sfAtomicId.getAndIncrement());
+            entryList.add(new TimeGraphEntryModel(id, -1, ss.getAttributeName(quark), ss.getStartTime(), endTime));
+        }
+
+        Status status = isComplete ? Status.COMPLETED : Status.RUNNING;
+        String msg = isComplete ? CommonStatusMessage.COMPLETED : CommonStatusMessage.RUNNING;
+        return new TmfModelResponse<>(new TmfTreeModel<>(Collections.emptyList(), entryList), status, msg);
+    }
+
+    @Override
+    public @NonNull String getId() {
+        return ID;
+    }
+
+    @Override
+    public @NonNull TmfModelResponse<TimeGraphModel> fetchRowModel(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        ITmfStateSystem ss = fModule.getStateSystem();
+        if (ss == null) {
+            return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+        }
+
+        try {
+            List<@NonNull ITimeGraphRowModel> rowModels = getDefaultRowModels(fetchParameters, ss, monitor);
+            if (rowModels == null) {
+                rowModels = Collections.emptyList();
+            }
+            return new TmfModelResponse<>(new TimeGraphModel(rowModels), Status.COMPLETED, CommonStatusMessage.COMPLETED);
+        } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
+            return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
+        }
+    }
+
+    private @Nullable List<ITimeGraphRowModel> getDefaultRowModels(Map<String, Object> fetchParameters, ITmfStateSystem ss, @Nullable IProgressMonitor monitor) throws IndexOutOfBoundsException, TimeRangeException, StateSystemDisposedException {
+        Map<Integer, ITimeGraphRowModel> quarkToRow = new HashMap<>();
+        // Prepare the quarks to display
+        Collection<Long> selectedItems = DataProviderParameterUtils.extractSelectedItems(fetchParameters);
+        if (selectedItems == null) {
+            // No selected items, take them all
+            selectedItems = fIDToDisplayQuark.keySet();
+        }
+        for (Long id : selectedItems) {
+            Integer quark = fIDToDisplayQuark.get(id);
+            if (quark != null) {
+                quarkToRow.put(quark, new TimeGraphRowModel(id, new ArrayList<>()));
+            }
+        }
+
+        // This regex map automatically filters or highlights the entry
+        // according to the global filter entered by the user
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull Object>>> predicates = new HashMap<>();
+        Multimap<@NonNull Integer, @NonNull String> regexesMap = DataProviderParameterUtils.extractRegexFilter(fetchParameters);
+        if (regexesMap != null) {
+            predicates.putAll(computeRegexPredicate(regexesMap));
+        }
+
+        // Query the state system to fill the states
+        long currentEndTime = ss.getCurrentEndTime();
+        for (ITmfStateInterval interval : ss.query2D(quarkToRow.keySet(), getTimes(ss, DataProviderParameterUtils.extractTimeRequested(fetchParameters)))) {
+            if (monitor != null && monitor.isCanceled()) {
+                return Collections.emptyList();
+            }
+            ITimeGraphRowModel row = quarkToRow.get(interval.getAttribute());
+            if (row != null) {
+                List<@NonNull ITimeGraphState> states = row.getStates();
+                ITimeGraphState timeGraphState = getStateFromInterval(interval, currentEndTime);
+                // This call will compare the state with the filter predicate
+                applyFilterAndAddState(states, timeGraphState, row.getEntryID(), predicates, monitor);
+            }
+        }
+        for (ITimeGraphRowModel model : quarkToRow.values()) {
+            model.getStates().sort(Comparator.comparingLong(ITimeGraphState::getStartTime));
+        }
+
+        return new ArrayList<>(quarkToRow.values());
+    }
+
+    private static TimeGraphState getStateFromInterval(ITmfStateInterval statusInterval, long currentEndTime) {
+        long time = statusInterval.getStartTime();
+        long duration = Math.min(currentEndTime, statusInterval.getEndTime() + 1) - time;
+        Object o = statusInterval.getValue();
+        if (!(o instanceof Long)) {
+            // Add a null state
+            return new TimeGraphState(time, duration, Integer.MIN_VALUE);
+        }
+        String styleName = "style" + ((Long) o) % 3; //$NON-NLS-1$
+        return new TimeGraphState(time, duration, String.valueOf(o), STYLE_MAP.get(styleName));
+    }
+
+    private static Set<Long> getTimes(ITmfStateSystem key, @Nullable List<Long> list) {
+        if (list == null) {
+            return Collections.emptySet();
+        }
+        Set<@NonNull Long> times = new HashSet<>();
+        for (long t : list) {
+            if (key.getStartTime() <= t && t <= key.getCurrentEndTime()) {
+                times.add(t);
+            }
+        }
+        return times;
+    }
+
+    @Override
+    public @NonNull TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        /**
+         * If there were arrows to be drawn, this is where they would be defined
+         */
+        return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    @Override
+    public @NonNull TmfModelResponse<Map<String, String>> fetchTooltip(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        /**
+         * If there were tooltips to be drawn, this is where they would be
+         * defined
+         */
+        return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    @Override
+    public TmfModelResponse<OutputStyleModel> fetchStyle(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        return new TmfModelResponse<>(new OutputStyleModel(STATE_MAP), Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<List<ITimeGraphEntryModel>> fetchTree(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
+        TmfModelResponse<TmfTreeModel<ITimeGraphEntryModel>> modelResponse = fetchTree(parameters, monitor);
+        TmfTreeModel<ITimeGraphEntryModel> model = modelResponse.getModel();
+        return new TmfModelResponse<>(model == null ? null : model.getEntries(), modelResponse.getStatus(), modelResponse.getStatusMessage());
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<List<ITimeGraphRowModel>> fetchRowModel(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
+        TmfModelResponse<TimeGraphModel> modelResponse = fetchRowModel(parameters, monitor);
+        TimeGraphModel model = modelResponse.getModel();
+        return new TmfModelResponse<>(model == null ? null : model.getRows(), modelResponse.getStatus(), modelResponse.getStatusMessage());
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
+        return fetchArrows(parameters, monitor);
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<Map<String, String>> fetchTooltip(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter);
+        return fetchTooltip(parameters, monitor);
+    }
+
+}
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleTimeGraphProviderFactory.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleTimeGraphProviderFactory.java
new file mode 100644
index 0000000..e607fed
--- /dev/null
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleTimeGraphProviderFactory.java
@@ -0,0 +1,64 @@
+/**********************************************************************
+ * Copyright (c) 2020 É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 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ **********************************************************************/
+
+package org.eclipse.tracecompass.examples.core.data.provider;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.DataProviderDescriptor;
+import org.eclipse.tracecompass.internal.tmf.core.model.xy.TmfTreeXYCompositeDataProvider;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+/**
+ * An example of a time graph data provider factory.
+ *
+ * This factory is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class ExampleTimeGraphProviderFactory implements IDataProviderFactory {
+
+    private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
+            .setId(ExampleTimeGraphDataProvider.ID)
+            .setName("Example time graph data provider") //$NON-NLS-1$
+            .setDescription("This is an example of a time graph data provider using a state system analysis as a source of data") //$NON-NLS-1$
+            .setProviderType(ProviderType.TIME_GRAPH)
+            .build();
+
+    @Override
+    public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+        Collection<@NonNull ITmfTrace> traces = TmfTraceManager.getTraceSet(trace);
+        if (traces.size() == 1) {
+            return ExampleTimeGraphDataProvider.create(trace);
+        }
+        return TmfTreeXYCompositeDataProvider.create(traces, "Example time graph data provider", ExampleTimeGraphDataProvider.ID); //$NON-NLS-1$
+    }
+
+    @Override
+    public Collection<IDataProviderDescriptor> getDescriptors(@NonNull ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
+    }
+
+}
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleXYDataProvider.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleXYDataProvider.java
new file mode 100644
index 0000000..7b0998b
--- /dev/null
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleXYDataProvider.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.examples.core.data.provider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
+import org.eclipse.tracecompass.internal.tmf.core.model.tree.AbstractTreeDataProvider;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
+import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
+import org.eclipse.tracecompass.tmf.core.model.TmfCommonXAxisModel;
+import org.eclipse.tracecompass.tmf.core.model.YModel;
+import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfTreeXYDataProvider;
+import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXyModel;
+import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
+import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
+import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+/**
+ * An example of an XY data provider.
+ *
+ * This class is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+@NonNullByDefault
+public class ExampleXYDataProvider extends AbstractTreeDataProvider<ExampleStateSystemAnalysisModule, TmfTreeDataModel> implements ITmfTreeXYDataProvider<TmfTreeDataModel> {
+
+    /**
+     * Provider unique ID.
+     */
+    public static final String ID = "org.eclipse.tracecompass.examples.xy.dataprovider"; //$NON-NLS-1$
+    private static final AtomicLong sfAtomicId = new AtomicLong();
+
+    private final BiMap<Long, Integer> fIDToDisplayQuark = HashBiMap.create();
+    private final Map<Integer, String> fQuarkToString = new HashMap<>();
+
+    /**
+     * Constructor
+     *
+     * @param trace
+     *            The trace this data provider is for
+     * @param analysisModule
+     *            The analysis module
+     */
+    public ExampleXYDataProvider(ITmfTrace trace, ExampleStateSystemAnalysisModule analysisModule) {
+        super(trace, analysisModule);
+    }
+
+    /**
+     * Create the time graph data provider
+     *
+     * @param trace
+     *            The trace for which is the data provider
+     * @return The data provider
+     */
+    public static @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> create(ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? new ExampleXYDataProvider(trace, module) : null;
+    }
+
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    protected boolean isCacheable() {
+        return true;
+    }
+
+    @Override
+    protected TmfTreeModel<TmfTreeDataModel> getTree(ITmfStateSystem ss, Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
+        // Make an entry for each base quark
+        List<TmfTreeDataModel> entryList = new ArrayList<>();
+        for (Integer quark : ss.getQuarks("CPUs", "*")) { //$NON-NLS-1$ //$NON-NLS-2$
+            int statusQuark = ss.optQuarkRelative(quark, "Status"); //$NON-NLS-1$
+            if (statusQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
+                Long id = fIDToDisplayQuark.inverse().computeIfAbsent(statusQuark, q -> sfAtomicId.getAndIncrement());
+                entryList.add(new TmfTreeDataModel(id, -1, ss.getAttributeName(quark)));
+                fQuarkToString.put(statusQuark, ss.getAttributeName(quark));
+            }
+        }
+        return new TmfTreeModel<>(Collections.emptyList(), entryList);
+    }
+
+    @Override
+    public TmfModelResponse<ITmfXyModel> fetchXY(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
+        ITmfStateSystem ss = getAnalysisModule().getStateSystem();
+        if (ss == null) {
+            return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
+        }
+
+        Map<Integer, double[]> quarkToValues = new HashMap<>();
+        // Prepare the quarks to display
+        Collection<Long> selectedItems = DataProviderParameterUtils.extractSelectedItems(fetchParameters);
+        if (selectedItems == null) {
+            // No selected items, take them all
+            selectedItems = fIDToDisplayQuark.keySet();
+        }
+        List<Long> times = getTimes(ss, DataProviderParameterUtils.extractTimeRequested(fetchParameters));
+        for (Long id : selectedItems) {
+            Integer quark = fIDToDisplayQuark.get(id);
+            if (quark != null) {
+                quarkToValues.put(quark, new double[times.size()]);
+            }
+        }
+        long[] nativeTimes = new long[times.size()];
+        for (int i = 0; i < times.size(); i++) {
+            nativeTimes[i] = times.get(i);
+        }
+
+        // Query the state system to fill the array of values
+        try {
+            for (ITmfStateInterval interval : ss.query2D(quarkToValues.keySet(), times)) {
+                if (monitor != null && monitor.isCanceled()) {
+                    return new TmfModelResponse<>(null, Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED);
+                }
+                double[] row = quarkToValues.get(interval.getAttribute());
+                Object value = interval.getValue();
+                if (row != null && (value instanceof Number)) {
+                    Double dblValue = ((Number) value).doubleValue();
+                    for (int i = 0; i < times.size(); i++) {
+                        Long time = times.get(i);
+                        if (interval.getStartTime() <= time && interval.getEndTime() >= time) {
+                            row[i] = dblValue;
+                        }
+                    }
+                }
+            }
+        } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) {
+            return new TmfModelResponse<>(null, Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
+        }
+        Map<String, IYModel> models = new HashMap<>();
+        for (Entry<Integer, double[]> values : quarkToValues.entrySet()) {
+            models.put(String.valueOf(fQuarkToString.get(values.getKey())), new YModel(fIDToDisplayQuark.inverse().getOrDefault(values.getKey(), -1L), String.valueOf(fQuarkToString.get(values.getKey())), values.getValue()));
+        }
+
+        return new TmfModelResponse<>(new TmfCommonXAxisModel("Example XY data provider", nativeTimes, models), Status.COMPLETED, CommonStatusMessage.COMPLETED); //$NON-NLS-1$
+    }
+
+    private static List<Long> getTimes(ITmfStateSystem key, @Nullable List<Long> list) {
+        if (list == null) {
+            return Collections.emptyList();
+        }
+        List<@NonNull Long> times = new ArrayList<>();
+        for (long t : list) {
+            if (key.getStartTime() <= t && t <= key.getCurrentEndTime()) {
+                times.add(t);
+            }
+        }
+        Collections.sort(times);
+        return times;
+    }
+
+    @Deprecated
+    @Override
+    public TmfModelResponse<ITmfXyModel> fetchXY(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
+        Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
+        return fetchXY(parameters, monitor);
+    }
+}
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleXYDataProviderFactory.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleXYDataProviderFactory.java
new file mode 100644
index 0000000..06f6d45
--- /dev/null
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/core/data/provider/ExampleXYDataProviderFactory.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.examples.core.data.provider;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.examples.core.analysis.ExampleStateSystemAnalysisModule;
+import org.eclipse.tracecompass.internal.tmf.core.model.DataProviderDescriptor;
+import org.eclipse.tracecompass.internal.tmf.core.model.xy.TmfTreeXYCompositeDataProvider;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
+import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+
+/**
+ * An example of an xy data provider factory.
+ *
+ * This factory is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class ExampleXYDataProviderFactory implements IDataProviderFactory {
+
+    private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
+            .setId(ExampleXYDataProvider.ID)
+            .setName("Example XY data provider") //$NON-NLS-1$
+            .setDescription("This is an example of a XY data provider using a state system analysis as a source of data") //$NON-NLS-1$
+            .setProviderType(ProviderType.TREE_TIME_XY)
+            .build();
+
+    @Override
+    public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(ITmfTrace trace) {
+        Collection<@NonNull ITmfTrace> traces = TmfTraceManager.getTraceSet(trace);
+        if (traces.size() == 1) {
+            return ExampleXYDataProvider.create(trace);
+        }
+        return TmfTreeXYCompositeDataProvider.create(traces, "Example time graph data provider", ExampleXYDataProvider.ID); //$NON-NLS-1$
+    }
+
+    @Override
+    public Collection<IDataProviderDescriptor> getDescriptors(@NonNull ITmfTrace trace) {
+        ExampleStateSystemAnalysisModule module = TmfTraceUtils.getAnalysisModuleOfClass(trace, ExampleStateSystemAnalysisModule.class, ExampleStateSystemAnalysisModule.ID);
+        return module != null ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
+    }
+
+}
+
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/ui/views/data/provider/ExampleTimeGraphDataProviderView.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/ui/views/data/provider/ExampleTimeGraphDataProviderView.java
new file mode 100644
index 0000000..99021b2
--- /dev/null
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/ui/views/data/provider/ExampleTimeGraphDataProviderView.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.examples.ui.views.data.provider;
+
+import org.eclipse.tracecompass.examples.core.data.provider.ExampleTimeGraphDataProvider;
+import org.eclipse.tracecompass.internal.provisional.tmf.ui.widgets.timegraph.BaseDataProviderTimeGraphPresentationProvider;
+import org.eclipse.tracecompass.tmf.ui.views.timegraph.BaseDataProviderTimeGraphView;
+
+/**
+ * An example of a data provider time graph view
+ *
+ * This class is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+@SuppressWarnings("restriction")
+public class ExampleTimeGraphDataProviderView extends BaseDataProviderTimeGraphView {
+
+    /** View ID. */
+    public static final String ID = "org.eclipse.tracecompass.examples.dataprovider.tgview"; //$NON-NLS-1$
+
+    /**
+     * Default constructor
+     */
+    public ExampleTimeGraphDataProviderView() {
+       super(ID, new BaseDataProviderTimeGraphPresentationProvider(), ExampleTimeGraphDataProvider.ID);
+    }
+
+}
\ No newline at end of file
diff --git a/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/ui/views/data/provider/ExampleXYDataProviderView.java b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/ui/views/data/provider/ExampleXYDataProviderView.java
new file mode 100644
index 0000000..b639bc6
--- /dev/null
+++ b/doc/org.eclipse.tracecompass.examples/src/org/eclipse/tracecompass/examples/ui/views/data/provider/ExampleXYDataProviderView.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2020 É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 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.examples.ui.views.data.provider;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.examples.core.data.provider.ExampleXYDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.AbstractSelectTreeViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.ITmfTreeColumnDataProvider;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeColumnData;
+import org.eclipse.tracecompass.tmf.ui.viewers.tree.TmfTreeViewerEntry;
+import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfFilteredXYChartViewer;
+import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfXYChartSettings;
+import org.eclipse.tracecompass.tmf.ui.views.TmfChartView;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * An example of a data provider XY view
+ *
+ * This class is also in the developer documentation of Trace Compass. If it is
+ * modified here, the doc should also be updated.
+ *
+ * @author Geneviève Bastien
+ */
+public class ExampleXYDataProviderView extends TmfChartView {
+
+    /** View ID. */
+    public static final String ID = "org.eclipse.tracecompass.examples.dataprovider.xyview"; //$NON-NLS-1$
+
+    /**
+     * Constructor
+     */
+    public ExampleXYDataProviderView() {
+        super("Example Tree XY View"); //$NON-NLS-1$
+    }
+
+    @Override
+    protected TmfXYChartViewer createChartViewer(@Nullable Composite parent) {
+        TmfXYChartSettings settings = new TmfXYChartSettings(null, null, null, 1);
+        return new TmfFilteredXYChartViewer(parent, settings, ExampleXYDataProvider.ID);
+    }
+
+    private static final class TreeXyViewer extends AbstractSelectTreeViewer {
+
+        private final class TreeXyLabelProvider extends AbstractSelectTreeViewer.TreeLabelProvider {
+            @Override
+            public @Nullable Image getColumnImage(@Nullable Object element, int columnIndex) {
+                if (columnIndex == 1 && element instanceof TmfTreeViewerEntry && isChecked(element)) {
+                    return getLegendImage(String.valueOf(((TmfTreeViewerEntry) element).getName()));
+                }
+                return null;
+            }
+        }
+
+        public TreeXyViewer(Composite parent) {
+            super(parent, 1, ExampleXYDataProvider.ID);
+            setLabelProvider(new TreeXyLabelProvider());
+        }
+
+        @Override
+        protected ITmfTreeColumnDataProvider getColumnDataProvider() {
+            return () -> ImmutableList.of(createColumn("Name", Comparator.comparing(TmfTreeViewerEntry::getName)), //$NON-NLS-1$
+                    new TmfTreeColumnData("Legend")); //$NON-NLS-1$
+        }
+    }
+
+    @Override
+    protected @NonNull TmfViewer createLeftChildViewer(@Nullable Composite parent) {
+        return new TreeXyViewer(Objects.requireNonNull(parent));
+    }
+}