ComparisonCriteria.java

/*
 * Copyright (C) 2020 INRAE AgroClim
 *
 * This file is part of Indicators.
 *
 * Indicators 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.
 *
 * Indicators 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 Indicators. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.inrae.agroclim.indicators.model.criteria;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import fr.inrae.agroclim.indicators.exception.IndicatorsException;
import fr.inrae.agroclim.indicators.exception.type.ComputationErrorType;
import fr.inrae.agroclim.indicators.model.Parameter;
import fr.inrae.agroclim.indicators.model.data.DailyData;
import fr.inrae.agroclim.indicators.model.data.Resource;
import fr.inrae.agroclim.indicators.model.data.Variable;
import fr.inrae.agroclim.indicators.util.Doublet;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;

/**
 * Criteria to compare two variables.
 *
 * Last changed : $Date$
 *
 * @author omaury
 * @author $Author$
 * @version $Revision$
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = {"leftVariable", "relationalOperator", "rightVariable"})
@EqualsAndHashCode(
        callSuper = true,
        of = {"leftVariable", "relationalOperator", "rightVariable"}
        )
@ToString
public final class ComparisonCriteria extends Criteria {

    /**
     * UID for Serializable.
     */
    private static final long serialVersionUID = 874604079323338994L;

    /**
     * Variable name (property of ClimaticDailyData) for left operand.
     */
    @XmlElement(name = "leftVariable", required = true)
    @Getter
    @Setter
    private Variable leftVariable;

    /**
     * The relational operator to compare leftVariable and rightVariable.
     */
    @XmlElement(name = "relationalOperator", required = true)
    @Setter
    @NonNull
    private RelationalOperator relationalOperator;

    /**
     * Variable name (property of ClimaticDailyData) for right operand.
     */
    @XmlElement(name = "rightVariable", required = true)
    @Getter
    @Setter
    private Variable rightVariable;

    @Override
    public ComparisonCriteria clone() {
        final ComparisonCriteria clone = new ComparisonCriteria();
        clone.leftVariable = leftVariable;
        clone.relationalOperator = relationalOperator;
        clone.rightVariable = rightVariable;
        if (getParameters() != null) {
            clone.setParameters(getParameters());
        }
        return clone;
    }

    @Override
    public boolean eval(final DailyData dailydata) throws IndicatorsException {
        if (relationalOperator == null) {
            throw new IndicatorsException(ComputationErrorType.WRONG_DEFINITION,
                    "The relational operator must be set!");
        }
        if (dailydata == null) {
            return false;
        }
        final Double left = dailydata.getValue(leftVariable);
        final Double right = dailydata.getValue(rightVariable);
        return relationalOperator.eval(left, right);
    }

    @Override
    public List<Doublet<Parameter, Number>> getParameterDefaults() {
        // no substitution
        return List.of();
    }

    @Override
    public List<Parameter> getParameters() {
        // no substitution
        return List.of();
    }

    @Override
    public Map<String, Double> getParametersValues() {
        // no substitution
        return Map.of();
    }

    @Override
    public Set<Variable> getVariables() {
        final Set<Variable> variables = new HashSet<>();
        variables.add(leftVariable);
        variables.add(rightVariable);
        return variables;
    }

    /**
     * @param res ClimaticResource
     * @return climaticResource contains "variable"
     */
    @Override
    public boolean isComputable(final Resource<? extends DailyData> res) {
        Objects.requireNonNull(leftVariable,
                "Left variable must be set for "
                        + getClass().getCanonicalName());
        Objects.requireNonNull(leftVariable,
                "Right variable must be set for "
                        + getClass().getCanonicalName());
        return !res.getMissingVariables().contains(leftVariable.name())
                && !res.getMissingVariables().contains(rightVariable.name());
    }

    @Override
    public void removeParameter(final Parameter param) {
        // Do nothing for this type of criteria
    }

    @Override
    public void setParametersValues(final Map<String, Double> values) {
        // No parameters for the comparison of variables
    }

    @Override
    public String toStringTree(final String indent) {
        final StringBuilder sb = new StringBuilder();
        sb.append(indent).append("  class: ")
        .append(getClass().getName()).append("\n");
        sb.append(indent).append("  leftVariable: ")
        .append(leftVariable).append("\n");
        sb.append(indent).append("  relationalOperator: ")
        .append(relationalOperator).append("\n");
        sb.append(indent).append("  rightVariable: ")
        .append(rightVariable).append("\n");
        return sb.toString();
    }
}