be.ugent.maf.cellmissy.gui.controller.analysis.doseresponse.generic.GenericDoseResponseController.java Source code

Java tutorial

Introduction

Here is the source code for be.ugent.maf.cellmissy.gui.controller.analysis.doseresponse.generic.GenericDoseResponseController.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package be.ugent.maf.cellmissy.gui.controller.analysis.doseresponse.generic;

import be.ugent.maf.cellmissy.gui.controller.analysis.doseresponse.DoseResponseController;
import be.ugent.maf.cellmissy.analysis.doseresponse.SigmoidFitter;
import be.ugent.maf.cellmissy.entity.result.doseresponse.DoseResponsePair;
import be.ugent.maf.cellmissy.entity.result.doseresponse.GenericDoseResponseAnalysisGroup;
import be.ugent.maf.cellmissy.entity.result.doseresponse.ImportedDRDataHolder;
import be.ugent.maf.cellmissy.entity.result.doseresponse.SigmoidFittingResultsHolder;
import be.ugent.maf.cellmissy.gui.CellMissyFrame;
import be.ugent.maf.cellmissy.gui.controller.CellMissyController;
import be.ugent.maf.cellmissy.gui.experiment.analysis.doseresponse.DRPanel;
import be.ugent.maf.cellmissy.gui.experiment.analysis.doseresponse.GenericDRParentPanel;
import be.ugent.maf.cellmissy.gui.view.renderer.table.TableHeaderRenderer;
import be.ugent.maf.cellmissy.gui.view.table.model.NonEditableTableModel;
import be.ugent.maf.cellmissy.utils.GuiUtils;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.swing.ButtonGroup;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;
import org.jfree.chart.JFreeChart;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 *
 * @author Gwendolien Sergeant
 */
@Controller("genericDoseResponseController")
public class GenericDoseResponseController extends DoseResponseController {

    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
            .getLogger(GenericDoseResponseController.class);

    //model: in super class
    private GenericDoseResponseAnalysisGroup dRAnalysisGroup;
    private ImportedDRDataHolder importedDRDataHolder;
    private boolean logTransform;
    //view: in super class
    private GenericDRParentPanel genericDRParentPanel;
    //parent controller
    @Autowired
    private CellMissyController cellMissyController;
    //child controllers
    @Autowired
    private LoadGenericDRDataController loadGenericDRDataController;
    @Autowired
    private GenericDRInputController dRInputController;
    @Autowired
    private GenericDRInitialController dRInitialController;
    @Autowired
    private GenericDRNormalizedController dRNormalizedController;
    @Autowired
    private GenericDRResultsController dRResultsController;
    // services
    @Autowired
    private SigmoidFitter sigmoidFitter;

    /**
     * Getters and setters
     *
     */
    public void setdRAnalysisGroup(GenericDoseResponseAnalysisGroup dRAnalysisGroup) {
        this.dRAnalysisGroup = dRAnalysisGroup;
    }

    public GenericDoseResponseAnalysisGroup getdRAnalysisGroup() {
        return dRAnalysisGroup;
    }

    public void showMessage(String message, String title, Integer messageType) {
        cellMissyController.showMessage(message, title, messageType);
    }

    protected String getNormalizationInfo() {
        return dRNormalizedController.getNormalizationInfo();
    }

    public CellMissyFrame getCellMissyFrame() {
        return cellMissyController.getCellMissyFrame();
    }

    public GenericDRParentPanel getGenericDRParentPanel() {
        return genericDRParentPanel;
    }

    public ImportedDRDataHolder getImportedDRDataHolder() {
        return importedDRDataHolder;
    }

    public boolean getLogTransform() {
        return logTransform;
    }

    public void setLogTransform(boolean logTransform) {
        this.logTransform = logTransform;
    }

    /**
     * Do a fitting according to initial, standard parameters and calculate
     * statistics. This method is called when the user switches to the initial
     * or normalized subview for the first time.
     */
    @Override
    protected void initFirstFitting() {
        dRInitialController.initDRInitialData();
        dRNormalizedController.initDRNormalizedData();
        calculateStatistics();
    }

    /**
     * Perform fitting according to user specifications. Called by subclasses.
     *
     * @param dataToFit The data (log-transformed concentration - velocity)
     * @param resultsHolder The class that will contain the results from fitting
     * @param bottomConstrained Double if user constrains, otherwise null
     * @param topConstrained Double if user constrains, otherwise null
     *
     */
    protected void performFitting(List<DoseResponsePair> dataToFit, SigmoidFittingResultsHolder resultsHolder,
            Double bottomConstrained, Double topConstrained) {
        performFitting(sigmoidFitter, dataToFit, resultsHolder, bottomConstrained, topConstrained);
    }

    /**
     * Returns a dose-response chart containing scattered xy experimental values
     * and the curve (line) from the fitting. To be visible in the program,
     * another method adds the chart to the right panel.
     *
     * @param dataToPlot Maps log-transformed concentration to replicate
     * (normalized) velocities.
     * @param normalized Whether the data is normalized or not
     * @return
     */
    @Override
    protected JFreeChart createDoseResponseChart(List<DoseResponsePair> dataToPlot, boolean normalized) {
        return createDoseResponseChart(dataToPlot, dRAnalysisGroup, normalized);
    }

    /**
     * Calculate statistics, method from results controller is called by other
     * child controllers on new fitting.
     */
    @Override
    protected void calculateStatistics() {
        dRResultsController.setStatistics(dRAnalysisGroup);
    }

    @Override
    public List<DoseResponsePair> getDataToFit(boolean normalized) {
        if (normalized) {
            return dRNormalizedController.getDataToFit();
        } else {
            return dRInitialController.getDataToFit();
        }
    }

    /**
     * Get the constrain values for the bottom and top parameter. (Double number
     * or null if not constrained)
     *
     * @param normalized True if from normalized fit
     * @return
     */
    @Override
    protected List<Double> getConstrainValues(boolean normalized) {
        List<Double> result = new ArrayList<>();
        if (!normalized) {
            result.add(dRInitialController.getBottomConstrainValue());
            result.add(dRInitialController.getTopConstrainValue());
        } else {
            result.add(dRNormalizedController.getBottomConstrainValue());
            result.add(dRNormalizedController.getTopConstrainValue());
        }
        return result;
    }

    /**
     * Edit a table's headers so that "Log-concentration" is replaced by "Dose"
     *
     * @param tableModel
     * @return
     */
    protected NonEditableTableModel updateTableModel(NonEditableTableModel tableModel) {
        String[] newIdentifiers = new String[tableModel.getColumnCount()];
        if (getLogTransform()) {
            newIdentifiers[0] = "Log10-dose";
        } else {
            newIdentifiers[0] = "Dose";
        }
        for (int i = 1; i < newIdentifiers.length; i++) {
            newIdentifiers[i] = tableModel.getColumnName(i);
        }
        tableModel.setColumnIdentifiers(newIdentifiers);
        return tableModel;

    }

    @Override
    protected List<String> getPlotAxesNames(boolean normalized) {
        List<String> result = new ArrayList<>();
        //check if dose is logtransformed
        if (logTransform) {
            result.add("Log10 of Dose");
        } else {
            result.add("Dose");
        }
        //check whether the responses are normalized
        if (normalized) {
            result.add("Response (%)");
        } else {
            result.add("Response");
        }
        return result;
    }

    /**
     * Reset views on cancel
     */
    @Override
    public void resetOnCancel() {
        super.resetOnCancel();
        loadGenericDRDataController.reset();
        dRInputController.reset();
        importedDRDataHolder = null;
        setFirstFitting(true);
        getCardLayout().first(genericDRParentPanel.getContentPanel());
        onCardSwitch();
        genericDRParentPanel.getCancelButton().setEnabled(false);
        dRAnalysisGroup = null;
        //remove tables, graphs and subpanels
        dRInputController.getdRInputPanel().getSlopesTable().setModel(new DefaultTableModel());
        dRInitialController.getInitialChartPanel().setChart(null);
        dRNormalizedController.getNormalizedChartPanel().setChart(null);
        dRResultsController.getDupeInitialChartPanel().setChart(null);
        dRResultsController.getDupeNormalizedChartPanel().setChart(null);
        //set view back to first one
        dRPanel.getInputDRButton().setSelected(true);
        dRPanel.revalidate();
        dRPanel.repaint();
    }

    /**
     * Initialise tables when switching to analysis card.
     */
    public void onDoseResponse() {
        dRInputController.initDRInputData();
        dRPanel.getInputDRButton().doClick();
        //switch shared table view
        updateModelInTable(dRInputController.getTableModel());
        dataTable.getTableHeader().setDefaultRenderer(new TableHeaderRenderer(SwingConstants.LEFT));
        updateTableInfoMessage("This table contains all conditions and their respective responses");
    }

    /**
     * Initialize controller
     */
    public void init() {
        gridBagConstraints = GuiUtils.getDefaultGridBagConstraints();
        //init view
        loadGenericDRDataController.init();
        initMainView();
        //init child controllers
        dRInputController.init();
        dRInitialController.init();
        dRNormalizedController.init();
        dRResultsController.init();
    }

    /**
     * Initialize main view
     */
    @Override
    protected void initMainView() {
        genericDRParentPanel = new GenericDRParentPanel();
        dRPanel = new DRPanel();
        //buttons disabled at start
        genericDRParentPanel.getCancelButton().setEnabled(false);
        genericDRParentPanel.getNextButton().setEnabled(false);
        getCardLayout().first(genericDRParentPanel.getContentPanel());
        onCardSwitch();
        //create a ButtonGroup for the radioButtons used for analysis
        ButtonGroup mainDRRadioButtonGroup = new ButtonGroup();
        //adding buttons to a ButtonGroup automatically deselect one when another one gets selected
        mainDRRadioButtonGroup.add(dRPanel.getInputDRButton());
        mainDRRadioButtonGroup.add(dRPanel.getInitialPlotDRButton());
        mainDRRadioButtonGroup.add(dRPanel.getNormalizedPlotDRButton());
        mainDRRadioButtonGroup.add(dRPanel.getResultsDRButton());
        //select as default first button
        dRPanel.getInputDRButton().setSelected(true);
        //init dataTable
        dataTable = new JTable();
        JScrollPane scrollPane = new JScrollPane(dataTable);
        //the table will take all the viewport height available
        dataTable.setFillsViewportHeight(true);
        scrollPane.getViewport().setBackground(Color.white);
        dataTable.getTableHeader().setReorderingAllowed(false);
        //row and column selection must be false
        //dataTable.setColumnSelectionAllowed(false);
        //dataTable.setRowSelectionAllowed(false);
        dRPanel.getDatatableDRPanel().add(scrollPane, BorderLayout.CENTER);
        setLogTransform(true);

        /**
         * Action listeners for uppermost panel.
         */
        //this button is ONLY used when going from the loading to the analysis
        genericDRParentPanel.getNextButton().addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                genericDRParentPanel.getNextButton().setEnabled(false);
                genericDRParentPanel.getCancelButton().setEnabled(true);
                //save any metadata that was provided manually
                loadGenericDRDataController.setManualMetaData(importedDRDataHolder);
                //switch between child panels
                getCardLayout().next(genericDRParentPanel.getContentPanel());
                onCardSwitch();
            }
        });

        genericDRParentPanel.getCancelButton().addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // warn the user and reset everything
                Object[] options = { "Yes", "No" };
                int showOptionDialog = JOptionPane.showOptionDialog(null,
                        "Current analysis won't be saved. Continue?", "", JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE, null, options, options[1]);
                if (showOptionDialog == 0) {
                    // reset everything
                    resetOnCancel();
                }
            }
        });

        /**
         * Action listeners for shared panel. When button is selected, switch
         * view to corresponding subview
         */
        dRPanel.getInputDRButton().addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                //switch shared table view
                updateModelInTable(dRInputController.getTableModel());
                updateTableInfoMessage("This table contains all conditions and their respective responses");
                /**
                 * for (int columnIndex = 0; columnIndex <
                 * dataTable.getColumnCount(); columnIndex++) {
                 * GuiUtils.packColumn(dataTable, columnIndex); }
                 */
                dataTable.getTableHeader().setDefaultRenderer(new TableHeaderRenderer(SwingConstants.LEFT));
                //remove other panels
                dRInitialController.getInitialChartPanel().setChart(null);
                dRNormalizedController.getNormalizedChartPanel().setChart(null);
                dRResultsController.getDupeInitialChartPanel().setChart(null);
                dRResultsController.getDupeNormalizedChartPanel().setChart(null);
                dRPanel.getGraphicsDRParentPanel().removeAll();
                dRPanel.getGraphicsDRParentPanel().revalidate();
                dRPanel.getGraphicsDRParentPanel().repaint();
                //add panel to view
                dRPanel.getGraphicsDRParentPanel().add(dRInputController.getdRInputPanel(), gridBagConstraints);
            }
        });

        dRPanel.getInitialPlotDRButton().addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (dRAnalysisGroup != null) {
                    if (isFirstFitting()) {
                        initFirstFitting();
                        setFirstFitting(false);
                    }
                    //switch shared table view
                    updateModelInTable(dRInitialController.getTableModel());
                    updateTableInfoMessage(
                            "If you checked the box at the import screen, the doses have been log-transformed. Responses have not been changed");
                    /**
                     * for (int columnIndex = 0; columnIndex <
                     * dataTable.getColumnCount(); columnIndex++) {
                     * GuiUtils.packColumn(dataTable, columnIndex); }
                     */
                    dataTable.getTableHeader().setDefaultRenderer(new TableHeaderRenderer(SwingConstants.LEFT));
                    //remove other panels
                    dRNormalizedController.getNormalizedChartPanel().setChart(null);
                    dRResultsController.getDupeInitialChartPanel().setChart(null);
                    dRResultsController.getDupeNormalizedChartPanel().setChart(null);
                    dRPanel.getGraphicsDRParentPanel().removeAll();
                    dRPanel.getGraphicsDRParentPanel().revalidate();
                    dRPanel.getGraphicsDRParentPanel().repaint();
                    dRPanel.getGraphicsDRParentPanel().add(dRInitialController.getDRInitialPlotPanel(),
                            gridBagConstraints);
                    //Plot fitted data in dose-response curve, along with R annotation
                    plotDoseResponse(dRInitialController.getInitialChartPanel(),
                            dRInitialController.getDRInitialPlotPanel().getDoseResponseChartParentPanel(),
                            getDataToFit(false), getdRAnalysisGroup(), false);
                }
            }
        });

        dRPanel.getNormalizedPlotDRButton().addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (dRAnalysisGroup != null) {
                    //in case user skips "initial" subview and goes straight to normalization
                    if (isFirstFitting()) {
                        initFirstFitting();
                        setFirstFitting(false);
                    }
                    //switch shared table view
                    updateModelInTable(dRNormalizedController.getTableModel());
                    updateTableInfoMessage(
                            "Potentially log-transformed doses with their normalized responses per replicate");
                    /**
                     * for (int columnIndex = 0; columnIndex <
                     * dataTable.getColumnCount(); columnIndex++) {
                     * GuiUtils.packColumn(dataTable, columnIndex); }
                     */
                    dataTable.getTableHeader().setDefaultRenderer(new TableHeaderRenderer(SwingConstants.LEFT));
                    //remove other panels
                    dRInitialController.getInitialChartPanel().setChart(null);
                    dRResultsController.getDupeInitialChartPanel().setChart(null);
                    dRResultsController.getDupeNormalizedChartPanel().setChart(null);
                    dRPanel.getGraphicsDRParentPanel().removeAll();
                    dRPanel.getGraphicsDRParentPanel().revalidate();
                    dRPanel.getGraphicsDRParentPanel().repaint();
                    dRPanel.getGraphicsDRParentPanel().add(dRNormalizedController.getDRNormalizedPlotPanel(),
                            gridBagConstraints);
                    //Plot fitted data in dose-response curve, along with R annotation
                    plotDoseResponse(dRNormalizedController.getNormalizedChartPanel(),
                            dRNormalizedController.getDRNormalizedPlotPanel().getDoseResponseChartParentPanel(),
                            getDataToFit(true), getdRAnalysisGroup(), true);
                }
            }
        });

        dRPanel.getResultsDRButton().addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (dRAnalysisGroup != null) {
                    //switch shared table view: create and set new table model with most recent statistical values
                    // (these values get recalculated after each new fitting)
                    dRResultsController.setTableModel(dRResultsController.reCreateTableModel(dRAnalysisGroup));
                    updateModelInTable(dRResultsController.getTableModel());
                    updateTableInfoMessage(
                            "Statistical values from the curve fit of the initial and normalized data.");

                    //remove other panels
                    dRInitialController.getInitialChartPanel().setChart(null);
                    dRNormalizedController.getNormalizedChartPanel().setChart(null);
                    dRPanel.getGraphicsDRParentPanel().removeAll();
                    dRPanel.getGraphicsDRParentPanel().revalidate();
                    dRPanel.getGraphicsDRParentPanel().repaint();
                    dRPanel.getGraphicsDRParentPanel().add(dRResultsController.getdRResultsPanel(),
                            gridBagConstraints);
                    //plot curves
                    dRResultsController.plotCharts();
                }
            }
        });

        //add views to parent panels
        cellMissyController.getCellMissyFrame().getDoseResponseAnalysisParentPanel().add(genericDRParentPanel,
                gridBagConstraints);
        genericDRParentPanel.getDataLoadingPanel().add(loadGenericDRDataController.getDataLoadingPanel(),
                gridBagConstraints);
        genericDRParentPanel.getDoseResponseParentPanel().add(dRPanel, gridBagConstraints);
    }

    /**
     * Update information message in the top panel.
     *
     * @param messageToShow
     */
    private void updateInfoMessage(String messageToShow) {
        cellMissyController.updateInfoLabel(genericDRParentPanel.getCardInfoLabel(), messageToShow);
    }

    /**
     * get Card Layout
     *
     * @return
     */
    private CardLayout getCardLayout() {
        return (CardLayout) genericDRParentPanel.getContentPanel().getLayout();
    }

    /**
     * Check for card name when switching.
     */
    private void onCardSwitch() {
        String currentCardName = GuiUtils.getCurrentCardName(genericDRParentPanel.getContentPanel());
        switch (currentCardName) {
        // First case is only called when starting up new import. Switching back from second card is not implemented.
        case "dataLoadingPanel":
            GuiUtils.highlightLabel(genericDRParentPanel.getDataLoadingLabel());
            GuiUtils.resetLabel(genericDRParentPanel.getDoseResponseLabel());
            updateInfoMessage("Load the dose-response data you want to analyze");
            //initialize new imported data holder.
            importedDRDataHolder = new ImportedDRDataHolder();
            break;

        case "doseResponseParentPanel":
            //next button disabled
            genericDRParentPanel.getNextButton().setEnabled(false);
            onDoseResponse();
            //highlight and reset labels
            GuiUtils.highlightLabel(genericDRParentPanel.getDoseResponseLabel());
            GuiUtils.resetLabel(genericDRParentPanel.getDataLoadingLabel());
            updateInfoMessage("Quantify the effect of a dose by fitting the responses to a sigmoid model");
            break;
        }
    }

    /**
     * Ask user to choose for a directory and invoke swing worker for creating
     * PDF report
     *
     * @throws IOException
     */
    protected void createPdfReport() throws IOException {
        // choose directory to save pdf file
        JFileChooser chooseDirectory = new JFileChooser();
        chooseDirectory.setDialogTitle("Choose a directory to save the report");
        chooseDirectory.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        //        needs more information
        chooseDirectory.setSelectedFile(
                new File("Dose Response Report " + importedDRDataHolder.getExperimentNumber() + ".pdf"));
        // in response to the button click, show open dialog
        //        TEST WHETHER THIS PARENT PANEL/FRAME IS OKAY
        int returnVal = chooseDirectory.showSaveDialog(cellMissyController.getCellMissyFrame());
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File directory = chooseDirectory.getCurrentDirectory();
            DoseResponseReportSwingWorker doseResponseReportSwingWorker = new DoseResponseReportSwingWorker(
                    directory, chooseDirectory.getSelectedFile().getName());
            doseResponseReportSwingWorker.execute();
        } else {
            cellMissyController.showMessage("Open command cancelled by user", "", JOptionPane.INFORMATION_MESSAGE);
        }
    }

    /**
     * Swing Worker to generate PDF report
     */
    private class DoseResponseReportSwingWorker extends SwingWorker<File, Void> {

        private final File directory;
        private final String reportName;

        public DoseResponseReportSwingWorker(File directory, String reportName) {
            this.directory = directory;
            this.reportName = reportName;
        }

        @Override
        protected File doInBackground() throws Exception {
            // disable button
            dRResultsController.getdRResultsPanel().getCreateReportButton().setEnabled(false);
            //set cursor to waiting one
            cellMissyController.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            //call the child controller to create report
            return dRResultsController.createAnalysisReport(directory, reportName);
        }

        @Override
        protected void done() {
            File file = null;
            try {
                file = get();
            } catch (InterruptedException | CancellationException | ExecutionException ex) {
                LOG.error(ex.getMessage(), ex);
                cellMissyController.handleUnexpectedError(ex);
            }
            try {
                //if export to PDF was successful, open the PDF file from the desktop
                if (Desktop.isDesktopSupported()) {
                    Desktop.getDesktop().open(file);
                }
            } catch (IOException ex) {
                LOG.error(ex.getMessage(), ex);
                cellMissyController.showMessage("Cannot open the file!" + "\n" + ex.getMessage(),
                        "error while opening file", JOptionPane.ERROR_MESSAGE);
            }
            //set cursor back to default
            cellMissyController.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            // enable button
            dRResultsController.getdRResultsPanel().getCreateReportButton().setEnabled(true);
        }
    }
}