View Javadoc
1   /**
2    * This file is part of Indicators.
3    *
4    * Indicators is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * Indicators is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with Indicators. If not, see <https://www.gnu.org/licenses/>.
16   */
17  package fr.inrae.agroclim.indicators.model.data.phenology;
18  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.Objects;
25  import java.util.stream.Collectors;
26  
27  import fr.inrae.agroclim.indicators.model.data.Data;
28  import fr.inrae.agroclim.indicators.resources.Messages;
29  import fr.inrae.agroclim.indicators.util.DateUtils;
30  import lombok.Getter;
31  import lombok.Setter;
32  
33  /**
34   * List of stages for a year.
35   *
36   * Last change $Date$
37   *
38   * @author $Author$
39   * @version $Revision$
40   */
41  public class AnnualStageData implements Cloneable, Data, Serializable {
42      /**
43       * UUID for Serializable.
44       */
45      private static final long serialVersionUID = 1367181411523509675L;
46      /**
47       * Name of required columns, used by GUI.
48       */
49      public static final List<String> REQUIRED = Arrays.asList("year");
50      /**
51       * Name of all variables, used by GUI.
52       */
53      public static final List<String> VAR_NAMES = Arrays.asList("year");
54      /**
55       * Error messages.
56       */
57      private final List<String> errors = new ArrayList<>();
58  
59      /**
60       * All stages are defined for the year.
61       *
62       * Ex.: false if phenological development does not ends (model was not able
63       * to compute all stages).
64       */
65      @Getter
66      @Setter
67      private boolean isComplete = true;
68  
69      /**
70       * Stage list.
71       */
72      @Getter
73      @Setter
74      private List<Stage> stages;
75  
76      /**
77       * Warning messages.
78       */
79      private final List<String> warnings = new ArrayList<>();
80  
81      /**
82       * Harvest year for the stage list.
83       */
84      @Getter
85      @Setter
86      private Integer year;
87  
88      /**
89       * Constructor.
90       */
91      public AnnualStageData() {
92          super();
93      }
94  
95      /**
96       * Constructor.
97       *
98       * @param y year of list
99       */
100     public AnnualStageData(final Integer y) {
101         this.year = y;
102         stages = new ArrayList<>();
103     }
104 
105     /**
106      * @param name stage name
107      * @param value doy
108      */
109     public final void add(final String name, final int value) {
110         if (getStages() == null) {
111             stages = new ArrayList<>();
112         }
113         Stage myStage = new Stage(name, value);
114         getStages().add(myStage);
115     }
116 
117     @Override
118     public final void check(final int line, final String path) {
119         if (year == 0) {
120             errors.add(Messages.format("error.year.null", path, line));
121         }
122 
123         if (stages.size() < 2) {
124             errors.add(Messages.format("error.minimal.stages", path, line));
125         }
126 
127         for (final Stage stage : stages) {
128             List<Stage> nextStages = stages.subList(stages.indexOf(stage),
129                     stages.size());
130             for (final Stage nextStage : nextStages) {
131                 // TODO ignorer les stades relatifs
132                 if (stage.getValue() > nextStage.getValue()) {
133                     errors.add(Messages.format(
134                             "error.endstage.superiorto.startstage",
135                             path, line));
136                 }
137             }
138         }
139     }
140 
141     @Override
142     public final AnnualStageData clone() throws CloneNotSupportedException {
143         AnnualStageData clone = (AnnualStageData) super.clone();
144         Collections.copy(clone.errors, errors);
145         if (stages != null) {
146             clone.stages = new ArrayList<>();
147             for (final Stage stage : stages) {
148                 clone.stages.add(stage.clone());
149             }
150         }
151         Collections.copy(clone.warnings, warnings);
152         clone.year = year;
153         return clone;
154     }
155 
156     @Override
157     public final boolean equals(final Object obj) {
158         if (this == obj) {
159             return true;
160         }
161         if (obj == null) {
162             return false;
163         }
164         if (getClass() != obj.getClass()) {
165             return false;
166         }
167         AnnualStageData other = (AnnualStageData) obj;
168         if (stages == null) {
169             if (other.stages != null) {
170                 return false;
171             }
172         } else if (!stages.equals(other.stages)) {
173             return false;
174         }
175         if (year == null) {
176             if (other.year != null) {
177                 return false;
178             }
179         } else if (!year.equals(other.year)) {
180             return false;
181         }
182         return true;
183     }
184 
185     /**
186      * @return if a stage is in next year
187      */
188     public final boolean existWinterCrop() {
189         final int nbOfDays = DateUtils.nbOfDays(year);
190         return stages.stream().anyMatch(
191                 stage -> stage.getValue() > nbOfDays
192         );
193     }
194 
195     @Override
196     public final List<String> getErrors() {
197         return errors;
198     }
199 
200     /**
201      * @param name stage name
202      * @return DOY of stage or null
203      */
204     public final Integer getStageValue(final String name) {
205         Integer value = null;
206         for (final Stage stage : stages) {
207             if (stage.getName().equals(name)) {
208                 return stage.getValue();
209             }
210         }
211         // s1p1 => s1+1
212         final String relativeName = name.replace("m", "-").replace("p", "+");
213         for (final Stage stage : stages) {
214             if (stage.getName().equals(relativeName)) {
215                 return stage.getValue();
216             }
217         }
218         return value;
219     }
220 
221     @Override
222     public final List<String> getWarnings() {
223         return warnings;
224     }
225 
226     @Override
227     public final int hashCode() {
228         final int prime = 31;
229         int result = 1;
230         result = prime * result + Objects.hashCode(stages);
231         result = prime * result + Objects.hashCode(year);
232         return result;
233     }
234     /**
235      * @return short string representation
236      */
237     public final String toShortString() {
238         final StringBuilder sb = new StringBuilder("AnnualStageData [year=").append(year).append(", stages=");
239         stages.stream() //
240                 .map(s -> s.getName().concat("=").concat(String.valueOf(s.getValue()))) //
241                 .collect(Collectors.joining(", ", "[", "]"));
242         sb.append("]");
243         return sb.toString();
244     }
245 
246     @Override
247     public final String toString() {
248         return "AnnualStageData [year=" + year + ", stages=" + stages + "]";
249     }
250 }