gov.nih.nci.ncicb.tcga.dcc.qclive.common.util.DateComparator.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.ncicb.tcga.dcc.qclive.common.util.DateComparator.java

Source

/*
 * Software License, Version 1.0 Copyright 2011 SRA International, Inc.
 * Copyright Notice.  The software subject to this notice and license includes both human
 * readable source code form and machine readable, binary, object code form (the "caBIG
 * Software").
 *
 * Please refer to the complete License text for full details at the root of the project.
 */

package gov.nih.nci.ncicb.tcga.dcc.qclive.common.util;

import org.apache.commons.lang.StringUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * This class holds the name of the 2 dates to be compared and the comparator to use for the comparison
 *
 * @author Julien Baboud
 *         Last updated by: $Author$
 * @version $Rev$
 */
public class DateComparator {

    public static final String PRECISION_YEAR = "year";
    public static final String PRECISION_DAY = "day";
    public static final String PRECISION_MONTH = "month";

    private String leftOperandName;
    private String rightOperandName;
    private Operator operator;

    /**
     * Parses the input looking for a comparator string as a separator of 2 operand names.
     * The comparator can be any of: '==', '<', '>', '<=', '>=' or '<>'
     *
     * @param datesToCompare the dates names to be compared, separated by a valid comparator string
     * @throws IllegalArgumentException if the argument is invalid
     */
    public DateComparator(final String datesToCompare) {

        if (datesToCompare == null) {
            throw new IllegalArgumentException("'datesToCompare' parameter can not be null");
        }

        // Parsing datesToCompare
        int indexOfOperator;
        boolean operatorFound = false;

        for (final Operator operator : Operator.values()) {

            if (!operatorFound) {

                indexOfOperator = datesToCompare.indexOf(operator.getValue());

                if (indexOfOperator != -1) {

                    operatorFound = true;

                    setOperator(operator);
                    setLeftOperandName(datesToCompare.substring(0, indexOfOperator).trim());
                    setRightOperandName(
                            datesToCompare.substring(indexOfOperator + operator.getValue().length()).trim());
                }
            }
        }

        if (!operatorFound) {
            throw new IllegalArgumentException(
                    "No valid operator could be found in the following dates to compare: " + datesToCompare);

        } else if (StringUtils.isBlank(getLeftOperandName())) {
            throw new IllegalArgumentException(
                    "The left operand in the following dates to compare is blank: " + datesToCompare);

        } else if (StringUtils.isBlank(getRightOperandName())) {
            throw new IllegalArgumentException(
                    "The right operand in the following dates to compare is blank: " + datesToCompare);
        }
    }

    /**
     * Create a <code>DateComparator</code> with the given input
     *
     * @param leftOperandName the left operand name
     * @param rightOperandName the right operand name
     * @param operator the operator
     */
    public DateComparator(final String leftOperandName, final String rightOperandName, final Operator operator) {

        if (StringUtils.isBlank(leftOperandName)) {
            throw new IllegalArgumentException("Parameter 'leftOperandName' can not be blank");

        } else if (StringUtils.isBlank(rightOperandName)) {
            throw new IllegalArgumentException("Parameter 'rightOperandName' can not be blank");

        } else if (operator == null) {
            throw new IllegalArgumentException("Parameter 'operator' can not be null");
        }

        setOperator(operator);
        setLeftOperandName(leftOperandName);
        setRightOperandName(rightOperandName);
    }

    /**
     * Return <code>true</code> if the comparison date1 OPERAND date2 is true, or if any of the operand is null, <code>false</code> otherwise
     *
     * @param operandNameToValueMap the map that holds the values for each operand
     * @param operandPrecision precision for values in the operand map, key is operandName and value is day, month, or year. If null assume day.
     * @return <code>true</code> if the comparison date1 OPERAND date2 is true, or if any of the operand is null, <code>false</code> otherwise
     */
    public boolean compare(final Map<String, Date> operandNameToValueMap,
            final Map<String, String> operandPrecision) {

        final Calendar leftOperandValue = getLeftOperandValue(operandNameToValueMap);
        final Calendar rightOperandValue = getRightOperandValue(operandNameToValueMap);
        final String leastPrecision = getLeastPrecision(operandPrecision);

        switch (getOperator()) {
        case EQ:
            return isEqual(leftOperandValue, rightOperandValue, leastPrecision);
        case LT:
            return isLowerThan(leftOperandValue, rightOperandValue, leastPrecision);
        case GT:
            return isGreaterThan(leftOperandValue, rightOperandValue, leastPrecision);
        case LE:
            return isLowerThanOrEqual(leftOperandValue, rightOperandValue, leastPrecision);
        case GE:
            return isGreaterThanOrEqual(leftOperandValue, rightOperandValue, leastPrecision);
        case NE:
            return isNotEqual(leftOperandValue, rightOperandValue, leastPrecision);
        default: // Should not happen
            return false;
        }
    }

    private String getLeastPrecision(final Map<String, String> operandPrecision) {
        String leftOperandPrecision = operandPrecision.get(getLeftOperandName());
        if (leftOperandPrecision == null) {
            leftOperandPrecision = PRECISION_DAY;
        }
        String rightOperandPrecision = operandPrecision.get(getRightOperandName());
        if (rightOperandPrecision == null) {
            rightOperandPrecision = PRECISION_DAY;
        }

        if (PRECISION_YEAR.equals(leftOperandPrecision) || PRECISION_YEAR.equals(rightOperandPrecision)) {
            return PRECISION_YEAR;
        } else if (PRECISION_MONTH.equals(leftOperandPrecision) || PRECISION_MONTH.equals(rightOperandPrecision)) {
            return PRECISION_MONTH;
        } else {
            return PRECISION_DAY;
        }

    }

    /**
     * Return <code>true</code> if left operand is equal to right operand, or if any of the operand is null, <code>false</code> otherwise
     *
     *
     * @param leftOperandValue the <code>Calendar</code> value for the left operand
     * @param rightOperandValue the <code>Calendar</code> value for the right operand
     * @param leastPrecision the least precision of the two operands
     * @return <code>true</code> if left operand is equal to right operand, or if any of the operand is null, <code>false</code> otherwise
     */
    private boolean isEqual(final Calendar leftOperandValue, final Calendar rightOperandValue,
            final String leastPrecision) {
        if (leftOperandValue == null || rightOperandValue == null) {
            return true;
        } else {

            if (PRECISION_YEAR.equals(leastPrecision)) {
                // compare only year, true if the same
                return leftOperandValue.get(Calendar.YEAR) == rightOperandValue.get(Calendar.YEAR);

            } else if (PRECISION_MONTH.equals(leastPrecision)) {
                // compare only month and year, true if the same
                return leftOperandValue.get(Calendar.YEAR) == rightOperandValue.get(Calendar.YEAR)
                        && leftOperandValue.get(Calendar.MONTH) == rightOperandValue.get(Calendar.MONTH);
            } else {
                return leftOperandValue.equals(rightOperandValue);
            }
        }
    }

    /**
     * Return <code>true</code> if left operand is strictly before right operand, or if any of the operand is null, <code>false</code> otherwise
     *
     *
     * @param leftOperandValue the <code>Calendar</code> value for the left operand
     * @param rightOperandValue the <code>Calendar</code> value for the right operand
     * @param leastPrecision the least-precise precision of the two dates: day, month, or year
     * @return <code>true</code> if left operand is strictly before right operand, or if any of the operand is null, <code>false</code> otherwise
     */
    private boolean isLowerThan(final Calendar leftOperandValue, final Calendar rightOperandValue,
            final String leastPrecision) {
        if (leftOperandValue == null || rightOperandValue == null) {
            return true;
        } else {

            if (PRECISION_YEAR.equals(leastPrecision)) {
                // if year is the same, can't say for sure so return true, so don't just do < comparison
                return leftOperandValue.get(Calendar.YEAR) <= rightOperandValue.get(Calendar.YEAR);

            } else if (PRECISION_MONTH.equals(leastPrecision)) {
                return (leftOperandValue.get(Calendar.YEAR) < rightOperandValue.get(Calendar.YEAR))
                        || (leftOperandValue.get(Calendar.YEAR) == rightOperandValue.get(Calendar.YEAR)
                                && leftOperandValue.get(Calendar.MONTH) <= rightOperandValue.get(Calendar.MONTH));
            } else {
                return leftOperandValue.before(rightOperandValue);
            }
        }
    }

    /**
     * Return <code>true</code> if left operand is strictly after right operand, or if any of the operand is null, <code>false</code> otherwise
     *
     *
     * @param leftOperandValue the <code>Calendar</code> value for the left operand
     * @param rightOperandValue the <code>Calendar</code> value for the right operand
     * @param leastPrecision the least precise precision of the 2 dates
     * @return <code>true</code> if left operand is strictly after right operand, or if any of the operand is null, <code>false</code> otherwise
     */
    private boolean isGreaterThan(final Calendar leftOperandValue, final Calendar rightOperandValue,
            final String leastPrecision) {
        if (leftOperandValue == null || rightOperandValue == null) {
            return true;
        } else {

            if (PRECISION_YEAR.equals(leastPrecision)) {
                // if the years are equal, or left year > right year then consider it true
                return leftOperandValue.get(Calendar.YEAR) >= rightOperandValue.get(Calendar.YEAR);

            } else if (PRECISION_MONTH.equals(leastPrecision)) {
                // if left year > right year then true; if left year = right year and left month >= right year then true
                return (leftOperandValue.get(Calendar.YEAR) > rightOperandValue.get(Calendar.YEAR))
                        || (leftOperandValue.get(Calendar.YEAR) == rightOperandValue.get(Calendar.YEAR)
                                && leftOperandValue.get(Calendar.MONTH) >= rightOperandValue.get(Calendar.MONTH));

            } else {
                // day precision or unknown precision, use full date
                return leftOperandValue.after(rightOperandValue);
            }
        }
    }

    /**
     * Return <code>true</code> if left operand is on or before right operand, or if any of the operand is null, <code>false</code> otherwise
     *
     *
     * @param leftOperandValue the <code>Calendar</code> value for the left operand
     * @param rightOperandValue the <code>Calendar</code> value for the right operand
     * @param leastPrecision the least precise precision of the 2 dates
     * @return <code>true</code> if left operand is on or before right operand, or if any of the operand is null, <code>false</code> otherwise
     */
    private boolean isLowerThanOrEqual(final Calendar leftOperandValue, final Calendar rightOperandValue,
            final String leastPrecision) {
        if (leftOperandValue == null || rightOperandValue == null) {
            return true;
        } else {

            if (PRECISION_YEAR.equals(leastPrecision)) {
                // if year is the same, can't say for sure so return true
                return leftOperandValue.get(Calendar.YEAR) <= rightOperandValue.get(Calendar.YEAR);

            } else if (PRECISION_MONTH.equals(leastPrecision)) {
                return (leftOperandValue.get(Calendar.YEAR) < rightOperandValue.get(Calendar.YEAR))
                        || (leftOperandValue.get(Calendar.YEAR) == rightOperandValue.get(Calendar.YEAR)
                                && leftOperandValue.get(Calendar.MONTH) <= rightOperandValue.get(Calendar.MONTH));
            } else {
                return !leftOperandValue.after(rightOperandValue);
            }
        }
    }

    /**
     * Return <code>true</code> if left operand is on or after right operand, or if any of the operand is null, <code>false</code> otherwise
     *
     *
     * @param leftOperandValue the <code>Calendar</code> value for the left operand
     * @param rightOperandValue the <code>Calendar</code> value for the right operand
     * @param leastPrecision the least precise precision of the 2 dates
     * @return <code>true</code> if left operand is on or after right operand, or if any of the operand is null, <code>false</code> otherwise
     */
    private boolean isGreaterThanOrEqual(final Calendar leftOperandValue, final Calendar rightOperandValue,
            final String leastPrecision) {
        if (leftOperandValue == null || rightOperandValue == null) {
            return true;
        } else {

            if (PRECISION_YEAR.equals(leastPrecision)) {
                return leftOperandValue.get(Calendar.YEAR) >= rightOperandValue.get(Calendar.YEAR);

            } else if (PRECISION_MONTH.equals(leastPrecision)) {
                return (leftOperandValue.get(Calendar.YEAR) > rightOperandValue.get(Calendar.YEAR))
                        || (leftOperandValue.get(Calendar.YEAR) == rightOperandValue.get(Calendar.YEAR)
                                && leftOperandValue.get(Calendar.MONTH) >= rightOperandValue.get(Calendar.MONTH));
            } else {
                return !leftOperandValue.before(rightOperandValue);
            }
        }
    }

    /**
     * Return <code>true</code> if left operand is equal to right operand, or if any of the operand is null, <code>false</code> otherwise
     *
     *
     * @param leftOperandValue the <code>Calendar</code> value for the left operand
     * @param rightOperandValue the <code>Calendar</code> value for the right operand
     * @param leastPrecision the least precise precision of the 2 dates
     * @return <code>true</code> if left operand is equal to right operand, or if any of the operand is null, <code>false</code> otherwise
     */
    private boolean isNotEqual(final Calendar leftOperandValue, final Calendar rightOperandValue,
            final String leastPrecision) {
        if (leftOperandValue == null || rightOperandValue == null) {
            return true;
        } else {
            if (PRECISION_YEAR.equals(leastPrecision) || PRECISION_MONTH.equals(leastPrecision)) {
                // if not precise to the day, it is impossible to say for sure that the dates are not equal
                return true;
            } else {
                return !leftOperandValue.equals(rightOperandValue);
            }
        }
    }

    /**
     * An enumeration of possible comparisons operators
     */
    public enum Operator {

        EQ("=="), LE("<="), GE(">="), NE("<>"),
        // The next 2 operators need to be last in the enum
        // so that a match for them is only found if a match for one of the others is not found first
        LT("<"), GT(">");

        /**
         * The human readable value of this enum
         */
        private String value;

        private Operator(final String value) {
            this.value = value;
        }

        public String getValue() {
            return this.value;
        }
    }

    /**
     * Retrieve the value for the left operand by looking up the map for the entry with the left operand name
     *
     * @param operandNameToValueMap the map that holds the values for each operand
     * @return the <code>Calendar</code> for the left operand
     */
    private Calendar getLeftOperandValue(final Map<String, Date> operandNameToValueMap) {
        return getOperandValue(operandNameToValueMap, getLeftOperandName());
    }

    /**
     * Retrieve the value for the right operand by looking up the map for the entry with the right operand name
     *
     * @param operandNameToValueMap the map that holds the values for each operand
     * @return the <code>Calendar</code> for the right operand
     */
    private Calendar getRightOperandValue(final Map<String, Date> operandNameToValueMap) {
        return getOperandValue(operandNameToValueMap, getRightOperandName());
    }

    private Calendar getOperandValue(final Map<String, Date> operandNameToValueMap, final String operandName) {
        Calendar dateOperand = null;
        if (operandNameToValueMap != null) {
            Date operand = operandNameToValueMap.get(operandName);
            if (operand != null) {
                dateOperand = Calendar.getInstance();
                dateOperand.setTime(operand);
            }
        }
        return dateOperand;
    }

    @Override
    public String toString() {

        return new StringBuilder("'").append(getLeftOperandName()).append("' ").append(getOperator().getValue())
                .append(" '").append(getRightOperandName()).append("'").toString();
    }

    //
    // Getter / Setter
    //

    public String getLeftOperandName() {
        return leftOperandName;
    }

    public void setLeftOperandName(final String leftOperandName) {
        this.leftOperandName = leftOperandName;
    }

    public String getRightOperandName() {
        return rightOperandName;
    }

    public void setRightOperandName(final String rightOperandName) {
        this.rightOperandName = rightOperandName;
    }

    public Operator getOperator() {
        return operator;
    }

    public void setOperator(final Operator operator) {
        this.operator = operator;
    }
}