| /********************************************************************** |
| * 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()); |
| } |
| |
| } |