timing.ui: Add dirty conditions for SWTbot to scatter graph viewer
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / scatter / AbstractSegmentStoreScatterGraphViewer.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 * Contributors:
10 * France Lapointe Nguyen - Initial API and implementation
11 * Bernd Hufmann - Extracted abstract class from LatencyScatterGraphViewer
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.scatter;
15
16 import java.text.Format;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.concurrent.atomic.AtomicInteger;
23
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.Status;
27 import org.eclipse.core.runtime.jobs.Job;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.eclipse.swt.widgets.Composite;
30 import org.eclipse.swt.widgets.Display;
31 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
32 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
33 import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.SubSecondTimeWithUnitFormat;
34 import org.eclipse.tracecompass.common.core.NonNullUtils;
35 import org.eclipse.tracecompass.internal.analysis.timing.ui.Activator;
36 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter.Messages;
37 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.scatter.SegmentStoreScatterGraphTooltipProvider;
38 import org.eclipse.tracecompass.segmentstore.core.ISegment;
39 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
40 import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
41 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
43 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
44 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
45 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
46 import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
47 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
48 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
49 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
50 import org.eclipse.tracecompass.tmf.ui.viewers.xycharts.linecharts.TmfCommonXLineChartViewer;
51 import org.swtchart.ILineSeries;
52 import org.swtchart.ILineSeries.PlotSymbolType;
53 import org.swtchart.ISeries.SeriesType;
54 import org.swtchart.ISeriesSet;
55 import org.swtchart.LineStyle;
56
57 import com.google.common.primitives.Doubles;
58
59 /**
60 * Displays the segment store provider data in a scatter graph
61 *
62 * @author France Lapointe Nguyen
63 * @author Matthew Khouzam - reduced memory usage
64 * @since 2.0
65 */
66 public abstract class AbstractSegmentStoreScatterGraphViewer extends TmfCommonXLineChartViewer {
67
68 private static final Format FORMAT = new SubSecondTimeWithUnitFormat();
69
70 private final AtomicInteger fDirty = new AtomicInteger();
71
72 private final class CompactingSegmentStoreQuery extends Job {
73 private static final long MAX_POINTS = 1000;
74 private final TmfTimeRange fCurrentRange;
75
76 private CompactingSegmentStoreQuery(TmfTimeRange currentRange) {
77 super(Messages.SegmentStoreScatterGraphViewer_compactTitle);
78 fCurrentRange = currentRange;
79 }
80
81 @Override
82 protected IStatus run(@Nullable IProgressMonitor monitor) {
83 final IProgressMonitor statusMonitor = monitor;
84 try {
85 if (statusMonitor == null) {
86 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Monitor is null"); //$NON-NLS-1$
87 }
88
89 ISegmentStoreProvider segmentProvider = getSegmentProvider();
90 final long startTimeInNanos = fCurrentRange.getStartTime().toNanos();
91 final long endTimeInNanos = fCurrentRange.getEndTime().toNanos();
92 if (segmentProvider == null) {
93 setWindowRange(startTimeInNanos, endTimeInNanos);
94 redraw(statusMonitor, startTimeInNanos, startTimeInNanos, Collections.EMPTY_LIST);
95 return new Status(IStatus.WARNING, Activator.PLUGIN_ID, "segment provider not available"); //$NON-NLS-1$
96 }
97
98 final ISegmentStore<ISegment> segStore = segmentProvider.getSegmentStore();
99 if (segStore == null) {
100 setWindowRange(startTimeInNanos, endTimeInNanos);
101 redraw(statusMonitor, startTimeInNanos, startTimeInNanos, Collections.EMPTY_LIST);
102 return new Status(IStatus.INFO, Activator.PLUGIN_ID, "Segment provider does not have segments"); //$NON-NLS-1$
103 }
104
105 final long startTime = fCurrentRange.getStartTime().getValue();
106 final long endTime = fCurrentRange.getEndTime().getValue();
107 fPixelStart = startTime;
108 fPixelSize = Math.max(1, (endTime - startTime) / MAX_POINTS);
109 final Iterable<ISegment> intersectingElements = segStore.getIntersectingElements(startTime, endTime);
110
111 final List<ISegment> list = convertIterableToList(intersectingElements, statusMonitor);
112 final List<ISegment> displayData = (!list.isEmpty()) ? compactList(startTime, list, statusMonitor) : list;
113
114 setWindowRange(startTimeInNanos, endTimeInNanos);
115 redraw(statusMonitor, startTime, endTime, displayData);
116
117 if (statusMonitor.isCanceled()) {
118 return Status.CANCEL_STATUS;
119 }
120 return Status.OK_STATUS;
121 } finally {
122 /*
123 * fDirty should have been incremented before creating a job, so
124 * we decrement it once the job is done
125 */
126 fDirty.decrementAndGet();
127 }
128
129 }
130
131 private void redraw(final IProgressMonitor statusMonitor, final long startTime, final long endTime, final List<ISegment> displayData) {
132 fDisplayData = displayData;
133 /*
134 * Increment at every redraw, since the content of the view is not
135 * current
136 */
137 fDirty.incrementAndGet();
138 Display.getDefault().asyncExec(new Runnable() {
139
140 @Override
141 public void run() {
142 try {
143 updateData(startTime, endTime, displayData.size(), statusMonitor);
144 } finally {
145 /* Decrement once the redraw is done */
146 fDirty.decrementAndGet();
147 }
148 }
149 });
150 }
151
152 private List<ISegment> compactList(final long startTime, final List<ISegment> listToCompact, final IProgressMonitor statusMonitor) {
153 List<ISegment> displayData = new ArrayList<>();
154 ISegment last = listToCompact.get(0);
155 if (last.getStart() >= startTime) {
156 displayData.add(last);
157 }
158 for (ISegment next : listToCompact) {
159 if (next.getStart() < startTime) {
160 continue;
161 }
162 if (statusMonitor.isCanceled()) {
163 return Collections.EMPTY_LIST;
164 }
165 if (!overlaps(last, next)) {
166 displayData.add(next);
167 last = next;
168 }
169 }
170 return displayData;
171 }
172
173 private List<ISegment> convertIterableToList(final Iterable<ISegment> iterable, final IProgressMonitor statusMonitor) {
174 final List<ISegment> list = new ArrayList<>();
175 for (ISegment seg : iterable) {
176 if (statusMonitor.isCanceled()) {
177 return Collections.EMPTY_LIST;
178 }
179 list.add(seg);
180 }
181 Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR);
182 return list;
183 }
184
185 private boolean overlaps(ISegment last, ISegment next) {
186 long timePerPix = fPixelSize;
187 final long start = last.getStart();
188 final long pixelStart = fPixelStart;
189 final long pixelDuration = start - pixelStart;
190 long startPixBoundL = pixelDuration / timePerPix * timePerPix + pixelStart;
191 long startPixBoundR = startPixBoundL + timePerPix;
192 final long currentStart = next.getStart();
193 if (currentStart >= startPixBoundL && currentStart <= startPixBoundR) {
194 long length = last.getLength();
195 long lengthNext = next.getLength();
196 long lengthLow = length / timePerPix * timePerPix;
197 long lengthHigh = lengthLow + timePerPix;
198 return (lengthNext >= lengthLow && lengthNext <= lengthHigh);
199 }
200 return false;
201 }
202 }
203
204 // ------------------------------------------------------------------------
205 // Attributes
206 // ------------------------------------------------------------------------
207
208 /**
209 * Listener to update the model with the segment store provider results once
210 * its segment store is fully completed
211 */
212 private final class SegmentStoreProviderProgressListener implements IAnalysisProgressListener {
213
214 @Override
215 public void onComplete(ISegmentStoreProvider segmentProvider, ISegmentStore<ISegment> segmentStore) {
216 // Only update the model if trace that was analyzed is active trace
217 if (segmentProvider.equals(getSegmentProvider())) {
218 updateModel(segmentStore);
219 }
220 }
221 }
222
223 private long fPixelSize = -1;
224
225 private long fPixelStart = 0;
226 /**
227 * Data to display
228 */
229 private Collection<ISegment> fDisplayData = Collections.EMPTY_LIST;
230
231 /**
232 * Provider completion listener
233 */
234 private SegmentStoreProviderProgressListener fListener;
235
236 /**
237 * Current segment provider
238 */
239 private @Nullable ISegmentStoreProvider fSegmentProvider;
240
241 private @Nullable Job fCompactingJob;
242
243 // ------------------------------------------------------------------------
244 // Constructor
245 // ------------------------------------------------------------------------
246
247 /**
248 * Constructor
249 *
250 * @param parent
251 * parent composite
252 * @param title
253 * name of the graph
254 * @param xLabel
255 * name of the x axis
256 * @param yLabel
257 * name of the y axis
258 */
259 public AbstractSegmentStoreScatterGraphViewer(Composite parent, String title, String xLabel, String yLabel) {
260 super(parent, title, xLabel, yLabel);
261 setTooltipProvider(new SegmentStoreScatterGraphTooltipProvider(this));
262 fListener = new SegmentStoreProviderProgressListener();
263 ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
264 initializeProvider(trace);
265 getSwtChart().getLegend().setVisible(false);
266 getSwtChart().getAxisSet().getYAxis(0).getTick().setFormat(FORMAT);
267 }
268
269 private final void initializeProvider(@Nullable ITmfTrace trace) {
270 if (trace != null) {
271 final ISegmentStoreProvider segmentStoreProvider = getSegmentStoreProvider(trace);
272 if (segmentStoreProvider != null) {
273 segmentStoreProvider.addListener(fListener);
274 setData(segmentStoreProvider);
275 }
276 }
277 }
278
279 // ------------------------------------------------------------------------
280 // Operations
281 // ------------------------------------------------------------------------
282
283 /**
284 * Update the data in the graph
285 *
286 * @param dataInput
287 * new model
288 */
289 public void updateModel(@Nullable ISegmentStore<ISegment> dataInput) {
290 // Update new window range
291 TmfTimeRange currentRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
292 long currentStart = currentRange.getStartTime().toNanos();
293 long currentEnd = currentRange.getEndTime().toNanos();
294 if (dataInput == null) {
295 if (!getDisplay().isDisposed()) {
296 Display.getDefault().syncExec(new Runnable() {
297 @Override
298 public void run() {
299 clearContent();
300 }
301 });
302 }
303 fDisplayData = Collections.EMPTY_LIST;
304 } else {
305 Collection<ISegment> elements = (Collection<ISegment>) dataInput.getIntersectingElements(currentStart, currentEnd);
306 // getIntersectingElements can return an unsorted iterable, make
307 // sure our collection is sorted
308 ArrayList<ISegment> list = new ArrayList<>(elements);
309 Collections.sort(list, SegmentComparators.INTERVAL_START_COMPARATOR);
310 fDisplayData = list;
311 }
312 setWindowRange(currentStart, currentEnd);
313 updateRange(currentRange);
314 }
315
316 @Override
317 protected void initializeDataSource() {
318 ITmfTrace trace = getTrace();
319 initializeProvider(trace);
320 if (trace != null) {
321 setData(getSegmentStoreProvider(trace));
322 }
323 }
324
325 @Override
326 protected void updateData(final long start, final long end, int nb, @Nullable IProgressMonitor monitor) {
327 // Third parameter is not used by implementation
328 // Determine data that needs to be visible
329 Collection<ISegment> data = fDisplayData;
330
331 final int dataSize = (nb == 0) ? data.size() : nb;
332 if (end == start) {
333 return;
334 }
335
336 List<Double> xSeries = new ArrayList<>(dataSize);
337 List<Double> ySeries = new ArrayList<>(dataSize);
338 // For each visible segments, add start time to x value and duration
339 // for y value
340 Iterator<ISegment> modelIter = data.iterator();
341 while (modelIter.hasNext()) {
342 ISegment segment = modelIter.next();
343 xSeries.add((double) (segment.getStart() - start));
344 ySeries.add((double) segment.getLength());
345 }
346 setXAxis(Doubles.toArray(xSeries));
347 setSeries(Messages.SegmentStoreScatterGraphViewer_legend, Doubles.toArray(ySeries));
348 updateDisplay();
349 }
350
351 @Override
352 protected void setWindowRange(final long windowStartTime, final long windowEndTime) {
353 super.setWindowRange(windowStartTime, windowEndTime);
354 }
355
356 @Override
357 protected ILineSeries addSeries(@Nullable String seriesName) {
358 ISeriesSet seriesSet = getSwtChart().getSeriesSet();
359 ILineSeries series = (ILineSeries) seriesSet.createSeries(SeriesType.LINE, seriesName);
360 series.setVisible(true);
361 series.enableArea(false);
362 series.setLineStyle(LineStyle.NONE);
363 series.setSymbolType(PlotSymbolType.DIAMOND);
364 return series;
365 }
366
367 /**
368 * Set the data into the viewer. If the provider is an analysis, it will
369 * update the model if the analysis is completed or run the analysis if not
370 * completed
371 *
372 * @param provider
373 * Segment store provider
374 */
375 public void setData(@Nullable ISegmentStoreProvider provider) {
376 if (provider == null) {
377 updateModel(null);
378 return;
379 }
380 ISegmentStore<ISegment> segStore = provider.getSegmentStore();
381 // If results are not null, then segment store is completed and model
382 // can be updated
383 if (segStore != null) {
384 updateModel(segStore);
385 setSegmentProvider(provider);
386 return;
387 }
388 updateModel(null);
389 provider.addListener(fListener);
390 if (provider instanceof IAnalysisModule) {
391 ((IAnalysisModule) provider).schedule();
392 }
393 setSegmentProvider(provider);
394 }
395
396 /**
397 * Returns the segment store provider
398 *
399 * @param trace
400 * The trace to consider
401 * @return the segment store provider
402 */
403 protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace);
404
405 // ------------------------------------------------------------------------
406 // Signal handlers
407 // ------------------------------------------------------------------------
408
409 /**
410 * @param signal
411 * Signal received when a different trace is selected
412 */
413 @Override
414 @TmfSignalHandler
415 public void traceSelected(@Nullable TmfTraceSelectedSignal signal) {
416 super.traceSelected(signal);
417 if (signal == null) {
418 return;
419 }
420 ITmfTrace trace = signal.getTrace();
421 setTrace(trace);
422 if (trace != null) {
423 final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
424 setWindowRange(
425 timeRange.getStartTime().toNanos(),
426 timeRange.getEndTime().toNanos());
427 setData(getSegmentStoreProvider(trace));
428 updateRange(timeRange);
429 }
430 }
431
432 /**
433 * @param signal
434 * Signal received when trace is opened
435 */
436 @Override
437 @TmfSignalHandler
438 public void traceOpened(@Nullable TmfTraceOpenedSignal signal) {
439 super.traceOpened(signal);
440 if (signal == null) {
441 return;
442 }
443 ITmfTrace trace = signal.getTrace();
444 setTrace(trace);
445 if (trace != null) {
446
447 final ISegmentStoreProvider segmentStoreProvider = getSegmentStoreProvider(trace);
448 final TmfTimeRange timeRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
449 setWindowRange(
450 timeRange.getStartTime().toNanos(),
451 timeRange.getEndTime().toNanos());
452 setData(segmentStoreProvider);
453 }
454
455 }
456
457 private void updateRange(final @Nullable TmfTimeRange timeRange) {
458 /*
459 * Update is request, content is not up to date, fDirty will be
460 * decremented in the compacting job
461 */
462 fDirty.incrementAndGet();
463 Job compactingJob = fCompactingJob;
464 if (compactingJob != null && compactingJob.getState() == Job.RUNNING) {
465 compactingJob.cancel();
466 }
467 compactingJob = new CompactingSegmentStoreQuery(NonNullUtils.checkNotNull(timeRange));
468 fCompactingJob = compactingJob;
469 compactingJob.schedule();
470 }
471
472 /**
473 * @param signal
474 * Signal received when last opened trace is closed
475 */
476 @Override
477 @TmfSignalHandler
478 public void traceClosed(@Nullable TmfTraceClosedSignal signal) {
479 super.traceClosed(signal);
480 if (signal != null) {
481 // Check if there is no more opened trace
482 if (TmfTraceManager.getInstance().getActiveTrace() == null) {
483 ISegmentStoreProvider provider = getSegmentProvider();
484 if (provider != null) {
485 provider.removeListener(fListener);
486 }
487 clearContent();
488 }
489 }
490 refresh();
491 }
492
493 /**
494 * @param signal
495 * Signal received when window range is updated
496 */
497 @Override
498 @TmfSignalHandler
499 public void windowRangeUpdated(@Nullable TmfWindowRangeUpdatedSignal signal) {
500 super.windowRangeUpdated(signal);
501 if (signal == null) {
502 return;
503 }
504 if (getTrace() != null) {
505 final TmfTimeRange currentRange = signal.getCurrentRange();
506 updateRange(currentRange);
507 } else {
508 Activator.getDefault().logInfo("No Trace to update"); //$NON-NLS-1$
509 }
510 }
511
512 private @Nullable ISegmentStoreProvider getSegmentProvider() {
513 return fSegmentProvider;
514 }
515
516 private void setSegmentProvider(ISegmentStoreProvider provider) {
517 fSegmentProvider = provider;
518 }
519
520 @Override
521 public boolean isDirty() {
522 /* Check the parent's or this view's own dirtiness */
523 return super.isDirty() || (fDirty.get() != 0);
524 }
525 }
This page took 0.046683 seconds and 5 git commands to generate.