Commit | Line | Data |
---|---|---|
b9fff7ad | 1 | /******************************************************************************* |
edded5c1 | 2 | * Copyright (c) 2015, 2016 Ericsson |
b9fff7ad BH |
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 | ||
edded5c1 | 14 | package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.table; |
b9fff7ad | 15 | |
ef47abba GB |
16 | import java.text.DecimalFormat; |
17 | import java.text.Format; | |
18 | ||
b9fff7ad BH |
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; | |
fce7c48b | 27 | import org.eclipse.swt.SWT; |
b9fff7ad BH |
28 | import org.eclipse.swt.events.SelectionAdapter; |
29 | import org.eclipse.swt.events.SelectionEvent; | |
30 | import org.eclipse.swt.widgets.Display; | |
fce7c48b BH |
31 | import org.eclipse.swt.widgets.Event; |
32 | import org.eclipse.swt.widgets.Listener; | |
101bcc65 | 33 | import org.eclipse.swt.widgets.Table; |
fce7c48b | 34 | import org.eclipse.swt.widgets.TableColumn; |
b9fff7ad | 35 | import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener; |
0f769d2b | 36 | import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider; |
b9fff7ad | 37 | import org.eclipse.tracecompass.common.core.NonNullUtils; |
edded5c1 BH |
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; | |
b9fff7ad BH |
40 | import org.eclipse.tracecompass.segmentstore.core.ISegment; |
41 | import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; | |
42 | import org.eclipse.tracecompass.segmentstore.core.SegmentComparators; | |
0f769d2b | 43 | import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule; |
b9fff7ad BH |
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; | |
b2c971ec | 52 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
b9fff7ad BH |
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 | ||
101bcc65 MK |
58 | import com.google.common.annotations.VisibleForTesting; |
59 | ||
b9fff7ad | 60 | /** |
0f769d2b | 61 | * Displays the segment store provider data in a column table |
b9fff7ad BH |
62 | * |
63 | * @author France Lapointe Nguyen | |
64 | * @since 2.0 | |
65 | */ | |
66 | public abstract class AbstractSegmentStoreTableViewer extends TmfSimpleTableViewer { | |
67 | ||
85c0f30a | 68 | private static final Format FORMATTER = new DecimalFormat("###,###.##"); //$NON-NLS-1$ |
ef47abba | 69 | |
b9fff7ad BH |
70 | // ------------------------------------------------------------------------ |
71 | // Attributes | |
72 | // ------------------------------------------------------------------------ | |
73 | ||
74 | /** | |
75 | * Abstract class for the column label provider for the segment store | |
0f769d2b | 76 | * provider table viewer |
b9fff7ad BH |
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 | /** | |
0f769d2b MK |
93 | * Listener to update the model with the segment store provider results once |
94 | * its store is fully completed | |
b9fff7ad | 95 | */ |
0f769d2b | 96 | private final class SegmentStoreProviderProgressListener implements IAnalysisProgressListener { |
b9fff7ad | 97 | @Override |
0f769d2b MK |
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)) { | |
b9fff7ad BH |
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()); | |
b2c971ec MK |
115 | ITmfTimestamp start = TmfTimestamp.fromNanos(selectedSegment.getStart()); |
116 | ITmfTimestamp end = TmfTimestamp.fromNanos(selectedSegment.getEnd()); | |
b9fff7ad BH |
117 | TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, start, end)); |
118 | } | |
119 | } | |
120 | ||
121 | /** | |
0f769d2b | 122 | * Current segment store provider |
b9fff7ad | 123 | */ |
0f769d2b | 124 | private @Nullable ISegmentStoreProvider fSegmentProvider = null; |
b9fff7ad BH |
125 | |
126 | /** | |
0f769d2b | 127 | * provider progress listener |
b9fff7ad | 128 | */ |
0f769d2b | 129 | private SegmentStoreProviderProgressListener fListener; |
b9fff7ad BH |
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()); | |
b9fff7ad BH |
150 | createColumns(); |
151 | getTableViewer().getTable().addSelectionListener(new TableSelectionListener()); | |
fce7c48b | 152 | addPackListener(); |
0f769d2b | 153 | fListener = new SegmentStoreProviderProgressListener(); |
b9fff7ad BH |
154 | } |
155 | ||
156 | // ------------------------------------------------------------------------ | |
157 | // Operations | |
158 | // ------------------------------------------------------------------------ | |
159 | ||
101bcc65 MK |
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 | ||
b9fff7ad BH |
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) { | |
ef47abba | 207 | return NonNullUtils.nullToEmptyString(FORMATTER.format(input.getLength())); |
b9fff7ad BH |
208 | } |
209 | }, SegmentComparators.INTERVAL_LENGTH_COMPARATOR); | |
210 | } | |
211 | ||
212 | /** | |
0f769d2b | 213 | * Create columns specific to the provider |
b9fff7ad | 214 | */ |
0f769d2b | 215 | protected void createProviderColumns() { |
b9fff7ad | 216 | if (!fColumnsCreated) { |
0f769d2b MK |
217 | ISegmentStoreProvider provider = getSegmentProvider(); |
218 | if (provider != null) { | |
219 | for (final ISegmentAspect aspect : provider.getSegmentAspects()) { | |
b9fff7ad BH |
220 | createColumn(aspect.getName(), new SegmentStoreTableColumnLabelProvider() { |
221 | @Override | |
222 | public String getTextForSegment(ISegment input) { | |
223 | return NonNullUtils.nullToEmptyString(aspect.resolve(input)); | |
224 | } | |
225 | }, | |
0f769d2b | 226 | aspect.getComparator()); |
b9fff7ad BH |
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 | */ | |
bd53eb28 | 239 | public void updateModel(final @Nullable Object dataInput) { |
b9fff7ad BH |
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 | } | |
fce7c48b | 254 | addPackListener(); |
b9fff7ad BH |
255 | tableViewer.setInput(dataInput); |
256 | SegmentStoreContentProvider contentProvider = (SegmentStoreContentProvider) getTableViewer().getContentProvider(); | |
257 | tableViewer.setItemCount(contentProvider.getSegmentCount()); | |
258 | } | |
259 | } | |
260 | }); | |
261 | } | |
262 | ||
263 | /** | |
0f769d2b MK |
264 | * Set the data into the viewer. It will update the model. If the provider |
265 | * is an analysis, the analysis will be scheduled. | |
b9fff7ad | 266 | * |
0f769d2b MK |
267 | * @param provider |
268 | * segment store provider | |
b9fff7ad | 269 | */ |
0f769d2b MK |
270 | public void setData(@Nullable ISegmentStoreProvider provider) { |
271 | // Set the current segment store provider | |
272 | fSegmentProvider = provider; | |
273 | if (provider == null) { | |
b9fff7ad BH |
274 | updateModel(null); |
275 | return; | |
276 | } | |
277 | ||
0f769d2b | 278 | createProviderColumns(); |
b9fff7ad | 279 | |
0f769d2b MK |
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 | |
73c74de7 JCK |
283 | if (segStore != null) { |
284 | updateModel(segStore); | |
b9fff7ad BH |
285 | return; |
286 | } | |
0f769d2b MK |
287 | // If results are null, then add completion listener and if the provider |
288 | // is an analysis, run the analysis | |
b9fff7ad | 289 | updateModel(null); |
0f769d2b MK |
290 | provider.addListener(fListener); |
291 | if (provider instanceof IAnalysisModule) { | |
292 | ((IAnalysisModule) provider).schedule(); | |
293 | } | |
b9fff7ad BH |
294 | } |
295 | ||
296 | /** | |
0f769d2b MK |
297 | * Returns the segment store provider |
298 | * | |
b9fff7ad BH |
299 | * @param trace |
300 | * The trace to consider | |
0f769d2b | 301 | * @return the segment store provider |
b9fff7ad | 302 | */ |
0f769d2b | 303 | protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace); |
b9fff7ad BH |
304 | |
305 | @Override | |
306 | protected void appendToTablePopupMenu(IMenuManager manager, IStructuredSelection sel) { | |
307 | final ISegment segment = (ISegment) sel.getFirstElement(); | |
8ef16fe6 BH |
308 | if (segment != null) { |
309 | IAction gotoStartTime = new Action(Messages.SegmentStoreTableViewer_goToStartEvent) { | |
310 | @Override | |
311 | public void run() { | |
b2c971ec | 312 | broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, TmfTimestamp.fromNanos(segment.getStart()))); |
8ef16fe6 BH |
313 | } |
314 | }; | |
b9fff7ad | 315 | |
8ef16fe6 BH |
316 | IAction gotoEndTime = new Action(Messages.SegmentStoreTableViewer_goToEndEvent) { |
317 | @Override | |
318 | public void run() { | |
b2c971ec | 319 | broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, TmfTimestamp.fromNanos(segment.getEnd()))); |
8ef16fe6 BH |
320 | } |
321 | }; | |
b9fff7ad | 322 | |
8ef16fe6 BH |
323 | manager.add(gotoStartTime); |
324 | manager.add(gotoEndTime); | |
325 | } | |
b9fff7ad BH |
326 | } |
327 | ||
328 | // ------------------------------------------------------------------------ | |
329 | // Getters | |
330 | // ------------------------------------------------------------------------ | |
331 | ||
332 | /** | |
0f769d2b | 333 | * Get current segment store provider |
b9fff7ad | 334 | * |
0f769d2b | 335 | * @return current segment store provider |
b9fff7ad | 336 | */ |
0f769d2b MK |
337 | public @Nullable ISegmentStoreProvider getSegmentProvider() { |
338 | return fSegmentProvider; | |
b9fff7ad BH |
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) { | |
0f769d2b | 356 | setData(getSegmentStoreProvider(trace)); |
b9fff7ad BH |
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) { | |
0f769d2b | 371 | setData(getSegmentStoreProvider(trace)); |
b9fff7ad BH |
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); | |
0945d5a8 | 387 | getTableViewer().setItemCount(0); |
b9fff7ad BH |
388 | refresh(); |
389 | } | |
76be6c00 | 390 | |
0f769d2b MK |
391 | ISegmentStoreProvider provider = getSegmentProvider(); |
392 | if ((provider != null)) { | |
393 | provider.removeListener(fListener); | |
76be6c00 | 394 | } |
b9fff7ad BH |
395 | } |
396 | } | |
fce7c48b BH |
397 | |
398 | // ------------------------------------------------------------------------ | |
399 | // Helper methods | |
400 | // ------------------------------------------------------------------------ | |
b2f6d73d | 401 | |
fce7c48b BH |
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) { | |
b2f6d73d PT |
409 | // Remove the listener before the pack |
410 | getControl().removeListener(SWT.SetData, this); | |
411 | ||
fce7c48b BH |
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 | } | |
fce7c48b BH |
418 | } |
419 | } | |
420 | }); | |
421 | } | |
b9fff7ad | 422 | } |