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 | */ |
f0b123a9 | 129 | private final @Nullable 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) { | |
f0b123a9 GB |
147 | this(tableViewer, true); |
148 | } | |
149 | ||
150 | /** | |
151 | * Constructor | |
152 | * | |
153 | * @param tableViewer | |
154 | * Table viewer of the view | |
155 | * @param withListener | |
156 | * Whether to add a listener to this table viewer. For instance, | |
157 | * for table viewers who are part of another view who update the | |
158 | * table's data, this value can be <code>false</code> so only the | |
159 | * other listeners will update the data | |
6dc61a6f | 160 | * @since 2.0 |
f0b123a9 GB |
161 | */ |
162 | public AbstractSegmentStoreTableViewer(TableViewer tableViewer, boolean withListener) { | |
b9fff7ad BH |
163 | super(tableViewer); |
164 | // Sort order of the content provider is by start time by default | |
165 | getTableViewer().setContentProvider(new SegmentStoreContentProvider()); | |
b9fff7ad BH |
166 | createColumns(); |
167 | getTableViewer().getTable().addSelectionListener(new TableSelectionListener()); | |
fce7c48b | 168 | addPackListener(); |
f0b123a9 | 169 | fListener = withListener ? new SegmentStoreProviderProgressListener() : null; |
b9fff7ad BH |
170 | } |
171 | ||
172 | // ------------------------------------------------------------------------ | |
173 | // Operations | |
174 | // ------------------------------------------------------------------------ | |
175 | ||
101bcc65 MK |
176 | /** |
177 | * Sets the segment provider, use only in test, only run in display thread | |
178 | * | |
179 | * @param segmentProvider | |
180 | * the segment provider | |
181 | * @since 1.2 | |
182 | */ | |
183 | @VisibleForTesting | |
184 | public void setSegmentProvider(ISegmentStoreProvider segmentProvider) { | |
185 | fSegmentProvider = segmentProvider; | |
186 | // Sort order of the content provider is by start time by default | |
187 | getTableViewer().setContentProvider(new SegmentStoreContentProvider()); | |
188 | ||
189 | Table table = getTableViewer().getTable(); | |
190 | table.setRedraw(false); | |
191 | while (table.getColumnCount() > 0) { | |
192 | table.getColumn(0).dispose(); | |
193 | } | |
194 | createColumns(); | |
195 | createProviderColumns(); | |
196 | getTableViewer().getTable().addSelectionListener(new TableSelectionListener()); | |
197 | addPackListener(); | |
101bcc65 MK |
198 | table.setRedraw(true); |
199 | } | |
200 | ||
b9fff7ad BH |
201 | /** |
202 | * Create default columns for start time, end time and duration | |
203 | */ | |
204 | private void createColumns() { | |
205 | createColumn(Messages.SegmentStoreTableViewer_startTime, new SegmentStoreTableColumnLabelProvider() { | |
206 | @Override | |
207 | public String getTextForSegment(ISegment input) { | |
208 | return NonNullUtils.nullToEmptyString(TmfTimestampFormat.getDefaulTimeFormat().format(input.getStart())); | |
209 | } | |
210 | }, SegmentComparators.INTERVAL_START_COMPARATOR); | |
211 | ||
212 | createColumn(Messages.SegmentStoreTableViewer_endTime, new SegmentStoreTableColumnLabelProvider() { | |
213 | @Override | |
214 | public String getTextForSegment(ISegment input) { | |
215 | return NonNullUtils.nullToEmptyString(TmfTimestampFormat.getDefaulTimeFormat().format(input.getEnd())); | |
216 | } | |
217 | }, SegmentComparators.INTERVAL_END_COMPARATOR); | |
218 | ||
219 | createColumn(Messages.SegmentStoreTableViewer_duration, new SegmentStoreTableColumnLabelProvider() { | |
220 | @Override | |
221 | public String getTextForSegment(ISegment input) { | |
ef47abba | 222 | return NonNullUtils.nullToEmptyString(FORMATTER.format(input.getLength())); |
b9fff7ad BH |
223 | } |
224 | }, SegmentComparators.INTERVAL_LENGTH_COMPARATOR); | |
225 | } | |
226 | ||
227 | /** | |
0f769d2b | 228 | * Create columns specific to the provider |
b9fff7ad | 229 | */ |
0f769d2b | 230 | protected void createProviderColumns() { |
b9fff7ad | 231 | if (!fColumnsCreated) { |
0f769d2b MK |
232 | ISegmentStoreProvider provider = getSegmentProvider(); |
233 | if (provider != null) { | |
234 | for (final ISegmentAspect aspect : provider.getSegmentAspects()) { | |
b9fff7ad BH |
235 | createColumn(aspect.getName(), new SegmentStoreTableColumnLabelProvider() { |
236 | @Override | |
237 | public String getTextForSegment(ISegment input) { | |
238 | return NonNullUtils.nullToEmptyString(aspect.resolve(input)); | |
239 | } | |
240 | }, | |
0f769d2b | 241 | aspect.getComparator()); |
b9fff7ad BH |
242 | } |
243 | } | |
244 | fColumnsCreated = true; | |
245 | } | |
246 | } | |
247 | ||
248 | /** | |
249 | * Update the data in the table viewer | |
250 | * | |
251 | * @param dataInput | |
252 | * New data input | |
253 | */ | |
bd53eb28 | 254 | public void updateModel(final @Nullable Object dataInput) { |
b9fff7ad BH |
255 | final TableViewer tableViewer = getTableViewer(); |
256 | Display.getDefault().asyncExec(new Runnable() { | |
257 | @Override | |
258 | public void run() { | |
259 | if (!tableViewer.getTable().isDisposed()) { | |
260 | // Go to the top of the table | |
261 | tableViewer.getTable().setTopIndex(0); | |
262 | // Reset selected row | |
263 | tableViewer.setSelection(StructuredSelection.EMPTY); | |
264 | if (dataInput == null) { | |
265 | tableViewer.setInput(null); | |
266 | tableViewer.setItemCount(0); | |
267 | return; | |
268 | } | |
fce7c48b | 269 | addPackListener(); |
b9fff7ad BH |
270 | tableViewer.setInput(dataInput); |
271 | SegmentStoreContentProvider contentProvider = (SegmentStoreContentProvider) getTableViewer().getContentProvider(); | |
fc409c43 | 272 | tableViewer.setItemCount((int) Math.min(Integer.MAX_VALUE, contentProvider.getSegmentCount())); |
b9fff7ad BH |
273 | } |
274 | } | |
275 | }); | |
276 | } | |
277 | ||
278 | /** | |
0f769d2b MK |
279 | * Set the data into the viewer. It will update the model. If the provider |
280 | * is an analysis, the analysis will be scheduled. | |
b9fff7ad | 281 | * |
0f769d2b MK |
282 | * @param provider |
283 | * segment store provider | |
b9fff7ad | 284 | */ |
0f769d2b MK |
285 | public void setData(@Nullable ISegmentStoreProvider provider) { |
286 | // Set the current segment store provider | |
287 | fSegmentProvider = provider; | |
288 | if (provider == null) { | |
b9fff7ad BH |
289 | updateModel(null); |
290 | return; | |
291 | } | |
292 | ||
0f769d2b | 293 | createProviderColumns(); |
b9fff7ad | 294 | |
0f769d2b MK |
295 | ISegmentStore<ISegment> segStore = provider.getSegmentStore(); |
296 | // If results are not null, then the segment of the provider is ready | |
297 | // and model can be updated | |
73c74de7 JCK |
298 | if (segStore != null) { |
299 | updateModel(segStore); | |
b9fff7ad BH |
300 | return; |
301 | } | |
0f769d2b MK |
302 | // If results are null, then add completion listener and if the provider |
303 | // is an analysis, run the analysis | |
b9fff7ad | 304 | updateModel(null); |
f0b123a9 GB |
305 | SegmentStoreProviderProgressListener listener = fListener; |
306 | if (listener != null) { | |
307 | provider.addListener(listener); | |
308 | } | |
0f769d2b MK |
309 | if (provider instanceof IAnalysisModule) { |
310 | ((IAnalysisModule) provider).schedule(); | |
311 | } | |
b9fff7ad BH |
312 | } |
313 | ||
314 | /** | |
0f769d2b MK |
315 | * Returns the segment store provider |
316 | * | |
b9fff7ad BH |
317 | * @param trace |
318 | * The trace to consider | |
0f769d2b | 319 | * @return the segment store provider |
b9fff7ad | 320 | */ |
0f769d2b | 321 | protected @Nullable abstract ISegmentStoreProvider getSegmentStoreProvider(ITmfTrace trace); |
b9fff7ad BH |
322 | |
323 | @Override | |
324 | protected void appendToTablePopupMenu(IMenuManager manager, IStructuredSelection sel) { | |
325 | final ISegment segment = (ISegment) sel.getFirstElement(); | |
8ef16fe6 BH |
326 | if (segment != null) { |
327 | IAction gotoStartTime = new Action(Messages.SegmentStoreTableViewer_goToStartEvent) { | |
328 | @Override | |
329 | public void run() { | |
b2c971ec | 330 | broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, TmfTimestamp.fromNanos(segment.getStart()))); |
8ef16fe6 BH |
331 | } |
332 | }; | |
b9fff7ad | 333 | |
8ef16fe6 BH |
334 | IAction gotoEndTime = new Action(Messages.SegmentStoreTableViewer_goToEndEvent) { |
335 | @Override | |
336 | public void run() { | |
b2c971ec | 337 | broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer.this, TmfTimestamp.fromNanos(segment.getEnd()))); |
8ef16fe6 BH |
338 | } |
339 | }; | |
b9fff7ad | 340 | |
8ef16fe6 BH |
341 | manager.add(gotoStartTime); |
342 | manager.add(gotoEndTime); | |
343 | } | |
b9fff7ad BH |
344 | } |
345 | ||
346 | // ------------------------------------------------------------------------ | |
347 | // Getters | |
348 | // ------------------------------------------------------------------------ | |
349 | ||
350 | /** | |
0f769d2b | 351 | * Get current segment store provider |
b9fff7ad | 352 | * |
0f769d2b | 353 | * @return current segment store provider |
b9fff7ad | 354 | */ |
0f769d2b MK |
355 | public @Nullable ISegmentStoreProvider getSegmentProvider() { |
356 | return fSegmentProvider; | |
b9fff7ad BH |
357 | } |
358 | ||
359 | // ------------------------------------------------------------------------ | |
360 | // Signal handlers | |
361 | // ------------------------------------------------------------------------ | |
362 | ||
363 | /** | |
364 | * Trace selected handler | |
365 | * | |
366 | * @param signal | |
367 | * Different opened trace (on which segment store analysis as | |
368 | * already been performed) has been selected | |
369 | */ | |
370 | @TmfSignalHandler | |
371 | public void traceSelected(TmfTraceSelectedSignal signal) { | |
372 | ITmfTrace trace = signal.getTrace(); | |
373 | if (trace != null) { | |
0f769d2b | 374 | setData(getSegmentStoreProvider(trace)); |
b9fff7ad BH |
375 | } |
376 | } | |
377 | ||
378 | /** | |
379 | * Trace opened handler | |
380 | * | |
381 | * @param signal | |
382 | * New trace (on which segment store analysis has not been | |
383 | * performed) is opened | |
384 | */ | |
385 | @TmfSignalHandler | |
386 | public void traceOpened(TmfTraceOpenedSignal signal) { | |
387 | ITmfTrace trace = signal.getTrace(); | |
388 | if (trace != null) { | |
0f769d2b | 389 | setData(getSegmentStoreProvider(trace)); |
b9fff7ad BH |
390 | } |
391 | } | |
392 | ||
393 | /** | |
394 | * Trace closed handler | |
395 | * | |
396 | * @param signal | |
397 | * Last opened trace was closed | |
398 | */ | |
399 | @TmfSignalHandler | |
400 | public void traceClosed(TmfTraceClosedSignal signal) { | |
401 | // Check if there is no more opened trace | |
402 | if (TmfTraceManager.getInstance().getActiveTrace() == null) { | |
403 | if (!getTableViewer().getTable().isDisposed()) { | |
404 | getTableViewer().setInput(null); | |
0945d5a8 | 405 | getTableViewer().setItemCount(0); |
b9fff7ad BH |
406 | refresh(); |
407 | } | |
76be6c00 | 408 | |
0f769d2b MK |
409 | ISegmentStoreProvider provider = getSegmentProvider(); |
410 | if ((provider != null)) { | |
f0b123a9 GB |
411 | SegmentStoreProviderProgressListener listener = fListener; |
412 | if (listener != null) { | |
413 | provider.removeListener(listener); | |
414 | } | |
76be6c00 | 415 | } |
b9fff7ad BH |
416 | } |
417 | } | |
fce7c48b BH |
418 | |
419 | // ------------------------------------------------------------------------ | |
420 | // Helper methods | |
421 | // ------------------------------------------------------------------------ | |
b2f6d73d | 422 | |
fce7c48b BH |
423 | /* |
424 | * Add the listener for SetData on the table | |
425 | */ | |
426 | private void addPackListener() { | |
427 | getControl().addListener(SWT.SetData, new Listener() { | |
428 | @Override | |
429 | public void handleEvent(@Nullable Event event) { | |
b2f6d73d PT |
430 | // Remove the listener before the pack |
431 | getControl().removeListener(SWT.SetData, this); | |
432 | ||
fce7c48b BH |
433 | // Pack the column the first time data is set |
434 | TableViewer tableViewer = getTableViewer(); | |
435 | if (tableViewer != null) { | |
436 | for (TableColumn col : tableViewer.getTable().getColumns()) { | |
437 | col.pack(); | |
438 | } | |
fce7c48b BH |
439 | } |
440 | } | |
441 | }); | |
442 | } | |
b9fff7ad | 443 | } |