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();
111 * Returns the segment store provider
114 * The trace to consider
117 protected @Nullable abstract ISegmentStoreProvider
getSegmentStoreProvider(ITmfTrace trace
);
120 private static ITmfTrace
getTrace() {
121 return TmfTraceManager
.getInstance().getActiveTrace();
124 private void updateDisplay(List
<ISegment
> data
) {
125 if (data
.isEmpty()) {
128 IBarSeries series
= (IBarSeries
) fChart
.getSeriesSet().createSeries(SeriesType
.BAR
, Messages
.AbstractSegmentStoreDensityViewer_SeriesLabel
);
129 series
.setVisible(true);
130 series
.setBarPadding(0);
132 series
.setBarColor(new Color(Display
.getDefault(), BAR_COLOR
));
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());
146 double timeWidth
= (double) maxLength
/ (double) width
;
147 for (int i
= 0; i
< width
; i
++) {
148 xOrigSeries
[i
] = i
* timeWidth
;
150 double maxY
= Double
.MIN_VALUE
;
151 for (int i
= 0; i
< width
; i
++) {
152 maxY
= Math
.max(maxY
, yOrigSeries
[i
]);
154 if (minX
== maxLength
) {
158 series
.setYSeries(yOrigSeries
);
159 series
.setXSeries(xOrigSeries
);
160 final IAxis xAxis
= fChart
.getAxisSet().getXAxis(0);
162 * adjustrange appears to bring origin back since we pad the series with
163 * 0s, not interesting.
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);
177 public Chart
getControl() {
182 * Select a range of latency durations in the viewer.
184 * @param durationRange
185 * a range of latency durations
187 public void select(Range durationRange
) {
188 computeDataAsync(fCurrentTimeRange
, durationRange
).thenAccept((data
) -> {
189 for (ISegmentStoreDensityViewerDataListener listener
: fListeners
) {
190 listener
.dataSelectionChanged(data
);
196 * Zoom to a range of latency durations in the viewer.
198 * @param durationRange
199 * a range of latency durations
201 public void zoom(Range durationRange
) {
202 computeDataAsync(fCurrentTimeRange
, durationRange
).thenAccept((data
) -> applyData(data
));
205 private CompletableFuture
<@Nullable List
<ISegment
>> computeDataAsync(final TmfTimeRange timeRange
, final Range durationRange
) {
206 return CompletableFuture
.supplyAsync(() -> computeData(timeRange
, durationRange
));
209 private @Nullable List
<ISegment
> computeData(final TmfTimeRange timeRange
, final Range durationRange
) {
210 final ISegmentStoreProvider segmentProvider
= fSegmentStoreProvider
;
211 if (segmentProvider
== null) {
214 final ISegmentStore
<ISegment
> segStore
= segmentProvider
.getSegmentStore();
215 if (segStore
== null) {
219 Iterator
<ISegment
> intersectingElements
= segStore
.getIntersectingElements(timeRange
.getStartTime().getValue(), timeRange
.getEndTime().getValue()).iterator();
221 if (durationRange
.lower
> Double
.MIN_VALUE
|| durationRange
.upper
< Double
.MAX_VALUE
) {
222 Predicate
<?
super ISegment
> predicate
= new Predicate
<ISegment
>() {
224 public boolean apply(@Nullable ISegment input
) {
225 return input
!= null && input
.getLength() >= durationRange
.lower
&& input
.getLength() <= durationRange
.upper
;
228 intersectingElements
= Iterators
.filter(intersectingElements
, predicate
);
231 return Lists
.newArrayList(intersectingElements
);
234 private void applyData(final @Nullable List
<ISegment
> data
) {
236 Collections
.sort(data
, SegmentComparators
.INTERVAL_LENGTH_COMPARATOR
);
237 Display
.getDefault().asyncExec(() -> updateDisplay(data
));
238 for (ISegmentStoreDensityViewerDataListener l
: fListeners
) {
245 * Signal handler for handling of the window range signal.
248 * The {@link TmfWindowRangeUpdatedSignal}
251 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal
) {
252 if (signal
== null) {
255 ITmfTrace trace
= getTrace();
259 fSegmentStoreProvider
= getSegmentStoreProvider(trace
);
260 fCurrentTimeRange
= NonNullUtils
.checkNotNull(signal
.getCurrentRange());
261 updateWithRange(fCurrentTimeRange
);
264 private void updateWithRange(final TmfTimeRange range
) {
265 computeDataAsync(range
, new Range(Double
.MIN_VALUE
, Double
.MAX_VALUE
)).thenAccept((data
) -> applyData(data
));
269 public void refresh() {
274 public void dispose() {
275 if (fSegmentStoreProvider
!= null && fListener
!= null) {
276 fSegmentStoreProvider
.removeListener(fListener
);
278 fDragZoomProvider
.deregister();
279 fTooltipProvider
.deregister();
280 fDragProvider
.deregister();
285 * Signal handler for handling of the trace opened signal.
288 * The trace opened signal {@link TmfTraceOpenedSignal}
291 public void traceOpened(TmfTraceOpenedSignal signal
) {
292 fTrace
= signal
.getTrace();
293 loadTrace(getTrace());
297 * Signal handler for handling of the trace selected signal.
300 * The trace selected signal {@link TmfTraceSelectedSignal}
303 public void traceSelected(TmfTraceSelectedSignal signal
) {
304 if (fTrace
!= signal
.getTrace()) {
305 fTrace
= signal
.getTrace();
306 loadTrace(getTrace());
311 * Signal handler for handling of the trace closed signal.
314 * The trace closed signal {@link TmfTraceClosedSignal}
317 public void traceClosed(TmfTraceClosedSignal signal
) {
319 if (signal
.getTrace() != fTrace
) {
328 * A Method to load a trace into the viewer.
331 * A trace to apply in the viewer
333 protected void loadTrace(@Nullable ITmfTrace trace
) {
337 TmfTraceContext ctx
= TmfTraceManager
.getInstance().getCurrentTraceContext();
338 TmfTimeRange windowRange
= ctx
.getWindowRange();
339 fCurrentTimeRange
= windowRange
;
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();
352 zoom(new Range(0, Long
.MAX_VALUE
));
356 * Clears the view content.
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());
366 for (IAxis axis
: chart
.getAxisSet().getAxes()) {
367 axis
.setRange(new Range(0, 1));
374 * Add a data listener.
376 * @param dataListener
377 * the data listener to add
379 public void addDataListener(ISegmentStoreDensityViewerDataListener dataListener
) {
380 fListeners
.add(dataListener
);
384 * Remove a data listener.
386 * @param dataListener
387 * the data listener to remove
389 public void removeDataListener(ISegmentStoreDensityViewerDataListener dataListener
) {
390 fListeners
.remove(dataListener
);