org.drugis.addis.presentation.wizard.NetworkMetaAnalysisWizardPM.java Source code

Java tutorial

Introduction

Here is the source code for org.drugis.addis.presentation.wizard.NetworkMetaAnalysisWizardPM.java

Source

/*
 * This file is part of ADDIS (Aggregate Data Drug Information System).
 * ADDIS is distributed from http://drugis.org/.
 * Copyright  2009 Gert van Valkenhoef, Tommi Tervonen.
 * Copyright  2010 Gert van Valkenhoef, Tommi Tervonen, Tijs Zwinkels,
 * Maarten Jacobs, Hanno Koeslag, Florin Schimbinschi, Ahmad Kamal, Daniel
 * Reid.
 * Copyright  2011 Gert van Valkenhoef, Ahmad Kamal, Daniel Reid, Florin
 * Schimbinschi.
 * Copyright  2012 Gert van Valkenhoef, Daniel Reid, Jol Kuiper, Wouter
 * Reckman.
 * Copyright  2013 Gert van Valkenhoef, Jol Kuiper.
 *
 * 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.addis.presentation.wizard;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.collections15.Predicate;
import org.apache.commons.lang.ArrayUtils;
import org.drugis.addis.entities.Arm;
import org.drugis.addis.entities.Domain;
import org.drugis.addis.entities.Drug;
import org.drugis.addis.entities.Indication;
import org.drugis.addis.entities.OutcomeMeasure;
import org.drugis.addis.entities.Study;
import org.drugis.addis.entities.Variable;
import org.drugis.addis.entities.analysis.MetaAnalysis;
import org.drugis.addis.entities.analysis.NetworkMetaAnalysis;
import org.drugis.addis.entities.treatment.Category;
import org.drugis.addis.entities.treatment.TreatmentCategorization;
import org.drugis.addis.entities.treatment.TreatmentDefinition;
import org.drugis.addis.presentation.ModifiableHolder;
import org.drugis.addis.presentation.PresentationModelFactory;
import org.drugis.addis.presentation.SelectableStudyCharTableModel;
import org.drugis.addis.presentation.SelectableTreatmentDefinitionsGraphModel;
import org.drugis.addis.presentation.StudyListPresentation;
import org.drugis.addis.presentation.TreatmentDefinitionsGraphModel;
import org.drugis.addis.presentation.TreatmentDefinitionsGraphModel.Edge;
import org.drugis.addis.presentation.TreatmentDefinitionsGraphModel.Vertex;
import org.drugis.addis.presentation.ValueHolder;
import org.drugis.common.CollectionUtil;
import org.drugis.common.beans.AffixedObservableList;
import org.drugis.common.beans.FilteredObservableList;
import org.drugis.common.event.IndifferentListDataListener;
import org.jgrapht.alg.ConnectivityInspector;
import org.jgrapht.event.GraphEdgeChangeEvent;
import org.jgrapht.event.GraphListener;
import org.jgrapht.event.GraphVertexChangeEvent;

import com.jgoodies.binding.list.ArrayListModel;
import com.jgoodies.binding.list.ObservableList;
import com.jgoodies.binding.value.AbstractValueModel;
import com.jgoodies.binding.value.ValueModel;

public class NetworkMetaAnalysisWizardPM extends AbstractAnalysisWizardPresentation<MetaAnalysis> {
    private static final String TEMPLATE_DEFINITIONS_DESCRIPTION = "Select the %1$s to be used for the network meta-analysis. Click to select (green) or deselect (gray).  "
            + "To continue, (1) at least %2$s must be selected, and (2) all selected %1$s must be connected.";

    private final TreatmentDefinitionsGraphModel d_overviewGraph;
    private final ValueHolder<Boolean> d_selectedStudyGraphConnectedModel;
    protected PresentationModelFactory d_pmf;
    protected ModifiableHolder<OutcomeMeasure> d_outcomeHolder;
    private ObservableList<OutcomeMeasure> d_outcomes = new ArrayListModel<OutcomeMeasure>();

    /** First graph containing only Trivial Categorizations (previously DrugSets) **/
    protected final ObservableList<TreatmentDefinition> d_rawTreatmentDefinitions;
    protected final SelectableTreatmentDefinitionsGraphModel d_rawAlternativesGraph;

    /** Second graph containing definitions transformed by the selection of TreatmentCategorizations **/
    protected final ObservableList<TreatmentDefinition> d_refinedTreatmentDefinitions;
    protected final SelectableTreatmentDefinitionsGraphModel d_refinedAlternativesGraph;

    private final StudiesMeasuringValueModel d_studiesMeasuringValueModel;
    protected final Map<Study, Map<TreatmentDefinition, ModifiableHolder<Arm>>> d_selectedArms;
    protected final SelectableStudyCharTableModel d_selectableStudyListPm;
    private final ObservableList<Study> d_studiesEndpointIndication;
    private final Map<Drug, ValueHolder<TreatmentCategorization>> d_selectedCategorizations = new HashMap<Drug, ValueHolder<TreatmentCategorization>>();
    private final ObservableList<Study> d_selectableStudies = new ArrayListModel<Study>();

    private boolean d_rebuildRawNeeded = true;
    private boolean d_armSelectionRebuildNeeded = true;

    /**
     * Create a filtered list of studies that includes only those studies that
     * compare at least two of the definitions on the given variable.
     */
    public static List<Study> filterStudiesComparing(final Variable var, final Collection<Study> studies,
            final Collection<TreatmentDefinition> defs) {
        return (List<Study>) CollectionUtils.select(studies, new Predicate<Study>() {
            public boolean evaluate(Study s) {
                int count = 0;
                for (TreatmentDefinition def : defs) {
                    if (!s.getMeasuredArms(var, def).isEmpty()) {
                        ++count;
                    }
                }
                return count > 1;
            }
        });
    }

    /**
     * Create a filtered list of definitions that includes only those definitions that
     * are measured on the given variable in at least one study.
     */
    public static List<TreatmentDefinition> filterDefinitionsMeasured(final Variable var,
            Collection<TreatmentDefinition> defs, final Collection<Study> studies) {
        return (List<TreatmentDefinition>) CollectionUtils.select(defs, new Predicate<TreatmentDefinition>() {
            public boolean evaluate(TreatmentDefinition def) {
                for (Study s : studies) {
                    if (!s.getMeasuredArms(var, def).isEmpty()) {
                        return true;
                    }
                }
                return false;
            }
        });
    }

    public NetworkMetaAnalysisWizardPM(Domain d, PresentationModelFactory pmf) {
        super(d, d.getMetaAnalyses());
        d_pmf = pmf;

        d_outcomeHolder = new ModifiableHolder<OutcomeMeasure>();

        d_indicationHolder.addPropertyChangeListener(new ClearValueModelsOnPropertyChangeListener(d_outcomeHolder));
        updateOutcomes();
        d_indicationHolder.addValueChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                updateOutcomes();
            }
        });

        d_studiesEndpointIndication = createStudiesIndicationOutcome();
        d_studiesEndpointIndication.addListDataListener(new IndifferentListDataListener() {
            protected void update() {
                d_rebuildRawNeeded = true;
            }
        });

        d_rawTreatmentDefinitions = new ArrayListModel<TreatmentDefinition>();
        d_rawAlternativesGraph = buildRawAlternativesGraph();

        d_refinedTreatmentDefinitions = new ArrayListModel<TreatmentDefinition>();
        d_refinedAlternativesGraph = buildRefinedAlternativesGraph();

        getSelectedRefinedTreatmentDefinitions().addListDataListener(new IndifferentListDataListener() {
            protected void update() {
                d_armSelectionRebuildNeeded = true;
            }
        });

        d_studiesMeasuringValueModel = new StudiesMeasuringValueModel();

        d_selectedArms = new HashMap<Study, Map<TreatmentDefinition, ModifiableHolder<Arm>>>();

        d_selectableStudyListPm = createSelectableStudyListPm();
        d_overviewGraph = new TreatmentDefinitionsGraphModel(getSelectedStudies(),
                getSelectedRefinedTreatmentDefinitions(), getOutcomeMeasureModel());
        d_selectedStudyGraphConnectedModel = new StudySelectionCompleteListener();
    }

    public ObservableList<TreatmentDefinition> getSelectedRawTreatmentDefinitions() {
        return d_rawAlternativesGraph.getSelectedDefinitions();
    }

    public ObservableList<TreatmentDefinition> getSelectedRefinedTreatmentDefinitions() {
        return d_refinedAlternativesGraph.getSelectedDefinitions();
    }

    public TreatmentDefinitionsGraphModel getOverviewGraph() {
        return d_overviewGraph;
    }

    protected SelectableTreatmentDefinitionsGraphModel buildRawAlternativesGraph() {
        return new SelectableTreatmentDefinitionsGraphModel(getStudiesEndpointAndIndication(),
                d_rawTreatmentDefinitions, d_outcomeHolder, 1, -1);
    }

    protected SelectableTreatmentDefinitionsGraphModel buildRefinedAlternativesGraph() {
        return new SelectableTreatmentDefinitionsGraphModel(getStudiesEndpointAndIndication(),
                d_refinedTreatmentDefinitions, d_outcomeHolder, 2, -1);
    }

    public ValueModel getRawSelectionCompleteModel() {
        return getRawAlternativesGraph().getSelectionCompleteModel();
    }

    public ValueModel getRefinedSelectionCompleteModel() {
        return getRefinedAlternativesGraph().getSelectionCompleteModel();
    }

    public ValueHolder<Boolean> getOverviewGraphConnectedModel() {
        return d_selectedStudyGraphConnectedModel;
    }

    @SuppressWarnings("serial")
    public class StudySelectionCompleteListener extends AbstractValueModel implements ValueHolder<Boolean> {
        private boolean d_value;

        public StudySelectionCompleteListener() {
            update();
            getOverviewGraph().addGraphListener(new GraphListener<Vertex, Edge>() {

                public void vertexRemoved(GraphVertexChangeEvent<Vertex> e) {
                    update();
                }

                public void vertexAdded(GraphVertexChangeEvent<Vertex> e) {
                    update();
                }

                public void edgeRemoved(GraphEdgeChangeEvent<Vertex, Edge> e) {
                    update();
                }

                public void edgeAdded(GraphEdgeChangeEvent<Vertex, Edge> e) {
                    update();
                }
            });
        }

        private void update() {
            Boolean oldValue = d_value;
            Boolean newValue = selectedStudiesConnected();
            if (oldValue != newValue) {
                d_value = newValue;
                fireValueChange(oldValue, newValue);
            }
        }

        public Boolean getValue() {
            return d_value;
        }

        public void setValue(Object newValue) {
            throw new RuntimeException();
        }
    }

    private boolean selectedStudiesConnected() {
        ConnectivityInspector<Vertex, Edge> inspectorGadget = new ConnectivityInspector<Vertex, Edge>(
                getOverviewGraph());
        return inspectorGadget.isGraphConnected();
    }

    @Override
    public MetaAnalysis createAnalysis(String name) {
        Indication indication = getIndicationModel().getValue();
        OutcomeMeasure om = getOutcomeMeasureModel().getValue();
        List<Study> studies = getSelectedStudies();
        List<TreatmentDefinition> definitions = getSelectedRefinedTreatmentDefinitions();
        Map<Study, Map<TreatmentDefinition, Arm>> armMap = getArmMap();

        return new NetworkMetaAnalysis(name, indication, om, studies, definitions, armMap);
    }

    private Map<Study, Map<TreatmentDefinition, Arm>> getArmMap() {
        Map<Study, Map<TreatmentDefinition, Arm>> map = new HashMap<Study, Map<TreatmentDefinition, Arm>>();
        for (Study s : d_selectedArms.keySet()) {
            map.put(s, new HashMap<TreatmentDefinition, Arm>());
            for (TreatmentDefinition d : d_selectedArms.get(s).keySet()) {
                map.get(s).put(d, d_selectedArms.get(s).get(d).getValue());
            }
        }
        return map;
    }

    public ObservableList<Study> getSelectedStudies() {
        return getSelectableStudyListPM().getSelectedStudiesModel();
    }

    public void rebuildOverviewGraph() {
        d_overviewGraph.rebuildGraph();
    }

    public void populateSelectableStudies() {
        if (d_armSelectionRebuildNeeded) {
            d_selectableStudies.clear();
            d_selectableStudies.addAll(filterStudiesComparing(getOutcomeMeasureModel().getValue(),
                    getStudiesEndpointAndIndication(), getRefinedAlternativesGraph().getSelectedDefinitions()));
        }
    }

    private SelectableStudyCharTableModel createSelectableStudyListPm() {
        SelectableStudyCharTableModel studyList = new SelectableStudyCharTableModel(
                new StudyListPresentation(d_selectableStudies), d_pmf);
        studyList.getSelectedStudiesModel().addListDataListener(new IndifferentListDataListener() {
            protected void update() {
                d_armSelectionRebuildNeeded = true;
            }
        });
        return studyList;
    }

    private void updateOutcomes() {
        SortedSet<OutcomeMeasure> outcomeSet = new TreeSet<OutcomeMeasure>();
        if (d_indicationHolder.getValue() != null) {
            for (Study s : d_domain.getStudies(d_indicationHolder.getValue())) {
                outcomeSet.addAll(Study.extractVariables(s.getEndpoints()));
                outcomeSet.addAll(Study.extractVariables(s.getAdverseEvents()));
            }
        }
        d_outcomes.clear();
        d_outcomes.addAll(outcomeSet);
    }

    private ObservableList<Study> createStudiesIndicationOutcome() {
        final FilteredObservableList<Study> studies = new FilteredObservableList<Study>(d_domain.getStudies(),
                getIndicationOutcomeFilter());
        PropertyChangeListener listener = new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                studies.setFilter(getIndicationOutcomeFilter());
            }
        };
        d_indicationHolder.addPropertyChangeListener(listener);
        d_outcomeHolder.addPropertyChangeListener(listener);
        return studies;
    }

    private Predicate<Study> getIndicationOutcomeFilter() {
        if (d_indicationHolder.getValue() == null || d_outcomeHolder.getValue() == null) {
            return new FalseFilter();
        } else {
            return new IndicationOutcomeFilter();
        }
    }

    /**
     * Return all studies that measure the selected endpoint on the selected indication for at least two drugs.
     * @return List of studies
     */
    protected ObservableList<Study> getStudiesEndpointAndIndication() {
        return d_studiesEndpointIndication;
    }

    public ValueModel getStudiesMeasuringLabelModel() {
        return d_studiesMeasuringValueModel;
    }

    public ObservableList<OutcomeMeasure> getAvailableOutcomeMeasures() {
        return d_outcomes;
    }

    public ValueHolder<OutcomeMeasure> getOutcomeMeasureModel() {
        return d_outcomeHolder;
    }

    public ObservableList<TreatmentDefinition> getAvailableRawTreatmentDefinitions() {
        return d_rawTreatmentDefinitions;
    }

    public ObservableList<TreatmentDefinition> getAvailableRefinedTreatmentDefinitions() {
        return d_refinedTreatmentDefinitions;
    }

    public boolean rebuildRefinedAlternativesGraph() {
        SortedSet<TreatmentDefinition> definitions = new TreeSet<TreatmentDefinition>(
                permuteTreatmentDefinitions());
        OutcomeMeasure om = getOutcomeMeasureModel().getValue();
        List<Study> studies = filterStudiesComparing(om, getStudiesEndpointAndIndication(), definitions);
        List<TreatmentDefinition> defs = filterDefinitionsMeasured(om, definitions, studies);

        if (d_refinedTreatmentDefinitions.equals(defs)) {
            return false;
        }

        d_refinedTreatmentDefinitions.clear();
        d_refinedTreatmentDefinitions.addAll(defs);

        d_refinedAlternativesGraph.rebuildGraph();
        return true;
    }

    public boolean rebuildRawAlternativesGraph() {
        if (!d_rebuildRawNeeded) {
            return false;
        }
        // Determine set of treatment definitions
        SortedSet<TreatmentDefinition> definitions = new TreeSet<TreatmentDefinition>();
        if (d_indicationHolder.getValue() != null && d_outcomeHolder.getValue() != null) {
            for (Study s : getStudiesEndpointAndIndication()) {
                definitions.addAll(s.getMeasuredTreatmentDefinitions(d_outcomeHolder.getValue()));
            }
        }
        d_rawTreatmentDefinitions.clear();
        d_rawTreatmentDefinitions.addAll(definitions);

        // Rebuild the graph
        d_rawAlternativesGraph.rebuildGraph();

        d_rebuildRawNeeded = false;
        return true;
    }

    public SelectableTreatmentDefinitionsGraphModel getRefinedAlternativesGraph() {
        return d_refinedAlternativesGraph;
    }

    public SelectableTreatmentDefinitionsGraphModel getRawAlternativesGraph() {
        return d_rawAlternativesGraph;
    }

    public void rebuildArmSelection() {
        if (!d_armSelectionRebuildNeeded) {
            return;
        }
        d_selectedArms.clear();

        for (Study s : getSelectableStudyListPM().getSelectedStudiesModel()) {
            d_selectedArms.put(s, new HashMap<TreatmentDefinition, ModifiableHolder<Arm>>());
            for (TreatmentDefinition d : getSelectedRefinedTreatmentDefinitions()) {
                if (!s.getMeasuredArms(d_outcomeHolder.getValue(), d).isEmpty()) {
                    d_selectedArms.get(s).put(d, new ModifiableHolder<Arm>(getDefaultArm(s, d)));
                }
            }
        }

        d_armSelectionRebuildNeeded = false;
    }

    private Arm getDefaultArm(Study s, TreatmentDefinition d) {
        return getArmsPerStudyPerDefinition(s, d).get(0);
    }

    public ObservableList<Arm> getArmsPerStudyPerDefinition(Study study, TreatmentDefinition definition) {
        return study.getMeasuredArms(d_outcomeHolder.getValue(), definition);
    }

    public ModifiableHolder<Arm> getSelectedArmModel(Study study, TreatmentDefinition definition) {
        return d_selectedArms.get(study).get(definition);
    }

    public SelectableStudyCharTableModel getSelectableStudyListPM() {
        return d_selectableStudyListPm;
    }

    /**
     * Get the set of drugs that occur in the selected raw treatment definitions.
     * @see {@link #getSelectedRawTreatmentDefinitions()}
     */
    public SortedSet<Drug> getSelectedDrugs() {
        SortedSet<Drug> drugs = new TreeSet<Drug>();
        for (TreatmentDefinition definition : getSelectedRawTreatmentDefinitions()) {
            for (Category category : definition.getContents()) {
                drugs.add(category.getDrug());
            }
        }
        return drugs;
    }

    protected Set<TreatmentDefinition> permuteTreatmentDefinitions() {
        Set<TreatmentDefinition> set = new HashSet<TreatmentDefinition>();
        for (TreatmentDefinition trivial : getRawAlternativesGraph().getSelectedDefinitions()) {
            // Find the categorizations relevant to the current combination of drugs
            List<TreatmentCategorization> catzs = new ArrayList<TreatmentCategorization>();
            List<Integer> nCat = new ArrayList<Integer>();
            for (Category category : trivial.getContents()) {
                TreatmentCategorization catz = (TreatmentCategorization) getCategorizationModel(category.getDrug())
                        .getValue();
                catzs.add(catz);
                nCat.add(catz.getCategories().size());
            }

            // Generate all permutations of the categories in the relevant categorizations
            int[] c = ArrayUtils.toPrimitive(nCat.toArray(new Integer[] {}));
            int[] x = new int[c.length]; // initialized to { 0, ... }
            do {
                Set<Category> categories = new HashSet<Category>();
                for (int i = 0; i < x.length; ++i) {
                    categories.add(catzs.get(i).getCategories().get(x[i]));
                }
                set.add(new TreatmentDefinition(categories));
            } while (CollectionUtil.nextLexicographicElement(x, c));
        }
        return set;
    }

    public List<TreatmentCategorization> getAvailableCategorizations(Drug drug) {
        return AffixedObservableList.createPrefixed(d_domain.getCategorizations(drug),
                TreatmentCategorization.createTrivial(drug));
    }

    public ValueModel getCategorizationModel(Drug drug) {
        if (d_selectedCategorizations.get(drug) == null) {
            d_selectedCategorizations.put(drug,
                    new ModifiableHolder<TreatmentCategorization>(TreatmentCategorization.createTrivial(drug)));
        }
        return d_selectedCategorizations.get(drug);
    }

    final class FalseFilter implements Predicate<Study> {
        public boolean evaluate(Study s) {
            return false;
        }
    }

    final class IndicationOutcomeFilter implements Predicate<Study> {
        public boolean evaluate(Study s) {
            return s.getIndication().equals(d_indicationHolder.getValue())
                    && s.getOutcomeMeasures().contains(d_outcomeHolder.getValue())
                    && s.getMeasuredTreatmentDefinitions(d_outcomeHolder.getValue()).size() >= 2;
        }
    }

    @SuppressWarnings("serial")
    public class StudiesMeasuringValueModel extends AbstractValueModel implements PropertyChangeListener {

        public StudiesMeasuringValueModel() {
            d_outcomeHolder.addValueChangeListener(this);
        }

        public Object getValue() {
            return constructString();
        }

        private Object constructString() {
            String indVal = d_indicationHolder.getValue() != null ? d_indicationHolder.getValue().toString() : "";
            String endpVal = d_outcomeHolder.getValue() != null ? d_outcomeHolder.getValue().toString() : "";
            return "Studies measuring " + indVal + " on " + endpVal;
        }

        public void setValue(Object newValue) {
            throw new RuntimeException("value set not allowed");
        }

        public void propertyChange(PropertyChangeEvent arg0) {
            fireValueChange(null, constructString());
        }
    }

    /** 
     * Internal utility for tests. This will reset all the graph states and usage is prohibited in the GUI.
     */
    protected void rebuildAllGraphs() {
        rebuildRawAlternativesGraph();
        permuteTreatmentDefinitions();
        rebuildRefinedAlternativesGraph();
        rebuildOverviewGraph();
    }

    public String getRawDescription() {
        return String.format(getDescriptionTemplate(), "drugs", "one drug");
    }

    public String getRefinedDescription() {
        return String.format(getDescriptionTemplate(), "treatment definitions", "two definitions");
    }

    protected String getDescriptionTemplate() {
        return TEMPLATE_DEFINITIONS_DESCRIPTION;
    }
}