1 /******************************************************************************
2 * Copyright (c) 2015, 2016 Ericsson
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 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.density
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.nullToEmptyString
;
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
;
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
;
58 import com
.google
.common
.base
.Predicate
;
59 import com
.google
.common
.collect
.Iterators
;
60 import com
.google
.common
.collect
.Lists
;
63 * Displays the segment store provider data in a density chart.
65 * @author Matthew Khouzam
66 * @author Marc-Andre Laperle
70 public abstract class AbstractSegmentStoreDensityViewer
extends TmfViewer
{
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
;
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
;
86 * Constructs a new density viewer.
89 * the parent of the viewer
91 public AbstractSegmentStoreDensityViewer(Composite 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
);
102 fDragZoomProvider
= new MouseDragZoomProvider(this);
103 fDragZoomProvider
.register();
104 fDragProvider
= new MouseSelectionProvider(this);
105 fDragProvider
.register();
106 fTooltipProvider
= new SimpleTooltipProvider(this);
107 fTooltipProvider
.register();
109 fChart
.addDisposeListener((e
) -> {
115 * Returns the segment store provider
118 * The trace to consider
121 protected @Nullable abstract ISegmentStoreProvider
getSegmentStoreProvider(ITmfTrace trace
);
124 private static ITmfTrace
getTrace() {
125 return TmfTraceManager
.getInstance().getActiveTrace();
128 private void updateDisplay(List
<ISegment
> data
) {
129 if (data
.isEmpty()) {
132 IBarSeries series
= (IBarSeries
) fChart
.getSeriesSet().createSeries(SeriesType
.BAR
, Messages
.AbstractSegmentStoreDensityViewer_SeriesLabel
);
133 series
.setVisible(true);
134 series
.setBarPadding(0);
136 series
.setBarColor(new Color(Display
.getDefault(), BAR_COLOR
));
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());
150 double timeWidth
= (double) maxLength
/ (double) width
;
151 for (int i
= 0; i
< width
; i
++) {
152 xOrigSeries
[i
] = i
* timeWidth
;
154 double maxY
= Double
.MIN_VALUE
;
155 for (int i
= 0; i
< width
; i
++) {
156 maxY
= Math
.max(maxY
, yOrigSeries
[i
]);
158 if (minX
== maxLength
) {
162 series
.setYSeries(yOrigSeries
);
163 series
.setXSeries(xOrigSeries
);
164 final IAxis xAxis
= fChart
.getAxisSet().getXAxis(0);
166 * adjustrange appears to bring origin back since we pad the series with
167 * 0s, not interesting.
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);
181 public Chart
getControl() {
186 * Select a range of latency durations in the viewer.
188 * @param durationRange
189 * a range of latency durations
191 public void select(Range durationRange
) {
192 computeDataAsync(fCurrentTimeRange
, durationRange
).thenAccept((data
) -> {
193 for (ISegmentStoreDensityViewerDataListener listener
: fListeners
) {
194 listener
.dataSelectionChanged(data
);
200 * Zoom to a range of latency durations in the viewer.
202 * @param durationRange
203 * a range of latency durations
205 public void zoom(Range durationRange
) {
206 computeDataAsync(fCurrentTimeRange
, durationRange
).thenAccept((data
) -> applyData(data
));
209 private CompletableFuture
<@Nullable List
<ISegment
>> computeDataAsync(final TmfTimeRange timeRange
, final Range durationRange
) {
210 return CompletableFuture
.supplyAsync(() -> computeData(timeRange
, durationRange
));
213 private @Nullable List
<ISegment
> computeData(final TmfTimeRange timeRange
, final Range durationRange
) {
214 final ISegmentStoreProvider segmentProvider
= fSegmentStoreProvider
;
215 if (segmentProvider
== null) {
218 final ISegmentStore
<ISegment
> segStore
= segmentProvider
.getSegmentStore();
219 if (segStore
== null) {
223 Iterator
<ISegment
> intersectingElements
= segStore
.getIntersectingElements(timeRange
.getStartTime().getValue(), timeRange
.getEndTime().getValue()).iterator();
225 if (durationRange
.lower
> Double
.MIN_VALUE
|| durationRange
.upper
< Double
.MAX_VALUE
) {
226 Predicate
<?
super ISegment
> predicate
= new Predicate
<ISegment
>() {
228 public boolean apply(@Nullable ISegment input
) {
229 return input
!= null && input
.getLength() >= durationRange
.lower
&& input
.getLength() <= durationRange
.upper
;
232 intersectingElements
= Iterators
.filter(intersectingElements
, predicate
);
235 return Lists
.newArrayList(intersectingElements
);
238 private void applyData(final @Nullable List
<ISegment
> data
) {
240 Collections
.sort(data
, SegmentComparators
.INTERVAL_LENGTH_COMPARATOR
);
241 Display
.getDefault().asyncExec(() -> updateDisplay(data
));
242 for (ISegmentStoreDensityViewerDataListener l
: fListeners
) {
249 * Signal handler for handling of the window range signal.
252 * The {@link TmfWindowRangeUpdatedSignal}
255 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal
) {
256 if (signal
== null) {
259 ITmfTrace trace
= getTrace();
263 fSegmentStoreProvider
= getSegmentStoreProvider(trace
);
264 fCurrentTimeRange
= NonNullUtils
.checkNotNull(signal
.getCurrentRange());
265 updateWithRange(fCurrentTimeRange
);
268 private void updateWithRange(final TmfTimeRange range
) {
269 computeDataAsync(range
, new Range(Double
.MIN_VALUE
, Double
.MAX_VALUE
)).thenAccept((data
) -> applyData(data
));
273 public void refresh() {
278 public void dispose() {
282 private void internalDispose() {
283 if (fSegmentStoreProvider
!= null && fListener
!= null) {
284 fSegmentStoreProvider
.removeListener(fListener
);
286 fDragZoomProvider
.deregister();
287 fTooltipProvider
.deregister();
288 fDragProvider
.deregister();
293 * Signal handler for handling of the trace opened signal.
296 * The trace opened signal {@link TmfTraceOpenedSignal}
299 public void traceOpened(TmfTraceOpenedSignal signal
) {
300 fTrace
= signal
.getTrace();
301 loadTrace(getTrace());
305 * Signal handler for handling of the trace selected signal.
308 * The trace selected signal {@link TmfTraceSelectedSignal}
311 public void traceSelected(TmfTraceSelectedSignal signal
) {
312 if (fTrace
!= signal
.getTrace()) {
313 fTrace
= signal
.getTrace();
314 loadTrace(getTrace());
319 * Signal handler for handling of the trace closed signal.
322 * The trace closed signal {@link TmfTraceClosedSignal}
325 public void traceClosed(TmfTraceClosedSignal signal
) {
327 if (signal
.getTrace() != fTrace
) {
336 * A Method to load a trace into the viewer.
339 * A trace to apply in the viewer
341 protected void loadTrace(@Nullable ITmfTrace trace
) {
345 TmfTraceContext ctx
= TmfTraceManager
.getInstance().getCurrentTraceContext();
346 TmfTimeRange windowRange
= ctx
.getWindowRange();
347 fCurrentTimeRange
= windowRange
;
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();
360 zoom(new Range(0, Long
.MAX_VALUE
));
364 * Clears the view content.
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());
374 for (IAxis axis
: chart
.getAxisSet().getAxes()) {
375 axis
.setRange(new Range(0, 1));
382 * Add a data listener.
384 * @param dataListener
385 * the data listener to add
387 public void addDataListener(ISegmentStoreDensityViewerDataListener dataListener
) {
388 fListeners
.add(dataListener
);
392 * Remove a data listener.
394 * @param dataListener
395 * the data listener to remove
397 public void removeDataListener(ISegmentStoreDensityViewerDataListener dataListener
) {
398 fListeners
.remove(dataListener
);