1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package fr.inrae.agroclim.indicators.model.data;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.EnumMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Set;
28
29 import fr.inrae.agroclim.indicators.exception.ErrorMessage;
30 import fr.inrae.agroclim.indicators.exception.type.ResourceErrorType;
31 import fr.inrae.agroclim.indicators.model.TimeScale;
32 import fr.inrae.agroclim.indicators.model.data.Variable.Type;
33 import fr.inrae.agroclim.indicators.model.data.climate.ClimaticResource;
34 import fr.inrae.agroclim.indicators.model.data.phenology.PhenologicalResource;
35 import fr.inrae.agroclim.indicators.util.DateUtils;
36 import lombok.Getter;
37 import lombok.NonNull;
38 import lombok.Setter;
39 import lombok.extern.log4j.Log4j2;
40
41
42
43
44
45
46
47
48
49
50
51
52 @Log4j2
53 public final class ResourceManager implements Serializable, Cloneable {
54
55
56
57
58 private static final long serialVersionUID = 5645729254868967485L;
59
60
61
62
63 private static void addErrorMessage(
64 final Map<ResourceErrorType, ErrorMessage> errors,
65 final ResourceErrorType errorI18nKey) {
66 errors.put(errorI18nKey.getParent(), new ErrorMessage(
67 "fr.inrae.agroclim.indicators.resources.messages",
68 errorI18nKey, null));
69 }
70
71
72
73
74 @Getter
75 private final ClimaticResource climaticResource;
76
77
78
79
80 @Setter
81 @NonNull
82 private Integer cropDevelopmentYears = 1;
83
84
85
86
87 @Getter
88 private final PhenologicalResource phenologicalResource;
89
90
91
92
93 @Setter
94 private TimeScale timeScale;
95
96
97
98
99 @Setter
100 private Set<Variable> variables;
101
102
103
104
105 public ResourceManager() {
106 climaticResource = new ClimaticResource();
107 phenologicalResource = new PhenologicalResource();
108 }
109
110
111
112
113
114
115
116
117
118 public ResourceManager(final ClimaticResource climatic,
119 final PhenologicalResource phenological) {
120 climaticResource = climatic;
121 phenologicalResource = phenological;
122 }
123
124 @Override
125 public ResourceManager clone() throws CloneNotSupportedException {
126 return new ResourceManager(climaticResource.clone(), phenologicalResource.clone());
127 }
128
129
130
131
132 public Map<ResourceErrorType, ErrorMessage> getConsitencyErrors() {
133 final Map<ResourceErrorType, ErrorMessage> errors = new EnumMap<>(ResourceErrorType.class);
134
135 if (variables == null) {
136 addErrorMessage(errors, ResourceErrorType.VARIABLES_MISSING);
137 return errors;
138 }
139 if (variables.isEmpty()) {
140 addErrorMessage(errors, ResourceErrorType.VARIABLES_EMPTY);
141 return errors;
142 }
143
144 final List<Integer> climaticYears = climaticResource.getYears();
145 if (hasClimaticVariables() && climaticResource.getData().isEmpty()) {
146 addErrorMessage(errors, ResourceErrorType.CLIMATE_EMPTY);
147 } else {
148
149 final int nbClimatic = climaticResource.getData().size();
150 int nb = 0;
151 nb = climaticYears.stream()
152 .map(DateUtils::nbOfDays)
153 .reduce(nb, Integer::sum);
154 if (timeScale == TimeScale.HOURLY) {
155 nb = nb * DateUtils.NB_OF_HOURS_IN_DAY;
156 }
157 if (nbClimatic != nb) {
158 addErrorMessage(errors, ResourceErrorType.CLIMATE_SIZE_WRONG);
159 }
160 }
161
162 if (phenologicalResource.isEmpty()) {
163 addErrorMessage(errors, ResourceErrorType.PHENO_EMPTY);
164 }
165 if (!errors.isEmpty()) {
166 return errors;
167 }
168
169 final List<Integer> phenoYears = phenologicalResource.getYears();
170 if (hasClimaticVariables() && climaticYears.isEmpty()) {
171 addErrorMessage(errors, ResourceErrorType.CLIMATE_YEARS_EMPTY);
172 }
173 if (phenoYears.isEmpty()) {
174 addErrorMessage(errors, ResourceErrorType.PHENO_YEARS_EMPTY);
175 }
176 if (!errors.isEmpty()) {
177 return errors;
178 }
179 if (cropDevelopmentYears == null) {
180 addErrorMessage(errors, ResourceErrorType.RESOURCES_CROPDEVELOPMENT_YEARS);
181 return errors;
182 }
183
184
185
186 final List<Integer> expectedClimateYears = new ArrayList<>(phenoYears);
187 Collections.sort(expectedClimateYears);
188 for (int i = 1; i < cropDevelopmentYears; i++) {
189 LOGGER.trace("First year {} will not have phenological stages.", expectedClimateYears.get(0));
190 expectedClimateYears.remove(0);
191 }
192 if (hasClimaticVariables()
193 && !climaticYears.containsAll(expectedClimateYears)) {
194 final Collection<Serializable> theMissing = new ArrayList<>(expectedClimateYears);
195 theMissing.removeAll(climaticYears);
196 final ErrorMessage error = new ErrorMessage(
197 "fr.inrae.agroclim.indicators.resources.messages",
198 ResourceErrorType.CLIMATE_YEARS_MISSING,
199 theMissing);
200 errors.put(ResourceErrorType.CLIMATE_YEARS_MISSING.getParent(), error);
201 }
202 if (!errors.isEmpty()) {
203 return errors;
204 }
205
206 if (hasClimaticVariables() && hasSoilVariables()) {
207 int nbDays = 0;
208 for (final int year : climaticYears) {
209 nbDays += DateUtils.nbOfDays(year);
210 }
211 final int nbSoil = climaticResource.getData().size();
212 if (nbSoil != nbDays) {
213 addErrorMessage(errors, ResourceErrorType.SOIL_SIZE_WRONG);
214 }
215 }
216 if (errors.isEmpty()) {
217 return null;
218 } else {
219 return errors;
220 }
221 }
222
223
224
225
226
227 public boolean hasClimaticVariables() {
228 Objects.requireNonNull(variables, "variables is not set!");
229 return variables.stream().anyMatch(variable -> variable != null && Type.CLIMATIC.equals(variable.getType()));
230 }
231
232
233
234
235
236 public boolean hasSoilVariables() {
237 Objects.requireNonNull(variables, "variables is not set!");
238 return variables.stream().anyMatch(variable -> variable != null && Type.SOIL.equals(variable.getType()));
239 }
240
241 }