gov.nih.nci.caintegrator.common.QueryUtil.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.caintegrator.common.QueryUtil.java

Source

/**
 * Copyright 5AM Solutions Inc, ESAC, ScenPro & SAIC
 *
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/caintegrator/LICENSE.txt for details.
 */
package gov.nih.nci.caintegrator.common;

import gov.nih.nci.caintegrator.application.query.InvalidCriterionException;
import gov.nih.nci.caintegrator.application.study.AnnotationFieldDescriptor;
import gov.nih.nci.caintegrator.domain.annotation.AbstractAnnotationValue;
import gov.nih.nci.caintegrator.domain.annotation.DateAnnotationValue;
import gov.nih.nci.caintegrator.domain.annotation.NumericAnnotationValue;
import gov.nih.nci.caintegrator.domain.annotation.PermissibleValue;
import gov.nih.nci.caintegrator.domain.annotation.StringAnnotationValue;
import gov.nih.nci.caintegrator.domain.application.AbstractCriterion;
import gov.nih.nci.caintegrator.domain.application.BooleanOperatorEnum;
import gov.nih.nci.caintegrator.domain.application.CompoundCriterion;
import gov.nih.nci.caintegrator.domain.application.CopyNumberAlterationCriterion;
import gov.nih.nci.caintegrator.domain.application.DateComparisonCriterion;
import gov.nih.nci.caintegrator.domain.application.DateComparisonOperatorEnum;
import gov.nih.nci.caintegrator.domain.application.ExpressionLevelCriterion;
import gov.nih.nci.caintegrator.domain.application.FoldChangeCriterion;
import gov.nih.nci.caintegrator.domain.application.GeneNameCriterion;
import gov.nih.nci.caintegrator.domain.application.GenomicCriterionTypeEnum;
import gov.nih.nci.caintegrator.domain.application.NumericComparisonCriterion;
import gov.nih.nci.caintegrator.domain.application.NumericComparisonOperatorEnum;
import gov.nih.nci.caintegrator.domain.application.Query;
import gov.nih.nci.caintegrator.domain.application.QueryResult;
import gov.nih.nci.caintegrator.domain.application.ResultColumn;
import gov.nih.nci.caintegrator.domain.application.ResultRow;
import gov.nih.nci.caintegrator.domain.application.ResultTypeEnum;
import gov.nih.nci.caintegrator.domain.application.ResultValue;
import gov.nih.nci.caintegrator.domain.application.StringComparisonCriterion;
import gov.nih.nci.caintegrator.domain.application.StudySubscription;
import gov.nih.nci.caintegrator.domain.application.WildCardTypeEnum;
import gov.nih.nci.caintegrator.domain.genomic.ReporterTypeEnum;
import gov.nih.nci.caintegrator.domain.genomic.Sample;
import gov.nih.nci.caintegrator.domain.genomic.SampleAcquisition;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

/**
 * This is a static utility class used by different caIntegrator2 query objects.
 */
public final class QueryUtil {

    private QueryUtil() {
    }

    /**
     * Retrieves the matching row from the given set that matches the given row.
     * @param rows - set of rows.
     * @param row -  row to test
     * @param isMultiplePlatformQuery - if multiple platforms in query then we don't need to match sample acquisitions.
     * @return the matching rows
     */
    public static ResultRow getMatchingRow(Set<ResultRow> rows, ResultRow row, boolean isMultiplePlatformQuery) {
        ResultRow foundRow = null;
        for (ResultRow currentRow : rows) {
            if (checkSubjectAssignmentMatch(row, currentRow) && checkImageSeriesMatch(row, currentRow)
                    && checkSampleMatch(isMultiplePlatformQuery, row, currentRow)) {
                foundRow = currentRow;
                break;
            }
        }
        return foundRow;
    }

    private static boolean checkSampleMatch(boolean isMultiplePlatformQuery, ResultRow rowToTest,
            ResultRow curRow) {
        if (isMultiplePlatformQuery) {
            return true;
        }
        if (curRow.getSampleAcquisition() == null && rowToTest.getSampleAcquisition() == null) { // both null
            return true;
        }
        if (curRow.getSampleAcquisition() == null || rowToTest.getSampleAcquisition() == null) { // only one is null
            return false;
        }
        return curRow.getSampleAcquisition().equals(rowToTest.getSampleAcquisition());
    }

    private static boolean checkImageSeriesMatch(ResultRow rowToTest, ResultRow curRow) {
        if (curRow.getImageSeries() == null && rowToTest.getImageSeries() == null) { // both null
            return true;
        }
        if (curRow.getImageSeries() == null || rowToTest.getImageSeries() == null) { // only one is null
            return false;
        }
        return StringUtils.equalsIgnoreCase(curRow.getImageSeries().getIdentifier(),
                rowToTest.getImageSeries().getIdentifier());
    }

    private static boolean checkSubjectAssignmentMatch(ResultRow rowToTest, ResultRow curRow) {
        if (curRow.getSubjectAssignment() == null && rowToTest.getSubjectAssignment() == null) { // both null
            return true;
        }
        if (curRow.getSubjectAssignment() == null || rowToTest.getSubjectAssignment() == null) { // only one is null
            return false;
        }
        return StringUtils.equalsIgnoreCase(curRow.getSubjectAssignment().getIdentifier(),
                rowToTest.getSubjectAssignment().getIdentifier());
    }

    /**
     * This function checks to see if an AnnotationValue belongs to a PermissibleValue.
     * @param value object to check to see if it belongs to permissible value.
     * @param permissibleValue object that the value uses to validate against.
     * @return true or false value.
     */
    public static boolean annotationValueBelongToPermissibleValue(AbstractAnnotationValue value,
            PermissibleValue permissibleValue) {
        if (value instanceof StringAnnotationValue) {
            return handleStringValues(value, permissibleValue);
        } else if (value instanceof NumericAnnotationValue) {
            return handleNumericValues(value, permissibleValue);
        } else if (value instanceof DateAnnotationValue) {
            return handleDateValues(value, permissibleValue);
        }
        return false;
    }

    private static boolean handleStringValues(AbstractAnnotationValue value, PermissibleValue permissibleValue) {
        StringAnnotationValue stringValue = (StringAnnotationValue) value;
        if (stringValue.getStringValue() != null
                && stringValue.getStringValue().equalsIgnoreCase(permissibleValue.getValue())) {
            return true;
        }
        return false;
    }

    private static boolean handleNumericValues(AbstractAnnotationValue value, PermissibleValue permissibleValue) {
        if (!NumberUtils.isNumber(permissibleValue.getValue())) {
            throw new IllegalArgumentException("value is of type Numeric, but permissibleValue is not.");
        }
        NumericAnnotationValue numericValue = (NumericAnnotationValue) value;
        if (numericValue.getNumericValue() != null
                && numericValue.getNumericValue().equals(Double.valueOf(permissibleValue.getValue()))) {
            return true;
        }
        return false;
    }

    private static boolean handleDateValues(AbstractAnnotationValue value, PermissibleValue permissibleValue) {
        DateAnnotationValue dateValue = (DateAnnotationValue) value;
        if (dateValue.getDateValue() != null && permissibleValue.getValue() != null
                && permissibleValue.getValue().equals(DateUtil.toString(dateValue.getDateValue()))) {
            return true;
        }
        return false;
    }

    /**
     * Determines if a query has any genomic data associated.
     * @param query to check if it is genomic.
     * @return T/F value.
     */
    public static boolean isQueryGenomic(Query query) {
        return isQueryGeneExpression(query) || isQueryCopyNumber(query);
    }

    /**
     * Determines if a query is gene expression (gene expression results type or if it has gene expression criterion).
     * @param query to check to see if it is gene expression.
     * @return T/F if it is gene expression type or not.
     */
    public static boolean isQueryGeneExpression(Query query) {
        return (ResultTypeEnum.GENE_EXPRESSION.equals(query.getResultType())
                || QueryUtil.isCompoundCriterionGeneExpression(query.getCompoundCriterion())) ? true : false;
    }

    /**
     * Determines if a query is copy number (copy number results type or if it has copy number criterion).
     * @param query to check to see if it is copy number.
     * @return T/F if it is copy number type or not.
     */
    public static boolean isQueryCopyNumber(Query query) {
        return (ResultTypeEnum.COPY_NUMBER.equals(query.getResultType())
                || QueryUtil.isCompoundCriterionCopyNumber(query.getCompoundCriterion())) ? true : false;
    }

    /**
     * Recursive function that goes through all criterion in a CompoundCriterion to determine
     * if any of them are copy number based criterion.
     * @param criterion input for the recursive function.
     * @return T/F value.
     */
    public static boolean isCompoundCriterionCopyNumber(CompoundCriterion criterion) {
        for (AbstractCriterion abstractCriterion : criterion.getCriterionCollection()) {
            if (abstractCriterion instanceof CompoundCriterion) {
                if (isCompoundCriterionCopyNumber((CompoundCriterion) abstractCriterion)) {
                    return true;
                }
            } else if (abstractCriterion instanceof GeneNameCriterion && (GenomicCriterionTypeEnum.COPY_NUMBER
                    .equals(((GeneNameCriterion) abstractCriterion).getGenomicCriterionType()))) {
                return true;
            } else if (abstractCriterion instanceof CopyNumberAlterationCriterion) {
                return true;
            }
        }
        return false;
    }

    /**
     * Recursive function that goes through all criterion in a CompoundCriterion to determine
     * if any of them are gene expression based criterion.
     * @param criterion input for the recursive function.
     * @return T/F value.
     */
    public static boolean isCompoundCriterionGeneExpression(CompoundCriterion criterion) {
        for (AbstractCriterion abstractCriterion : criterion.getCriterionCollection()) {
            if (abstractCriterion instanceof CompoundCriterion) {
                if (isCompoundCriterionGeneExpression((CompoundCriterion) abstractCriterion)) {
                    return true;
                }
            } else if (abstractCriterion instanceof GeneNameCriterion && (GenomicCriterionTypeEnum.GENE_EXPRESSION
                    .equals(((GeneNameCriterion) abstractCriterion).getGenomicCriterionType()))) {
                return true;
            } else if (abstractCriterion instanceof FoldChangeCriterion) {
                return true;
            } else if (abstractCriterion instanceof ExpressionLevelCriterion) {
                return true;
            }
        }
        return false;
    }

    /**
     * Determines if a query has fold change criterion.
     * @param query check to see if this query has fold change criterion.
     * @return T/F value.
     */
    public static boolean isFoldChangeQuery(Query query) {
        return !getFoldChangeCriterion(query).isEmpty();
    }

    /**
     * Retrieves a list of fold change criterion for a given query.
     * @param query to retrieve fold change criterion for.
     * @return the fold change criterion.
     */
    public static List<FoldChangeCriterion> getFoldChangeCriterion(Query query) {
        return getFoldChangeCriterionFromCompoundCriterion(query.getCompoundCriterion());
    }

    private static List<FoldChangeCriterion> getFoldChangeCriterionFromCompoundCriterion(
            CompoundCriterion compoundCriterion) {
        List<FoldChangeCriterion> foldChangeCriterionResults = new ArrayList<FoldChangeCriterion>();
        for (AbstractCriterion criterion : compoundCriterion.getCriterionCollection()) {
            getFoldChangeCriterion(criterion, foldChangeCriterionResults);
        }
        return foldChangeCriterionResults;
    }

    private static void getFoldChangeCriterion(AbstractCriterion criterion,
            List<FoldChangeCriterion> foldChangeCriterionResults) {
        if (criterion instanceof FoldChangeCriterion) {
            foldChangeCriterionResults.add((FoldChangeCriterion) criterion);
        } else if (criterion instanceof CompoundCriterion) {
            CompoundCriterion compoundCriterion = (CompoundCriterion) criterion;
            foldChangeCriterionResults.addAll(getFoldChangeCriterionFromCompoundCriterion(compoundCriterion));
        }
    }

    /**
     * Creates a query for all gene expression or copy number data in the study based on the input result type,
     *   and limits it to the given queries (if any are given).
     * @param studySubscription for querying.
     * @param queries to limit gene expression data returned based.
     * @param platformName if this is not null, specifies the platform to use for the gene expression query.
     * @param resultType the type for querying.
     * @return a Query which contains all gene expression data for all clinical queries given (if any).
     * @throws InvalidCriterionException if the queries passed in have different reporter types.
     */
    public static Query createAllGenomicDataQuery(StudySubscription studySubscription, Set<Query> queries,
            String platformName, ResultTypeEnum resultType) throws InvalidCriterionException {
        ReporterTypeEnum reporterType = getReporterTypeFromQueries(queries, resultType);
        Query query = createQuery(studySubscription);
        query.setAllGenomicDataQuery(true);
        query.setResultType(resultType);
        query.setReporterType(reporterType);
        if (CollectionUtils.isNotEmpty(queries)) {
            CompoundCriterion compoundCriterions = new CompoundCriterion();
            compoundCriterions.setBooleanOperator(BooleanOperatorEnum.OR);
            for (Query currentQuery : queries) {
                compoundCriterions.getCriterionCollection().add(currentQuery.getCompoundCriterion());
            }
            query.getCompoundCriterion().getCriterionCollection().add(compoundCriterions);
        }
        if (StringUtils.isNotBlank(platformName)) {
            GenomicCriterionTypeEnum criterionType = ResultTypeEnum.GENE_EXPRESSION == resultType
                    ? GenomicCriterionTypeEnum.GENE_EXPRESSION
                    : GenomicCriterionTypeEnum.COPY_NUMBER;
            GeneNameCriterion geneNameCriterion = new GeneNameCriterion();
            geneNameCriterion.setPlatformName(platformName);
            geneNameCriterion.setGenomicCriterionType(criterionType);
            query.getCompoundCriterion().getCriterionCollection().add(geneNameCriterion);
        }
        return query;
    }

    private static ReporterTypeEnum getReporterTypeFromQueries(Set<Query> queries, ResultTypeEnum resultType)
            throws InvalidCriterionException {
        if (queries == null || queries.isEmpty()) {
            return ResultTypeEnum.GENE_EXPRESSION.equals(resultType) ? ReporterTypeEnum.GENE_EXPRESSION_PROBE_SET
                    : ReporterTypeEnum.DNA_ANALYSIS_REPORTER;
        }
        ReporterTypeEnum reporterType = null;
        for (Query query : queries) {
            if (reporterType == null) {
                reporterType = query.getReporterType();
            } else {
                if (reporterType != query.getReporterType()) {
                    throw new InvalidCriterionException(
                            "Trying to create a combined genomic query where two of the "
                                    + "queries have different reporter types.");
                }
            }
        }
        return reporterType;
    }

    private static Query createQuery(StudySubscription studySubscription) {
        Query query = new Query();
        query.setColumnCollection(new HashSet<ResultColumn>());
        query.setCompoundCriterion(new CompoundCriterion());
        query.getCompoundCriterion().setBooleanOperator(BooleanOperatorEnum.AND);
        query.getCompoundCriterion().setCriterionCollection(new HashSet<AbstractCriterion>());
        query.setSubscription(studySubscription);
        return query;
    }

    /**
     * Goes through all criterion in a query looking for the given abstractCriterionType and returning that set.
     * @param <T> a subclass of AbstractCriterion.
     * @param query to retrieve criterion from.
     * @param abstractCriterionType must be a subclass of AbstractCriterion.
     * @return Set of all criterion matching the given class type.
     */
    public static <T> Set<T> getCriterionTypeFromQuery(Query query, Class<T> abstractCriterionType) {
        Set<T> criterionSet = new HashSet<T>();
        CompoundCriterion compoundCriterion = query.getCompoundCriterion();
        getCriterionFromCompoundCriterion(abstractCriterionType, criterionSet, compoundCriterion);
        return criterionSet;
    }

    @SuppressWarnings("unchecked") // converting T to the class type.
    private static <T> void getCriterionFromCompoundCriterion(Class<T> type, Set<T> criterionSet,
            CompoundCriterion compoundCriterion) {
        for (AbstractCriterion criterion : compoundCriterion.getCriterionCollection()) {
            if (criterion.getClass().equals(type)) {
                criterionSet.add((T) criterion);
            } else if (criterion instanceof CompoundCriterion) {
                getCriterionFromCompoundCriterion(type, criterionSet, (CompoundCriterion) criterion);
            }

        }
    }

    /**
     * Converts a query result into a hashmap of Sample -> Column -> Value.  Used for the
     * <code>IGVSampleInfoFileWriter</code>.
     * @param result query result to convert.
     * @return the hashmap.
     */
    public static Map<Sample, Map<String, String>> retrieveSampleValuesMap(QueryResult result) {
        Map<Sample, Map<String, String>> sampleValuesMap = new HashMap<Sample, Map<String, String>>();
        for (ResultRow row : result.getRowCollection()) {
            for (SampleAcquisition sampleAcquisition : row.getSubjectAssignment()
                    .getSampleAcquisitionCollection()) {
                Sample sample = sampleAcquisition.getSample();
                if (sampleValuesMap.get(sample) == null) {
                    Map<String, String> columnToValueMap = new HashMap<String, String>();
                    for (ResultValue value : row.getValueCollection()) {
                        columnToValueMap.put(value.getColumn().getDisplayName(),
                                value == null ? "" : value.toString());
                    }
                    sampleValuesMap.put(sample, columnToValueMap);
                }
            }
        }
        return sampleValuesMap;
    }

    /**
     * Generates a numeric comparison criterion from the given annotation field descriptor.
     * @param descriptor the annotation field descriptor
     * @param value the value
     * @return the created numeric comparison criterion
     */
    public static NumericComparisonCriterion createNumericComparisonCriterion(AnnotationFieldDescriptor descriptor,
            String value) {
        NumericComparisonCriterion criterion = new NumericComparisonCriterion();
        criterion.setNumericComparisonOperator(NumericComparisonOperatorEnum.EQUAL);
        criterion.setNumericValue(Double.valueOf(value));
        criterion.setAnnotationFieldDescriptor(descriptor);
        return criterion;
    }

    /**
     * Generates a date comparison criterion from the given annotation field descriptor.
     * @param descriptor the annotation field descriptor
     * @param value the value
     * @return the created date comparison criterion
     */
    public static DateComparisonCriterion createDateComparisonCriterion(AnnotationFieldDescriptor descriptor,
            String value) {
        DateComparisonCriterion criterion = new DateComparisonCriterion();
        criterion.setDateComparisonOperator(DateComparisonOperatorEnum.EQUAL);
        try {
            criterion.setDateValue(DateUtil.createDate(value));
        } catch (ParseException e) {
            throw new IllegalStateException("Invalid date format for date " + value, e);
        }
        criterion.setAnnotationFieldDescriptor(descriptor);
        return criterion;
    }

    /**
     * Generates a string comparison criterion from the given annotation field descriptor.
     * @param descriptor the annotation field descriptor
     * @param value the value
     * @return the string numeric comparison criterion
     */
    public static StringComparisonCriterion createStringComparisonCriterion(AnnotationFieldDescriptor descriptor,
            String value) {
        StringComparisonCriterion criterion = new StringComparisonCriterion();
        criterion.setWildCardType(WildCardTypeEnum.WILDCARD_OFF);
        criterion.setStringValue(value);
        criterion.setAnnotationFieldDescriptor(descriptor);
        return criterion;
    }
}