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

Java tutorial

Introduction

Here is the source code for org.drugis.addis.presentation.wizard.TreatmentCategorizationWizardPresentation.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 static org.apache.commons.collections15.CollectionUtils.find;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.HashMap;

import org.apache.commons.collections15.Predicate;
import org.drugis.addis.entities.AbstractDose;
import org.drugis.addis.entities.Domain;
import org.drugis.addis.entities.DoseUnit;
import org.drugis.addis.entities.Drug;
import org.drugis.addis.entities.FixedDose;
import org.drugis.addis.entities.FlexibleDose;
import org.drugis.addis.entities.UnknownDose;
import org.drugis.addis.entities.treatment.Category;
import org.drugis.addis.entities.treatment.ChoiceNode;
import org.drugis.addis.entities.treatment.DecisionTree;
import org.drugis.addis.entities.treatment.DecisionTreeEdge;
import org.drugis.addis.entities.treatment.DecisionTreeNode;
import org.drugis.addis.entities.treatment.DoseQuantityChoiceNode;
import org.drugis.addis.entities.treatment.LeafNode;
import org.drugis.addis.entities.treatment.RangeEdge;
import org.drugis.addis.entities.treatment.TreatmentCategorization;
import org.drugis.addis.presentation.DecisionTreeChildModel;
import org.drugis.addis.presentation.DecisionTreeOutEdgesModel;
import org.drugis.addis.presentation.DoseUnitPresentation;
import org.drugis.addis.presentation.ModifiableHolder;
import org.drugis.addis.presentation.ValueHolder;
import org.drugis.addis.presentation.ValueModelWrapper;
import org.drugis.common.EqualsUtil;
import org.drugis.common.beans.AffixedObservableList;
import org.drugis.common.beans.ContentAwareListModel;
import org.drugis.common.beans.FilteredObservableList;
import org.drugis.common.beans.TransformOnceObservableList;
import org.drugis.common.beans.TransformedObservableList.Transform;
import org.drugis.common.beans.ValueEqualsModel;
import org.drugis.common.validation.BooleanNotModel;
import org.drugis.common.validation.PropertyUniqueModel;

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

import edu.uci.ics.jung.graph.event.GraphEvent;
import edu.uci.ics.jung.graph.event.GraphEventListener;
import edu.uci.ics.jung.graph.util.Pair;

@SuppressWarnings("serial")
public class TreatmentCategorizationWizardPresentation extends PresentationModel<TreatmentCategorization> {
    private final class CategoryUsedModel extends AbstractValueModel {
        private final Category d_category;

        private boolean d_value;

        private CategoryUsedModel(Category category) {
            d_category = category;

            getBean().getDecisionTree().getObservableGraph()
                    .addGraphEventListener(new GraphEventListener<DecisionTreeNode, DecisionTreeEdge>() {
                        public void handleGraphEvent(GraphEvent<DecisionTreeNode, DecisionTreeEdge> evt) {
                            update();
                        }
                    });
            d_knownDoseChoice.addValueChangeListener(new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    update();
                }
            });

            d_value = calculate();
        }

        protected void update() {
            boolean oldValue = d_value;
            d_value = calculate();
            fireValueChange(oldValue, d_value);
        }

        private boolean calculate() {
            DecisionTreeNode knownDose = d_knownDoseChoice.getValue();
            if (knownDose instanceof LeafNode) {
                LeafNode leafNode = (LeafNode) knownDose;
                return EqualsUtil.equal(leafNode.getCategory(), d_category)
                        || isLeafNodeReachable(getEdgeTarget(findTypeEdge(UnknownDose.class)), d_category);
            }
            if (knownDose.equals(CategorySpecifiers.DO_NOT_CONSIDER)) {
                return isLeafNodeReachable(getEdgeTarget(findTypeEdge(FixedDose.class)), d_category)
                        || isLeafNodeReachable(getEdgeTarget(findTypeEdge(UnknownDose.class)), d_category);
            }
            return isLeafNodeReachable(getBean().getDecisionTree().getRoot(), d_category); // CONSIDER
        }

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

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

    public static enum CategorySpecifiers implements DecisionTreeNode {
        CONSIDER("Consider dose type"), DO_NOT_CONSIDER("Do not consider dose type");

        private final String d_title;

        private CategorySpecifiers(final String title) {
            d_title = title;
        }

        @Override
        public String getName() {
            return d_title;
        }

        @Override
        public boolean equivalent(DecisionTreeNode o) {
            if (!(o instanceof CategorySpecifiers)) {
                return false;
            } else {
                return equals(o);
            }
        }

        @Override
        public String toString() {
            return getName();
        }
    }

    private final Domain d_domain;
    private final ObservableList<Category> d_contentAwareCategories;

    private final ValueHolder<DecisionTreeNode> d_knownDoseChoice;
    private final ObservableList<DecisionTreeNode> d_knownDoseOptions;

    private final ValueModel d_considerDoseType;
    private final ValueModel d_ignoreDoseType;

    private final DoseQuantityChoiceNode d_fixedRangeNode;
    private final DoseQuantityChoiceNode d_flexibleLowerNode;
    private final DoseQuantityChoiceNode d_flexibleUpperNode;

    private final ValueModel d_considerFixed;
    private final ValueModel d_considerFlexibleLower;
    private final ValueModel d_considerFlexibleUpper;

    private final HashMap<DecisionTreeEdge, ValueModel> d_choiceForEdge = new HashMap<DecisionTreeEdge, ValueModel>();
    private final HashMap<DecisionTreeEdge, ObservableList<DecisionTreeNode>> d_optionsForEdge = new HashMap<DecisionTreeEdge, ObservableList<DecisionTreeNode>>();
    private final HashMap<Category, ValueHolder<Boolean>> d_categoryUsed = new HashMap<Category, ValueHolder<Boolean>>();
    private ValueModel d_nameAvailableModel;

    public TreatmentCategorizationWizardPresentation(final TreatmentCategorization bean, final Domain domain) {
        super(bean);
        d_domain = domain;
        d_contentAwareCategories = new ContentAwareListModel<Category>(bean.getCategories());
        d_nameAvailableModel = createNameAvailableModel();

        // Magic nodes
        d_fixedRangeNode = new DoseQuantityChoiceNode(FixedDose.class, FixedDose.PROPERTY_QUANTITY,
                getBean().getDoseUnit());
        d_flexibleLowerNode = createMinDoseNode();
        d_flexibleUpperNode = createMaxDoseNode();

        d_knownDoseChoice = new ModifiableHolder<DecisionTreeNode>(new LeafNode());
        d_knownDoseOptions = createOptions(CategorySpecifiers.CONSIDER, CategorySpecifiers.DO_NOT_CONSIDER,
                d_knownDoseChoice.getValue());

        d_considerDoseType = new ValueEqualsModel(d_knownDoseChoice, CategorySpecifiers.CONSIDER);
        d_ignoreDoseType = new ValueEqualsModel(d_knownDoseChoice, CategorySpecifiers.DO_NOT_CONSIDER);

        final DecisionTreeEdge unknownDoseEdge = findTypeEdge(UnknownDose.class);
        d_choiceForEdge.put(unknownDoseEdge, createModelForEdge(unknownDoseEdge));
        d_optionsForEdge.put(unknownDoseEdge, createOptions(getEdgeTarget(unknownDoseEdge)));

        final DecisionTreeEdge fixedDoseEdge = findTypeEdge(FixedDose.class);
        d_choiceForEdge.put(fixedDoseEdge, createModelForEdge(fixedDoseEdge));
        d_optionsForEdge.put(fixedDoseEdge, createOptions(d_fixedRangeNode, getEdgeTarget(fixedDoseEdge)));

        final DecisionTreeEdge flexibleDoseEdge = findTypeEdge(FlexibleDose.class);
        d_choiceForEdge.put(flexibleDoseEdge, createModelForEdge(flexibleDoseEdge));
        d_optionsForEdge.put(flexibleDoseEdge,
                createOptions(d_flexibleLowerNode, d_flexibleUpperNode, getEdgeTarget(flexibleDoseEdge)));

        d_considerFixed = new ValueEqualsModel(getModelForFixedDose(), d_fixedRangeNode);
        d_considerFlexibleLower = new ValueEqualsModel(getModelForFlexibleDose(), d_flexibleLowerNode);
        d_considerFlexibleUpper = new ValueEqualsModel(getModelForFlexibleDose(), d_flexibleUpperNode);
    }

    public boolean isLeafNodeReachable(DecisionTreeNode node, Category category) {
        if (node == null) {
            return false;
        }
        if (node instanceof LeafNode) {
            LeafNode leafNode = (LeafNode) node;
            return EqualsUtil.equal(leafNode.getCategory(), category);
        }

        if (node instanceof ChoiceNode) {
            for (DecisionTreeEdge edge : getBean().getDecisionTree().getOutEdges(node)) {
                if (isLeafNodeReachable(getEdgeTarget(edge), category)) {
                    return true;
                }
            }
        }
        return false;
    }

    private ValueHolder<Boolean> createCategoryNotUsedModel(final Category category) {
        return new ValueModelWrapper<Boolean>(new BooleanNotModel(new CategoryUsedModel(category)));
    }

    private DecisionTreeNode getEdgeTarget(final DecisionTreeEdge edge) {
        return getBean().getDecisionTree().getEdgeTarget(edge);
    }

    public ValueModel getDrug() {
        return getModel(TreatmentCategorization.PROPERTY_DRUG);
    }

    public ValueModel getName() {
        return getModel(TreatmentCategorization.PROPERTY_NAME);
    }

    public ObservableList<Category> getCategories() {
        return getBean().getCategories();
    }

    public DoseUnit getDoseUnit() {
        return getBean().getDoseUnit();
    }

    public DoseUnitPresentation getDoseUnitPresentation() {
        return new DoseUnitPresentation(getDoseUnit());
    }

    /**
     * Add the DosedDrugTreatment to the domain. Throws an exception if the treatment is already in the domain.
     * Note that domain can be null in which case a null-pointer exception will occur.
     * @return The DosedDrugTreatment that was added.
     */
    public TreatmentCategorization commit() {
        if (d_domain.getTreatmentCategorizations().contains(getBean())) {
            throw new IllegalStateException("Treatment already exists in domain");
        }

        d_domain.getTreatmentCategorizations().add(getBean());
        return getBean();
    }

    public ObservableList<DecisionTreeEdge> getOutEdges(final DecisionTreeNode node) {
        return new DecisionTreeOutEdgesModel(getBean().getDecisionTree(), node);
    }

    public String getCategory(final AbstractDose dose) {
        return getBean().getCategory(dose).toString();
    }

    /**
     * ValueModel that holds the decision (DecisionTreeNode) for the given edge.
     */
    private ValueModel createModelForEdge(final DecisionTreeEdge edge) {
        final DecisionTree tree = getBean().getDecisionTree();
        final DecisionTreeChildModel model = new DecisionTreeChildModel(tree, edge);
        return model;
    }

    public DecisionTreeEdge findTypeEdge(final Class<?> type) {
        final DecisionTree tree = getBean().getDecisionTree();
        return getBean().getDecisionTree().findMatchingEdge(tree.getRoot(), type);
    }

    public ValueModel getModelForEdge(final DecisionTreeEdge edge) {
        if (d_choiceForEdge.get(edge) == null) {
            d_choiceForEdge.put(edge, createModelForEdge(edge));
        }
        return d_choiceForEdge.get(edge);
    }

    /**
     * Selection holder for action on unknown doses.
     */
    public ValueModel getModelForUnknownDose() {
        return getModelForEdge(findTypeEdge(UnknownDose.class));
    }

    /**
     * Selection holder for action on "known" doses (fixed or flexible).
     */
    public ValueModel getModelForKnownDose() {
        return d_knownDoseChoice;
    }

    /**
     * Selection holder for action on fixed doses.
     */
    public ValueModel getModelForFixedDose() {
        return getModelForEdge(findTypeEdge(FixedDose.class));
    }

    /**
     * Selection holder for action on flexible doses.
     */
    public ValueModel getModelForFlexibleDose() {
        return getModelForEdge(findTypeEdge(FlexibleDose.class));
    }

    /**
     * ValueModel (Boolean) that indicates whether fixed and flexible doses should be treated separately.
     */
    public ValueModel getConsiderDoseType() {
        return d_considerDoseType;
    }

    /**
     * ValueModel (Boolean) that indicates whether fixed and flexible doses should be treated identically.
     */
    public ValueModel getIgnoreDoseType() {
        return d_ignoreDoseType;
    }

    /**
     * ValueModel (Boolean) that indicates whether quantity should be considered for fixed doses.
     */
    public ValueModel getConsiderFixed() {
        return d_considerFixed;
    }

    /**
     * ValueModel (Boolean) that indicates whether the MIN_DOSE should be considered first for flexible doses.
     */
    public ValueModel getConsiderFlexibleLowerFirst() {
        return d_considerFlexibleLower;
    }

    /**
     * ValueModel (Boolean) that indicates whether the MAX_DOSE should be considered first for flexible doses.
     */
    public ValueModel getConsiderFlexibleUpperFirst() {
        return d_considerFlexibleUpper;
    }

    public ObservableList<DecisionTreeEdge> getFlexibleLowerRanges() {
        return new DecisionTreeOutEdgesModel(getBean().getDecisionTree(), d_flexibleLowerNode);
    }

    public ObservableList<DecisionTreeEdge> getFlexibleUpperRanges() {
        return new DecisionTreeOutEdgesModel(getBean().getDecisionTree(), d_flexibleUpperNode);
    }

    public ChoiceNode getFlexibleLowerRangeNode() {
        return d_flexibleLowerNode;
    }

    public ChoiceNode getFlexibleUpperRangeNode() {
        return d_flexibleUpperNode;
    }

    public ChoiceNode getFixedRangeNode() {
        return d_fixedRangeNode;
    }

    /**
     * A utility for checking if a category is used in the tree.
     * @param category
     * @return a ValueModel which is true when the category is used in the tree, false otherwise
     */
    public ValueModel getCategoryUsed(Category category) {
        if (d_categoryUsed.get(category) == null) {
            d_categoryUsed.put(category, createCategoryNotUsedModel(category));
        }
        return d_categoryUsed.get(category);
    }

    private ObservableList<DecisionTreeNode> createOptions(final DecisionTreeNode... extraOptions) {
        final TransformOnceObservableList<Category, DecisionTreeNode> transformedCategories = new TransformOnceObservableList<Category, DecisionTreeNode>(
                d_contentAwareCategories, new Transform<Category, DecisionTreeNode>() {
                    public DecisionTreeNode transform(final Category a) {
                        return new LeafNode(a);
                    }
                });
        return AffixedObservableList.createSuffixed(transformedCategories, extraOptions);
    }

    public ObservableList<DecisionTreeNode> getOptionsForEdge(final DecisionTreeEdge edge) {
        if (d_optionsForEdge.get(edge) == null) {
            d_optionsForEdge.put(edge, createOptions(new LeafNode()));
        }
        return d_optionsForEdge.get(edge);
    }

    public ObservableList<DecisionTreeNode> getOptionsForKnownDose() {
        return d_knownDoseOptions;
    }

    public ObservableList<DecisionTreeNode> getOptionsForUnknownDose() {
        return getOptionsForEdge(findTypeEdge(UnknownDose.class));
    }

    public ObservableList<DecisionTreeNode> getOptionsForFixedDose() {
        return getOptionsForEdge(findTypeEdge(FixedDose.class));
    }

    public ObservableList<DecisionTreeNode> getOptionsForFlexibleDose() {
        return getOptionsForEdge(findTypeEdge(FlexibleDose.class));
    }

    /**
     * Transform the tree according to the option chosen for "known dose".
     */
    public void transformTree() {
        Object knownDose = getModelForKnownDose().getValue();
        if (knownDose instanceof LeafNode) {
            transformLeaf();
        } else if (knownDose.equals(CategorySpecifiers.DO_NOT_CONSIDER)) {
            assert (d_fixedRangeNode == getModelForFixedDose().getValue());
            transformSubtree();
        }
    }

    private void transformSubtree() {
        DecisionTree tree = getBean().getDecisionTree();
        getModelForFlexibleDose().setValue(d_flexibleLowerNode);

        for (DecisionTreeEdge edge : getOutEdges(d_fixedRangeNode)) {
            DoseQuantityChoiceNode node = new DoseQuantityChoiceNode(FlexibleDose.class,
                    FlexibleDose.PROPERTY_MAX_DOSE, getDoseUnit());
            RangeEdge rangeEdge = (RangeEdge) edge;
            tree.addChild(RangeEdge.copy(rangeEdge), d_flexibleLowerNode, node);

            LeafNode leafChild = new LeafNode(((LeafNode) tree.getEdgeTarget(edge)).getCategory());

            if (leafChild.getCategory() == null) {
                RangeEdge upperEdge = new RangeEdge(rangeEdge.getLowerBound(), rangeEdge.isLowerBoundOpen(),
                        Double.POSITIVE_INFINITY, true);
                tree.addChild(upperEdge, node, new LeafNode());
            } else {
                tree.addChild(RangeEdge.copy(rangeEdge), node, leafChild);
                if (!Double.isInfinite(rangeEdge.getUpperBound())) {
                    RangeEdge upperEdge = new RangeEdge(rangeEdge.getUpperBound(), !rangeEdge.isUpperBoundOpen(),
                            Double.POSITIVE_INFINITY, true);
                    tree.addChild(upperEdge, node, new LeafNode());
                }
            }
        }
    }

    private void transformLeaf() {
        final Category category = ((LeafNode) getModelForKnownDose().getValue()).getCategory();
        getModelForFlexibleDose().setValue(findLeafNode(getOptionsForFlexibleDose(), category));
        getModelForFixedDose().setValue(findLeafNode(getOptionsForFixedDose(), category));
    }

    /**
     * {@link TreatmentCategorization#splitRange(RangeEdge, double, boolean)}
     */
    public Pair<RangeEdge> splitRange(final RangeEdge range, final double value, final boolean lowerRangeOpen) {
        ObservableList<DecisionTreeNode> options1 = getOptionsForEdge(range);
        Pair<RangeEdge> ranges = getBean().splitRange(range, value, lowerRangeOpen);

        // Ensure options list is preserved for first range
        d_optionsForEdge.remove(range);
        d_optionsForEdge.put(ranges.getFirst(), options1);

        // Ensure exclude-node for second range is in the options list
        DecisionTree tree = getBean().getDecisionTree();
        ObservableList<DecisionTreeNode> options2 = createOptionsForChildrenOfNode(
                (ChoiceNode) tree.getEdgeSource(ranges.getSecond()));
        d_optionsForEdge.put(ranges.getSecond(), options2);
        getModelForEdge(ranges.getSecond()).setValue(findLeafNode(options2, null));

        return ranges;
    }

    /**
     * In the haystack, find a leaf node with the needle as its category.
     */
    public static LeafNode findLeafNode(final Collection<DecisionTreeNode> haystack, final Category needle) {
        return (LeafNode) find(haystack, new Predicate<DecisionTreeNode>() {
            public boolean evaluate(DecisionTreeNode object) {
                if (object instanceof LeafNode) {
                    LeafNode node = (LeafNode) object;
                    return EqualsUtil.equal(node.getCategory(), needle);
                }
                return false;
            }
        });
    }

    public RangeEdge addDefaultRangeEdge(ChoiceNode node) {
        RangeEdge rangeEdge = createRangeEdge(node);
        ObservableList<DecisionTreeNode> options = createOptionsForChildrenOfNode(node);
        d_optionsForEdge.put(rangeEdge, options);
        getBean().getDecisionTree().addChild(rangeEdge, node, findLeafNode(options, null));
        return rangeEdge;
    }

    private RangeEdge createRangeEdge(ChoiceNode node) {
        DecisionTree tree = getBean().getDecisionTree();
        String nodeProperty = node.getPropertyName();
        ChoiceNode parentNode = (ChoiceNode) tree.getParent(node);
        if (EqualsUtil.equal(nodeProperty, FlexibleDose.PROPERTY_MIN_DOSE)
                && EqualsUtil.equal(parentNode.getPropertyName(), FlexibleDose.PROPERTY_MAX_DOSE)) {
            RangeEdge range = (RangeEdge) tree.getParentEdge(node);
            return new RangeEdge(0.0, false, range.getUpperBound(), range.isUpperBoundOpen());

        } else if (EqualsUtil.equal(nodeProperty, FlexibleDose.PROPERTY_MAX_DOSE)
                && EqualsUtil.equal(parentNode.getPropertyName(), FlexibleDose.PROPERTY_MIN_DOSE)) {
            RangeEdge range = (RangeEdge) tree.getParentEdge(node);
            return new RangeEdge(range.getLowerBound(), range.isLowerBoundOpen(), Double.POSITIVE_INFINITY, true);

        } else {
            return RangeEdge.createDefault();
        }
    }

    private ObservableList<DecisionTreeNode> createOptionsForChildrenOfNode(ChoiceNode node) {
        DecisionTree tree = getBean().getDecisionTree();
        String nodeProperty = node.getPropertyName();
        String parentProperty = ((ChoiceNode) tree.getParent(node)).getPropertyName();
        if (nodeProperty.equals(FlexibleDose.PROPERTY_MIN_DOSE)
                && !parentProperty.equals(FlexibleDose.PROPERTY_MAX_DOSE)) {
            return createOptions(createMaxDoseNode(), new LeafNode());
        } else if (nodeProperty.equals(FlexibleDose.PROPERTY_MAX_DOSE)
                && !parentProperty.equals(FlexibleDose.PROPERTY_MIN_DOSE)) {
            return createOptions(createMinDoseNode(), new LeafNode());
        } else {
            return createOptions(new LeafNode());
        }
    }

    private DoseQuantityChoiceNode createMinDoseNode() {
        return new DoseQuantityChoiceNode(FlexibleDose.class, FlexibleDose.PROPERTY_MIN_DOSE,
                getBean().getDoseUnit());
    }

    private DoseQuantityChoiceNode createMaxDoseNode() {
        return new DoseQuantityChoiceNode(FlexibleDose.class, FlexibleDose.PROPERTY_MAX_DOSE,
                getBean().getDoseUnit());
    }

    public ValueModel getNameAvailableModel() {
        return d_nameAvailableModel;
    }

    private ValueModel createNameAvailableModel() {
        final FilteredObservableList<TreatmentCategorization> categorizations = new FilteredObservableList<TreatmentCategorization>(
                d_domain.getTreatmentCategorizations(), createDrugFilter((Drug) getDrug().getValue()));
        getDrug().addValueChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                categorizations.setFilter(createDrugFilter((Drug) getDrug().getValue()));
            }
        });
        return new PropertyUniqueModel<TreatmentCategorization>(categorizations, getBean(),
                TreatmentCategorization.PROPERTY_NAME);
    }

    private Predicate<TreatmentCategorization> createDrugFilter(final Drug drug) {
        return new Predicate<TreatmentCategorization>() {
            public boolean evaluate(TreatmentCategorization obj) {
                return EqualsUtil.equal(obj.getDrug(), drug);
            }
        };
    }
}