1 /*******************************************************************************
2 * Copyright (c) 2015, 2016 Ericsson
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
10 * France Lapointe Nguyen - Initial API and implementation
11 * Bernd Hufmann - Move abstract class to TMF
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.table
;
16 import java
.text
.DecimalFormat
;
17 import java
.text
.Format
;
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
;
58 import com
.google
.common
.annotations
.VisibleForTesting
;
61 * Displays the segment store provider data in a column table
63 * @author France Lapointe Nguyen
66 public abstract class AbstractSegmentStoreTableViewer
extends TmfSimpleTableViewer
{
68 private static final Format FORMATTER
= new DecimalFormat("###,###.##"); //$NON-NLS-1$
70 // ------------------------------------------------------------------------
72 // ------------------------------------------------------------------------
75 * Abstract class for the column label provider for the segment store
76 * provider table viewer
78 private abstract class SegmentStoreTableColumnLabelProvider
extends ColumnLabelProvider
{
81 public String
getText(@Nullable Object input
) {
82 if (!(input
instanceof ISegment
)) {
83 /* Doubles as a null check */
84 return ""; //$NON-NLS-1$
86 return getTextForSegment((ISegment
) input
);
89 public abstract String
getTextForSegment(ISegment input
);
93 * Listener to update the model with the segment store provider results once
94 * its store is fully completed
96 private final class SegmentStoreProviderProgressListener
implements IAnalysisProgressListener
{
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
)) {
108 * Listener to select a range in other viewers when a cell of the segment
109 * store table view is selected
111 private class TableSelectionListener
extends SelectionAdapter
{
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
));
122 * Current segment store provider
124 private @Nullable ISegmentStoreProvider fSegmentProvider
= null;
127 * provider progress listener
129 private final @Nullable SegmentStoreProviderProgressListener fListener
;
132 * Flag to create columns once
134 boolean fColumnsCreated
= false;
136 // ------------------------------------------------------------------------
138 // ------------------------------------------------------------------------
144 * Table viewer of the view
146 public AbstractSegmentStoreTableViewer(TableViewer tableViewer
) {
147 this(tableViewer
, true);
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
162 public AbstractSegmentStoreTableViewer(TableViewer tableViewer
, boolean withListener
) {
164 // Sort order of the content provider is by start time by default
165 getTableViewer().setContentProvider(new SegmentStoreContentProvider());
167 getTableViewer().getTable().addSelectionListener(new TableSelectionListener());
169 fListener
= withListener ?
new SegmentStoreProviderProgressListener() : null;
172 // ------------------------------------------------------------------------
174 // ------------------------------------------------------------------------
177 * Sets the segment provider, use only in test, only run in display thread
179 * @param segmentProvider
180 * the segment provider
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());
189 Table table
= getTableViewer().getTable();
190 table
.setRedraw(false);
191 while (table
.getColumnCount() > 0) {
192 table
.getColumn(0).dispose();
195 createProviderColumns();
196 getTableViewer().getTable().addSelectionListener(new TableSelectionListener());
198 table
.setRedraw(true);
202 * Create default columns for start time, end time and duration
204 private void createColumns() {
205 createColumn(Messages
.SegmentStoreTableViewer_startTime
, new SegmentStoreTableColumnLabelProvider() {
207 public String
getTextForSegment(ISegment input
) {
208 return NonNullUtils
.nullToEmptyString(TmfTimestampFormat
.getDefaulTimeFormat().format(input
.getStart()));
210 }, SegmentComparators
.INTERVAL_START_COMPARATOR
);
212 createColumn(Messages
.SegmentStoreTableViewer_endTime
, new SegmentStoreTableColumnLabelProvider() {
214 public String
getTextForSegment(ISegment input
) {
215 return NonNullUtils
.nullToEmptyString(TmfTimestampFormat
.getDefaulTimeFormat().format(input
.getEnd()));
217 }, SegmentComparators
.INTERVAL_END_COMPARATOR
);
219 createColumn(Messages
.SegmentStoreTableViewer_duration
, new SegmentStoreTableColumnLabelProvider() {
221 public String
getTextForSegment(ISegment input
) {
222 return NonNullUtils
.nullToEmptyString(FORMATTER
.format(input
.getLength()));
224 }, SegmentComparators
.INTERVAL_LENGTH_COMPARATOR
);
228 * Create columns specific to the provider
230 protected void createProviderColumns() {
231 if (!fColumnsCreated
) {
232 ISegmentStoreProvider provider
= getSegmentProvider();
233 if (provider
!= null) {
234 for (final ISegmentAspect aspect
: provider
.getSegmentAspects()) {
235 createColumn(aspect
.getName(), new SegmentStoreTableColumnLabelProvider() {
237 public String
getTextForSegment(ISegment input
) {
238 return NonNullUtils
.nullToEmptyString(aspect
.resolve(input
));
241 aspect
.getComparator());
244 fColumnsCreated
= true;
249 * Update the data in the table viewer
254 public void updateModel(final @Nullable Object dataInput
) {
255 final TableViewer tableViewer
= getTableViewer();
256 Display
.getDefault().asyncExec(new Runnable() {
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);
270 tableViewer
.setInput(dataInput
);
271 SegmentStoreContentProvider contentProvider
= (SegmentStoreContentProvider
) getTableViewer().getContentProvider();
272 tableViewer
.setItemCount((int) Math
.min(Integer
.MAX_VALUE
, contentProvider
.getSegmentCount()));
279 * Set the data into the viewer. It will update the model. If the provider
280 * is an analysis, the analysis will be scheduled.
283 * segment store provider
285 public void setData(@Nullable ISegmentStoreProvider provider
) {
286 // Set the current segment store provider
287 fSegmentProvider
= provider
;
288 if (provider
== null) {
293 createProviderColumns();
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
298 if (segStore
!= null) {
299 updateModel(segStore
);
302 // If results are null, then add completion listener and if the provider
303 // is an analysis, run the analysis
305 SegmentStoreProviderProgressListener listener
= fListener
;
306 if (listener
!= null) {
307 provider
.addListener(listener
);
309 if (provider
instanceof IAnalysisModule
) {
310 ((IAnalysisModule
) provider
).schedule();
315 * Returns the segment store provider
318 * The trace to consider
319 * @return the segment store provider
321 protected @Nullable abstract ISegmentStoreProvider
getSegmentStoreProvider(ITmfTrace trace
);
324 protected void appendToTablePopupMenu(IMenuManager manager
, IStructuredSelection sel
) {
325 final ISegment segment
= (ISegment
) sel
.getFirstElement();
326 if (segment
!= null) {
327 IAction gotoStartTime
= new Action(Messages
.SegmentStoreTableViewer_goToStartEvent
) {
330 broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer
.this, TmfTimestamp
.fromNanos(segment
.getStart())));
334 IAction gotoEndTime
= new Action(Messages
.SegmentStoreTableViewer_goToEndEvent
) {
337 broadcast(new TmfSelectionRangeUpdatedSignal(AbstractSegmentStoreTableViewer
.this, TmfTimestamp
.fromNanos(segment
.getEnd())));
341 manager
.add(gotoStartTime
);
342 manager
.add(gotoEndTime
);
346 // ------------------------------------------------------------------------
348 // ------------------------------------------------------------------------
351 * Get current segment store provider
353 * @return current segment store provider
355 public @Nullable ISegmentStoreProvider
getSegmentProvider() {
356 return fSegmentProvider
;
359 // ------------------------------------------------------------------------
361 // ------------------------------------------------------------------------
364 * Trace selected handler
367 * Different opened trace (on which segment store analysis as
368 * already been performed) has been selected
371 public void traceSelected(TmfTraceSelectedSignal signal
) {
372 ITmfTrace trace
= signal
.getTrace();
374 setData(getSegmentStoreProvider(trace
));
379 * Trace opened handler
382 * New trace (on which segment store analysis has not been
383 * performed) is opened
386 public void traceOpened(TmfTraceOpenedSignal signal
) {
387 ITmfTrace trace
= signal
.getTrace();
389 setData(getSegmentStoreProvider(trace
));
394 * Trace closed handler
397 * Last opened trace was closed
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);
405 getTableViewer().setItemCount(0);
409 ISegmentStoreProvider provider
= getSegmentProvider();
410 if ((provider
!= null)) {
411 SegmentStoreProviderProgressListener listener
= fListener
;
412 if (listener
!= null) {
413 provider
.removeListener(listener
);
419 // ------------------------------------------------------------------------
421 // ------------------------------------------------------------------------
424 * Add the listener for SetData on the table
426 private void addPackListener() {
427 getControl().addListener(SWT
.SetData
, new Listener() {
429 public void handleEvent(@Nullable Event event
) {
430 // Remove the listener before the pack
431 getControl().removeListener(SWT
.SetData
, this);
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()) {