1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package fr.inrae.agroclim.indicators.model.criteria;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import fr.inrae.agroclim.indicators.exception.IndicatorsException;
27 import fr.inrae.agroclim.indicators.exception.type.ComputationErrorType;
28 import fr.inrae.agroclim.indicators.model.Parameter;
29 import fr.inrae.agroclim.indicators.model.data.DailyData;
30 import fr.inrae.agroclim.indicators.model.data.Resource;
31 import fr.inrae.agroclim.indicators.model.data.Variable;
32 import fr.inrae.agroclim.indicators.model.data.climate.ClimaticDailyData;
33 import fr.inrae.agroclim.indicators.util.Doublet;
34 import lombok.EqualsAndHashCode;
35 import lombok.Getter;
36 import lombok.NonNull;
37 import lombok.Setter;
38
39
40
41
42
43
44
45
46
47 @EqualsAndHashCode(
48 callSuper = true,
49 of = {"criteria", "logicalOperator"}
50 )
51 public final class CompositeCriteria extends Criteria {
52
53
54
55 private static final long serialVersionUID = 8096714573528536936L;
56
57
58
59
60 @Getter
61 @Setter
62 private List<Criteria> criteria;
63
64
65
66
67 @Setter
68 @NonNull
69 private LogicalOperator logicalOperator = LogicalOperator.AND;
70
71
72
73
74 public CompositeCriteria() {
75 super();
76 }
77
78 @Override
79 public CompositeCriteria clone() {
80 CompositeCriteria clone = new CompositeCriteria();
81 clone.criteria = new ArrayList<>();
82 criteria.forEach(clone.criteria::add);
83 clone.logicalOperator = logicalOperator;
84 return clone;
85 }
86
87 @Override
88 public boolean eval(final DailyData dailyData) throws IndicatorsException {
89 if (logicalOperator == null) {
90 throw new IndicatorsException(ComputationErrorType.WRONG_DEFINITION, "The logical operator must be set!");
91 }
92 switch (logicalOperator) {
93 case AND:
94 for (final Criteria aCriteria : criteria) {
95 if (!aCriteria.eval(dailyData)) {
96 return false;
97 }
98 }
99 return true;
100 case OR:
101 for (final Criteria aCriteria : criteria) {
102 if (aCriteria.eval(dailyData)) {
103 return true;
104 }
105 }
106 return false;
107 case XOR:
108 int nbTrue = 0;
109 for (final Criteria aCriteria : criteria) {
110 if (aCriteria.eval(dailyData)) {
111 nbTrue++;
112 }
113 }
114 return nbTrue == 1;
115 default:
116 throw new RuntimeException("The logical operator is not handled: "
117 + logicalOperator.name());
118 }
119 }
120
121
122
123
124
125
126
127
128 public String getFormula(final ClimaticDailyData data) throws IndicatorsException {
129 StringBuilder sb = new StringBuilder();
130 boolean first = true;
131 for (final Criteria aCriteria : criteria) {
132 sb.append("(");
133 if (aCriteria instanceof SimpleCriteria crit) {
134 sb.append(crit.getFormula(data));
135 } else if (aCriteria instanceof CompositeCriteria crit) {
136 sb.append(crit.getFormula(data));
137 }
138 sb.append(")");
139 if (first) {
140 sb.append(" && ");
141 first = false;
142 }
143 }
144 return sb.toString();
145 }
146
147
148
149
150 public LogicalOperator getLogicalOperator() {
151 return logicalOperator;
152 }
153
154 @Override
155 public List<Doublet<Parameter, Number>> getParameterDefaults() {
156 final List<Doublet<Parameter, Number>> val = new ArrayList<>();
157 criteria.forEach(i -> val.addAll(i.getParameterDefaults()));
158 return val;
159 }
160
161 @Override
162 public List<Parameter> getParameters() {
163 List<Parameter> parameters = new ArrayList<>();
164 criteria.stream()
165 .filter(crit -> crit.getParameters() != null)
166 .forEach(crit ->
167 crit.getParameters().forEach(param -> {
168 if (!parameters.contains(param)) {
169 parameters.add(param);
170 }
171 })
172 );
173 return parameters;
174 }
175
176 @Override
177 public Map<String, Double> getParametersValues() {
178 final Map<String, Double> val = new HashMap<>();
179 criteria.forEach(crit -> val.putAll(crit.getParametersValues()));
180 return val;
181 }
182
183 @Override
184 public Set<Variable> getVariables() {
185 Set<Variable> variables = new HashSet<>();
186 if (criteria == null) {
187 return variables;
188 }
189 criteria.forEach(crit -> variables.addAll(crit.getVariables()));
190 return variables;
191 }
192
193 @Override
194 public boolean isComputable(final Resource<? extends DailyData> res) {
195 for (final Criteria aCriteria : criteria) {
196 if (!aCriteria.isComputable(res)) {
197 return false;
198 }
199 }
200 return true;
201 }
202
203 @Override
204 public void setParametersValues(final Map<String, Double> values) {
205 criteria.forEach(crit -> crit.setParametersValues(values));
206 }
207
208 @Override
209 public String toStringTree(final String indent) {
210 StringBuilder sb = new StringBuilder();
211 sb.append(indent).append(" class: ").append(getClass().getName())
212 .append("\n");
213 sb.append(indent).append(" logicalOperator: ");
214 if (logicalOperator != null) {
215 sb.append(logicalOperator.name());
216 }
217 sb.append("\n");
218 sb.append(indent).append(" criteria:\n");
219 criteria.forEach(crit -> sb.append(crit.toStringTree(indent + " ")));
220 return sb.toString();
221 }
222
223 @Override
224 public void removeParameter(final Parameter param) {
225 for (final Criteria c : this.criteria) {
226 if (c.getParameters().contains(param)) {
227 c.getParameters().remove(param);
228 }
229 }
230 }
231
232 }