analysis: introduce ISegmentStoreProvider
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / density / AbstractSegmentStoreDensityViewer.java
1 /******************************************************************************
2 * Copyright (c) 2015, 2016 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9
10 package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.density;
11
12 import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
13
14 import java.text.Format;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.concurrent.CompletableFuture;
21
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.graphics.Color;
25 import org.eclipse.swt.graphics.RGB;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
29 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
30 import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat;
31 import org.eclipse.tracecompass.common.core.NonNullUtils;
32 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseDragZoomProvider;
33 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseSelectionProvider;
34 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.SimpleTooltipProvider;
35 import org.eclipse.tracecompass.segmentstore.core.ISegment;
36 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
37 import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
38 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
39 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
43 import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
44 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
45 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
46 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
47 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
48 import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
49 import org.swtchart.Chart;
50 import org.swtchart.IAxis;
51 import org.swtchart.IBarSeries;
52 import org.swtchart.ISeries;
53 import org.swtchart.ISeries.SeriesType;
54 import org.swtchart.ISeriesSet;
55 import org.swtchart.LineStyle;
56 import org.swtchart.Range;
57
58 import com.google.common.base.Predicate;
59 import com.google.common.collect.Iterators;
60 import com.google.common.collect.Lists;
61
62 /**
63 * Displays the segment store provider data in a density chart.
64 *
65 * @author Matthew Khouzam
66 * @author Marc-Andre Laperle
67 *
68 * @since 2.0
69 */
70 public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
71
72 private static final Format DENSITY_TIME_FORMATTER = new SubSecondTimeWithUnitFormat();
73 private static final RGB BAR_COLOR = new RGB(0x42, 0x85, 0xf4);
74 private final Chart fChart;
75 private final MouseDragZoomProvider fDragZoomProvider;
76 private final MouseSelectionProvider fDragProvider;
77 private final SimpleTooltipProvider fTooltipProvider;
78
79 private @Nullable ITmfTrace fTrace;
80 private @Nullable IAnalysisProgressListener fListener;
81 private @Nullable ISegmentStoreProvider fSegmentStoreProvider;
82 private TmfTimeRange fCurrentTimeRange = TmfTimeRange.NULL_RANGE;
83 private List<ISegmentStoreDensityViewerDataListener> fListeners;
84
85 /**
86 * Constructs a new density viewer.
87 *
88 * @param parent
89 * the parent of the viewer
90 */
91 public AbstractSegmentStoreDensityViewer(Composite parent) {
92 super(parent);
93 fListeners = new ArrayList<>();
94 fChart = new Chart(parent, SWT.NONE);
95 fChart.getLegend().setVisible(false);
96 fChart.getTitle().setVisible(false);
97 fChart.getAxisSet().getXAxis(0).getTitle().setText(nullToEmptyString(Messages.AbstractSegmentStoreDensityViewer_TimeAxisLabel));
98 fChart.getAxisSet().getYAxis(0).getTitle().setText(nullToEmptyString(Messages.AbstractSegmentStoreDensityViewer_CountAxisLabel));
99 fChart.getAxisSet().getXAxis(0).getGrid().setStyle(LineStyle.DOT);
100 fChart.getAxisSet().getYAxis(0).getGrid().setStyle(LineStyle.DOT);
101
102 fDragZoomProvider = new MouseDragZoomProvider(this);
103 fDragZoomProvider.register();
104 fDragProvider = new MouseSelectionProvider(this);
105 fDragProvider.register();
106 fTooltipProvider = new SimpleTooltipProvider(this);
107 fTooltipProvider.register();
108 }
109
110 /**
111 * Returns the segment store provider
112 *
113 * @param trace
114 * The trace to consider
115 * @return the
116 */
117 protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace);
118
119 @Nullable
120 private static ITmfTrace getTrace() {
121 return TmfTraceManager.getInstance().getActiveTrace();
122 }
123
124 private void updateDisplay(List<ISegment> data) {
125 if (data.isEmpty()) {
126 return;
127 }
128 IBarSeries series = (IBarSeries) fChart.getSeriesSet().createSeries(SeriesType.BAR, Messages.AbstractSegmentStoreDensityViewer_SeriesLabel);
129 series.setVisible(true);
130 series.setBarPadding(0);
131
132 series.setBarColor(new Color(Display.getDefault(), BAR_COLOR));
133 int barWidth = 4;
134 final int width = fChart.getPlotArea().getBounds().width / barWidth;
135 double[] xOrigSeries = new double[width];
136 double[] yOrigSeries = new double[width];
137 Arrays.fill(yOrigSeries, 1.0);
138 long maxLength = data.get(data.size() - 1).getLength();
139 double maxFactor = 1.0 / (maxLength + 1.0);
140 long minX = Long.MAX_VALUE;
141 for (ISegment segment : data) {
142 double xBox = segment.getLength() * maxFactor * width;
143 yOrigSeries[(int) xBox]++;
144 minX = Math.min(minX, segment.getLength());
145 }
146 double timeWidth = (double) maxLength / (double) width;
147 for (int i = 0; i < width; i++) {
148 xOrigSeries[i] = i * timeWidth;
149 }
150 double maxY = Double.MIN_VALUE;
151 for (int i = 0; i < width; i++) {
152 maxY = Math.max(maxY, yOrigSeries[i]);
153 }
154 if (minX == maxLength) {
155 maxLength++;
156 minX--;
157 }
158 series.setYSeries(yOrigSeries);
159 series.setXSeries(xOrigSeries);
160 final IAxis xAxis = fChart.getAxisSet().getXAxis(0);
161 /*
162 * adjustrange appears to bring origin back since we pad the series with
163 * 0s, not interesting.
164 */
165 xAxis.adjustRange();
166 Range range = xAxis.getRange();
167 // fix for overly aggressive lower after an adjust range
168 range.lower = minX - range.upper + maxLength;
169 xAxis.setRange(range);
170 xAxis.getTick().setFormat(DENSITY_TIME_FORMATTER);
171 fChart.getAxisSet().getYAxis(0).setRange(new Range(1.0, maxY));
172 fChart.getAxisSet().getYAxis(0).enableLogScale(true);
173 fChart.redraw();
174 }
175
176 @Override
177 public Chart getControl() {
178 return fChart;
179 }
180
181 /**
182 * Select a range of latency durations in the viewer.
183 *
184 * @param durationRange
185 * a range of latency durations
186 */
187 public void select(Range durationRange) {
188 computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> {
189 for (ISegmentStoreDensityViewerDataListener listener : fListeners) {
190 listener.dataSelectionChanged(data);
191 }
192 });
193 }
194
195 /**
196 * Zoom to a range of latency durations in the viewer.
197 *
198 * @param durationRange
199 * a range of latency durations
200 */
201 public void zoom(Range durationRange) {
202 computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> applyData(data));
203 }
204
205 private CompletableFuture<@Nullable List<ISegment>> computeDataAsync(final TmfTimeRange timeRange, final Range durationRange) {
206 return CompletableFuture.supplyAsync(() -> computeData(timeRange, durationRange));
207 }
208
209 private @Nullable List<ISegment> computeData(final TmfTimeRange timeRange, final Range durationRange) {
210 final ISegmentStoreProvider segmentProvider = fSegmentStoreProvider;
211 if (segmentProvider == null) {
212 return null;
213 }
214 final ISegmentStore<ISegment> segStore = segmentProvider.getSegmentStore();
215 if (segStore == null) {
216 return null;
217 }
218
219 Iterator<ISegment> intersectingElements = segStore.getIntersectingElements(timeRange.getStartTime().getValue(), timeRange.getEndTime().getValue()).iterator();
220
221 if (durationRange.lower > Double.MIN_VALUE || durationRange.upper < Double.MAX_VALUE) {
222 Predicate<? super ISegment> predicate = new Predicate<ISegment>() {
223 @Override
224 public boolean apply(@Nullable ISegment input) {
225 return input != null && input.getLength() >= durationRange.lower && input.getLength() <= durationRange.upper;
226 }
227 };
228 intersectingElements = Iterators.filter(intersectingElements, predicate);
229 }
230
231 return Lists.newArrayList(intersectingElements);
232 }
233
234 private void applyData(final @Nullable List<ISegment> data) {
235 if (data != null) {
236 Collections.sort(data, SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
237 Display.getDefault().asyncExec(() -> updateDisplay(data));
238 for (ISegmentStoreDensityViewerDataListener l : fListeners) {
239 l.dataChanged(data);
240 }
241 }
242 }
243
244 /**
245 * Signal handler for handling of the window range signal.
246 *
247 * @param signal
248 * The {@link TmfWindowRangeUpdatedSignal}
249 */
250 @TmfSignalHandler
251 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal) {
252 if (signal == null) {
253 return;
254 }
255 ITmfTrace trace = getTrace();
256 if (trace == null) {
257 return;
258 }
259 fSegmentStoreProvider = getSegmentStoreProvider(trace);
260 fCurrentTimeRange = NonNullUtils.checkNotNull(signal.getCurrentRange());
261 updateWithRange(fCurrentTimeRange);
262 }
263
264 private void updateWithRange(final TmfTimeRange range) {
265 computeDataAsync(range, new Range(Double.MIN_VALUE, Double.MAX_VALUE)).thenAccept((data) -> applyData(data));
266 }
267
268 @Override
269 public void refresh() {
270 fChart.redraw();
271 }
272
273 @Override
274 public void dispose() {
275 if (fSegmentStoreProvider != null && fListener != null) {
276 fSegmentStoreProvider.removeListener(fListener);
277 }
278 fDragZoomProvider.deregister();
279 fTooltipProvider.deregister();
280 fDragProvider.deregister();
281 super.dispose();
282 }
283
284 /**
285 * Signal handler for handling of the trace opened signal.
286 *
287 * @param signal
288 * The trace opened signal {@link TmfTraceOpenedSignal}
289 */
290 @TmfSignalHandler
291 public void traceOpened(TmfTraceOpenedSignal signal) {
292 fTrace = signal.getTrace();
293 loadTrace(getTrace());
294 }
295
296 /**
297 * Signal handler for handling of the trace selected signal.
298 *
299 * @param signal
300 * The trace selected signal {@link TmfTraceSelectedSignal}
301 */
302 @TmfSignalHandler
303 public void traceSelected(TmfTraceSelectedSignal signal) {
304 if (fTrace != signal.getTrace()) {
305 fTrace = signal.getTrace();
306 loadTrace(getTrace());
307 }
308 }
309
310 /**
311 * Signal handler for handling of the trace closed signal.
312 *
313 * @param signal
314 * The trace closed signal {@link TmfTraceClosedSignal}
315 */
316 @TmfSignalHandler
317 public void traceClosed(TmfTraceClosedSignal signal) {
318
319 if (signal.getTrace() != fTrace) {
320 return;
321 }
322
323 fTrace = null;
324 clearContent();
325 }
326
327 /**
328 * A Method to load a trace into the viewer.
329 *
330 * @param trace
331 * A trace to apply in the viewer
332 */
333 protected void loadTrace(@Nullable ITmfTrace trace) {
334 clearContent();
335
336 fTrace = trace;
337 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
338 TmfTimeRange windowRange = ctx.getWindowRange();
339 fCurrentTimeRange = windowRange;
340
341 if (trace != null) {
342 fSegmentStoreProvider = getSegmentStoreProvider(trace);
343 final ISegmentStoreProvider provider = fSegmentStoreProvider;
344 if (provider != null) {
345 fListener = (segmentProvider, data) -> updateWithRange(windowRange);
346 provider.addListener(fListener);
347 if( provider instanceof IAnalysisModule) {
348 ((IAnalysisModule) provider).schedule();
349 }
350 }
351 }
352 zoom(new Range(0, Long.MAX_VALUE));
353 }
354
355 /**
356 * Clears the view content.
357 */
358 private void clearContent() {
359 final Chart chart = fChart;
360 if (!chart.isDisposed()) {
361 ISeriesSet set = chart.getSeriesSet();
362 ISeries[] series = set.getSeries();
363 for (int i = 0; i < series.length; i++) {
364 set.deleteSeries(series[i].getId());
365 }
366 for (IAxis axis : chart.getAxisSet().getAxes()) {
367 axis.setRange(new Range(0, 1));
368 }
369 chart.redraw();
370 }
371 }
372
373 /**
374 * Add a data listener.
375 *
376 * @param dataListener
377 * the data listener to add
378 */
379 public void addDataListener(ISegmentStoreDensityViewerDataListener dataListener) {
380 fListeners.add(dataListener);
381 }
382
383 /**
384 * Remove a data listener.
385 *
386 * @param dataListener
387 * the data listener to remove
388 */
389 public void removeDataListener(ISegmentStoreDensityViewerDataListener dataListener) {
390 fListeners.remove(dataListener);
391 }
392 }
This page took 0.049395 seconds and 5 git commands to generate.