org.drugis.mtc.gui.AnalysisView.java Source code

Java tutorial

Introduction

Here is the source code for org.drugis.mtc.gui.AnalysisView.java

Source

/*
 * This file is part of the GeMTC software for MTC model generation and
 * analysis. GeMTC is distributed from http://drugis.org/gemtc.
 * Copyright (C) 2009-2012 Gert van Valkenhoef.
 *
 * 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/>.
 */

package org.drugis.mtc.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.TableModel;
import javax.swing.tree.TreePath;

import org.drugis.common.beans.ValueEqualsModel;
import org.drugis.common.gui.LayoutUtil;
import org.drugis.common.gui.TextComponentFactory;
import org.drugis.common.gui.table.EnhancedTable;
import org.drugis.common.gui.table.TablePanel;
import org.drugis.common.validation.BooleanAndModel;
import org.drugis.common.validation.BooleanNotModel;
import org.drugis.mtc.ConsistencyModel;
import org.drugis.mtc.DefaultModelFactory;
import org.drugis.mtc.InconsistencyModel;
import org.drugis.mtc.MCMCSettings;
import org.drugis.mtc.MCMCSettingsCache;
import org.drugis.mtc.ModelFactory;
import org.drugis.mtc.NodeSplitModel;
import org.drugis.mtc.data.DataType;
import org.drugis.mtc.graph.GraphUtil;
import org.drugis.mtc.gui.GeneratedCodeWindow.SyntaxType;
import org.drugis.mtc.gui.results.ConsistencyView;
import org.drugis.mtc.gui.results.InconsistencyView;
import org.drugis.mtc.gui.results.NodeSplitView;
import org.drugis.mtc.gui.results.ResultsComponentFactory;
import org.drugis.mtc.gui.results.SimulationComponentFactory;
import org.drugis.mtc.gui.results.SummaryCellRenderer;
import org.drugis.mtc.model.Network;
import org.drugis.mtc.model.Treatment;
import org.drugis.mtc.parameterization.BasicParameter;
import org.drugis.mtc.parameterization.NetworkModel;
import org.drugis.mtc.presentation.ConsistencyWrapper;
import org.drugis.mtc.presentation.InconsistencyWrapper;
import org.drugis.mtc.presentation.MCMCModelWrapper;
import org.drugis.mtc.presentation.MCMCPresentation;
import org.drugis.mtc.presentation.MTCModelWrapper;
import org.drugis.mtc.presentation.NodeSplitWrapper;
import org.drugis.mtc.presentation.SimulationConsistencyWrapper;
import org.drugis.mtc.presentation.SimulationInconsistencyWrapper;
import org.drugis.mtc.presentation.SimulationNodeSplitWrapper;
import org.drugis.mtc.presentation.results.NodeSplitResultsTableModel;
import org.drugis.mtc.summary.NodeSplitPValueSummary;
import org.drugis.mtc.summary.QuantileSummary;

import com.jgoodies.binding.adapter.Bindings;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.beans.PropertyConnector;
import com.jgoodies.binding.value.AbstractValueModel;
import com.jgoodies.binding.value.ConverterFactory;
import com.jgoodies.binding.value.ValueHolder;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class AnalysisView extends JPanel {
    private static final String WARNING_DATASET_CHANGED = "WARNING: the data set has changed since the models were last generated. Regenerate the models to take the new data into account.";

    private static final long serialVersionUID = -3923180226772918488L;

    private final JFrame d_parent;
    private final DataSetModel d_dataset;
    private final AnalysesModel d_analyses = new AnalysesModel();
    private Network d_network;
    private JSplitPane d_mainPane;

    private ModelFactory d_factory = DefaultModelFactory.instance();

    private ValueHolder d_chains = new ValueHolder(d_factory.getDefaults().getNumberOfChains());
    private ValueHolder d_scale = new ValueHolder(d_factory.getDefaults().getVarianceScalingFactor());
    private ValueHolder d_tuning = new ValueHolder(d_factory.getDefaults().getTuningIterations());
    private ValueHolder d_simulation = new ValueHolder(d_factory.getDefaults().getSimulationIterations());
    private ValueHolder d_thinning = new ValueHolder(d_factory.getDefaults().getThinningInterval());

    private MCMCPresentation d_consistency;

    private ValueEqualsModel d_revisionEqualsModel;
    private ValueModel d_haveAnalysesModel;
    private ValueModel d_dataSetChangedModel;

    public class CompleteModel extends AbstractValueModel {
        private static final long serialVersionUID = 5139716568335240809L;

        private boolean d_value;

        public CompleteModel() {
            PropertyChangeListener listener = new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    update();
                }
            };
            d_chains.addPropertyChangeListener(listener);
            d_scale.addPropertyChangeListener(listener);
            d_tuning.addPropertyChangeListener(listener);
            d_simulation.addPropertyChangeListener(listener);
            d_thinning.addPropertyChangeListener(listener);
            update();
        }

        private void update() {
            boolean oldValue = d_value;
            boolean newValue = d_chains.getValue() != null && d_scale.getValue() != null
                    && d_tuning.getValue() != null && d_simulation.getValue() != null
                    && d_thinning.getValue() != null && getInt(d_chains) > 0 && getDouble(d_scale) > 0.0
                    && getInt(d_tuning) > 0 && getInt(d_tuning) % 100 == 0 && getInt(d_simulation) > 0
                    && getInt(d_simulation) % 100 == 0 && getInt(d_thinning) > 0;
            d_value = newValue;
            fireValueChange(oldValue, newValue);
        }

        @Override
        public Object getValue() {
            return d_value;
        }

        @Override
        public void setValue(Object newValue) {
            throw new UnsupportedOperationException();
        }
    }

    public AnalysisView(JFrame parent, DataSetModel model) {
        d_parent = parent;
        d_dataset = model;
        final PropertyAdapter<DataSetModel> revision = new PropertyAdapter<DataSetModel>(d_dataset,
                DataSetModel.PROPERTY_REVISION, true);
        d_revisionEqualsModel = new ValueEqualsModel(revision, d_dataset.getRevision());
        d_haveAnalysesModel = new ValueHolder(false);
        d_dataSetChangedModel = new BooleanAndModel(new BooleanNotModel(d_revisionEqualsModel),
                d_haveAnalysesModel);
        setLayout(new BorderLayout());
        initComponents();
    }

    private void initComponents() {
        JTree tree = new JTree(d_analyses);
        tree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                setViewForPath(e.getPath());
            }
        });
        tree.setEditable(false);

        JScrollPane treePane = new JScrollPane(tree);
        treePane.setMinimumSize(new Dimension(150, 100));
        JSplitPane mainPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treePane, new JPanel());
        add(mainPane, BorderLayout.CENTER);
        d_mainPane = mainPane;
        setViewForPath(new TreePath(d_analyses.getRoot()));
    }

    protected JPanel buildDataChangedWarningPanel() {
        JPanel panel = AuxComponentFactory.createWarningPanel(WARNING_DATASET_CHANGED);
        PropertyConnector.connectAndUpdate(d_dataSetChangedModel, panel, "visible");
        return panel;
    }

    private void generateModels() {
        try {
            d_network = d_dataset.cloneNetwork();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        d_revisionEqualsModel.setExpected(d_dataset.getRevision());
        d_haveAnalysesModel.setValue(true);

        d_factory.setDefaults(getSettings());
        d_consistency = buildConsistencyModel();
        d_analyses.add(ModelType.Consistency, d_consistency);
        d_analyses.add(ModelType.Inconsistency, buildInconsistencyModel());
        for (BasicParameter node : d_factory.getSplittableNodes(d_network)) {
            d_analyses.add(ModelType.NodeSplit, buildNodeSplitModel(node));
        }
    }

    private MCMCSettings getSettings() {
        return new MCMCSettingsCache(0, getInt(d_simulation), getInt(d_thinning), getInt(d_tuning),
                getDouble(d_scale), getInt(d_chains));
    }

    private JTextField createDoubleField(ValueHolder model) {
        JTextField field = new JTextField(10);
        field.setHorizontalAlignment(JTextField.RIGHT);
        Bindings.bind(field, ConverterFactory.createStringConverter(model, new DecimalFormat("0.#####")), true);
        return field;
    }

    private JTextField createIntegerField(ValueHolder model) {
        JTextField field = new JTextField(10);
        field.setHorizontalAlignment(JTextField.RIGHT);
        Bindings.bind(field, ConverterFactory.createStringConverter(model, NumberFormat.getIntegerInstance()),
                true);
        return field;
    }

    private int getInt(ValueModel model) {
        return ((Number) model.getValue()).intValue();
    }

    private double getDouble(ValueModel model) {
        return ((Number) model.getValue()).doubleValue();
    }

    private MCMCPresentation buildConsistencyModel() {
        ConsistencyModel model = d_factory.getConsistencyModel(d_network);
        MCMCModelWrapper wrapper = new SimulationConsistencyWrapper<Treatment>(model, d_network.getTreatments(),
                Util.identityMap(d_network.getTreatments()));
        MCMCPresentation presentation = new MCMCPresentation(wrapper, "Consistency model");
        return presentation;
    }

    private MCMCPresentation buildInconsistencyModel() {
        InconsistencyModel model = d_factory.getInconsistencyModel(d_network);
        MCMCModelWrapper wrapper = new SimulationInconsistencyWrapper<Treatment>(model,
                Util.identityMap(d_network.getTreatments()));
        MCMCPresentation presentation = new MCMCPresentation(wrapper, "Inconsistency model");
        return presentation;
    }

    private MCMCPresentation buildNodeSplitModel(BasicParameter split) {
        NodeSplitModel model = d_factory.getNodeSplitModel(d_network, split);
        MCMCModelWrapper wrapper = new SimulationNodeSplitWrapper<Treatment>(model,
                Util.identityMap(d_network.getTreatments()));
        MCMCPresentation presentation = new MCMCPresentation(wrapper, split.getName());
        return presentation;
    }

    private JPanel buildRootPanel() {
        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("pref, 3dlu, pref, fill:0:grow", "p");
        PanelBuilder builder = new PanelBuilder(layout);
        builder.setDefaultDialogBorder();

        int row = LayoutUtil.addRow(layout, 1);
        builder.add(
                TextComponentFactory.createTextPane(boxHtmlText(Help.getHelpText("networkMetaAnalysis")), false),
                cc.xyw(1, row, 4));

        row = LayoutUtil.addRow(layout, row);
        builder.addSeparator("Generate models", cc.xyw(1, 1, layout.getColumnCount()));

        row = LayoutUtil.addRow(layout, row);
        builder.add(new JLabel("Number of chains: "), cc.xy(1, row));
        builder.add(createIntegerField(d_chains), cc.xy(3, row));

        row = LayoutUtil.addRow(layout, row);
        builder.add(new JLabel("Initial values scaling: "), cc.xy(1, row));
        builder.add(createDoubleField(d_scale), cc.xy(3, row));

        row = LayoutUtil.addRow(layout, row);
        builder.add(new JLabel("Tuning iterations: "), cc.xy(1, row));
        builder.add(createIntegerField(d_tuning), cc.xy(3, row));

        row = LayoutUtil.addRow(layout, row);
        builder.add(new JLabel("Simulation iterations: "), cc.xy(1, row));
        builder.add(createIntegerField(d_simulation), cc.xy(3, row));

        row = LayoutUtil.addRow(layout, row);
        builder.add(new JLabel("Thinning interval: "), cc.xy(1, row));
        builder.add(createIntegerField(d_thinning), cc.xy(3, row));

        final JButton button = new JButton("Generate models");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                final DataSetModel model = d_dataset;
                if (model.getTreatments().size() < 2 || model.getStudies().size() < 2) {
                    JOptionPane.showMessageDialog(d_parent,
                            "You need to define at least two studies and treatments.", "Cannot generate model",
                            JOptionPane.WARNING_MESSAGE);
                    return;
                }
                if (model.getMeasurementType().getValue() == DataType.NONE) {
                    JOptionPane.showMessageDialog(d_parent, "Model generation not possible with 'None' measuments.",
                            "Cannot generate model", JOptionPane.WARNING_MESSAGE);
                    return;
                }
                Network network = model.getNetwork();
                if (!GraphUtil.isWeaklyConnected(NetworkModel.createStudyGraph(network))) {
                    JOptionPane.showMessageDialog(d_parent,
                            "The network needs to be connected in order to generate an MTC model.",
                            "Cannot generate model", JOptionPane.WARNING_MESSAGE);
                    return;
                }
                // FIXME: further validation / checking of data.
                //            final String name = model.getFile() == null ? "unnamed" : model.getFile().getName().replaceFirst(".gemtc$", "");

                d_analyses.clear();
                generateModels();
            }
        });
        PropertyConnector.connectAndUpdate(new CompleteModel(), button, "enabled");
        row = LayoutUtil.addRow(layout, row);
        builder.add(button, cc.xy(3, row));

        return builder.getPanel();
    }

    private JPanel buildTypePanel(ModelType type) {
        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("pref, pref:grow:fill", "p, 3dlu, p, 3dlu, p");
        PanelBuilder builder = new PanelBuilder(layout);
        builder.setDefaultDialogBorder();
        switch (type) {
        case Consistency:
            builder.addSeparator("Consistency model", cc.xyw(1, 1, 2));
            builder.add(TextComponentFactory.createTextPane(boxHtmlText(Help.getHelpText("consistency")), false),
                    cc.xy(1, 3));
            break;
        case Inconsistency:
            builder.addSeparator("Inconsistency model", cc.xy(1, 1));
            builder.add(TextComponentFactory.createTextPane(boxHtmlText(Help.getHelpText("inconsistency")), false),
                    cc.xy(1, 3));
            break;
        case NodeSplit:
            builder.addSeparator("Node splitting models", cc.xy(1, 1));
            builder.add(TextComponentFactory.createTextPane(boxHtmlText(Help.getHelpText("nodeSplit")), false),
                    cc.xy(1, 3));
            LayoutUtil.addRow(layout);
            List<NodeSplitWrapper<?>> wrappers = new ArrayList<NodeSplitWrapper<?>>();
            for (MCMCPresentation p : d_analyses.getModels(ModelType.NodeSplit)) {
                wrappers.add((NodeSplitWrapper<?>) p.getWrapper());
            }
            TableModel model = new NodeSplitResultsTableModel((ConsistencyWrapper<?>) d_consistency.getWrapper(),
                    wrappers, false);
            EnhancedTable table = EnhancedTable.createBare(model);
            table.setDefaultRenderer(QuantileSummary.class, new SummaryCellRenderer(false));
            table.setDefaultRenderer(NodeSplitPValueSummary.class, new SummaryCellRenderer(false));
            table.autoSizeColumns();
            builder.add(new TablePanel(table), cc.xy(1, 5));
            break;
        default:
            break;
        }

        return builder.getPanel();
    }

    private String boxHtmlText(String text) {
        return "<div style='width: 400px; padding: 0px 10px 10px 10px'>" + text + "</div>";
    }

    private JPanel buildModelPanel(final MCMCPresentation presentation) {
        JPanel results = new JPanel();
        if (presentation.getModel() instanceof ConsistencyModel) {
            results = new ConsistencyView(d_network.getTreatments(),
                    (ConsistencyWrapper<?>) presentation.getWrapper(), d_network.getType().equals(DataType.RATE));
        } else if (presentation.getModel() instanceof InconsistencyModel) {
            results = new InconsistencyView(d_network.getTreatments(), presentation,
                    (InconsistencyWrapper<?>) presentation.getWrapper(), d_network.getType().equals(DataType.RATE));
        } else if (presentation.getModel() instanceof NodeSplitModel) {
            NodeSplitModel nodeSplitModel = (NodeSplitModel) presentation.getModel();
            results = new NodeSplitView(nodeSplitModel.getSplitNode(),
                    (NodeSplitWrapper<?>) presentation.getWrapper(),
                    (ConsistencyWrapper<?>) d_consistency.getWrapper());
        }

        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("pref:grow:fill", "p, 3dlu, p, 3dlu, p");
        PanelBuilder builder = new PanelBuilder(layout);

        Runnable onReset = new Runnable() {
            public void run() {
                MCMCPresentation newPresentation = null;
                if (presentation.getModel() instanceof ConsistencyModel) {
                    newPresentation = buildConsistencyModel();
                    d_analyses.replace(ModelType.Consistency, presentation, newPresentation);
                } else if (presentation.getModel() instanceof InconsistencyModel) {
                    newPresentation = buildInconsistencyModel();
                    d_analyses.replace(ModelType.Inconsistency, presentation, newPresentation);
                } else if (presentation.getModel() instanceof NodeSplitModel) {
                    NodeSplitModel nodeSplitModel = (NodeSplitModel) presentation.getModel();
                    newPresentation = buildNodeSplitModel(nodeSplitModel.getSplitNode());
                    d_analyses.replace(ModelType.NodeSplit, presentation, newPresentation);
                }
                setView(buildModelPanel(newPresentation));
            }
        };

        builder.setDefaultDialogBorder();
        builder.add(
                SimulationComponentFactory.createSimulationControls(presentation, d_parent, true, null, onReset),
                cc.xy(1, 1));
        builder.add(buildToolsPanel((MTCModelWrapper<?>) presentation.getWrapper(), d_parent), cc.xy(1, 3));
        builder.add(results, cc.xy(1, 5));
        return builder.getPanel();
    }

    private Component buildToolsPanel(MTCModelWrapper<?> model, JFrame parent) {
        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("left:pref, 3dlu, left:pref, 3dlu, pref, 3dlu, pref, fill:0:grow",
                "p, 3dlu, p");
        PanelBuilder builder = new PanelBuilder(layout);
        builder.addSeparator("Tools", cc.xyw(1, 1, 8));
        int row = ResultsComponentFactory.buildMemoryUsage(model, "Memory usage: ", builder, layout, 3, parent);

        builder.add(createGenerateCodeButton(d_network, SyntaxType.BUGS, model), cc.xy(1, row));
        builder.add(createGenerateCodeButton(d_network, SyntaxType.JAGS, model), cc.xy(3, row));

        return builder.getPanel();
    }

    private JButton createGenerateCodeButton(final Network network, final SyntaxType type,
            final MTCModelWrapper<?> model) {
        JButton button = new JButton("Generate " + type.toString() + " code");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new GeneratedCodeWindow(type, network, model).setVisible(true);
            }
        });
        return button;
    }

    private void setViewForPath(TreePath path) {
        JPanel view = null;
        if (path.getLastPathComponent() instanceof MCMCPresentation) {
            view = buildModelPanel((MCMCPresentation) path.getLastPathComponent());
        } else if (path.getLastPathComponent() instanceof ModelType) {
            view = buildTypePanel((ModelType) path.getLastPathComponent());
        } else {
            view = buildRootPanel();
        }
        setView(view);
    }

    private void setView(JPanel view) {
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(buildDataChangedWarningPanel(), BorderLayout.NORTH);
        panel.add(new JScrollPane(view), BorderLayout.CENTER);
        d_mainPane.setRightComponent(panel);
    }
}