SimpleCriteria.java
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package fr.inrae.agroclim.indicators.model.criteria;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import fr.inrae.agroclim.indicators.exception.IndicatorsException;
import fr.inrae.agroclim.indicators.model.Parameter;
import fr.inrae.agroclim.indicators.model.data.DailyData;
import fr.inrae.agroclim.indicators.model.data.Variable;
import fr.inrae.agroclim.indicators.model.data.climate.ClimaticDailyData;
import fr.inrae.agroclim.indicators.util.Doublet;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.XmlType;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
/**
* Criteria to compare value with threshold or to define variable to use in
* aggregation indicator (eg.: Sum).
*
* Last changed : $Date$
*
* @author jcufi
* @author $Author$
* @version $Revision$
*/
@XmlRootElement
@XmlType(propOrder = {"inferiorToThreshold", "strict", "operator", "threshold", "noThreshold"})
@ToString
@Log4j2
@EqualsAndHashCode(callSuper = true, of = {"noThreshold", "operator", "threshold"})
public final class SimpleCriteria extends VariableCriteria {
/**
* UUID for Serializable.
*/
private static final long serialVersionUID = -4284278102762199667L;
/**
* Tag name.
*/
private static final String THRESHOLD = "threshold";
/**
* true for comparison inferior or inferior or equal, false for superior or superior or equal.
*/
@XmlTransient
private boolean inferiorToThreshold = true;
/**
* No threshold, to simply use the value (eg.: in sum).
*/
@XmlElement
@Getter
@Setter
private boolean noThreshold = false;
/**
* The relational operator to compare variable and threshold.
*/
@XmlElement
@Getter
@Setter
private RelationalOperator operator = RelationalOperator.LT;
/**
* Comparison is strictly inferior or superior.
*/
@XmlTransient
private boolean strict = true;
/**
* Threshold value.
*/
@XmlElement
@Getter
private double threshold;
/**
* Criteria.
*/
public SimpleCriteria() {
super();
}
/**
* Constructor.
*
* @param variable
* variable name
* @param op
* operator for comparison
* @param thr
* threshold value
*/
public SimpleCriteria(final String variable, final RelationalOperator op, final double thr) {
setOperator(op);
setVariable(Variable.getByName(variable));
setThreshold(thr);
}
@Override
public SimpleCriteria clone() {
final SimpleCriteria clone = new SimpleCriteria();
clone.threshold = threshold;
clone.setVariable(getVariable());
clone.inferiorToThreshold = inferiorToThreshold;
clone.strict = strict;
clone.noThreshold = noThreshold;
clone.operator = operator;
if (getParameters() != null) {
clone.setParameters(getParameters());
}
return clone;
}
@Override
public boolean eval(final DailyData dailydata) throws IndicatorsException {
if (dailydata == null) {
return false;
}
if (isNoThreshold()) {
return true;
}
final double val = getValueOf(dailydata);
return operator.eval(val, getThreshold());
}
/**
* @param data
* climatic data
* @return formula
* @throws IndicatorsException
* exception while getting data
*/
public String getFormula(final ClimaticDailyData data) throws IndicatorsException {
final StringBuilder sb = new StringBuilder();
sb.append(getValueOf(data));
sb.append(" ");
sb.append(operator.getRepr());
sb.append(" ");
sb.append(getThreshold());
return sb.toString();
}
@Override
public List<Doublet<Parameter, Number>> getParameterDefaults() {
if (getParameters() == null) {
return List.of();
}
final Optional<Parameter> found = getParameters().stream() //
.filter(a -> THRESHOLD.equals(a.getAttribute())) //
.findFirst();
if (found.isEmpty()) {
return List.of();
}
return List.of(Doublet.of(found.get(), threshold));
}
@Override
public Map<String, Double> getParametersValues() {
final Map<String, Double> val = new HashMap<>();
// if no substitution is defined
if (getParameters() == null) {
return val;
}
for (final Parameter param : getParameters()) {
if (Objects.equals(param.getAttribute(), THRESHOLD)) {
val.put(param.getId(), threshold);
}
}
return val;
}
/**
* @deprecated use operator
* @return always null for migration
*/
@Deprecated(forRemoval = true)
public Boolean isInferiorToThreshold() {
return null;
}
/**
* @deprecated use operator
* @return always null for migration.
*/
@Deprecated(forRemoval = true)
public Boolean isStrict() {
return null;
}
@Override
public void removeParameter(final Parameter param) {
if (this.getParameters() != null) {
this.getParameters().remove(param);
}
}
/**
* @param value true for comparison inferior or inferior or equal, false for superior or superior or equal.
*/
@XmlElement
public void setInferiorToThreshold(final Boolean value) {
this.inferiorToThreshold = value;
setOperator();
}
/**
* Set operator according to inferiorToThreshold and strict.
*/
private void setOperator() {
if (this.inferiorToThreshold) {
if (strict) {
operator = RelationalOperator.LT;
} else {
operator = RelationalOperator.LE;
}
} else {
if (strict) {
operator = RelationalOperator.GT;
} else {
operator = RelationalOperator.GE;
}
}
}
@Override
public void setParametersValues(final Map<String, Double> values) {
// if no substitution is defined
if (getParameters() == null || values == null || values.isEmpty()) {
return;
}
for (final Parameter param : getParameters()) {
if (Objects.equals(param.getAttribute(), THRESHOLD)) {
final String id = param.getId();
if (values.containsKey(id) && values.get(id) != null) {
if (values.get(id) == null) {
LOGGER.error("Strange, value of parameter {} is null!", id);
} else {
setThreshold(values.get(id));
}
}
} else {
LOGGER.error("Attribute {} not handled!", param.getAttribute());
}
}
}
/**
* @param value Comparison is strictly inferior or superior.
*/
@XmlElement
public void setStrict(final Boolean value) {
this.strict = value;
setOperator();
}
/**
* @param value Threshold value.
*/
public void setThreshold(final double value) {
final double old = threshold;
threshold = value;
getPropertySupport().firePropertyChange(THRESHOLD, old, value);
}
@Override
public String toStringTree(final String ident) {
final StringBuilder sb = new StringBuilder();
sb.append(ident).append(" class: ").append(getClass().getName())
.append("\n");
sb.append(ident).append(" operator: ")
.append(operator.getRepr()).append("\n");
sb.append(ident).append(" noThreshold: ").append(noThreshold)
.append("\n");
sb.append(ident).append(" threshold: ").append(threshold)
.append("\n");
sb.append(ident).append(" variable: ").append(getVariable())
.append("\n");
return sb.toString();
}
}