blob: 327c7f5c5ccd774c6784598ca7d7e4afb630d485 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2020 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.incubator.internal.tmf.ui.multiview.ui.view.timegraph;
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.Objects;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.Annotation;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.AnnotationCategoriesModel;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.AnnotationModel;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.IAnnotation.AnnotationType;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations.IOutputAnnotationProvider;
import org.eclipse.tracecompass.internal.provisional.tmf.ui.widgets.timegraph.BaseDataProviderTimeGraphPresentationProvider;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
import org.eclipse.tracecompass.tmf.core.TmfStrings;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
import org.eclipse.tracecompass.tmf.core.event.lookup.TmfCallsite;
import org.eclipse.tracecompass.tmf.core.model.IOutputElement;
import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty;
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.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.TimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.ui.actions.OpenSourceCodeAction;
import org.eclipse.tracecompass.tmf.ui.views.timegraph.BaseDataProviderTimeGraphView;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.MarkerEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NamedTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry.Sampling;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeLinkEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPartSite;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
/**
* {@link AbstractTimeGraphMultiViewer} for viewers with data providers.
* <p>
* The viewer corresponds (copy-pasted actually) to
* {@link BaseDataProviderTimeGraphView}.
*
* @author Ivan Grinenko
*
*/
@SuppressWarnings("restriction")
public class BaseDataProviderTimeGraphMultiViewer extends AbstractTimeGraphMultiViewer {
/**
* Timeout between updates in the build thread in ms
*/
protected static final long BUILD_UPDATE_TIMEOUT = 500;
private static final Pattern SOURCE_REGEX = Pattern.compile("(.*):(\\d+)"); //$NON-NLS-1$
/**
* Table of (data provider, model id) to time graph entry. The table should
* be filled by {@link #buildEntryList} and is read by {@link #zoomEntries}
* and {@link #getLinkList}.
*/
protected final Table<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>, Long, TimeGraphEntry> fEntries = HashBasedTable.create();
/**
* Table of (time graph entry, data provider) to model id. The table should
* be filled by {@link #buildEntryList}.
*
* @since 5.2
*/
protected final Table<@NonNull TimeGraphEntry, ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>, Long> fEntryIds = HashBasedTable.create();
/**
* Table of (scope, model id) to time graph entry. The table should be
* filled by {@link #buildEntryList}.
*
* @since 5.2
*/
protected final Table<Object, Long, TimeGraphEntry> fScopedEntries = HashBasedTable.create();
/** Map of parent trace to providers */
private Multimap<ITmfTrace, ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>> fProviders = HashMultimap.create();
/** Map of parent trace to scopes, for cleanup */
private Multimap<ITmfTrace, Object> fScopes = HashMultimap.create();
private final String fProviderId;
/**
* Constructor.
*
* @param parent
* parent composite
* @param pres
* presentation provider
* @param site
* workbench site
* @param providerId
* provider ID
*/
public BaseDataProviderTimeGraphMultiViewer(Composite parent, ITimeGraphPresentationProvider pres, IWorkbenchPartSite site, String providerId) {
super(parent, pres, site);
fProviderId = providerId;
createTimeEventContextMenu();
}
@Override
public Control getControl() {
return getTimeGraphViewer().getTimeAlignedComposite();
}
/**
* Get a data provider ID
*
* @return the data provider ID
*/
protected String getProviderId() {
return fProviderId;
}
@Override
protected void buildEntryList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull IProgressMonitor monitor) {
ITimeGraphDataProvider<@NonNull TimeGraphEntryModel> dataProvider = DataProviderManager
.getInstance().getDataProvider(trace, getProviderId(), ITimeGraphDataProvider.class);
if (dataProvider == null) {
return;
}
ITimeGraphPresentationProvider presentationProvider = getPresentationProvider();
if (presentationProvider instanceof BaseDataProviderTimeGraphPresentationProvider) {
((BaseDataProviderTimeGraphPresentationProvider) presentationProvider).addProvider(dataProvider, getTooltipResolver(dataProvider));
}
boolean complete = false;
while (!complete && !monitor.isCanceled()) {
Map<@NonNull String, @NonNull Object> parameters = getFetchTreeParameters();
TmfModelResponse<TmfTreeModel<@NonNull TimeGraphEntryModel>> response = dataProvider.fetchTree(parameters, monitor);
if (response.getStatus() == ITmfResponse.Status.FAILED) {
Activator.getDefault().logError(getClass().getSimpleName() + " Data Provider failed: " + response.getStatusMessage()); //$NON-NLS-1$
return;
} else if (response.getStatus() == ITmfResponse.Status.CANCELLED) {
return;
}
complete = response.getStatus() == ITmfResponse.Status.COMPLETED;
TmfTreeModel<@NonNull TimeGraphEntryModel> model = response.getModel();
if (model != null) {
synchronized (fEntries) {
Object scope = (model.getScope() == null) ? dataProvider : model.getScope();
fProviders.put(parentTrace, dataProvider);
fScopes.put(parentTrace, scope);
/*
* The provider may send entries unordered and parents may
* not exist when child is constructor, we'll re-unite
* families at the end
*/
List<TimeGraphEntry> orphaned = new ArrayList<>();
for (TimeGraphEntryModel entry : model.getEntries()) {
TimeGraphEntry uiEntry = fScopedEntries.get(scope, entry.getId());
if (entry.getParentId() != -1) {
if (uiEntry == null) {
uiEntry = new TimeGraphEntry(entry);
TimeGraphEntry parent = fScopedEntries.get(scope, entry.getParentId());
if (parent != null) {
// TODO: the order of children from
// different data providers is undefined
parent.addChild(uiEntry);
} else {
orphaned.add(uiEntry);
}
fScopedEntries.put(scope, entry.getId(), uiEntry);
} else {
uiEntry.updateModel(entry);
}
} else {
if (entry.getStartTime() != Long.MIN_VALUE) {
setStartTime(Long.min(getStartTime(), entry.getStartTime()));
}
setEndTime(Long.max(getEndTime(), entry.getEndTime() + 1));
if (uiEntry != null) {
uiEntry.updateModel(entry);
} else {
// Do not assume that parentless entries are
// trace entries
uiEntry = new TraceEntry(entry, trace, dataProvider);
fScopedEntries.put(scope, entry.getId(), uiEntry);
addToEntryList(parentTrace, Collections.singletonList(uiEntry));
}
}
fEntries.put(dataProvider, entry.getId(), uiEntry);
fEntryIds.put(uiEntry, dataProvider, entry.getId());
}
// Find missing parents
for (TimeGraphEntry orphanedEntry : orphaned) {
TimeGraphEntry parent = fScopedEntries.get(scope, orphanedEntry.getEntryModel().getParentId());
parent.addChild(orphanedEntry);
}
}
long start = getStartTime();
long end = getEndTime();
final long resolution = Long.max(1, (end - start) / getDisplayWidth());
@NonNull
Iterable<@NonNull TimeGraphEntry> entries;
synchronized (fEntries) {
entries = ImmutableList.copyOf(fEntries.values());
}
zoomEntries(entries, start, end, resolution, monitor);
}
if (monitor.isCanceled()) {
return;
}
if (parentTrace.equals(getTrace())) {
synchingToTime(getTimeGraphViewer().getSelectionBegin());
refresh();
}
monitor.worked(1);
if (!complete && !monitor.isCanceled()) {
try {
Thread.sleep(BUILD_UPDATE_TIMEOUT);
} catch (InterruptedException e) {
Activator.getDefault().logError("Failed to wait for data provider", e); //$NON-NLS-1$
Thread.currentThread().interrupt();
}
}
}
}
/**
* Class to represent a parent entry, for which we keep a link to the trace
* and data provider to avoid having to do so for its children. This type of
* entry is otherwise not different from any other time graph entry.
*
* @author Loic Prieur-Drevon
*/
protected static class TraceEntry extends TimeGraphEntry {
private final @NonNull ITmfTrace fTrace;
private final @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> fProvider;
/**
* Constructor
*
* @param model
* trace level model
* @param trace
* The trace corresponding to this trace entry.
* @param provider
* reference to the provider for this trace and view
*/
public TraceEntry(@NonNull TimeGraphEntryModel model, @NonNull ITmfTrace trace,
@NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider) {
super(model);
fTrace = trace;
fProvider = provider;
}
/**
* Getter for this trace entry's trace
*
* @return the trace for this trace entry and its children
*/
public @NonNull ITmfTrace getTrace() {
return fTrace;
}
/**
* Getter for the data provider for this {@link TraceEntry}
*
* @return this entry's {@link ITimeGraphDataProvider}
*/
public @NonNull ITimeGraphDataProvider<? extends TimeGraphEntryModel> getProvider() {
return fProvider;
}
}
/**
* Get the {@link ITmfTrace} from a {@link TimeGraphEntry}'s parent.
*
* @param entry
* queried {@link TimeGraphEntry}.
* @return the {@link ITmfTrace}
*/
public static @NonNull ITmfTrace getTrace(TimeGraphEntry entry) {
return getTraceEntry(entry).getTrace();
}
/**
* Get the {@link ITimeGraphDataProvider} from a {@link TimeGraphEntry}'s
* parent.
*
* @param entry
* queried {@link TimeGraphEntry}.
* @return the {@link ITimeGraphDataProvider}
*/
public static ITimeGraphDataProvider<? extends TimeGraphEntryModel> getProvider(TimeGraphEntry entry) {
TraceEntry traceEntry = getTraceEntry(entry);
// TODO: A trace can have many providers
return traceEntry.getProvider();
}
private Collection<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>> getProviders(ITmfTrace viewTrace) {
Collection<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>> providers;
synchronized (fEntries) {
providers = fProviders.get(viewTrace);
}
if (!providers.isEmpty()) {
return providers;
}
List<@NonNull TimeGraphEntry> traceEntries = getEntryList(viewTrace);
if (traceEntries == null) {
return Collections.emptyList();
}
providers = new ArrayList<>();
for (TraceEntry traceEntry : Iterables.filter(traceEntries, TraceEntry.class)) {
providers.add(traceEntry.getProvider());
}
return providers;
}
private static TraceEntry getTraceEntry(TimeGraphEntry entry) {
ITimeGraphEntry parent = entry;
while (parent != null) {
if (parent instanceof TraceEntry) {
return ((TraceEntry) parent);
}
parent = parent.getParent();
}
throw new IllegalStateException(entry + " should have a TraceEntry parent"); //$NON-NLS-1$
}
@Override
protected void zoomEntries(@NonNull Iterable<@NonNull TimeGraphEntry> entries, long zoomStartTime, long zoomEndTime,
long resolution, boolean fullSearch, @NonNull IProgressMonitor monitor) {
if (resolution < 0) {
// StateSystemUtils.getTimes would throw an illegal argument
// exception.
return;
}
long start = Long.min(zoomStartTime, zoomEndTime);
long end = Long.max(zoomStartTime, zoomEndTime);
Sampling sampling = new Sampling(start, end, resolution);
Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = filterGroupEntries(entries, zoomStartTime, zoomEndTime);
SubMonitor subMonitor = SubMonitor.convert(monitor, getClass().getSimpleName() + "#zoomEntries", providersToModelIds.size()); //$NON-NLS-1$
for (Entry<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Collection<Long>> entry : providersToModelIds.asMap().entrySet()) {
ITimeGraphDataProvider<? extends TimeGraphEntryModel> dataProvider = entry.getKey();
Map<@NonNull String, @NonNull Object> parameters = getFetchRowModelParameters(start, end, resolution, fullSearch, entry.getValue());
TmfModelResponse<TimeGraphModel> response = dataProvider.fetchRowModel(parameters, monitor);
TimeGraphModel model = response.getModel();
if (model != null) {
zoomEntries(fEntries.row(dataProvider), model.getRows(), response.getStatus() == ITmfResponse.Status.COMPLETED, sampling);
}
subMonitor.worked(1);
}
}
/**
* Filter the entries to return only the Non Null {@link TimeGraphEntry}
* which intersect the time range.
*
* @param visible
* the input list of visible entries
* @param zoomStartTime
* the leftmost time bound of the view
* @param zoomEndTime
* the rightmost time bound of the view
* @return A Multimap of data providers to their visible entries' model IDs.
*/
private Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> filterGroupEntries(
Iterable<TimeGraphEntry> visible, long zoomStartTime, long zoomEndTime) {
Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = HashMultimap.create();
for (TimeGraphEntry entry : visible) {
if (zoomStartTime <= entry.getEndTime() && zoomEndTime >= entry.getStartTime() && entry.hasTimeEvents()) {
synchronized (fEntries) {
if (!fEntryIds.isEmpty()) {
fEntryIds.row(entry).forEach((provider, modelId) -> providersToModelIds.put(provider, modelId));
} else {
ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider = getProvider(entry);
if (provider != null) {
providersToModelIds.put(provider, entry.getEntryModel().getId());
}
}
}
}
}
return providersToModelIds;
}
private void zoomEntries(Map<Long, TimeGraphEntry> map, List<ITimeGraphRowModel> model, boolean completed, Sampling sampling) {
boolean isZoomThread = Thread.currentThread() instanceof ZoomThread;
for (ITimeGraphRowModel rowModel : model) {
TimeGraphEntry entry;
synchronized (fEntries) {
entry = map.get(rowModel.getEntryID());
}
if (entry != null) {
List<ITimeEvent> events = createTimeEvents(entry, rowModel.getStates());
if (isZoomThread) {
applyResults(() -> {
entry.setZoomedEventList(events);
if (completed) {
entry.setSampling(sampling);
}
});
} else {
entry.setEventList(events);
}
}
}
}
/**
* Create {@link ITimeEvent}s for an entry from the list of
* {@link ITimeGraphState}s, filling in the gaps.
*
* @param entry
* the {@link TimeGraphEntry} on which we are working
* @param values
* the list of {@link ITimeGraphState}s from the
* {@link ITimeGraphDataProvider}.
* @return a contiguous List of {@link ITimeEvent}s
*/
protected List<ITimeEvent> createTimeEvents(TimeGraphEntry entry, List<ITimeGraphState> values) {
List<ITimeEvent> events = new ArrayList<>(values.size());
ITimeEvent prev = null;
for (ITimeGraphState state : values) {
ITimeEvent event = createTimeEvent(entry, state);
if (prev != null) {
long prevEnd = prev.getTime() + prev.getDuration();
if (prevEnd < event.getTime() && (getTimeEventFilterDialog() == null
|| !getTimeEventFilterDialog().hasActiveSavedFilters())) {
// fill in the gap.
TimeEvent timeEvent = new TimeEvent(entry, prevEnd, event.getTime() - prevEnd);
if (getTimeEventFilterDialog() != null && getTimeEventFilterDialog().isFilterActive()) {
timeEvent.setProperty(IFilterProperty.DIMMED, true);
}
events.add(timeEvent);
}
}
prev = event;
events.add(event);
}
return events;
}
/**
* Create a {@link TimeEvent} for a {@link TimeGraphEntry} and a
* {@link TimeGraphState}
*
* @param entry
* {@link TimeGraphEntry} for which we create a state
* @param state
* {@link ITimeGraphState} from the data provider
* @return a new {@link TimeEvent} for these arguments
*/
protected TimeEvent createTimeEvent(TimeGraphEntry entry, ITimeGraphState state) {
String label = state.getLabel();
if (state.getValue() == Integer.MIN_VALUE && label == null && state.getStyle() == null) {
return new NullTimeEvent(entry, state.getStartTime(), state.getDuration());
}
if (label != null) {
return new NamedTimeEvent(entry, label, state);
}
return new TimeEvent(entry, state);
}
@Override
protected List<@NonNull ILinkEvent> getLinkList(long zoomStartTime, long zoomEndTime, long resolution,
@NonNull IProgressMonitor monitor) {
Collection<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>> providers = getProviders(getTrace());
if (providers.isEmpty()) {
return Collections.emptyList();
}
List<@NonNull ILinkEvent> linkList = new ArrayList<>();
List<@NonNull Long> times = StateSystemUtils.getTimes(zoomStartTime, zoomEndTime, resolution);
Map<@NonNull String, @NonNull Object> parameters = getFetchArrowsParameters(times);
for (ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider : providers) {
TmfModelResponse<List<ITimeGraphArrow>> response = provider.fetchArrows(parameters, monitor);
List<ITimeGraphArrow> model = response.getModel();
if (model != null) {
for (ITimeGraphArrow arrow : model) {
ITimeGraphEntry prevEntry;
ITimeGraphEntry nextEntry;
synchronized (fEntries) {
prevEntry = fEntries.get(provider, arrow.getSourceId());
nextEntry = fEntries.get(provider, arrow.getDestinationId());
}
if (prevEntry != null && nextEntry != null) {
linkList.add(new TimeLinkEvent(arrow, prevEntry, nextEntry));
}
}
}
}
return linkList;
}
@Override
protected @NonNull List<String> getViewMarkerCategories() {
List<String> viewerMarkerCategories = super.getViewMarkerCategories();
Collection<ITimeGraphDataProvider<? extends @NonNull TimeGraphEntryModel>> providers = getProviders(getTrace());
if (providers.isEmpty()) {
return viewerMarkerCategories;
}
for (ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider : providers) {
if (provider instanceof IOutputAnnotationProvider) {
Map<@NonNull String, @NonNull Object> parameters = getFetchAnnotationCategoriesParameters();
TmfModelResponse<@NonNull AnnotationCategoriesModel> response = ((IOutputAnnotationProvider) provider)
.fetchAnnotationCategories(parameters, new NullProgressMonitor());
AnnotationCategoriesModel model = response.getModel();
if (model != null) {
viewerMarkerCategories.addAll(model.getAnnotationCategories());
}
}
}
return viewerMarkerCategories;
}
@Override
protected @NonNull List<IMarkerEvent> getViewMarkerList(long startTime, long endTime, long resolution,
@NonNull IProgressMonitor monitor) {
List<IMarkerEvent> viewerMarkerList = super.getViewMarkerList(startTime, endTime, resolution, monitor);
List<@NonNull TimeGraphEntry> traceEntries = getEntryList(getTrace());
if (traceEntries == null) {
return viewerMarkerList;
}
List<@NonNull Long> times = StateSystemUtils.getTimes(startTime, endTime, resolution);
for (TraceEntry traceEntry : Iterables.filter(traceEntries, TraceEntry.class)) {
Multimap<ITimeGraphDataProvider<? extends TimeGraphEntryModel>, Long> providersToModelIds = filterGroupEntries(Utils.flatten(traceEntry), startTime, endTime);
for (ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider : providersToModelIds.keySet()) {
if (provider instanceof IOutputAnnotationProvider) {
Map<@NonNull String, @NonNull Object> parameters = getFetchAnnotationsParameters(times, providersToModelIds.get(provider));
TmfModelResponse<@NonNull AnnotationModel> response = ((IOutputAnnotationProvider) provider).fetchAnnotations(parameters, new NullProgressMonitor());
AnnotationModel model = response.getModel();
if (model != null) {
for (Entry<String, Collection<Annotation>> entry : model.getAnnotations().entrySet()) {
String category = entry.getKey();
for (Annotation annotation : entry.getValue()) {
if (annotation.getType() == AnnotationType.CHART) {
// If the annotation entry ID is -1 we want
// the
// marker to span across all entries
ITimeGraphEntry markerEntry = null;
if (annotation.getEntryId() != -1) {
synchronized (fEntries) {
markerEntry = fEntries.get(provider, annotation.getEntryId());
}
}
MarkerEvent markerEvent = new MarkerEvent(annotation, markerEntry, category, true);
viewerMarkerList.add(markerEvent);
}
}
}
}
}
}
}
return viewerMarkerList;
}
/**
* Get the fetch parameters to pass to a fetchTree call
*
* @return Map of parameters for fetchTree
*/
protected @NonNull Map<@NonNull String, @NonNull Object> getFetchTreeParameters() {
return new HashMap<>();
}
/**
* Get the fetch parameters to pass to a fetchRowModel call
*
* @param start
* Start time of query
* @param end
* End time of query
* @param resolution
* Resolution of query
* @param fullSearch
* True to perform a full search
* @param items
* The unique keys of the entries to query.
* @return Map of parameters for fetchRowModel
*/
protected @NonNull Map<@NonNull String, @NonNull Object> getFetchRowModelParameters(long start, long end,
long resolution, boolean fullSearch, @NonNull Collection<Long> items) {
@NonNull
Map<@NonNull String, @NonNull Object> parameters = new HashMap<>();
parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, StateSystemUtils.getTimes(start, end, resolution));
parameters.put(DataProviderParameterUtils.REQUESTED_ITEMS_KEY, items);
Multimap<@NonNull Integer, @NonNull String> regexesMap = getRegexes();
if (!regexesMap.isEmpty()) {
parameters.put(DataProviderParameterUtils.REGEX_MAP_FILTERS_KEY, regexesMap.asMap());
}
if (fullSearch) {
parameters.put(DataProviderParameterUtils.FULL_SEARCH_KEY, Boolean.TRUE);
}
return parameters;
}
/**
* Get the fetch parameters to pass to a fetchArrows call
*
* @param times
* Sorted list of times to query.
* @return Map of parameters for fetchArrows
*/
protected @NonNull Map<@NonNull String, @NonNull Object> getFetchArrowsParameters(@NonNull List<@NonNull Long> times) {
@NonNull
Map<@NonNull String, @NonNull Object> parameters = new HashMap<>();
parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, times);
return parameters;
}
/**
* Get the fetch parameters to pass to a fetchTooltip call
*
* @param time
* The time of the tooltip.
* @param item
* The unique key of the tooltip entry.
* @param element
* The model element of the tooltip.
* @return Map of parameters for fetchTooltip
*/
protected @NonNull Map<@NonNull String, @NonNull Object> getFetchTooltipParameters(long time, long item, @Nullable IOutputElement element) {
@NonNull
Map<@NonNull String, @NonNull Object> parameters = new HashMap<>();
parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, Collections.singletonList(time));
parameters.put(DataProviderParameterUtils.REQUESTED_ITEMS_KEY, Collections.singletonList(item));
if (element != null) {
parameters.put(DataProviderParameterUtils.REQUESTED_ELEMENT_KEY, element);
}
return parameters;
}
/**
* Get the fetch parameters to pass to a fetchAnnotationCategories call
*
* @return Map of parameters for fetchAnnotationCategories
*/
protected @NonNull Map<@NonNull String, @NonNull Object> getFetchAnnotationCategoriesParameters() {
return new HashMap<>();
}
/**
* Get the fetch parameters to pass to a fetchAnnotations call
*
* @param times
* Sorted list of times to query.
* @param items
* The unique keys of the entries to query.
* @return Map of parameters for fetchAnnotations
*/
protected @NonNull Map<@NonNull String, @NonNull Object> getFetchAnnotationsParameters(
@NonNull List<Long> times, @NonNull Collection<Long> items) {
@NonNull
Map<@NonNull String, @NonNull Object> parameters = new HashMap<>();
parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, times);
parameters.put(DataProviderParameterUtils.REQUESTED_ITEMS_KEY, items);
return parameters;
}
private BiFunction<ITimeEvent, Long, Map<String, String>> getTooltipResolver(
ITimeGraphDataProvider<? extends TimeGraphEntryModel> provider) {
return (event, time) -> {
Long entryId = null;
synchronized (fEntries) {
entryId = fEntryIds.get(event.getEntry(), provider);
}
if (entryId == null) {
return Collections.emptyMap();
}
IOutputElement element = null;
if (event instanceof TimeEvent) {
element = ((TimeEvent) event).getModel();
}
Map<@NonNull String, @NonNull Object> parameters = getFetchTooltipParameters(time, entryId, element);
TmfModelResponse<Map<String, String>> response = provider.fetchTooltip(parameters, new NullProgressMonitor());
Map<String, String> tooltip = response.getModel();
return (tooltip == null) ? Collections.emptyMap() : tooltip;
};
}
@Override
protected void resetViewer(ITmfTrace viewTrace) {
List<@NonNull TimeGraphEntry> entryList = getEntryList(viewTrace);
super.resetViewer(viewTrace);
// Remove the entries for this trace
if (entryList != null) {
synchronized (fEntries) {
if (!fProviders.isEmpty()) {
fProviders.removeAll(viewTrace).forEach(provider -> {
fEntries.row(provider).clear();
fEntryIds.column(provider).clear();
});
} else {
for (TimeGraphEntry entry : entryList) {
if (entry instanceof TraceEntry) {
fEntries.row(((TraceEntry) entry).getProvider()).clear();
}
}
}
fScopes.removeAll(viewTrace).forEach(scope -> fScopedEntries.row(scope).clear());
}
}
}
private IContributionItem createOpenSourceCodeAction(Map<String, String> model) {
if (model != null) {
String callsite = model.get(TmfStrings.source());
return OpenSourceCodeAction.create(Messages.BaseDataProviderTimeGraphMultiViewer_OpenSourceActionName, () -> {
if (callsite != null) {
Matcher matcher = SOURCE_REGEX.matcher(callsite);
if (matcher.matches()) {
return new TmfCallsite(Objects.requireNonNull(matcher.group(1)), Long.parseLong(matcher.group(2)));
}
}
return null;
}, this.getTimeGraphViewer().getTimeGraphControl().getShell());
}
return null;
}
/**
* Fill context menu
*
* @param menuManager
* a menuManager to fill
*/
protected void fillTimeEventContextMenu(@NonNull IMenuManager menuManager) {
ISelection selection = getSite().getSelectionProvider().getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection sSel = (IStructuredSelection) selection;
Object firstElement = sSel.getFirstElement();
if (firstElement instanceof TimeGraphEntry) {
Object entryObject = sSel.toArray()[1];
if (entryObject instanceof TimeEvent) {
TimeEvent event = (TimeEvent) entryObject;
IContributionItem contribItem = createOpenSourceCodeAction(getPresentationProvider().getEventHoverToolTipInfo(event, getTimeGraphViewer().getSelectionBegin()));
if (contribItem != null) {
menuManager.add(contribItem);
}
}
}
}
}
private void createTimeEventContextMenu() {
MenuManager eventMenuManager = new MenuManager();
eventMenuManager.setRemoveAllWhenShown(true);
TimeGraphControl timeGraphControl = getTimeGraphViewer().getTimeGraphControl();
final Menu timeEventMenu = eventMenuManager.createContextMenu(timeGraphControl);
timeGraphControl.addTimeEventMenuListener(event -> {
Menu menu = timeEventMenu;
if (event.data instanceof TimeEvent) {
timeGraphControl.setMenu(menu);
return;
}
timeGraphControl.setMenu(null);
event.doit = false;
});
eventMenuManager.addMenuListener(manager -> {
fillTimeEventContextMenu(eventMenuManager);
eventMenuManager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
});
getSite().registerContextMenu(eventMenuManager, getTimeGraphViewer().getSelectionProvider());
}
}