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