/* * #%L * org.gitools.ui.app * %% * Copyright (C) 2013 - 2014 Universitat Pompeu Fabra - Biomedical Genomics group * %% * This program 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 3 of the * License, or (at your option) any later version. * * This program 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 this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ package org.gitools.ui.app.analysis.groupcomparison.wizard; import org.apache.commons.lang.ArrayUtils; import org.gitools.analysis.clustering.ClusteringData; import org.gitools.analysis.clustering.annotations.AnnPatClusteringData; import org.gitools.analysis.clustering.annotations.AnnPatClusteringMethod; import org.gitools.analysis.groupcomparison.dimensiongroups.*; import org.gitools.analysis.groupcomparison.filters.GroupByLabelPredicate; import org.gitools.analysis.groupcomparison.filters.GroupByValuePredicate; import org.gitools.api.analysis.Clusters; import org.gitools.api.matrix.IMatrixLayer; import org.gitools.api.matrix.IMatrixLayers; import org.gitools.api.matrix.IMatrixPredicate; import org.gitools.heatmap.Heatmap; import org.gitools.heatmap.HeatmapDimension; import org.gitools.matrix.filter.DataIntegrationCriteria; import org.gitools.matrix.filter.MatrixPredicates; import org.gitools.matrix.filter.PatternFunction; import org.gitools.ui.app.wizard.add.data.DataIntegrationCriteriaDialog; import org.gitools.ui.core.Application; import org.gitools.ui.core.pages.common.PatternSourcePage; import org.gitools.ui.platform.IconUtils; import org.gitools.ui.platform.dialog.MessageStatus; import org.gitools.ui.platform.icons.IconNames; import org.gitools.ui.platform.wizard.AbstractWizardPage; import org.gitools.ui.platform.wizard.PageDialog; import org.gitools.utils.cutoffcmp.CutoffCmp; import org.gitools.utils.operators.Operator; import org.gitools.utils.progressmonitor.DefaultProgressMonitor; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.TableColumnModel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; public class GroupComparisonGroupingPage extends AbstractWizardPage { private JPanel panel1; private JComboBox layerCb; private JComboBox dimensionCb; private JButton addButton; private JButton removeButton; private JTable groupsTable; private JLabel dataLabel; private JButton mergeButton; private JButton splitButton; private JRadioButton annotationRadioButton; private JRadioButton valueRadioButton; private JRadioButton noConstraintRadioButton; private JRadioButton nullConversionRadioButton; private JRadioButton nullDiscardRadioButton; private JTextField nullConversionTextArea; private JCheckBox copyHeadersCheckBox; private DimensionGroupTableModel tableModel = new DimensionGroupTableModel(); private List<DimensionGroup> removedItems = new ArrayList<DimensionGroup>(); private Heatmap heatmap; private DimensionGroupEnum groupingType; private String groupingPattern; private static int MIN_GROUP_SIZE = 3; public GroupComparisonGroupingPage(Heatmap heatmap, DimensionGroupEnum groupingType) { super(); this.heatmap = heatmap; this.groupingType = groupingType; setLogo(IconUtils.getImageIconResourceScaledByHeight(IconNames.LOGO_METHOD, 96)); layerCb.setModel(new DefaultComboBoxModel(heatmap.getLayers().getIds())); layerCb.setSelectedItem(heatmap.getLayers().getTopLayer().getId()); groupsTable.setModel(tableModel); setTitle("Group selection"); TableColumnModel columnModel = groupsTable.getColumnModel(); columnModel.getColumn(2).setPreferredWidth(50); columnModel.getColumn(2).setCellEditor(new SpinnerCellEditor(new SpinnerNumberModel())); columnModel.getColumn(2).getCellEditor().addCellEditorListener(new CellEditorListener() { @Override public void editingStopped(ChangeEvent e) { tableModel.fireTableDataChanged(); } @Override public void editingCanceled(ChangeEvent e) { tableModel.fireTableDataChanged(); } }); groupsTable.setRowHeight(25); groupsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { updateControls(); } }); removeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeSelected(); } }); addButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (getSelectedGroupingType().equals(DimensionGroupEnum.Annotation)) { //TODO: create Dialog with removedItems } else if (getSelectedGroupingType().equals(DimensionGroupEnum.Free)) { createFreeGroup(); } else if (getSelectedGroupingType().equals(DimensionGroupEnum.Value)) { createValueGroup(); } updateControls(); } }); dimensionCb.setModel(new DefaultComboBoxModel(new String[]{"Columns", "Rows"})); mergeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { performMerge(); } }); splitButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { performSplit(); } }); dimensionCb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { initGroups(); } }); ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { initGroups(); } }; annotationRadioButton.addActionListener(listener); valueRadioButton.addActionListener(listener); noConstraintRadioButton.addActionListener(listener); ActionListener nullConversionListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateControls(); } }; nullDiscardRadioButton.addActionListener(nullConversionListener); nullConversionRadioButton.addActionListener(nullConversionListener); nullConversionTextArea.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { updateControls(); } @Override public void removeUpdate(DocumentEvent e) { updateControls(); } @Override public void changedUpdate(DocumentEvent e) { updateControls(); } }); updateControls(); layerCb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateControls(); } }); } private void updateNullConversion() { nullConversionTextArea.setEnabled(nullConversionRadioButton.isSelected()); } private void updateButtons() { if (getSelectedGroupingType().equals(DimensionGroupEnum.Annotation)) { mergeButton.setVisible(true); if (groupsTable.getSelectedRowCount() > 0) { //SPLIT button int groupNumber = -1; boolean enableSplit = true; // all selected rows are same group? for (int i : groupsTable.getSelectedRows()) { if (groupNumber < 0) { groupNumber = (int) tableModel.getValueAt(i, 2); } else if (groupNumber != (int) tableModel.getValueAt(i, 2)) { enableSplit = false; break; } } // unselected of same group? if (groupsTable.getSelectedRowCount() == 1) { enableSplit = false; for (int i = 0; i < groupsTable.getRowCount(); i++) { if (groupsTable.isRowSelected(i)) { continue; } if (groupNumber == (int) tableModel.getValueAt(i, 2)) { enableSplit = true; break; } } } //MERGE button mergeButton.setEnabled(!enableSplit && groupsTable.getSelectedRowCount() > 1); } } else { mergeButton.setVisible(false); } splitButton.setEnabled(false); splitButton.setVisible(false); //ADD button boolean enableAdd = true; if (getSelectedGroupingType().equals(DimensionGroupEnum.Annotation)) { enableAdd = !removedItems.isEmpty(); } addButton.setEnabled(enableAdd); //Remove button removeButton.setEnabled(groupsTable.getSelectedRowCount() > 0); } @Override public void updateControls() { boolean isComplete = false; updateNullConversion(); updateButtons(); updateGroupingType(); // Are there at least two groups? isComplete = tableModel.getRowCount() > 1 & layerCb.getSelectedItem() != null; if (!isComplete) { setMessage(MessageStatus.INFO, "Create at least 2 groups to compare and select data layer for group comparison"); setComplete(false); return; } else { setMessage(MessageStatus.INFO, "Click next to proceed."); } // Do all groups have at least 3 columns (if applicable) if (annotationRadioButton.isSelected() | noConstraintRadioButton.isSelected()) { for (DimensionGroup g : tableModel.getGroupList()) { if (g.getGroupSize() < MIN_GROUP_SIZE) { setMessage(MessageStatus.ERROR, "Groups must contain 3 or more items. Remove or merge groups" + "that are too small."); setComplete(false); return; } } } // Valid Null Conversion? try { Double.valueOf(nullConversionTextArea.getText()); } catch (Exception e) { setMessage(MessageStatus.ERROR, "\" " + nullConversionTextArea.getText() + "\" can is not a numeric value"); setComplete(false); return; } setComplete(isComplete); } private void performSplit() { int currentGroupNumber = (int) tableModel.getValueAt(groupsTable.getSelectedRows()[0], 2); int occurrences = 0; for (int i = 0; i < groupsTable.getRowCount(); i++) { if (currentGroupNumber == (int) tableModel.getValueAt(i, 2)) { occurrences++; } } boolean totalSplit = occurrences == groupsTable.getSelectedRowCount(); int newGroups = 1; for (int i = 0; i < groupsTable.getRowCount(); i++) { int g = (int) tableModel.getValueAt(i, 2); if (g > currentGroupNumber | groupsTable.isRowSelected(i)) { groupsTable.setValueAt(g + newGroups, i, 2); newGroups = totalSplit && groupsTable.isRowSelected(i) ? ++newGroups : newGroups; } } tableModel.fireTableDataChanged(); } private void performMerge() { int[] selection = groupsTable.getSelectedRows(); List<IMatrixPredicate> predicateList = new ArrayList<>(); StringBuilder groupName = new StringBuilder(""); StringBuilder groupProperty = new StringBuilder(""); int size = 0; for (int i : selection) { DimensionGroup group = tableModel.getGroupAt(i); if (!groupName.toString().equals("")) { groupName.append(" + "); groupProperty.append(" + "); } groupName.append(group.getName()); groupProperty.append(group.getProperty()); predicateList.add(group.getPredicate()); size += group.getGroupSize(); } MatrixPredicates.OrPredicate newpredicate = new MatrixPredicates.OrPredicate(predicateList); tableModel.setGroup(new DimensionGroup(groupName.toString(), newpredicate, DimensionGroupEnum.Annotation, groupProperty.toString(), size), selection[0]); tableModel.removeGroups(ArrayUtils.removeElement(selection, selection[0])); tableModel.fireTableDataChanged(); updateControls(); } private void initGroups() { DimensionGroup[] newGroups = new DimensionGroup[0]; if (getSelectedGroupingType().equals(DimensionGroupEnum.Annotation)) { newGroups = initAnnotationGroups(); if (newGroups == null) { setSelectedGroupingType(groupingType); return; } } removedItems.clear(); tableModel.setGroups(newGroups); updateControls(); } public boolean isCopyHeatmapHeaders() { return copyHeadersCheckBox.isSelected(); } private DimensionGroup[] initAnnotationGroups() { DimensionGroup[] newGroups; HeatmapDimension hdim = dimensionCb.getSelectedItem().equals("Columns") ? heatmap.getColumns() : heatmap.getRows(); if (hdim.getAnnotations() == null) { setMessage(MessageStatus.WARN, "No annotations found"); return null; } PatternSourcePage page = new PatternSourcePage(hdim, true); PageDialog dlg = new PageDialog(Application.get(), page); dlg.open(); if (dlg.isCancelled()) { return null; } // get all clusters from $pattern ClusteringData data = new AnnPatClusteringData(hdim, page.getPattern()); Clusters results = new AnnPatClusteringMethod().cluster(data, new DefaultProgressMonitor()); List<DimensionGroup> annGroups = new ArrayList<>(); for (String groupAnnotationPattern : results.getClusters()) { DimensionGroupAnnotation g = new DimensionGroupAnnotation( groupAnnotationPattern, new GroupByLabelPredicate( hdim, groupAnnotationPattern, new PatternFunction(page.getPattern(), hdim.getAnnotations()))); if (groupAnnotationPattern.equals("")) { g.setName("Not annotated"); } annGroups.add(g); } newGroups = annGroups.toArray(new DimensionGroup[annGroups.size()]); this.groupingPattern = page.getPattern(); return newGroups; } private void createValueGroup() { String[] ops = new String[]{Operator.AND.getAbbreviation(), Operator.OR.getAbbreviation()}; DataIntegrationCriteriaDialog dlg = new DataIntegrationCriteriaDialog( Application.get(), heatmap.getLayers(), CutoffCmp.comparators, ops, null, "Group " + Integer.toString(tableModel.getRowCount() + 1)); dlg.setVisible(true); if (dlg.isCancelled()) { return; } List<DataIntegrationCriteria> criteria = dlg.getCriteriaList(); tableModel.addGroup( new DimensionGroupValue(dlg.getGroupName(), new GroupByValuePredicate(criteria)) ); } private void createFreeGroup() { HeatmapDimension hdim = dimensionCb.getSelectedItem().equals("Columns") ? heatmap.getColumns() : heatmap.getRows(); DimensionGroupSelectPage page = new DimensionGroupSelectPage(hdim, "Group " + String.valueOf(groupsTable.getRowCount() + 1)); PageDialog dlg = new PageDialog(Application.get(), page); dlg.open(); if (dlg.isCancelled()) { return; } tableModel.addGroup( new DimensionGroupFree( page.getGroupName(), new GroupByLabelPredicate( hdim, page.getGroup()) ) ); } private void removeSelected() { if (getSelectedGroupingType().equals(DimensionGroupEnum.Annotation)) { for (int cg : groupsTable.getSelectedRows()) { removedItems.add(tableModel.getGroupAt(cg)); } } tableModel.removeGroups(groupsTable.getSelectedRows()); } public void addGroups(DimensionGroupValue... groups) { tableModel.setGroups(groups); } public Double getNullConversion() { if (nullConversionRadioButton.isSelected()) { return Double.valueOf(nullConversionTextArea.getText()); } else { return null; } } public List<DimensionGroup> getGroups() { return (tableModel.getGroupList()); } public class AttrOption { private String name; private IMatrixLayer attr; /** * @noinspection UnusedDeclaration */ public AttrOption(String name) { this.name = name; } public AttrOption(IMatrixLayer attr) { this.attr = attr; } public IMatrixLayer getAttr() { return attr; } @Override public String toString() { return attr != null ? attr.getName() : name; } } public void setSelectedGroupingType(DimensionGroupEnum groupingType) { if (groupingType.equals(DimensionGroupEnum.Annotation)) { annotationRadioButton.setSelected(true); } else if (groupingType.equals(DimensionGroupEnum.Value)) { valueRadioButton.setSelected(true); } else if (groupingType.equals(DimensionGroupEnum.Free)) { noConstraintRadioButton.setSelected(true); } this.groupingType = groupingType; } public DimensionGroupEnum getSelectedGroupingType() { if (annotationRadioButton.isSelected()) { return DimensionGroupEnum.Annotation; } else if (valueRadioButton.isSelected()) { return DimensionGroupEnum.Value; } else if (noConstraintRadioButton.isSelected()) { return DimensionGroupEnum.Free; } return null; } private void updateGroupingType() { if (annotationRadioButton.isSelected()) { this.groupingType = DimensionGroupEnum.Annotation; } else if (valueRadioButton.isSelected()) { this.groupingType = DimensionGroupEnum.Value; } else if (noConstraintRadioButton.isSelected()) { this.groupingType = DimensionGroupEnum.Free; } } @Override public JComponent createControls() { return panel1; } public void setAttributes(IMatrixLayers attrs) { if (attrs != null) { AttrOption[] attrOptions = new AttrOption[attrs.size()]; for (int i = 0; i < attrs.size(); i++) attrOptions[i] = new AttrOption(attrs.get(i)); layerCb.setModel(new DefaultComboBoxModel(attrOptions)); layerCb.setSelectedIndex(0); layerCb.setEnabled(true); layerCb.setVisible(true); //attributeLabel.setVisible(true); } else { dissableAttrCb(); } } private void dissableAttrCb() { layerCb.setModel(new DefaultComboBoxModel()); layerCb.setEnabled(false); layerCb.setVisible(false); //attributeLabel.setVisible(false); } public String getLayer() { return layerCb.getSelectedItem().toString(); } public DimensionGroupEnum getGroupingType() { return groupingType; } public String getGroupingPattern() { return groupingPattern; } }