1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package fr.inrae.agroclim.indicators.model;
18
19 import java.io.InputStream;
20 import java.util.ArrayList;
21 import java.util.EnumMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import fr.inrae.agroclim.indicators.exception.IndicatorsException;
26 import fr.inrae.agroclim.indicators.model.criteria.ComparisonCriteria;
27 import fr.inrae.agroclim.indicators.model.criteria.CompositeCriteria;
28 import fr.inrae.agroclim.indicators.model.criteria.FormulaCriteria;
29 import fr.inrae.agroclim.indicators.model.criteria.LogicalOperator;
30 import fr.inrae.agroclim.indicators.model.criteria.NoCriteria;
31 import fr.inrae.agroclim.indicators.model.criteria.RelationalOperator;
32 import fr.inrae.agroclim.indicators.model.criteria.SimpleCriteria;
33 import fr.inrae.agroclim.indicators.model.function.aggregation.AggregationFunction;
34 import fr.inrae.agroclim.indicators.model.function.aggregation.JEXLFunction;
35 import fr.inrae.agroclim.indicators.model.function.normalization.Exponential;
36 import fr.inrae.agroclim.indicators.model.function.normalization.Linear;
37 import fr.inrae.agroclim.indicators.model.function.normalization.Normal;
38 import fr.inrae.agroclim.indicators.model.function.normalization.NormalizationFunction;
39 import fr.inrae.agroclim.indicators.model.function.normalization.Sigmoid;
40 import fr.inrae.agroclim.indicators.model.indicator.Average;
41 import fr.inrae.agroclim.indicators.model.indicator.AverageOfDiff;
42 import fr.inrae.agroclim.indicators.model.indicator.CompositeIndicator;
43 import fr.inrae.agroclim.indicators.model.indicator.DayOfYear;
44 import fr.inrae.agroclim.indicators.model.indicator.DiffOfSum;
45 import fr.inrae.agroclim.indicators.model.indicator.Formula;
46 import fr.inrae.agroclim.indicators.model.indicator.Indicator;
47 import fr.inrae.agroclim.indicators.model.indicator.IndicatorCategory;
48 import fr.inrae.agroclim.indicators.model.indicator.InjectedParameter;
49 import fr.inrae.agroclim.indicators.model.indicator.Max;
50 import fr.inrae.agroclim.indicators.model.indicator.MaxWaveLength;
51 import fr.inrae.agroclim.indicators.model.indicator.Min;
52 import fr.inrae.agroclim.indicators.model.indicator.NumberOfDays;
53 import fr.inrae.agroclim.indicators.model.indicator.NumberOfWaves;
54 import fr.inrae.agroclim.indicators.model.indicator.PotentialSowingDaysFrequency;
55 import fr.inrae.agroclim.indicators.model.indicator.Quotient;
56 import fr.inrae.agroclim.indicators.model.indicator.SimpleIndicator;
57 import fr.inrae.agroclim.indicators.model.indicator.Sum;
58 import fr.inrae.agroclim.indicators.model.indicator.Tamm;
59 import fr.inrae.agroclim.indicators.xml.XMLUtil;
60 import jakarta.xml.bind.annotation.XmlAccessType;
61 import jakarta.xml.bind.annotation.XmlAccessorType;
62 import jakarta.xml.bind.annotation.XmlAttribute;
63 import jakarta.xml.bind.annotation.XmlElement;
64 import jakarta.xml.bind.annotation.XmlElementWrapper;
65 import jakarta.xml.bind.annotation.XmlRootElement;
66 import lombok.Getter;
67 import lombok.NonNull;
68 import lombok.Setter;
69 import lombok.ToString;
70 import lombok.extern.log4j.Log4j2;
71
72
73
74
75
76
77 @XmlRootElement
78 @XmlAccessorType(XmlAccessType.FIELD)
79 @Log4j2
80 @ToString
81 public final class Knowledge implements Cloneable {
82
83
84
85 public static final Class<?>[] CLASSES_FOR_JAXB = {Knowledge.class,
86 LocalizedString.class, Parameter.class, TimeScale.class, Note.class,
87
88 Average.class, AverageOfDiff.class, CompositeIndicator.class, DayOfYear.class, DiffOfSum.class,
89 Formula.class, Indicator.class, InjectedParameter.class, Max.class, MaxWaveLength.class, Min.class,
90 Normal.class, NumberOfDays.class, NumberOfWaves.class,
91 PotentialSowingDaysFrequency.class, Quotient.class,
92 SimpleIndicator.class, Sum.class, Tamm.class,
93
94 AggregationFunction.class, Exponential.class, JEXLFunction.class,
95 Linear.class, NormalizationFunction.class, Sigmoid.class,
96
97 ComparisonCriteria.class, CompositeCriteria.class, FormulaCriteria.class,
98 LogicalOperator.class, NoCriteria.class, RelationalOperator.class,
99 SimpleCriteria.class};
100
101
102
103
104 public static final Map<TimeScale, String> RESOURCES;
105
106 static {
107 RESOURCES = new EnumMap<>(TimeScale.class);
108 RESOURCES.put(TimeScale.DAILY, "/fr/inrae/agroclim/indicators/knowledge.xml");
109 RESOURCES.put(TimeScale.HOURLY, "/fr/inrae/agroclim/indicators/knowledge_hourly.xml");
110 }
111
112
113
114
115
116
117
118
119 private static Indicator getIndicator(@NonNull final String indicatorId,
120 final List<? extends Indicator> indicators) {
121 Indicator result = null;
122 for (final Indicator i : indicators) {
123 if (indicatorId.equals(i.getId())) {
124 result = i;
125 break;
126 }
127 }
128 return result;
129 }
130
131
132
133
134
135
136
137 public static Knowledge load() throws IndicatorsException {
138 return load(TimeScale.DAILY);
139 }
140
141
142
143
144
145
146
147
148
149
150 private static Knowledge load(final InputStream stream) throws IndicatorsException {
151 try {
152 final Knowledge knowledge = (Knowledge) XMLUtil.loadResource(stream,
153 CLASSES_FOR_JAXB);
154 knowledge.ecophysiologicalProcesses.forEach(ind ->
155 ind.setIndicatorCategory(IndicatorCategory.ECOPHYSIOLOGICAL_PROCESSES));
156 knowledge.indicators.forEach(ind ->
157 ind.setIndicatorCategory(IndicatorCategory.CLIMATIC_EFFECTS));
158 knowledge.culturalPractices.forEach(ind ->
159 ind.setIndicatorCategory(IndicatorCategory.CULTURAL_PRATICES));
160 return knowledge;
161 } catch (final IndicatorsException ex) {
162 LOGGER.error(ex);
163 throw ex;
164 }
165 }
166
167
168
169
170
171
172
173
174 public static Knowledge load(final TimeScale timescale) throws IndicatorsException {
175 final InputStream stream = Knowledge.class.getResourceAsStream(RESOURCES.get(timescale));
176 return load(stream);
177 }
178
179
180
181
182 @XmlElementWrapper(name = "culturalPractices")
183 @XmlElement(name = "culturalPractice")
184 @Setter
185 private List<CompositeIndicator> culturalPractices;
186
187
188
189
190 @XmlElementWrapper(name = "ecophysiologicalProcesses")
191 @XmlElement(name = "ecophysiologicalProcess")
192 @Setter
193 private List<CompositeIndicator> ecophysiologicalProcesses;
194
195
196
197
198 @XmlElementWrapper(name = "parameters")
199 @XmlElement(name = "parameter")
200 @Getter
201 @Setter
202 private List<Parameter> parameters;
203
204
205
206
207 @Getter
208 @Setter
209 @XmlAttribute
210 private TimeScale timescale = TimeScale.DAILY;
211
212
213
214
215 @XmlElementWrapper(name = "notes")
216 @XmlElement(name = "note")
217 @Getter
218 @Setter
219 private List<Note> notes;
220
221
222
223
224 @XmlElementWrapper(name = "units")
225 @XmlElement(name = "unit")
226 @Getter
227 @Setter
228 private List<Unit> units;
229
230
231
232
233 @XmlElementWrapper(name = "climaticEffects")
234 @XmlElement(name = "climaticEffect")
235 @Getter
236 @Setter
237 private List<CompositeIndicator> indicators;
238
239
240
241
242 public Knowledge() {
243 culturalPractices = new ArrayList<>();
244 ecophysiologicalProcesses = new ArrayList<>();
245 indicators = new ArrayList<>();
246 }
247
248 @Override
249 public Knowledge clone() {
250 final Knowledge clone = new Knowledge();
251 culturalPractices.forEach(practice -> clone.culturalPractices.add(practice.clone()));
252 ecophysiologicalProcesses.forEach(process -> clone.ecophysiologicalProcesses.add(process.clone()));
253 indicators.forEach(clone.indicators::add);
254 return clone;
255 }
256
257
258
259
260
261 private Indicator getClimaticEffectsIndicator(final String indicatorId) {
262 return getIndicator(indicatorId, indicators);
263 }
264
265
266
267
268 public List<CompositeIndicator> getCulturalPractices() {
269 return culturalPractices;
270 }
271
272
273
274
275 public List<CompositeIndicator> getEcophysiologicalProcesses() {
276 return ecophysiologicalProcesses;
277 }
278
279
280
281
282
283
284
285 public Indicator getIndicator(@NonNull final String indicatorId) {
286 Indicator result;
287 for (final CompositeIndicator indicator : indicators) {
288 if (indicatorId.equals(indicator.getId())) {
289 return indicator;
290 }
291 result = getIndicator(indicatorId, indicator.getIndicators());
292 if (result != null) {
293 result.setParent(indicator);
294 return result;
295 }
296 }
297 for (final CompositeIndicator indicator : culturalPractices) {
298 if (indicatorId.equals(indicator.getId())) {
299 return indicator;
300 }
301 result = getIndicator(indicatorId, indicator.getIndicators());
302 if (result != null) {
303 result.setParent(indicator);
304 return result;
305 }
306 }
307 for (final CompositeIndicator indicator : ecophysiologicalProcesses) {
308 if (indicatorId.equals(indicator.getId())) {
309 return indicator;
310 }
311 result = getIndicator(indicatorId, indicator.getIndicators());
312 if (result != null) {
313 result.setParent(indicator);
314 return result;
315 }
316 }
317 return null;
318 }
319
320
321
322
323
324
325
326 public List<? extends Indicator> getNextIndicators(
327 final CompositeIndicator ind) {
328 final IndicatorCategory category = ind.getIndicatorCategory();
329 final String id = ind.getId();
330 if (category == null) {
331 throw new IllegalArgumentException("No indicator category for "
332 + id);
333 }
334 switch (category) {
335 case PHENO_PHASES:
336 return getEcophysiologicalProcesses();
337 case CULTURAL_PRATICES:
338 if (ind.isPhase()) {
339 return getCulturalPractices();
340 }
341 return getIndicators();
342 case ECOPHYSIOLOGICAL_PROCESSES:
343 if (ind.isPhase()) {
344 return getEcophysiologicalProcesses();
345 }
346 return getIndicators();
347 case CLIMATIC_EFFECTS:
348 return ((CompositeIndicator) getClimaticEffectsIndicator(id))
349 .getIndicators();
350 default:
351 LOGGER.fatal("Not handled category: " + category);
352 throw new IllegalArgumentException("Not handled category: "
353 + category);
354 }
355 }
356
357 public Parameter getParameterById(final String id) {
358 return this.parameters.stream()
359 .filter(parameter -> parameter.getId().equals(id))
360 .findFirst().orElse(null);
361 }
362
363
364
365
366
367 public void setI18n(final Indicator indicator) {
368 if (indicator.getId() == null) {
369 throw new IllegalArgumentException("indicator.id must not be null! "
370 + indicator.getId());
371 }
372 final Indicator ind = getIndicator(indicator.getId());
373 if (ind != null) {
374 if (indicator.getNames() == null) {
375 indicator.setNames(new ArrayList<>());
376 } else {
377 indicator.getNames().clear();
378 }
379 if (ind.getNames() != null) {
380 indicator.getNames().addAll(ind.getNames());
381 }
382 if (indicator.getDescriptions() == null) {
383 indicator.setDescriptions(new ArrayList<>());
384 } else {
385 indicator.getDescriptions().clear();
386 }
387 if (ind.getDescriptions() != null) {
388 indicator.getDescriptions().addAll(ind.getDescriptions());
389 }
390 }
391 if (indicator instanceof CompositeIndicator) {
392 ((CompositeIndicator) indicator).getIndicators().forEach(this::setI18n);
393 }
394 }
395 }