1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package fr.inrae.agroclim.indicators.model.indicator;
18
19 import java.io.Serializable;
20 import java.text.NumberFormat;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Locale;
24 import java.util.Map;
25 import java.util.Objects;
26
27 import javax.swing.event.EventListenerList;
28
29 import fr.inrae.agroclim.indicators.model.Computable;
30 import fr.inrae.agroclim.indicators.model.EvaluationType;
31 import fr.inrae.agroclim.indicators.model.HasParameters;
32 import fr.inrae.agroclim.indicators.model.Knowledge;
33 import fr.inrae.agroclim.indicators.model.LocalizedString;
34 import fr.inrae.agroclim.indicators.model.Nameable;
35 import fr.inrae.agroclim.indicators.model.Note;
36 import fr.inrae.agroclim.indicators.model.Parameter;
37 import fr.inrae.agroclim.indicators.model.Quantifiable;
38 import fr.inrae.agroclim.indicators.model.TimeScale;
39 import fr.inrae.agroclim.indicators.model.Unit;
40 import fr.inrae.agroclim.indicators.model.data.UseVariables;
41 import fr.inrae.agroclim.indicators.model.function.normalization.NormalizationFunction;
42 import fr.inrae.agroclim.indicators.model.indicator.listener.HasIndicatorListener;
43 import fr.inrae.agroclim.indicators.model.indicator.listener.IndicatorEvent;
44 import fr.inrae.agroclim.indicators.model.indicator.listener.IndicatorListener;
45 import jakarta.xml.bind.annotation.XmlAccessType;
46 import jakarta.xml.bind.annotation.XmlAccessorType;
47 import jakarta.xml.bind.annotation.XmlElement;
48 import jakarta.xml.bind.annotation.XmlElementWrapper;
49 import jakarta.xml.bind.annotation.XmlIDREF;
50 import jakarta.xml.bind.annotation.XmlSeeAlso;
51 import jakarta.xml.bind.annotation.XmlTransient;
52 import jakarta.xml.bind.annotation.XmlType;
53 import lombok.AccessLevel;
54 import lombok.EqualsAndHashCode;
55 import lombok.Getter;
56 import lombok.NonNull;
57 import lombok.Setter;
58 import lombok.extern.log4j.Log4j2;
59
60
61
62
63
64
65
66
67
68
69 @EqualsAndHashCode(
70 callSuper = false,
71 of = {"category", "id", "timescale", "parent"}
72 )
73 @Log4j2
74 @XmlAccessorType(XmlAccessType.FIELD)
75 @XmlSeeAlso({SimpleIndicator.class, CompositeIndicator.class})
76 @XmlType(propOrder = {"descriptions", "names", "id", "category", "color",
77 "timescale", "normalizationFunction", "parameters", "notes", "unit"})
78 public abstract class Indicator implements Cloneable,
79 Computable, HasIndicatorListener, HasParameters, Nameable, Quantifiable,
80 Serializable, UseVariables {
81
82
83
84
85 private static final long serialVersionUID = 6030595237342422007L;
86
87
88
89
90
91
92 @XmlElement
93 @Getter
94 @Setter
95 private String category;
96
97
98
99
100 @XmlElement
101 @Getter
102 @Setter
103 private String color;
104
105
106
107
108 @XmlElement(name = "description")
109 @Getter
110 @Setter
111 private List<LocalizedString> descriptions;
112
113
114
115
116 @XmlElement(required = true)
117 @Getter
118 @Setter
119 private String id;
120
121
122
123
124
125 @XmlTransient
126 @Getter
127 @Setter
128 private boolean isComputable = true;
129
130
131
132
133 @XmlTransient
134 @Getter(AccessLevel.PROTECTED)
135 private final EventListenerList listeners = new EventListenerList();
136
137
138
139
140 @XmlElement(name = "name")
141 @Getter(AccessLevel.PUBLIC)
142 @Setter
143 private List<LocalizedString> names;
144
145
146
147
148 @XmlElement
149 @Getter
150 @Setter
151 private NormalizationFunction normalizationFunction;
152
153
154
155
156 @XmlTransient
157 @Getter
158 @Setter
159 private Double notNormalizedValue;
160
161
162
163
164 @XmlElementWrapper(name = "parameters")
165 @XmlElement(name = "parameter")
166 @Getter
167 @Setter
168 private List<Parameter> parameters;
169
170
171
172
173 @XmlTransient
174 @Getter
175 @Setter
176 private Indicator parent;
177
178
179
180
181 @Getter
182 @Setter
183 private TimeScale timescale = TimeScale.DAILY;
184
185
186
187
188 @XmlTransient
189 @Getter
190 @Setter
191 private Double value;
192
193
194
195
196 @XmlElement(name = "note")
197 @Getter
198 @Setter
199 @XmlIDREF
200 private List<Note> notes;
201
202
203
204
205
206
207 @XmlElement(name = "unit")
208 @Getter
209 @Setter
210 @XmlIDREF
211 private Unit unit;
212
213
214
215
216 protected Indicator() {
217 }
218
219 @Override
220 public final void addIndicatorListener(final IndicatorListener listener) {
221 if (getIndicatorListeners() != null) {
222 for (final IndicatorListener l : getIndicatorListeners()) {
223 if (listener.equals(l)) {
224 return;
225 }
226 }
227 }
228 listeners.add(IndicatorListener.class, listener);
229 if (this instanceof CompositeIndicator compositeIndicator) {
230 compositeIndicator.getIndicators()
231 .forEach(i -> i.addIndicatorListener(listener));
232 }
233 }
234
235 @Override
236 @SuppressWarnings("checkstyle:DesignForExtension")
237 public Indicator clone() throws CloneNotSupportedException {
238 final Indicator clone = (Indicator) super.clone();
239 clone.category = category;
240 clone.color = color;
241 if (descriptions != null) {
242 clone.descriptions = new ArrayList<>();
243 for (final LocalizedString description : descriptions) {
244 clone.descriptions.add(description.clone());
245 }
246 }
247 clone.id = id;
248
249 clone.isComputable = isComputable;
250 if (names != null) {
251 clone.names = new ArrayList<>();
252 for (final LocalizedString name : names) {
253 clone.names.add(name.clone());
254 }
255 }
256 if (normalizationFunction != null) {
257 clone.normalizationFunction = normalizationFunction.clone();
258 }
259 clone.notNormalizedValue = notNormalizedValue;
260 if (parameters != null) {
261 clone.parameters = new ArrayList<>();
262 for (final Parameter param : parameters) {
263 clone.parameters.add(param.clone());
264 }
265 }
266
267 clone.parent = parent;
268 clone.value = value;
269 return clone;
270 }
271
272 @Override
273 public final void fireIndicatorEvent(final IndicatorEvent event) {
274 LOGGER.traceEntry("{} : {} {}", getId(), event.getSource().getId(),
275 event.getAssociatedType());
276 if (getIndicatorListeners() == null
277 || getIndicatorListeners().length == 0) {
278 if (getParent() != null) {
279 getParent().fireIndicatorEvent(event);
280 }
281 return;
282 }
283 for (final IndicatorListener listener : getIndicatorListeners()) {
284 listener.onIndicatorEvent(event);
285 }
286 }
287
288
289
290
291 public abstract void fireValueUpdated();
292
293
294
295
296
297 public final String getDescription(@NonNull final String languageCode) {
298 return LocalizedString.getString(descriptions, languageCode);
299 }
300
301
302
303
304 public final IndicatorCategory getIndicatorCategory() {
305 return IndicatorCategory.getByTag(category);
306 }
307
308 @Override
309 public final IndicatorListener[] getIndicatorListeners() {
310 return listeners.getListeners(IndicatorListener.class);
311 }
312
313 @Override
314 public final String getName() {
315 final String langCode = Locale.getDefault().getLanguage();
316 return getName(langCode);
317 }
318
319 @Override
320 public final String getName(final Locale locale) {
321 final String langCode = locale.getLanguage();
322 return getName(langCode);
323 }
324
325
326
327
328
329 public final String getName(@NonNull final String languageCode) {
330 return LocalizedString.getString(names, languageCode);
331 }
332
333
334
335
336 public final String getPath() {
337 if (parent != null) {
338 return parent.getPath() + "/" + id;
339 }
340 return id;
341 }
342
343
344
345
346
347
348 public final String getPrettyDescription(
349 @NonNull final String languageCode) {
350 String description = getDescription(languageCode);
351 final Locale locale = Locale.forLanguageTag(languageCode);
352 final NumberFormat nf = NumberFormat.getInstance(locale);
353 for (final Map.Entry<String, Double> entry : getParametersValues()
354 .entrySet()) {
355 description = description.replace(
356 "{" + entry.getKey() + "}",
357 nf.format(entry.getValue())
358 );
359 }
360 return description;
361 }
362
363
364
365
366 public abstract EvaluationType getType();
367
368
369
370
371 public final void setIndicatorCategory(
372 @NonNull final IndicatorCategory indicatorCategory) {
373 category = indicatorCategory.getTag();
374 }
375
376
377
378
379
380 public final void setName(
381 @NonNull final String languageCode, final String name) {
382 if (names == null) {
383 names = new ArrayList<>();
384 }
385 for (final LocalizedString string : names) {
386 if (Objects.equals(string.getLang(), languageCode)) {
387 string.setValue(name);
388 return;
389 }
390 }
391 final LocalizedString string = new LocalizedString();
392 string.setLang(languageCode);
393 string.setValue(name);
394 names.add(string);
395 }
396
397
398
399
400
401
402
403 public abstract void setParametersFromKnowledge(Knowledge knowledge);
404
405 @Override
406 public final String toString() {
407 return getName();
408 }
409
410
411
412
413
414 public abstract String toStringTree(String indent);
415
416
417
418
419
420 protected final String toStringTreeBase(final String indent) {
421 final StringBuilder sb = new StringBuilder();
422 sb.append(indent).append(" id: ").append(getId()).append("\n");
423 sb.append(indent).append(" name: ").append(getName()).append("\n");
424 sb.append(indent).append(" class: ").append(getClass().getName())
425 .append("\n");
426 sb.append(indent).append(" category: ").append(category).append("\n");
427 return sb.toString();
428 }
429 }