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