timing.swtbot: add Generic SegmentTable tests
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.timing.ui / src / org / eclipse / tracecompass / analysis / timing / ui / views / segmentstore / table / AbstractSegmentStoreTableViewer.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 - Move abstract class to TMF
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.table;
15
16 import java.text.DecimalFormat;
17 import java.text.Format;
18
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.eclipse.jface.action.Action;
21 import org.eclipse.jface.action.IAction;
22 import org.eclipse.jface.action.IMenuManager;
23 import org.eclipse.jface.viewers.ColumnLabelProvider;
24 import org.eclipse.jface.viewers.IStructuredSelection;
25 import org.eclipse.jface.viewers.StructuredSelection;
26 import org.eclipse.jface.viewers.TableViewer;
27 import org.eclipse.swt.SWT;
28 import org.eclipse.swt.events.SelectionAdapter;
29 import org.eclipse.swt.events.SelectionEvent;
30 import org.eclipse.swt.widgets.Display;
31 import org.eclipse.swt.widgets.Event;
32 import org.eclipse.swt.widgets.Listener;
33 import org.eclipse.swt.widgets.Table;
34 import org.eclipse.swt.widgets.TableColumn;
35 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
36 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
37 import org.eclipse.tracecompass.common.core.NonNullUtils;
38 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.table.Messages;
39 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.table.SegmentStoreContentProvider;
40 import org.eclipse.tracecompass.segmentstore.core.ISegment;
41 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
42 import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
43 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
44 import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect;
45 import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
46 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
47 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
48 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
49 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
50 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
51 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
52 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
53 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
54 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
55 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
56 import org.eclipse.tracecompass.tmf.ui.viewers.table.TmfSimpleTableViewer;
57
58 import com.google.common.annotations.VisibleForTesting;
59
60 /**
61 * Displays the segment store provider data in a column table
62 *
63 * @author France Lapointe Nguyen
64 * @since 2.0
65 */
66 public abstract class AbstractSegmentStoreTableViewer extends TmfSimpleTableViewer {
67
68 private static final Format FORMATTER = new DecimalFormat("###,###.##"); //$NON-NLS-1$
69
70 // ------------------------------------------------------------------------
71 // Attributes
72 // ------------------------------------------------------------------------
73
74 /**
75 * Abstract class for the column label provider for the segment store
76 * provider table viewer
77 */
78 private abstract class SegmentStoreTableColumnLabelProvider extends ColumnLabelProvider {
79
80 @Override
81 public String getText(@Nullable Object input) {
82 if (!(input instanceof ISegment)) {
83 /* Doubles as a null check */
84 return ""; //$NON-NLS-1$
85 }
86 return getTextForSegment((ISegment) input);
87 }
88
89 public abstract String getTextForSegment(ISegment input);
90 }
91
92 /**
93 * Listener to update the model with the segment store provider results once
94 * its store is fully completed
95 */
96 private final class SegmentStoreProviderProgressListener implements IAnalysisProgressListener {
97 @Override
98 public void onComplete(ISegmentStoreProvider activeProvider, ISegmentStore<ISegment> data) {
99 // Check if the active trace was changed while the provider was
100 // building its segment store
101 if (activeProvider.equals(fSegmentProvider)) {
102 updateModel(data);
103 }
104 }
105 }
106
107 /**
108 * Listener to select a range in other viewers when a cell of the segment
109 * store table view is selected
110 */
111 private class TableSelectionListener extends SelectionAdapter {
112 @Override
113 public void widgetSelected(@Nullable SelectionEvent e) {
114 ISegment selectedSegment = ((ISegment) NonNullUtils.checkNotNull(e).item.getData());
115 ITmfTimestamp start = TmfTimestamp.fromNanos(selectedSegment.getStart());
116 ITmfTimestamp end = TmfTimestamp.fromNanos(selectedSegment.getEnd());
117 TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, start, end));
118 }
119 }
120
121 /**
122 * Current segment store provider
123 */
124 private @Nullable ISegmentStoreProvider fSegmentProvider = null;
125
126 /**
127 * provider progress listener
128 */
129 private SegmentStoreProviderProgressListener fListener;
130
131 /**
132 * Flag to create columns once
133 */
134 boolean fColumnsCreated = false;
135
136 // ------------------------------------------------------------------------
137 // Constructor
138 // ------------------------------------------------------------------------
139
140 /**
141 * Constructor
142 *
143 * @param tableViewer
144 * Table viewer of the view
145 */
146 public AbstractSegmentStoreTableViewer(TableViewer tableViewer) {
147 super(tableViewer);
148 // Sort order of the content provider is by start time by default
149 getTableViewer().setContentProvider(new SegmentStoreContentProvider());
150 createColumns();
151 getTableViewer().getTable().addSelectionListener(new TableSelectionListener());
152 addPackListener();
153 fListener = new SegmentStoreProviderProgressListener();
154 }
155
156 // ------------------------------------------------------------------------
157 // Operations
158 // ------------------------------------------------------------------------
159
160 /**
161 * Sets the segment provider, use only in test, only run in display thread
162 *
163 * @param segmentProvider
164 * the segment provider
165 * @since 1.2
166 */
167 @VisibleForTesting
168 public void setSegmentProvider(ISegmentStoreProvider segmentProvider) {
169 fSegmentProvider = segmentProvider;
170 // Sort order of the content provider is by start time by default
171 getTableViewer().setContentProvider(new SegmentStoreContentProvider());
172
173 Table table = getTableViewer().getTable();
174 table.setRedraw(false);
175 while (table.getColumnCount() > 0) {
176 table.getColumn(0).dispose();
177 }
178 createColumns();
179 createProviderColumns();
180 getTableViewer().getTable().addSelectionListener(new TableSelectionListener());
181 addPackListener();
182 fListener = new SegmentStoreProviderProgressListener();
183 table.setRedraw(true);
184 }
185
186 /**
187 * Create default columns for start time, end time and duration
188 */
189 private void createColumns() {
190 createColumn(Messages.SegmentStoreTableViewer_startTime, new SegmentStoreTableColumnLabelProvider() {
191 @Override
192 public String getTextForSegment(ISegment input) {
193 return NonNullUtils.nullToEmptyString(TmfTimestampFormat.getDefaulTimeFormat().format(input.getStart()));
194 }
195 }, SegmentComparators.INTERVAL_START_COMPARATOR);
196
197 createColumn(Messages.SegmentStoreTableViewer_endTime, new SegmentStoreTableColumnLabelProvider() {
198 @Override
199 public String getTextForSegment(ISegment input) {
200 return NonNullUtils.nullToEmptyString(TmfTimestampFormat.getDefaulTimeFormat().format(input.getEnd()));
201 }
202 }, SegmentComparators.INTERVAL_END_COMPARATOR);
203
204 createColumn(Messages.SegmentStoreTableViewer_duration, new SegmentStoreTableColumnLabelProvider() {
205 @Override
206 public String getTextForSegment(ISegment input) {
207 return NonNullUtils.nullToEmptyString(FORMATTER.format(input.getLength()));
208 }
209 }, SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
210 }
211
212 /**
213 * Create columns specific to the provider
214 */
215 protected void createProviderColumns() {
216 if (!fColumnsCreated) {
217 ISegmentStoreProvider provider = getSegmentProvider();
218 if (provider != null) {
219 for (final ISegmentAspect aspect : provider.getSegmentAspects()) {
220 createColumn(aspect.getName(), new SegmentStoreTableColumnLabelProvider() {
221 @Override
222 public String getTextForSegment(ISegment input) {
223 return NonNullUtils.nullToEmptyString(aspect.resolve(input));
224 }
225 },
226 aspect.getComparator());
227 }
228 }
229 fColumnsCreated = true;
230 }
231 }
232
233 /**
234 * Update the data in the table viewer
235 *
236 * @param dataInput
237 * New data input
238 */
239 public void updateModel(final @Nullable Object dataInput) {
240 final TableViewer tableViewer = getTableViewer();
241 Display.getDefault().asyncExec(new Runnable() {
242 @Override
243 public void run() {
244 if (!tableViewer.getTable().isDisposed()) {
245 // Go to the top of the table
246 tableViewer.getTable().setTopIndex(0);
247 // Reset selected row
248 tableViewer.setSelection(StructuredSelection.EMPTY);
249 if (dataInput == null) {
250 tableViewer.setInput(null);
251 tableViewer.setItemCount(0);
252 return;
253 }
254 addPackListener();
255 tableViewer.setInput(dataInput);
256 SegmentStoreContentProvider contentProvider = (SegmentStoreContentProvider) getTableViewer().getContentProvider();
257 tableViewer.setItemCount(contentProvider.getSegmentCount());
258 }
259 }
260 });
261 }
262
263 /**
264 * Set the data into the viewer. It will update the model. If the provider
265 * is an analysis, the analysis will be scheduled.
266 *
267 * @param provider
268 * segment store provider
269 */
270 public void setData(@Nullable ISegmentStoreProvider provider) {
271 // Set the current segment store provider
272 fSegmentProvider = provider;
273 if (provider == null) {
274 updateModel(null);
275 return;
276 }
277
278 createProviderColumns();
279
280 ISegmentStore<ISegment> segStore = provider.getSegmentStore();
281 // If results are not null, then the segment of the provider is ready
282 // and model can be updated
283 if (segStore != null) {
284 updateModel(segStore);
285 return;
286 }
287 // If results are null, then add completion listener and if the provider
288 // is an analysis, run the analysis
289 updateModel(null);
290 provider.addListener(fListener);
291 if (provider instanceof IAnalysisModule) {
292 ((IAnalysisModule) provider).schedule();
293 }
294 }
295
296 /**
297 * Returns the segment store provider
298 *
299 * @param trace
300 * The trace to consider
301 * @return the segment store provider
302 */
303 protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace);
304
305 @Override
306 protected void appendToTablePopupMenu(IMenuManager manager, IStructuredSelection sel) {
307 final ISegment segment = (ISegment) sel.getFirstElement();
308 if (segment != null) {
309 IAction gotoStartTime = new Action(Messages.SegmentStoreTableViewer_goToStartEvent) {
310 @Override
311 public void run() {
312 broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, TmfTimestamp.fromNanos(segment.getStart())));
313 }
314 };
315
316 IAction gotoEndTime = new Action(Messages.SegmentStoreTableViewer_goToEndEvent) {
317 @Override
318 public void run() {
319 broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, TmfTimestamp.fromNanos(segment.getEnd())));
320 }
321 };
322
323 manager.add(gotoStartTime);
324 manager.add(gotoEndTime);
325 }
326 }
327
328 // ------------------------------------------------------------------------
329 // Getters
330 // ------------------------------------------------------------------------
331
332 /**
333 * Get current segment store provider
334 *
335 * @return current segment store provider
336 */
337 public @Nullable ISegmentStoreProvider getSegmentProvider() {
338 return fSegmentProvider;
339 }
340
341 // ------------------------------------------------------------------------
342 // Signal handlers
343 // ------------------------------------------------------------------------
344
345 /**
346 * Trace selected handler
347 *
348 * @param signal
349 * Different opened trace (on which segment store analysis as
350 * already been performed) has been selected
351 */
352 @TmfSignalHandler
353 public void traceSelected(TmfTraceSelectedSignal signal) {
354 ITmfTrace trace = signal.getTrace();
355 if (trace != null) {
356 setData(getSegmentStoreProvider(trace));
357 }
358 }
359
360 /**
361 * Trace opened handler
362 *
363 * @param signal
364 * New trace (on which segment store analysis has not been
365 * performed) is opened
366 */
367 @TmfSignalHandler
368 public void traceOpened(TmfTraceOpenedSignal signal) {
369 ITmfTrace trace = signal.getTrace();
370 if (trace != null) {
371 setData(getSegmentStoreProvider(trace));
372 }
373 }
374
375 /**
376 * Trace closed handler
377 *
378 * @param signal
379 * Last opened trace was closed
380 */
381 @TmfSignalHandler
382 public void traceClosed(TmfTraceClosedSignal signal) {
383 // Check if there is no more opened trace
384 if (TmfTraceManager.getInstance().getActiveTrace() == null) {
385 if (!getTableViewer().getTable().isDisposed()) {
386 getTableViewer().setInput(null);
387 getTableViewer().setItemCount(0);
388 refresh();
389 }
390
391 ISegmentStoreProvider provider = getSegmentProvider();
392 if ((provider != null)) {
393 provider.removeListener(fListener);
394 }
395 }
396 }
397
398 // ------------------------------------------------------------------------
399 // Helper methods
400 // ------------------------------------------------------------------------
401
402 /*
403 * Add the listener for SetData on the table
404 */
405 private void addPackListener() {
406 getControl().addListener(SWT.SetData, new Listener() {
407 @Override
408 public void handleEvent(@Nullable Event event) {
409 // Remove the listener before the pack
410 getControl().removeListener(SWT.SetData, this);
411
412 // Pack the column the first time data is set
413 TableViewer tableViewer = getTableViewer();
414 if (tableViewer != null) {
415 for (TableColumn col : tableViewer.getTable().getColumns()) {
416 col.pack();
417 }
418 }
419 }
420 });
421 }
422 }
This page took 0.040234 seconds and 5 git commands to generate.