/*******************************************************************************
* Copyright (c) 2015, 2016 Ericsson, EfficiOS Inc. and others
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.viewers;
import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiTableEntry;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.signals.LamiSelectionUpdateSignal;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportView;
import org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views.LamiReportViewTabPage;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.ui.viewers.table.TmfSimpleTableViewer;
/**
* Table viewer to use in {@link LamiReportView}s.
*
* @author Alexandre Montplaisir
*/
public final class LamiTableViewer extends TmfSimpleTableViewer implements ILamiViewer {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
private final LamiReportViewTabPage fPage;
private Set<Integer> fSelections;
// ------------------------------------------------------------------------
// Inner class definitions
// ------------------------------------------------------------------------
/**
* Abstract class for the column label provider for the latency analysis
* table viewer
*/
private static class LamiTableColumnLabelProvider extends ColumnLabelProvider {
private final LamiTableEntryAspect fColumnAspect;
public LamiTableColumnLabelProvider(LamiTableEntryAspect aspect) {
fColumnAspect = aspect;
}
@Override
public String getText(@Nullable Object input) {
if (!(input instanceof LamiTableEntry)) {
/* Doubles as a null check */
return ""; //$NON-NLS-1$
}
LamiTableEntry entry = (LamiTableEntry) input;
return nullToEmptyString(fColumnAspect.resolveString(entry));
}
}
/**
* Listener to update in other viewers when a cell of the latency
* table view is selected
*/
private class LamiTableSelectionListener extends SelectionAdapter {
@Override
public void widgetSelected(@Nullable SelectionEvent e) {
IStructuredSelection selections = getTableViewer().getStructuredSelection();
Set<Integer> selectionIndexes = new HashSet<>();
for (Object selectedEntry : selections.toArray() ) {
selectionIndexes.add(fPage.getResultTable().getEntries().indexOf(selectedEntry));
}
fSelections = selectionIndexes;
/* Signal all Lami viewers & views of the selection */
LamiSelectionUpdateSignal signal = new LamiSelectionUpdateSignal(LamiTableViewer.this, selectionIndexes, fPage);
TmfSignalManager.dispatchSignal(signal);
}
}
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Constructor
*
* @param tableViewer
* Table viewer of the view
* @param page
* The {@link LamiReportViewTabPage} parent page
*/
public LamiTableViewer(TableViewer tableViewer, LamiReportViewTabPage page) {
super(tableViewer);
/*
* The table viewer should always be the first element in the control.
*/
tableViewer.getTable().moveAbove(null);
fPage = page;
fSelections = new HashSet<>();
/* Default sort order of the content provider is by its first column */
getTableViewer().setContentProvider(new LamiTableContentProvider());
getTableViewer().getTable().addSelectionListener(new LamiTableSelectionListener());
createColumns();
fillData();
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
private void createColumns() {
final List<LamiTableEntryAspect> aspects = fPage.getResultTable().getTableClass().getAspects();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
for (LamiTableEntryAspect aspect : aspects) {
createColumn(aspect.getLabel(), new LamiTableColumnLabelProvider(aspect), aspect.getComparator());
}
}
});
}
/**
* Update the data in the table viewer
*
* @param dataInput
* New data input
*/
private void fillData() {
final TableViewer tableViewer = getTableViewer();
Display.getDefault().asyncExec(() -> {
if (tableViewer.getTable().isDisposed()) {
return;
}
// Go to the top of the table
tableViewer.getTable().setTopIndex(0);
// Reset selected row
tableViewer.setSelection(StructuredSelection.EMPTY);
/* Fill the table data */
tableViewer.setInput(fPage.getResultTable().getEntries());
LamiTableContentProvider latencyContentProvider = (LamiTableContentProvider) getTableViewer().getContentProvider();
tableViewer.setItemCount(latencyContentProvider.getNbEntries());
/* Set the column's alignment and pack them */
TableColumn[] cols = tableViewer.getTable().getColumns();
for (int i = 0; i < cols.length; i++) {
LamiTableEntryAspect colAspect = fPage.getResultTable().getTableClass().getAspects().get(i);
int alignment = (colAspect.isContinuous() ? SWT.RIGHT : SWT.LEFT);
cols[i].setAlignment(alignment);
}
/*
* On creation check if there is selections if so update the table
* selections here. Selections needs the ContentProvider for valid
* index lookup and since the content provider is set in an
* asynchronous task we cannot use the normal signal handler since
* we have no guarantee of time of execution of the fill data.
*/
if (!fSelections.isEmpty()) {
int[] selectionsIndexes = fSelections.stream()
.map(index -> fPage.getResultTable().getEntries().get(index))
.mapToInt(entry -> ((LamiTableContentProvider) getTableViewer().getContentProvider()).getIndexOf(entry))
.toArray();
Display.getDefault().asyncExec(() -> {
getTableViewer().getTable().setSelection(selectionsIndexes);
getTableViewer().getTable().redraw();
});
}
});
Display.getDefault().asyncExec(() -> {
if (tableViewer.getTable().isDisposed()) {
return;
}
TableColumn[] cols = tableViewer.getTable().getColumns();
for (int i = 0; i < cols.length; i++) {
cols[i].pack();
}
});
}
/**
* The signal handler for selection update.
*
* @param signal
* The selection update signal
*/
@TmfSignalHandler
public void updateSelection(LamiSelectionUpdateSignal signal) {
if (fPage != signal.getSignalKey() || equals(signal.getSource())) {
/* The signal is not for us */
return;
}
/* Fetch the position of the selected entry in the actual table since it could be sorted by another column */
LamiTableContentProvider latencyContentProvider = (LamiTableContentProvider) getTableViewer().getContentProvider();
Set<Integer> selections = signal.getEntryIndex();
int[] selectionsIndexes = selections.stream()
.map(index -> fPage.getResultTable().getEntries().get(index))
.mapToInt(entry -> latencyContentProvider.getIndexOf(entry))
.toArray();
fSelections = new HashSet<>(selections);
Display.getDefault().asyncExec(() -> {
getTableViewer().getTable().setSelection(selectionsIndexes);
getTableViewer().getTable().redraw();
});
}
}