| /******************************************************************************* |
| * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir |
| * |
| * 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.internal.provisional.analysis.lami.ui.views; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiResultTable; |
| import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry; |
| import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.types.LamiTimeRange; |
| import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartData; |
| import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.chart.ChartModel; |
| import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.signal.ChartSelectionUpdateSignal; |
| import org.eclipse.tracecompass.internal.provisional.tmf.chart.ui.dialog.ChartMakerDialog; |
| import org.eclipse.tracecompass.tmf.core.component.TmfComponent; |
| import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal; |
| import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; |
| import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager; |
| import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; |
| |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * Sub-view of a {@link LamiReportView} that shows the contents of one table of |
| * the analysis report. While it is not a View object directly, its |
| * responsibilities are the same. |
| * |
| * @author Alexandre Montplaisir |
| * @author Jonathan Rajotte-Julien |
| * @author Gabriel-Andrew Pollo-Guilbert |
| */ |
| public final class LamiReportViewTabPage extends TmfComponent { |
| |
| // ------------------------------------------------------------------------ |
| // Attributes |
| // ------------------------------------------------------------------------ |
| |
| private final LamiResultTable fResultTable; |
| private final LamiViewerControl fTableViewerControl; |
| private final List<LamiViewerControl> fCustomGraphViewerControls = new ArrayList<>(); |
| private final Composite fControl; |
| |
| private Set<Object> fSelection; |
| |
| // ------------------------------------------------------------------------ |
| // Constructor |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Constructor |
| * |
| * @param parent |
| * Parent composite |
| * @param table |
| * The result table to display in this tab |
| */ |
| public LamiReportViewTabPage(Composite parent, LamiResultTable table) { |
| super(table.getTableClass().getTableTitle()); |
| |
| fResultTable = table; |
| |
| fControl = parent; |
| |
| /* Map the current trace selection to our lami entry */ |
| fSelection = getEntriesIntersectingTimerange(fResultTable, TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange()); |
| |
| /* Prepare the table viewer, which is always present */ |
| LamiViewerControl tableViewerControl = new LamiViewerControl(fControl, this); |
| fTableViewerControl = tableViewerControl; |
| |
| /* Automatically open the table viewer initially */ |
| fTableViewerControl.getToggleAction().run(); |
| |
| /* Simulate a new external signal to the default viewer */ |
| ChartSelectionUpdateSignal chartSignal = new ChartSelectionUpdateSignal(this, fResultTable, fSelection); |
| TmfSignalManager.dispatchSignal(chartSignal); |
| |
| /* Dispose this class's resource */ |
| fControl.addDisposeListener(e -> { |
| fTableViewerControl.dispose(); |
| clearAllCustomViewers(); |
| super.dispose(); |
| }); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Overriden methods |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public void dispose() { |
| /* fControl's listner will dispose other resources */ |
| fControl.dispose(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Operations |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * This method is used for creating a chart from the result table of the |
| * analyse. It uses the custom charts plugin to configure and create the |
| * chart. |
| */ |
| public void createNewCustomChart() { |
| Shell shell = this.getControl().getShell(); |
| if (shell == null) { |
| return; |
| } |
| |
| /* Open the chart maker dialog */ |
| ChartMakerDialog dialog = new ChartMakerDialog(shell, fResultTable); |
| if (dialog.open() != Window.OK) { |
| return; |
| } |
| |
| /* Make sure the data for making a chart was generated */ |
| ChartData data = dialog.getDataSeries(); |
| ChartModel model = dialog.getChartModel(); |
| if (data == null || model == null) { |
| return; |
| } |
| |
| /* Make a chart with the factory constructor */ |
| LamiViewerControl viewerControl = new LamiViewerControl(fControl, data, model); |
| fCustomGraphViewerControls.add(viewerControl); |
| viewerControl.getToggleAction().run(); |
| /* Signal the current selection to the newly created graph */ |
| ChartSelectionUpdateSignal signal = new ChartSelectionUpdateSignal(LamiReportViewTabPage.this, |
| fResultTable, fSelection); |
| TmfSignalManager.dispatchSignal(signal); |
| |
| |
| } |
| |
| /** |
| * Clear all the custom graph viewers in this tab. |
| */ |
| public void clearAllCustomViewers() { |
| fCustomGraphViewerControls.forEach(LamiViewerControl::dispose); |
| fCustomGraphViewerControls.clear(); |
| } |
| |
| /** |
| * Toggle the display of the table viewer in this tab. This shows it if it |
| * is currently hidden, and vice versa. |
| */ |
| public void toggleTableViewer() { |
| fTableViewerControl.getToggleAction().run(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Accessors |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Get the table viewer |
| * |
| * @return the table viewer |
| */ |
| public LamiViewerControl getTableViewerControl() { |
| return fTableViewerControl; |
| } |
| |
| /** |
| * Get the custom graphs |
| * @return the Custom Graphs |
| */ |
| public List<LamiViewerControl> getCustomGraphViewerControls() { |
| return Collections.unmodifiableList(fCustomGraphViewerControls); |
| } |
| |
| /** |
| * Get the SWT control associated with this tab page. |
| * |
| * @return The SWT control |
| */ |
| public Composite getControl() { |
| return fControl; |
| } |
| |
| /** |
| * Get the result table shown in this tab. |
| * |
| * @return The report result table |
| */ |
| public LamiResultTable getResultTable() { |
| return fResultTable; |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Signals |
| // ------------------------------------------------------------------------ |
| |
| // Custom chart signals |
| /** |
| * Signal handler for a chart selection update. It will try to propagate a |
| * {@link TmfSelectionRangeUpdatedSignal} if possible. |
| * |
| * @param signal |
| * The selection update signal |
| */ |
| @TmfSignalHandler |
| public void updateSelection(ChartSelectionUpdateSignal signal) { |
| |
| /* Make sure we are not sending a signal to ourself */ |
| if (signal.getSource() == this) { |
| return; |
| } |
| |
| /* Make sure the signal comes from the data provider's scope */ |
| if (fResultTable.hashCode() != signal.getDataProvider().hashCode()) { |
| return; |
| } |
| |
| /* Find which index row has been selected */ |
| Set<Object> entries = signal.getSelectedObject(); |
| |
| /* Update the selection */ |
| fSelection = entries; |
| |
| /* Only propagate to all TraceCompass if there is a single selection */ |
| if (entries.size() == 1) { |
| LamiTableEntry entry = (LamiTableEntry) Iterables.getOnlyElement(entries); |
| |
| /* Make sure the selection represent a time range */ |
| LamiTimeRange timeRange = entry.getCorrespondingTimeRange(); |
| if (timeRange == null) { |
| return; |
| } |
| |
| /* Get the timestamps from the time range */ |
| /** |
| * TODO: Consider low and high limits of timestamps here. |
| */ |
| Number tsBeginValueNumber = timeRange.getBegin().getValue(); |
| Number tsEndValueNumber = timeRange.getEnd().getValue(); |
| if(tsBeginValueNumber == null || tsEndValueNumber == null) { |
| return; |
| } |
| |
| /* Send Range update to other views */ |
| ITmfTimestamp start = TmfTimestamp.fromNanos(tsBeginValueNumber.longValue()); |
| ITmfTimestamp end = TmfTimestamp.fromNanos(tsEndValueNumber.longValue()); |
| TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(this, start, end)); |
| } |
| |
| } |
| |
| /** |
| * Signal handler for a trace selection range update signal. It will try to |
| * map the external selection to our lami table entry. |
| * |
| * @param signal |
| * The received signal |
| */ |
| @TmfSignalHandler |
| public void externalUpdateSelectionCustomCharts(TmfSelectionRangeUpdatedSignal signal) { |
| /* Make sure we are not sending a signal to ourself */ |
| if (signal.getSource() == this) { |
| return; |
| } |
| |
| TmfTimeRange range = new TmfTimeRange(signal.getBeginTime(), signal.getEndTime()); |
| |
| // Custom chart signal |
| /* Find which lami table entry intersects the signal */ |
| Set<Object> selection = getEntriesIntersectingTimerange(fResultTable, range); |
| |
| /* Update all LamiViewer */ |
| ChartSelectionUpdateSignal updateSignal = new ChartSelectionUpdateSignal(this, fResultTable, selection); |
| TmfSignalManager.dispatchSignal(updateSignal); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Util methods |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Util method that returns {@link LamiTableEntry} that intersects a |
| * {@link TmfTimeRange}. |
| * |
| * @param table |
| * The result table to search for entries |
| * @param range |
| * The time range itself |
| * @return The set of entries that intersect with the time range |
| */ |
| private static Set<Object> getEntriesIntersectingTimerange(LamiResultTable table, TmfTimeRange range) { |
| Set<Object> entries = new HashSet<>(); |
| for (LamiTableEntry entry : table.getEntries()) { |
| LamiTimeRange lamiTimeRange = entry.getCorrespondingTimeRange(); |
| |
| /* Make sure the table has time ranges */ |
| if (lamiTimeRange == null) { |
| return entries; |
| } |
| |
| /* Get the timestamps from the time range */ |
| /** |
| * TODO: Consider low and high limits of timestamps here. |
| */ |
| Number tsBeginValueNumber = lamiTimeRange.getBegin().getValue(); |
| Number tsEndValueNumber = lamiTimeRange.getEnd().getValue(); |
| if(tsBeginValueNumber == null || tsEndValueNumber == null) { |
| return entries; |
| } |
| |
| /* Convert the timestamps into TMF timestamps */ |
| ITmfTimestamp start = TmfTimestamp.fromNanos(tsBeginValueNumber.longValue()); |
| ITmfTimestamp end = TmfTimestamp.fromNanos(tsEndValueNumber.longValue()); |
| |
| /* Add iff the time range intersects the the signal */ |
| TmfTimeRange tempTimeRange = new TmfTimeRange(start, end); |
| if (tempTimeRange.getIntersection(range) != null) { |
| entries.add(entry); |
| } |
| } |
| |
| return entries; |
| } |
| } |