1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package fr.inrae.agroclim.indicators.model.criteria;
20
21 import java.io.IOException;
22 import java.io.ObjectInputStream;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import fr.inrae.agroclim.indicators.exception.IndicatorsException;
30 import fr.inrae.agroclim.indicators.exception.type.ComputationErrorType;
31 import fr.inrae.agroclim.indicators.exception.type.ResourceErrorType;
32 import fr.inrae.agroclim.indicators.model.ExpressionParameter;
33 import fr.inrae.agroclim.indicators.model.JEXLFormula;
34 import fr.inrae.agroclim.indicators.model.Parameter;
35 import fr.inrae.agroclim.indicators.model.data.DailyData;
36 import fr.inrae.agroclim.indicators.model.data.Resource;
37 import fr.inrae.agroclim.indicators.model.data.Variable;
38 import fr.inrae.agroclim.indicators.util.Doublet;
39 import jakarta.xml.bind.annotation.XmlElement;
40 import jakarta.xml.bind.annotation.XmlRootElement;
41 import jakarta.xml.bind.annotation.XmlType;
42 import lombok.EqualsAndHashCode;
43 import lombok.Getter;
44 import lombok.Setter;
45 import lombok.ToString;
46 import lombok.extern.log4j.Log4j2;
47
48
49
50
51
52
53
54
55
56 @XmlRootElement
57 @XmlType(propOrder = {"expression", "expressionParameters"})
58 @Log4j2
59 @ToString
60 @EqualsAndHashCode(callSuper = true, of = {"expression", "expressionParameters"})
61 public final class FormulaCriteria extends Criteria {
62
63
64
65
66 private static final long serialVersionUID = 874604079323338993L;
67
68
69
70
71
72
73
74
75
76 public static boolean between(final Number value, final Number minValueInclusive, final Number maxValueInclusive) {
77 if (value == null) {
78 return false;
79 }
80 final double v = value.doubleValue();
81 if (minValueInclusive != null) {
82 final double min = minValueInclusive.doubleValue();
83 if (v < min) {
84 return false;
85 }
86 }
87 if (maxValueInclusive != null) {
88 final double max = maxValueInclusive.doubleValue();
89 if (v > max) {
90 return false;
91 }
92 }
93 return true;
94 }
95
96
97
98
99 @Getter
100 @Setter
101 private String expression;
102
103
104
105
106 @Getter
107 @Setter
108 @XmlElement(name = "expressionParameter")
109 private List<ExpressionParameter> expressionParameters;
110
111
112
113
114 private transient JEXLFormula formula = new JEXLFormula();
115
116
117
118
119 @Setter
120 @Getter
121 private transient Map<String, Double> parametersValues = new HashMap<>();
122
123 @Override
124 public FormulaCriteria clone() {
125 final FormulaCriteria clone = new FormulaCriteria();
126 clone.expression = expression;
127 if (expressionParameters != null) {
128 clone.expressionParameters = new ArrayList<>();
129 for (final ExpressionParameter p : expressionParameters) {
130 try {
131 clone.expressionParameters.add(p.clone());
132 } catch (final CloneNotSupportedException ex) {
133 LOGGER.catching(ex);
134 }
135 }
136 }
137 clone.parametersValues = parametersValues;
138 if (getParameters() != null) {
139 clone.setParameters(getParameters());
140 }
141 return clone;
142 }
143
144 @Override
145 public boolean eval(final DailyData data) throws IndicatorsException {
146 if (expression == null) {
147 throw new IndicatorsException(ComputationErrorType.FORMULA_EXPRESSION_NULL);
148 }
149 if (data == null) {
150 throw new IndicatorsException(ResourceErrorType.CLIMATE_EMPTY);
151 }
152 formula.setExpression(expression);
153 final Map<String, Double> values = new HashMap<>();
154 getVariables().forEach(variable -> values.put(variable.name(), data.getValue(variable)));
155 if (parametersValues != null) {
156 values.putAll(parametersValues);
157 }
158 if (expressionParameters != null) {
159 expressionParameters.forEach(p -> values.put(p.getName(), p.getValue()));
160 }
161 try {
162 return formula.evaluate(values, Boolean.class);
163 } catch (final IndicatorsException ex) {
164 throw new IndicatorsException(ComputationErrorType.FORMULA, ex, "Failed to evaluate the criteria.");
165 }
166 }
167
168 @Override
169 public List<Doublet<Parameter, Number>> getParameterDefaults() {
170 if (getParameters() == null) {
171 return List.of();
172 }
173 final List<Doublet<Parameter, Number>> val = new ArrayList<>();
174 if (expressionParameters != null) {
175 expressionParameters.forEach(p -> getParameters().stream()
176 .filter(a -> p.getName().equals(a.getAttribute()))
177 .findFirst()
178 .ifPresent(param -> val.add(Doublet.of(param, p.getValue()))));
179 }
180 return val;
181 }
182
183 @Override
184 public Set<Variable> getVariables() {
185 formula.setExpression(expression);
186 return formula.getDataVariables();
187 }
188
189 @Override
190 public boolean isComputable(final Resource<? extends DailyData> res) {
191 if (res == null) {
192 throw new IllegalArgumentException("resource must no be null!");
193 }
194 formula.setExpression(expression);
195 return formula.isValid();
196 }
197
198
199
200
201
202
203
204
205
206
207
208 private void readObject(final ObjectInputStream ois) throws ClassNotFoundException, IOException {
209
210 ois.defaultReadObject();
211 formula = new JEXLFormula();
212 }
213
214 @Override
215 public void removeParameter(final Parameter param) {
216
217 }
218
219 @Override
220 public String toStringTree(final String indent) {
221 final StringBuilder sb = new StringBuilder();
222 sb.append(indent).append(" class: ").append(getClass().getName()).append("\n");
223 sb.append(indent).append(" expression: ").append(expression).append("\n");
224 if (expressionParameters != null) {
225 expressionParameters.forEach(p -> sb.append(indent).append(" expressionParameter: ")
226 .append(p.getName()).append("=").append(p.getValue()).append("\n"));
227 }
228 return sb.toString();
229 }
230 }