/*
* Copyright 2006-2016 The MZmine 3 Development Team
*
* This file is part of MZmine 3.
*
* MZmine 3 is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* MZmine 3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with MZmine 3; if not,
* write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package io.github.mzmine.modules.featuretable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.msdk.datamodel.featuretables.ColumnName;
import io.github.msdk.datamodel.featuretables.FeatureTable;
import io.github.msdk.datamodel.featuretables.FeatureTableColumn;
import io.github.msdk.datamodel.featuretables.FeatureTableRow;
import io.github.msdk.datamodel.featuretables.Sample;
import io.github.mzmine.gui.MZmineGUI;
import io.github.mzmine.modules.MZmineRunnableModule;
import io.github.mzmine.parameters.ParameterSet;
import io.github.mzmine.project.MZmineProject;
import io.github.mzmine.util.TableUtils;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.control.TreeTablePosition;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
public class FeatureTableModule implements MZmineRunnableModule {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Nonnull
private static final String MODULE_NAME = "Feature Table";
@Nonnull
private static final String MODULE_DESCRIPTION =
"This module creates a TableView of a feature table.";
private static Map<Integer, TreeTableColumn<FeatureTableRow, Object>> columnMap;
private static Map<Integer, TreeItem<FeatureTableRow>> rowMap;
@Override
public @Nonnull String getName() {
return MODULE_NAME;
}
@Override
public @Nonnull String getDescription() {
return MODULE_DESCRIPTION;
}
@Override
public void runModule(@Nonnull MZmineProject project, @Nonnull ParameterSet parameters,
@Nonnull Collection<Task<?>> tasks) {
// FeatureTable featureTable
final List<FeatureTable> featureTables =
parameters.getParameter(FeatureTableModuleParameters.featureTables).getValue()
.getMatchingFeatureTables();
if (featureTables.isEmpty()) {
logger.warn("No feature table selected, cannot proceed");
return;
}
FeatureTable featureTable = featureTables.get(0);
// Variables
final List<FeatureTableColumn<?>> columns = featureTable.getColumns();
final List<FeatureTableRow> rows = featureTable.getRows();
columnMap = new HashMap<Integer, TreeTableColumn<FeatureTableRow, Object>>();
rowMap = new HashMap<Integer, TreeItem<FeatureTableRow>>();
TreeTableColumn<FeatureTableRow, Object> tableColumn = null;
TreeTableColumn<FeatureTableRow, Object> sampleColumn = null;
Sample prevSample = null, currentSample = null;
int totalColumns = 0;
// Table tree root
final TreeItem<FeatureTableRow> root = new TreeItem<>();
root.setExpanded(true);
// Table tree items
TreeItem<FeatureTableRow> treeItem;
// Group rows
FeatureTableColumn<Integer> groupColoumn = featureTable.getColumn(ColumnName.GROUPID, null);
FeatureTableColumn<Integer> idColoumn = featureTable.getColumn(ColumnName.ID, null);
for (FeatureTableRow row : rows) {
// No group column
if (groupColoumn == null) {
treeItem = new TreeItem<>(row);
root.getChildren().add(treeItem);
}
// No group id
else if (row.getData(groupColoumn) == null) {
treeItem = new TreeItem<>(row);
treeItem.setExpanded(true);
root.getChildren().add(treeItem);
rowMap.put(row.getData(idColoumn), treeItem);
}
// The row has a group id
else {
Integer groupID = row.getData(groupColoumn);
TreeItem<FeatureTableRow> parentTreeItem = rowMap.get(groupID);
if (parentTreeItem == null) {
parentTreeItem = root;
}
treeItem = new TreeItem<>(row);
parentTreeItem.getChildren().add(treeItem);
rowMap.put(row.getData(idColoumn), treeItem);
}
}
// New tree table
TreeTableView<FeatureTableRow> treeTable = new TreeTableView<>(root);
// Common columns
for (FeatureTableColumn<?> column : columns) {
// Don't show Group ID column
if (column.getName() == ColumnName.GROUPID.getName())
continue;
currentSample = column.getSample();
if (currentSample == null) {
tableColumn = new TreeTableColumn<FeatureTableRow, Object>(column.getName());
tableColumn.setCellValueFactory(
new Callback<CellDataFeatures<FeatureTableRow, Object>, ObservableValue<Object>>() {
public ObservableValue<Object> call(CellDataFeatures<FeatureTableRow, Object> p) {
if (p.getValue() != null) {
if (p.getValue().getValue() != null) {
FeatureTableRow featureTableRow = (FeatureTableRow) p.getValue().getValue();
if (featureTableRow.getData(column) != null) {
return new SimpleObjectProperty<>(featureTableRow.getData(column));
}
}
}
return null;
}
});
// Set column renderer
Class<?> renderClass = ColumnRenderers.getRenderClass(column.getName());
Callback<TreeTableColumn<FeatureTableRow, Object>, TreeTableCell<FeatureTableRow, Object>> rendeder =
null;
try {
rendeder =
(Callback<TreeTableColumn<FeatureTableRow, Object>, TreeTableCell<FeatureTableRow, Object>>) renderClass
.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
tableColumn.setCellFactory(rendeder);
treeTable.getColumns().add(tableColumn);
columnMap.put(totalColumns, tableColumn);
totalColumns++;
}
}
// Sample columns
for (FeatureTableColumn<?> column : columns) {
currentSample = column.getSample();
if (currentSample != null) {
// Create sample header
if (prevSample == null || !prevSample.equals(currentSample)) {
sampleColumn = new TreeTableColumn(currentSample.getName());
treeTable.getColumns().add(sampleColumn);
prevSample = currentSample;
}
// Creates sample columns
tableColumn = new TreeTableColumn<>(column.getName());
tableColumn.setCellValueFactory(
new Callback<CellDataFeatures<FeatureTableRow, Object>, ObservableValue<Object>>() {
public ObservableValue<Object> call(CellDataFeatures<FeatureTableRow, Object> p) {
if (p.getValue() != null) {
if (p.getValue().getValue() != null) {
FeatureTableRow featureTableRow = (FeatureTableRow) p.getValue().getValue();
if (featureTableRow.getData(column) != null) {
return new SimpleObjectProperty<>(featureTableRow.getData(column));
}
}
}
return null;
}
});
// Set column renderer
Class<?> renderClass = ColumnRenderers.getRenderClass(column.getName());
Callback<TreeTableColumn<FeatureTableRow, Object>, TreeTableCell<FeatureTableRow, Object>> rendeder =
null;
try {
rendeder =
(Callback<TreeTableColumn<FeatureTableRow, Object>, TreeTableCell<FeatureTableRow, Object>>) renderClass
.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
tableColumn.setCellFactory(rendeder);
tableColumn.setStyle("-fx-alignment: CENTER;");
sampleColumn.getColumns().add(tableColumn);
columnMap.put(totalColumns, tableColumn);
totalColumns++;
}
}
// Add right padding on last column to fix issue with scroll bar
if (tableColumn != null)
tableColumn.setStyle("-fx-padding: 0 20 0 0;");
// Table preferences
treeTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
treeTable.getSelectionModel().setCellSelectionEnabled(true);
treeTable.setShowRoot(false);
// Add right click menu
FeatureTablePopupMenu popupMenu = new FeatureTablePopupMenu(featureTable, treeTable);
treeTable.setContextMenu(popupMenu);
// Add double click to open XIC chromatogram
treeTable.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown() && event.getClickCount() == 2) {
// Find feature table row
Node node = ((Node) event.getTarget()).getParent();
TreeTableRow<?> row;
while (!(node instanceof TreeTableRow)) {
node = node.getParent();
}
row = (TreeTableRow<?>) node;
FeatureTableRow featureTableRow = (FeatureTableRow) row.getItem();
// Find sample
Sample sample = null;
ObservableList<TreeTablePosition<FeatureTableRow, ?>> cells =
treeTable.getSelectionModel().getSelectedCells();
for (TreeTablePosition<FeatureTableRow, ?> cell : cells) {
if (cell.getTableColumn().getParentColumn() != null) {
TreeTableColumn<?, ?> parentColumn =
(TreeTableColumn) cell.getTableColumn().getParentColumn();
List<Sample> samples = featureTable.getSamples();
for (Sample s : samples) {
if (s.getName().equals(parentColumn.getText())) {
sample = s;
break;
}
}
}
}
/*
* TODO: Show XIC chromatogram
*/
System.out.println("Show XIC chromatogram for row " + featureTableRow);
System.out.println("Sample: " + sample);
}
}
});
// Add column selection button
treeTable.setTableMenuButtonVisible(true);
// Enable copy to clipboard
TableUtils.addCopyHandler(treeTable, columnMap);
// Add new window with table
MZmineGUI.addWindow(treeTable, featureTable.getName());
// Add custom table menu
FeatureTableMenu.addCustomTableMenu(treeTable);
}
public Map<Integer, TreeTableColumn<FeatureTableRow, Object>> getColumnMap() {
return columnMap;
}
@Override
@Nonnull
public Class<? extends ParameterSet> getParameterSetClass() {
return FeatureTableModuleParameters.class;
}
}