/*******************************************************************************
* GenPlay, Einstein Genome Analyzer
* Copyright (C) 2009, 2014 Albert Einstein College of Medicine
*
* 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/>.
* Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu>
* Nicolas Fourel <nicolas.fourel@einstein.yu.edu>
* Eric Bouhassira <eric.bouhassira@einstein.yu.edu>
*
* Website: <http://genplay.einstein.yu.edu>
******************************************************************************/
package edu.yu.einstein.genplay.gui.dialog;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Comparator;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import edu.yu.einstein.genplay.core.manager.project.ProjectChromosomes;
import edu.yu.einstein.genplay.core.manager.project.ProjectManager;
import edu.yu.einstein.genplay.gui.track.layer.ColoredLayer;
import edu.yu.einstein.genplay.gui.track.layer.Layer;
import edu.yu.einstein.genplay.util.Images;
import edu.yu.einstein.genplay.util.NumberFormats;
import edu.yu.einstein.genplay.util.Utils;
import edu.yu.einstein.genplay.util.colors.Colors;
/**
* A dialog window showing the correlations for each chromosome between two layers
* @author Julien Lajugie
* @version 0.1
*/
public class CorrelationReportDialog extends JDialog {
/**
* An extension of the {@link JTable} class that sets the renderer and makes the cells not editable
* @author Julien Lajugie
* @version 0.1
*/
private class CorrelationJTable extends JTable {
private static final long serialVersionUID = 1023098920948600203L; // generated ID
/**
* Creates an instance of {@link CorrelationJTable}.
* Sets the renderer as a {@link CorrelationJTableRenderer}
* @param rowData
* @param columnNames
*/
public CorrelationJTable(Object[][] rowData, Object[] columnNames) {
super(rowData, columnNames);
CorrelationJTableRenderer renderer = new CorrelationJTableRenderer();
setDefaultRenderer(Object.class, renderer);
setRowSorter(new CorrelationJTableSorter<TableModel>(rowData, getModel()));
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
/**
* An extension of the {@link DefaultTableCellRenderer} class that changes the background color and the alignment of the cells
* @author Julien Lajugie
* @version 0.1
*/
private class CorrelationJTableRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = -7966573391861597792L; // generated ID
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if ((!isSelected) && ((row % 2) == 1)) {
component.setBackground(Colors.LIGHT_GREY);
} else {
component.setBackground(Colors.WHITE);
}
if (column == 0) {
((JLabel) component).setHorizontalAlignment(SwingConstants.LEFT);
} else {
((JLabel) component).setHorizontalAlignment(SwingConstants.RIGHT);
}
setMaxMinStyle(table, value, row, column, component);
setTotalStyle(table, value, row, column, component);
return component;
}
/**
* Sets the style of rows with the minimum and the maximum correlation
* @param table the JTable
* @param value the value to assign to the cell at [row, column]
* @param row the row of the cell to render
* @param column the column of the cell to render
* @param component the component whose style needs to be changed
*/
private void setMaxMinStyle(JTable table, Object value, int row, int column, Component component) {
boolean greatest = true;
boolean smallest = true;
double valueAsDouble = 0;
// try to retrieve the value of the current row
try {
if (column == 0) {
valueAsDouble = Double.parseDouble((String) table.getValueAt(row, 1));
} else {
valueAsDouble = Double.parseDouble((String) value);
}
} catch (NumberFormatException e) {
// if we can't parse the value it might be because it's a line having "-" as a value
component.setForeground(Color.BLACK);
return;
}
// check if it's the greatest or the smallest value
for (int i = 0; i < table.getRowCount(); i++) {
try {
double currentAsDouble = Double.parseDouble((String) table.getValueAt(i, 1));
if (valueAsDouble < currentAsDouble) {
greatest = false;
}
if (valueAsDouble > currentAsDouble) {
smallest = false;
}
} catch (NumberFormatException e) {
// do nothing if we can't parse
// it must be a line with "-" as a value
}
}
if ((greatest) && (!smallest)) {
// case were we have the greatest value
component.setForeground(Color.RED);
component.setFont(getFont().deriveFont(Font.BOLD));
} else if ((smallest) && (!greatest)) {
// case where we have the smallest one
component.setForeground(Color.BLUE);
component.setFont(getFont().deriveFont(Font.BOLD));
} else {
// when it's neither the minimum nor the maximum
component.setForeground(Color.BLACK);
}
}
/**
* Sets the style of the "Total" line
* @param table the JTable
* @param value the value to assign to the cell at [row, column]
* @param row the row of the cell to render
* @param column the column of the cell to render
* @param component the component whose style needs to be changed
*/
private void setTotalStyle(JTable table, Object value, int row, int column, Component component) {
String valueStr = null;
if (column == 0) {
valueStr = value.toString();
} else {
valueStr = table.getValueAt(row, 0).toString();
}
if ((valueStr != null) && (valueStr.equalsIgnoreCase("total"))) {
component.setFont(getFont().deriveFont(Font.BOLD + Font.ITALIC));
}
}
}
/**
* Redefines the sorting process for the column containing the chromosome
* @author Julien Lajugie
* @param <T>
*/
private class CorrelationJTableSorter<T extends TableModel> extends TableRowSorter<T > {
private final Object[][] data; // data inside the JTable in the original order
/**
* Redefines the sorting process for the column containing the chromosome.
* The order of the chromosome is define by the order used when the JTable is created
* @param tableData data inside the JTable in the original order
* @param model model of the JTable
*/
public CorrelationJTableSorter(Object[][] tableData, T model) {
super(model);
this.data = tableData;
Comparator<Object> comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
int index1 = 0;
boolean found = false;
while ((!found) && (index1 < data.length)) {
if (data[index1][0].equals(o1)) {
found = true;
} else {
index1++;
}
}
int index2 = 0;
found = false;
while ((!found) && (index2 < data.length)) {
if (data[index2][0].equals(o2)) {
found = true;
} else {
index2++;
}
}
return new Integer(index1).compareTo(index2);
}
};
setComparator(0, comparator);
}
}
private static final long serialVersionUID = 5952700526094523963L; // generated ID
private static final String text1 = "Correlation Between "; // text before the first layer name in the text pane
private static final String text2 = "\n And "; // text after the first layer name in the text pane
private static JScrollPane jsp; // scroll pane containing the JTable
private static JTable jt; // JTable showing the result of the correlation for each chromosome plus the genome wide result
private static JButton jbOk; // button OK
private static JTextPane jtaLayerNames; // text pane with the name of the layers
/**
* Show a {@link CorrelationReportDialog} containing the specified data
* @param parent parents component in which the dialog is shown
* @param correlations array containing the correlation for each chromosome plus a row with the total correlation
* @param layer1 1st layer
* @param layer2 2nd layer
*/
public static void showDialog(Component parent, Double[] correlations, Layer<?> layer1, Layer<?> layer2) {
new CorrelationReportDialog(parent, correlations, layer1, layer2).setVisible(true);
}
/**
* Privates constructor. Creates an instance of {@link CorrelationReportDialog}
* @param parent parent Component in which the dialog is shown
* @param correlations array containing the correlations for each chromosome and the correlation genome wide
* @param layer1 1st layer
* @param layer2 2nd layer
*/
private CorrelationReportDialog(Component parent, Double[] correlations, Layer<?> layer1, Layer<?> layer2) {
super();
Object[][] tableData = new Object[correlations.length][2];
ProjectChromosomes cm = ProjectManager.getInstance().getProjectChromosomes();
// we fill the correlation for each chromosome
for (int i = 0; i < cm.size(); i++) {
tableData[i][0] = cm.get(i);
if (correlations[i] == null) {
tableData[i][1] = "-";
} else {
tableData[i][1] = NumberFormats.getScoreFormat().format(correlations[i]);
}
}
// we fill the total correlation
int lastIndex = tableData.length - 1;
tableData[lastIndex][0] = "Total";
tableData[lastIndex][1] = NumberFormats.getScoreFormat().format(correlations[lastIndex]);
// we fill the column names
Object[] columnNames = {"Chromosome", "Correlation"};
// create the jtable
jt = new CorrelationJTable(tableData, columnNames);
// create the JScrollPane
jsp = new JScrollPane(jt);
jsp.getVerticalScrollBar().setUnitIncrement(Utils.SCROLL_INCREMENT_UNIT);
// create the OK button
jbOk = new JButton("OK");
jbOk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
// retrieve layer properties
String name1 = layer1.getName();
String name2 = layer2.getName();
Color color1 = Colors.BLACK;
if (layer1 instanceof ColoredLayer) {
color1 = ((ColoredLayer) layer1).getColor();
}
Color color2 = Colors.BLACK;
if (layer2 instanceof ColoredLayer) {
color2 = ((ColoredLayer) layer2).getColor();
}
// create the text area
jtaLayerNames = new JTextPane();
// retrieve the document
StyledDocument document = jtaLayerNames.getStyledDocument();
// add a centered justification to the default style
MutableAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setAlignment(attributes, StyleConstants.ALIGN_CENTER);
document.setParagraphAttributes(0, 0, attributes, false);
// set the text of the document
jtaLayerNames.setText(text1 + name1 + text2 + name2);
// change the style of the layer names
StyleConstants.setForeground(attributes, color1);
document.setCharacterAttributes(text1.length(), name1.length(), attributes, false);
StyleConstants.setForeground(attributes, color2);
document.setCharacterAttributes(text1.length() + name1.length() + text2.length() , name2.length(), attributes, false);
// set the text area non editable and change the color of the background
jtaLayerNames.setEditable(false);
jtaLayerNames.setBackground(getContentPane().getBackground());
// add the components
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.CENTER;
add(jtaLayerNames, c);
c.gridy = 1;
c.fill = GridBagConstraints.BOTH;
add(jsp, c);
c.gridy = 2;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LAST_LINE_END;
add(jbOk, c);
pack();
setResizable(false);
setLocationRelativeTo(parent);
setModalityType(ModalityType.APPLICATION_MODAL);
getRootPane().setDefaultButton(jbOk);
setTitle("Correlation Report");
setIconImages(Images.getApplicationImages());
}
}