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