blob: dcef858d70877d860d1573119cda3fa011fb70ee [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2017 Ericsson, Draeger, Auriga
*
* 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.tmf.ui.viewers.xycharts.linecharts;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGBA;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLogBuilder;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TmfFilterAppliedSignal;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.TraceCompassFilter;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.TimeQueryRegexFilter;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty;
import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel;
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfCommonXAxisModel;
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXYDataProvider;
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXyModel;
import org.eclipse.tracecompass.tmf.core.presentation.IXYPresentationProvider;
import org.eclipse.tracecompass.tmf.core.presentation.IYAppearance;
import org.eclipse.tracecompass.tmf.core.presentation.RGBAColor;
import org.eclipse.tracecompass.tmf.core.presentation.XYPresentationProvider;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.ui.colors.RGBAUtil;
import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentInfo;
import org.eclipse.tracecompass.tmf.ui.signal.TmfTimeViewAlignmentSignal;
import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.AxisRange;
import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfChartTimeStampFormat;
import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.TmfXYChartViewer;
import org.swtchart.IAxisSet;
import org.swtchart.IAxisTick;
import org.swtchart.IBarSeries;
import org.swtchart.ILineSeries;
import org.swtchart.ISeries;
import org.swtchart.ISeries.SeriesType;
import org.swtchart.ISeriesSet;
import org.swtchart.LineStyle;
import org.swtchart.Range;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Multimap;
/**
* XY Chart viewer class implementation. All series in this viewer use the same
* X axis values. They are automatically created as values are provided for a
* key. Series by default will be displayed as a line. Each series appearance
* can be overridden when creating it.
*
* @author Yonni Chen
* @since 3.2
*/
public abstract class TmfCommonXAxisChartViewer extends TmfXYChartViewer {
private static final String DIRTY_UNDERFLOW_ERROR = "Dirty underflow error"; //$NON-NLS-1$
private static final Map<String, ILineSeries.PlotSymbolType> SYMBOL_MAP;
private static final ColorRegistry COLOR_REGISTRY = new ColorRegistry();
static {
ImmutableMap.Builder<String, ILineSeries.PlotSymbolType> builder = new Builder<>();
builder.put(IYAppearance.SymbolStyle.NONE, ILineSeries.PlotSymbolType.NONE);
builder.put(IYAppearance.SymbolStyle.CIRCLE, ILineSeries.PlotSymbolType.CIRCLE);
builder.put(IYAppearance.SymbolStyle.CROSS, ILineSeries.PlotSymbolType.CROSS);
builder.put(IYAppearance.SymbolStyle.DIAMOND, ILineSeries.PlotSymbolType.DIAMOND);
builder.put(IYAppearance.SymbolStyle.INVERTED_TRIANGLE, ILineSeries.PlotSymbolType.INVERTED_TRIANGLE);
builder.put(IYAppearance.SymbolStyle.TRIANGLE, ILineSeries.PlotSymbolType.TRIANGLE);
builder.put(IYAppearance.SymbolStyle.PLUS, ILineSeries.PlotSymbolType.PLUS);
builder.put(IYAppearance.SymbolStyle.SQUARE, ILineSeries.PlotSymbolType.SQUARE);
SYMBOL_MAP = builder.build();
}
private static final double DEFAULT_MAXY = Double.MIN_VALUE;
private static final double DEFAULT_MINY = Double.MAX_VALUE;
/** Timeout between updates in the updateData thread **/
private static final long BUILD_UPDATE_TIMEOUT = 500;
private static final @NonNull Logger LOGGER = TraceCompassLog.getLogger(TmfCommonXAxisChartViewer.class);
private static final int DEFAULT_SERIES_WIDTH = 1;
private static final String DIMMED_SERIES_SUFFIX = ".dimmed"; //$NON-NLS-1$
private final double fResolution;
private final AtomicInteger fDirty = new AtomicInteger();
private final Map<ITmfTrace, IXYPresentationProvider> fXYPresentationProvider;
private UpdateThread fUpdateThread;
/** Used for testing **/
private int fOverrideNbPoints = 0;
/**
* Constructor
*
* @param parent
* The parent composite
* @param settings
* See {@link TmfXYChartSettings} to know what it contains
*/
public TmfCommonXAxisChartViewer(Composite parent, TmfXYChartSettings settings) {
super(parent, settings.getTitle(), settings.getXLabel(), settings.getYLabel());
getSwtChart().getTitle().setVisible(false);
getSwtChart().getLegend().setPosition(SWT.BOTTOM);
getSwtChart().getAxisSet().getXAxes()[0].getTitle().setVisible(false);
fResolution = settings.getResolution();
setTooltipProvider(new TmfCommonXLineChartTooltipProvider(this));
fXYPresentationProvider = new HashMap<>();
}
@Override
public void loadTrace(ITmfTrace trace) {
super.loadTrace(trace);
fXYPresentationProvider.putIfAbsent(trace, new XYPresentationProvider());
}
@Override
public boolean isDirty() {
/* Check the parent's or this view's own dirtiness */
return super.isDirty() || (fDirty.get() != 0);
}
/**
* Force the number of points to a fixed value
*
* @param nbPoints
* The number of points to display, cannot be negative. 0
* means use native resolution. any positive integer means
* that number of points
*/
public synchronized void setNbPoints(int nbPoints) {
if (nbPoints < 0) {
throw new IllegalArgumentException("Number of points cannot be negative"); //$NON-NLS-1$
}
fOverrideNbPoints = nbPoints;
updateContent();
}
/**
* Initialize the data provider of this viewer
*
* FIXME Make this abstract when incrementing the major version
*
* @param trace
* The trace
* @return the data provider
* @since 4.0
*/
protected ITmfXYDataProvider initializeDataProvider(@NonNull ITmfTrace trace) {
throw new UnsupportedOperationException("This needs to be implemented concrete classes"); //$NON-NLS-1$
}
/**
* Gets the presentation provider
*
* @return The presentation provider
* @since 4.0
*/
protected IXYPresentationProvider getPresentationProvider() {
return Objects.requireNonNull(fXYPresentationProvider.get(getTrace()));
}
/**
* Create an instance of {@link TimeQueryFilter} that will be used by updateData
* method. If a viewer need a more specialized instance of
* {@link TimeQueryFilter}, it's its responsibility to override this method and
* provide the desired instance.
*
* @param start
* The starting value
* @param end
* The ending value
* @param nb
* The number of entries
* @return An {@link TimeQueryFilter} instance that data provider will use to
* extract a model
* @since 4.0
* @deprecated Use createQueryParameters instead
*/
@Deprecated
protected @NonNull TimeQueryFilter createQueryFilter(long start, long end, int nb) {
return new TimeQueryRegexFilter(getWindowStartTime(), getWindowEndTime(), nb, getRegexes());
}
/**
* Create map of parameters that will be used by updateData method. If a
* viewer need a more specialized map than just the time requested it's its
* responsibility to override this method and provide the desired instance.
*
* @param start
* The starting value
* @param end
* The ending value
* @param nb
* The number of entries
* @return Map of parameters
* @since 5.0
*/
protected @NonNull Map<String, Object> createQueryParameters(long start, long end, int nb) {
Map<@NonNull String, @NonNull Object> parameters = FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(start, end, nb));
Multimap<@NonNull Integer, @NonNull String> regexesMap = getRegexes();
if (!regexesMap.isEmpty()) {
parameters.put(DataProviderParameterUtils.REGEX_MAP_FILTERS_KEY, regexesMap.asMap());
}
return parameters;
}
/**
* Gets the appearance of a given series. If appearance doesn't exist, a new one
* will be created by the presentation provider
*
* @param seriesName
* The name of the series
* @return An {@link IYAppearance} instance for the series
* @since 4.0
*/
public @NonNull IYAppearance getSeriesAppearance(@NonNull String seriesName) {
return getPresentationProvider().getAppearance(seriesName, IYAppearance.Type.LINE, DEFAULT_SERIES_WIDTH);
}
/**
* Cancels the currently running update thread. It is automatically called when
* the content is updated, but child viewers may want to call it manually to do
* some operations before calling
* {@link TmfCommonXAxisChartViewer#updateContent}
*/
protected synchronized void cancelUpdate() {
if (fUpdateThread != null) {
fUpdateThread.cancel();
}
}
@Override
protected void updateContent() {
ITmfTrace trace = getTrace();
if (trace == null) {
return;
}
cancelUpdate();
try (FlowScopeLog parentScope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "CommonXLineChart:ContentUpdateRequested").setCategory(getViewerId()).build()) { //$NON-NLS-1$
/*
* Content is not up to date, so we increment fDirty. It will be decremented at
* the end of the update thread
*/
fDirty.incrementAndGet();
getDisplay().asyncExec(() -> {
if (!trace.equals(getTrace())) {
return;
}
try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "CommonXLineChart:CreatingUpdateThread").setParentScope(parentScope).build()) { //$NON-NLS-1$
newUpdateThread(trace, scope);
}
});
}
}
@Override
protected void clearContent() {
getSwtChart().getAxisSet().getXAxis(0).getTick().setFormat(null);
super.clearContent();
}
private @NonNull String getViewerId() {
return getClass().getName();
}
private class UpdateThread extends Thread {
private final ITmfTrace fTrace;
private final IProgressMonitor fMonitor;
private final int fNumRequests;
private final @NonNull FlowScopeLog fScope;
public UpdateThread(ITmfTrace trace, int numRequests, @NonNull FlowScopeLog log) {
super("Line chart update"); //$NON-NLS-1$
fTrace = trace;
fNumRequests = numRequests;
fMonitor = new NullProgressMonitor();
fScope = log;
}
@Override
public void run() {
try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "CommonXLineChart:UpdateThread", "numRequests=", fNumRequests).setParentScope(fScope).build()) { //$NON-NLS-1$ //$NON-NLS-2$
ITmfXYDataProvider dataProvider = null;
try (FlowScopeLog scopeDp = new FlowScopeLogBuilder(LOGGER, Level.FINE, "CommonXLineChart:InitializeDataProvider").setParentScope(fScope).build()) { //$NON-NLS-1$
dataProvider = initializeDataProvider(fTrace);
}
if (dataProvider == null) {
TraceCompassLogUtils.traceInstant(LOGGER, Level.WARNING, "Data provider for this viewer is not available"); //$NON-NLS-1$
return;
}
try {
int numRequests = fNumRequests;
if (numRequests == 0) {
return;
}
Map<String, Object> parameters = createQueryParameters(getWindowStartTime(), getWindowEndTime(), numRequests);
updateData(dataProvider, parameters, fMonitor);
} finally {
/*
* fDirty should have been incremented before creating the thread, so we
* decrement it once it is finished
*/
if (fDirty.decrementAndGet() < 0) {
Activator.getDefault().logError(DIRTY_UNDERFLOW_ERROR, new Throwable());
}
}
updateThreadFinished(this);
}
}
public void cancel() {
TraceCompassLogUtils.traceInstant(LOGGER, Level.FINE, "CommonXLineChart:UpdateThreadCanceled"); //$NON-NLS-1$
fMonitor.setCanceled(true);
}
/**
* This method is responsible for calling the
* {@link UpdateThread#updateDisplay(ITmfCommonXAxisModel)} when needed for the
* new values to be displayed.
*
* @param dataProvider
* A data provider
* @param parameters
* A query filter
* @param monitor
* A monitor for canceling task
*/
private void updateData(@NonNull ITmfXYDataProvider dataProvider, @NonNull Map<String, Object> parameters, IProgressMonitor monitor) {
boolean isComplete = false;
do {
TmfModelResponse<ITmfXyModel> response = dataProvider.fetchXY(parameters, monitor);
ITmfXyModel model = response.getModel();
if (model != null) {
updateDisplay(model, monitor);
}
ITmfResponse.Status status = response.getStatus();
if (status == ITmfResponse.Status.COMPLETED) {
/* Model is complete, no need to request again the data provider */
isComplete = true;
} else if (status == ITmfResponse.Status.FAILED || status == ITmfResponse.Status.CANCELLED) {
/* Error occurred, log and return */
TraceCompassLogUtils.traceInstant(LOGGER, Level.WARNING, response.getStatusMessage());
isComplete = true;
} else {
/**
* Status is RUNNING. Sleeping current thread to wait before request data
* provider again
**/
try {
Thread.sleep(BUILD_UPDATE_TIMEOUT);
} catch (InterruptedException e) {
/**
* InterruptedException is throw by Thread.Sleep and we should retry querying
* the data provider
**/
TraceCompassLogUtils.traceInstant(LOGGER, Level.INFO, e.getMessage());
Thread.currentThread().interrupt();
}
}
} while (!isComplete);
}
/**
* Update the chart's values before refreshing the viewer
*/
private void updateDisplay(ITmfXyModel model, IProgressMonitor monitor) {
try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "TmfCommonXAxisChart:UpdateDisplayRequested").setCategory(getViewerId()).build()) { //$NON-NLS-1$
/* Content is not up to date, increment dirtiness */
final ITmfXyModel seriesValues = model;
fDirty.incrementAndGet();
Display.getDefault().asyncExec(() -> {
final TmfChartTimeStampFormat tmfChartTimeStampFormat = new TmfChartTimeStampFormat(getTimeOffset());
try (FlowScopeLog log = new FlowScopeLogBuilder(LOGGER, Level.FINE, "TmfCommonXAxisChart:UpdateDisplay").setParentScope(scope).build()) { //$NON-NLS-1$
if (!fTrace.equals(getTrace())) {
return;
}
if (getSwtChart().isDisposed()) {
return;
}
if (monitor != null && monitor.isCanceled()) {
return;
}
double maxy = DEFAULT_MAXY;
double miny = DEFAULT_MINY;
long delta = getWindowEndTime() - getWindowStartTime();
if (delta > 0) {
for (ISeriesModel entry : seriesValues.getData().values()) {
double[] extractXValuesToDisplay = extractXValuesToDisplay(entry.getXAxis());
List<Double> dimmedX = new ArrayList<>(extractXValuesToDisplay.length);
List<Double> dimmedY = new ArrayList<>(extractXValuesToDisplay.length);
List<Double> brightX = new ArrayList<>(extractXValuesToDisplay.length);
List<Double> brightY = new ArrayList<>(extractXValuesToDisplay.length);
int[] propertiesArray = entry.getProperties();
double[] data = entry.getData();
for (int i = 0; i < extractXValuesToDisplay.length; i++) {
double value = data[i];
/*
* Find the minimal and maximum values in this series
*/
maxy = Math.max(maxy, value);
miny = Math.min(miny, value);
int properties = (i < propertiesArray.length) ? propertiesArray[i] : 0;
if ((properties & IFilterProperty.EXCLUDE) == 0) {
if ((properties & IFilterProperty.DIMMED) == 0) {
brightX.add(extractXValuesToDisplay[i]);
brightY.add(value);
} else {
dimmedX.add(extractXValuesToDisplay[i]);
dimmedY.add(value);
}
}
}
double[] brightXArray = new double[brightX.size()];
double[] brightYArray = new double[brightY.size()];
for (int i = 0; i < brightX.size(); i++) {
brightXArray[i] = brightX.get(i);
brightYArray[i] = brightY.get(i);
}
double[] dimmedXArray = new double[dimmedX.size()];
double[] dimmedYArray = new double[dimmedY.size()];
for (int i = 0; i < dimmedX.size(); i++) {
dimmedXArray[i] = dimmedX.get(i);
dimmedYArray[i] = dimmedY.get(i);
}
// Create and fill the series
ISeriesSet seriesSet = getSwtChart().getSeriesSet();
ISeries series = seriesSet.getSeries(entry.getName());
ISeries dimmedSeries = seriesSet.getSeries(entry.getName() + DIMMED_SERIES_SUFFIX);
if (brightX.isEmpty()) {
// Remove the base series since there is
// nothing to show
if (series != null) {
seriesSet.deleteSeries(entry.getName());
}
} else {
if (series == null) {
series = createSWTSeriesFromModel(entry);
}
series.setXSeries(brightXArray);
series.setYSeries(brightYArray);
}
if (dimmedX.isEmpty()) {
// Remove the base series since there is
// nothing to show
if (dimmedSeries != null) {
seriesSet.deleteSeries(entry.getName() + DIMMED_SERIES_SUFFIX);
}
} else {
if (dimmedSeries == null) {
dimmedSeries = createDimmedSeriesFromModel(entry);
}
dimmedSeries.setXSeries(dimmedXArray);
dimmedSeries.setYSeries(dimmedYArray);
}
}
maxy = maxy == DEFAULT_MAXY ? 1.0 : maxy;
} else {
clearContent();
delta = 1;
}
IAxisSet axisSet = getSwtChart().getAxisSet();
IAxisTick xTick = axisSet.getXAxis(0).getTick();
xTick.setFormat(tmfChartTimeStampFormat);
final double start = 1.0;
axisSet.getXAxis(0).setRange(new Range(start, start + delta));
AxisRange fixedYRange = getFixedYRange();
if (fixedYRange == null) {
axisSet.getYAxis(0).adjustRange();
} else {
axisSet.getYAxis(0).setRange(
new Range(fixedYRange.getLower(), fixedYRange.getUpper()));
}
getSwtChart().redraw();
if (isSendTimeAlignSignals()) {
/*
* The width of the chart might have changed and its time axis might be
* misaligned with the other views
*/
Composite parent = TmfCommonXAxisChartViewer.this.getParent();
if (parent == null || parent.getParent() == null) {
return;
}
Point viewPos = parent.getParent().toDisplay(0, 0);
int axisPos = getSwtChart().toDisplay(0, 0).x + getPointAreaOffset();
int timeAxisOffset = axisPos - viewPos.x;
TmfTimeViewAlignmentInfo timeAlignmentInfo = new TmfTimeViewAlignmentInfo(getControl().getShell(), viewPos, timeAxisOffset);
TmfSignalManager.dispatchSignal(new TmfTimeViewAlignmentSignal(TmfCommonXAxisChartViewer.this, timeAlignmentInfo, true));
}
} finally {
/* Content has been updated, decrement dirtiness */
if (fDirty.decrementAndGet() < 0) {
Activator.getDefault().logError(DIRTY_UNDERFLOW_ERROR, new Throwable());
}
}
});
}
}
/**
* Since the XY Model returned by data provider contains directly the requested
* time as long array, we need to convert it to double array for the SWT Chart.
*/
private double[] extractXValuesToDisplay(long[] xValuesRequested) {
double[] xValuesToDisplay = new double[xValuesRequested.length];
long offset = getTimeOffset();
for (int i = 0; i < xValuesRequested.length; ++i) {
xValuesToDisplay[i] = (xValuesRequested[i] - offset);
}
return xValuesToDisplay;
}
private @NonNull ISeries createSWTSeriesFromModel(ISeriesModel yModel) {
ISeriesSet seriesSet = getSwtChart().getSeriesSet();
String seriesName = yModel.getName();
IYAppearance appearance = getSeriesAppearance(seriesName);
String type = appearance.getType();
RGBAColor rgb = appearance.getColor();
COLOR_REGISTRY.put(rgb.toString(), RGBAUtil.fromRGBAColor(rgb).rgb);
Color color = COLOR_REGISTRY.get(rgb.toString());
String symbolType = appearance.getSymbolStyle();
if (type.equals(IYAppearance.Type.BAR)) {
IBarSeries barSeries = (IBarSeries) seriesSet.createSeries(SeriesType.BAR, seriesName);
barSeries.enableStack(true);
barSeries.setBarColor(color);
barSeries.setBarPadding(0);
barSeries.setVisible(true);
return barSeries;
}
/**
* Default is line chart
*/
ILineSeries lineSeries = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName);
lineSeries.enableArea(IYAppearance.Type.AREA.equals(type));
lineSeries.setLineStyle(LineStyle.valueOf(appearance.getStyle()));
lineSeries.setSymbolType(SYMBOL_MAP.getOrDefault(symbolType, ILineSeries.PlotSymbolType.NONE));
lineSeries.setLineColor(color);
lineSeries.setSymbolColor(color);
lineSeries.setVisible(true);
lineSeries.setLineWidth(appearance.getWidth());
return lineSeries;
}
private @NonNull ISeries createDimmedSeriesFromModel(ISeriesModel yModel) {
ISeriesSet seriesSet = getSwtChart().getSeriesSet();
String seriesName = yModel.getName() + DIMMED_SERIES_SUFFIX;
IYAppearance appearance = getSeriesAppearance(yModel.getName());
String type = appearance.getType();
float[] rgb = appearance.getColor().getHSBA();
COLOR_REGISTRY.put(rgb.toString(), new RGBA(rgb[0], rgb[1] * 0.5f, rgb[2] * 0.5f, rgb[3]).rgb);
Color color = COLOR_REGISTRY.get(rgb.toString());
String symbolType = appearance.getSymbolStyle();
if (type.equals(IYAppearance.Type.BAR)) {
IBarSeries barSeries = (IBarSeries) seriesSet.createSeries(SeriesType.BAR, seriesName);
barSeries.enableStack(true);
barSeries.setBarColor(color);
barSeries.setBarPadding(0);
barSeries.setVisible(true);
return barSeries;
}
/**
* Default is line chart
*/
ILineSeries lineSeries = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName);
lineSeries.enableArea(IYAppearance.Type.AREA.equals(type));
lineSeries.setLineStyle(LineStyle.valueOf(appearance.getStyle()));
lineSeries.setSymbolType(SYMBOL_MAP.getOrDefault(symbolType, ILineSeries.PlotSymbolType.NONE));
lineSeries.setLineColor(color);
lineSeries.setSymbolColor(color);
lineSeries.setVisible(true);
lineSeries.setLineWidth(appearance.getWidth());
return lineSeries;
}
private synchronized void updateThreadFinished(UpdateThread thread) {
if (thread == fUpdateThread) {
fUpdateThread = null;
}
}
}
private synchronized void newUpdateThread(@NonNull ITmfTrace trace, @NonNull FlowScopeLog fScope) {
if (getSwtChart().isDisposed()) {
return;
}
int numRequests = fOverrideNbPoints != 0 ? fOverrideNbPoints : (int) Math.min(getWindowEndTime() - getWindowStartTime() + 1, (long) (getSwtChart().getPlotArea().getBounds().width * fResolution));
fUpdateThread = new UpdateThread(trace, numRequests, fScope);
fUpdateThread.start();
}
@TmfSignalHandler
@Override
public void traceClosed(@Nullable TmfTraceClosedSignal signal) {
cancelUpdate();
super.traceClosed(signal);
if (signal != null) {
fXYPresentationProvider.remove(signal.getTrace());
}
}
/**
* Set or remove the global regex filter value
*
* @param signal
* the signal carrying the regex value
* @since 4.2
*/
@TmfSignalHandler
public void regexFilterApplied(TmfFilterAppliedSignal signal) {
updateContent();
}
/**
* This method build the multimap of regexes by property that will be used to
* filter the timegraph states
*
* Override this method to add other regexes with their properties. The data
* provider should handle everything after.
*
* @return The multimap of regexes by property
* @since 4.2
*/
protected @NonNull Multimap<@NonNull Integer, @NonNull String> getRegexes() {
Multimap<@NonNull Integer, @NonNull String> regexes = HashMultimap.create();
ITmfTrace trace = getTrace();
if (trace == null) {
return regexes;
}
TraceCompassFilter globalFilter = TraceCompassFilter.getFilterForTrace(trace);
if (globalFilter == null) {
return regexes;
}
regexes.putAll(IFilterProperty.DIMMED, globalFilter.getRegexes());
return regexes;
}
}