View Javadoc
1   package fr.ifremer.dali.dto;
2   
3   /*
4    * #%L
5    * Dali :: Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   *
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  //import com.google.common.collect.*;
27  
28  import com.google.common.collect.Multimap;
29  import fr.ifremer.dali.dto.configuration.control.ControlRuleDTO;
30  import fr.ifremer.dali.dto.configuration.control.RulePmfmDTO;
31  import fr.ifremer.dali.dto.configuration.filter.FilterDTO;
32  import fr.ifremer.dali.dto.configuration.programStrategy.AppliedStrategyDTO;
33  import fr.ifremer.dali.dto.configuration.programStrategy.PmfmStrategyDTO;
34  import fr.ifremer.dali.dto.configuration.programStrategy.ProgramDTO;
35  import fr.ifremer.dali.dto.data.measurement.MeasurementAware;
36  import fr.ifremer.dali.dto.data.measurement.MeasurementDTO;
37  import fr.ifremer.dali.dto.data.sampling.SamplingOperationDTO;
38  import fr.ifremer.dali.dto.data.survey.CampaignDTO;
39  import fr.ifremer.dali.dto.data.survey.SurveyDTO;
40  import fr.ifremer.dali.dto.enums.*;
41  import fr.ifremer.dali.dto.referential.LocationDTO;
42  import fr.ifremer.dali.dto.referential.pmfm.PmfmDTO;
43  import fr.ifremer.dali.dto.system.extraction.ExtractionDTO;
44  import fr.ifremer.dali.dto.system.extraction.ExtractionPeriodDTO;
45  import fr.ifremer.quadrige3.core.dao.technical.Dates;
46  import fr.ifremer.quadrige3.core.dao.technical.Times;
47  import fr.ifremer.quadrige3.synchro.vo.SynchroDateOperatorVO;
48  import fr.ifremer.quadrige3.ui.core.dto.CodeOnly;
49  import fr.ifremer.quadrige3.ui.core.dto.QuadrigeBean;
50  import fr.ifremer.quadrige3.ui.core.dto.QuadrigeBeans;
51  import org.apache.commons.collections4.CollectionUtils;
52  import org.apache.commons.lang3.StringUtils;
53  import org.nuiton.util.DateUtil;
54  
55  import javax.annotation.Nonnull;
56  import java.sql.Array;
57  import java.sql.SQLException;
58  import java.time.format.DateTimeFormatter;
59  import java.util.*;
60  import java.util.function.Function;
61  import java.util.stream.Collectors;
62  
63  /**
64   * useful methods around Dali bean.
65   *
66   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
67   * <p>
68   * TODO move common methods to fr.ifremer.quadrige3.core.dao.technical.Beans
69   */
70  public class DaliBeans extends QuadrigeBeans {
71  
72      /**
73       * <p>Constructor for DaliBeans.</p>
74       */
75      protected DaliBeans() {
76          // helper class does not instantiate
77      }
78  
79      /**
80       * <p>toString.</p>
81       *
82       * @param beans a {@link Collection} object.
83       * @return a {@link String} object.
84       */
85      public static String toString(Collection<? extends QuadrigeBean> beans) {
86          return beans != null
87                  ? transformCollection(beans, DaliBeans::toString).stream().collect(Collectors.joining(",", "[", "]"))
88                  : "[]";
89      }
90  
91      /**
92       * Utility method to represent a bean as String where a decorator is not declared (for dates)
93       *
94       * @param bean a {@link QuadrigeBean} object.
95       * @return a {@link String} object.
96       */
97      public static String toString(QuadrigeBean bean) {
98  
99          // try to represent a bean to a string
100         if (bean instanceof SurveyDTO) {
101             SurveyDTO survey = (SurveyDTO) bean;
102             final StringBuilder result = new StringBuilder(50);
103             if (survey.getLocation() != null) {
104                 result.append(survey.getLocation().getName());
105             }
106             if (survey.getDate() != null) {
107                 result.append(DEFAULT_STRING_SEPARATOR);
108                 result.append(survey.getDate().format(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
109             }
110             if (survey.getTime() != null) {
111                 result.append(DEFAULT_STRING_SEPARATOR);
112                 result.append(Times.secondsToString(survey.getTime()));
113             }
114             if (survey.getProgram() != null) {
115                 result.append(DEFAULT_STRING_SEPARATOR);
116                 result.append(survey.getProgram().getCode());
117             }
118             if (survey.getName() != null) {
119                 result.append(DEFAULT_STRING_SEPARATOR);
120                 result.append(survey.getName());
121             }
122             return result.toString();
123 
124         } else if (bean instanceof CampaignDTO) {
125             CampaignDTO campaign = (CampaignDTO) bean;
126             final StringBuilder result = new StringBuilder(50);
127             result.append(campaign.getName()).append(DEFAULT_STRING_SEPARATOR).append(DateUtil.formatDate(campaign.getStartDate(), DEFAULT_DATE_FORMAT));
128             if (campaign.getEndDate() != null) {
129                 result.append(" => ").append(DateUtil.formatDate(campaign.getEndDate(), DEFAULT_DATE_FORMAT));
130             }
131             return result.toString();
132 
133         } else if (bean instanceof ExtractionPeriodDTO) {
134             ExtractionPeriodDTO period = (ExtractionPeriodDTO) bean;
135             return Dates.formatDate(period.getStartDate(), DEFAULT_DATE_FORMAT)
136                     + " => "
137                     + Dates.formatDate(period.getEndDate(), DEFAULT_DATE_FORMAT);
138         }
139 
140         return bean != null ? bean.toString() : "null";
141     }
142 
143     public static List<MeasurementDTO> duplicate(List<MeasurementDTO> measurements) {
144         if (measurements == null)
145             return new ArrayList<>();
146         return measurements.stream().map(DaliBeans::duplicate).collect(Collectors.toList());
147     }
148 
149     public static MeasurementDTO duplicate(MeasurementDTO measurement) {
150         // Clone object
151         MeasurementDTO clone = clone(measurement);
152 
153         // Remove Id & individualId
154         clone.setId(null);
155         clone.setIndividualId(null);
156 
157         // remove errors
158         clone.setErrors(null);
159 
160         return clone;
161     }
162 
163     public static SamplingOperationDTO duplicate(SamplingOperationDTO samplingOperation) {
164 
165         // Duplicate sampling operation
166         final SamplingOperationDTO clone = DaliBeans.clone(samplingOperation);
167 
168         // Remove ID
169         clone.setId(null);
170 
171         // duplicate coordinate
172         clone.setCoordinate(DaliBeans.clone(samplingOperation.getCoordinate()));
173 
174         // remove errors
175         clone.setErrors(null);
176 
177         // duplicate measurements
178         clone.setMeasurements(DaliBeans.duplicate(samplingOperation.getMeasurements()));
179         clone.setIndividualMeasurements(DaliBeans.duplicate(samplingOperation.getIndividualMeasurements()));
180 
181         return clone;
182 
183     }
184 
185     /**
186      * <p>filterNotEmptyAppliedPeriod.</p>
187      *
188      * @param appliedPeriods a {@link Collection} object.
189      * @return a {@link Collection} object.
190      */
191     public static Collection<AppliedStrategyDTO> filterNotEmptyAppliedPeriod(Collection<AppliedStrategyDTO> appliedPeriods) {
192         if (appliedPeriods == null) {
193             return null;
194         }
195         return filterCollection(appliedPeriods, input -> input != null && (input.getStartDate() != null || input.getEndDate() != null || input.getSamplingDepartment() != null));
196 //                Collections2.filter(appliedPeriods, input -> input != null && (input.getStartDate() != null || input.getEndDate() != null || input.getSamplingDepartment() != null));
197     }
198 
199     /**
200      * <p>getDateOperator.</p>
201      *
202      * @param searchDate a {@link SearchDateDTO} object.
203      * @return a {@link SynchroDateOperatorVO} object.
204      */
205     public static SynchroDateOperatorVO getDateOperator(SearchDateDTO searchDate) {
206         if (searchDate != null) {
207             SearchDateValues searchDateValue = SearchDateValues.values()[searchDate.getId()];
208             switch (searchDateValue) {
209 
210                 case EQUALS:
211                     return SynchroDateOperatorVO.EQUALS;
212                 case BETWEEN:
213                     return SynchroDateOperatorVO.BETWEEN;
214                 case BEFORE:
215                     return SynchroDateOperatorVO.BEFORE;
216                 case BEFORE_OR_EQUALS:
217                     return SynchroDateOperatorVO.BEFORE_OR_EQUALS;
218                 case AFTER:
219                     return SynchroDateOperatorVO.AFTER;
220                 case AFTER_OR_EQUALS:
221                     return SynchroDateOperatorVO.AFTER_OR_EQUALS;
222             }
223         }
224         return null;
225     }
226 
227     /**
228      * <p>locationToAppliedStrategyDTO.</p>
229      *
230      * @param location a {@link LocationDTO} object.
231      * @return a {@link AppliedStrategyDTO} object.
232      */
233     public static AppliedStrategyDTO locationToAppliedStrategyDTO(LocationDTO location) {
234         if (location == null) {
235             return null;
236         }
237         AppliedStrategyDTO result = DaliBeanFactory.newAppliedStrategyDTO();
238         result.setId(location.getId());
239         result.setLabel(location.getLabel());
240         result.setName(location.getName());
241         result.setComment(location.getComment());
242         result.setGrouping(location.getGrouping());
243         result.setStatus(location.getStatus());
244         return result;
245     }
246 
247     /**
248      * <p>locationsToAppliedStrategyDTOs.</p>
249      *
250      * @param locations a {@link List} object.
251      * @return a {@link List} object.
252      */
253     public static List<AppliedStrategyDTO> locationsToAppliedStrategyDTOs(List<LocationDTO> locations) {
254 
255         List<AppliedStrategyDTO> result = null;
256         if (locations != null) {
257             result = locations.stream().map(DaliBeans::locationToAppliedStrategyDTO).collect(Collectors.toList());
258         }
259         return result;
260     }
261 
262     public static PmfmStrategyDTO pmfmToPmfmStrategy(PmfmDTO pmfm) {
263         if (pmfm == null)
264             return null;
265         PmfmStrategyDTO result = DaliBeanFactory.newPmfmStrategyDTO();
266         result.setPmfm(pmfm);
267         return result;
268     }
269 
270     public static RulePmfmDTO pmfmToRulePmfm(PmfmDTO pmfm) {
271         if (pmfm == null)
272             return null;
273         RulePmfmDTO rulePmfm = DaliBeanFactory.newRulePmfmDTO();
274         rulePmfm.setPmfm(pmfm);
275         return rulePmfm;
276     }
277 
278     /**
279      * <p>filterPmfm.</p>
280      *
281      * @param pmfms           a {@link Collection} object.
282      * @param pmfmIdsToIgnore a {@link Collection} object.
283      * @return a {@link List} object.
284      */
285     public static List<PmfmDTO> filterPmfm(Collection<PmfmDTO> pmfms, final Collection<Integer> pmfmIdsToIgnore) {
286         return filterCollection(pmfms, input -> input != null && !pmfmIdsToIgnore.contains(input.getId()));
287     }
288 
289     public static List<PmfmDTO> filterQualitativePmfms(Collection<PmfmDTO> pmfms) {
290         return pmfms.stream().filter(pmfm -> pmfm.getParameter().isQualitative() && !pmfm.isQualitativeValuesEmpty()).collect(Collectors.toList());
291     }
292 
293     public static List<PmfmDTO> filterNumericalPmfms(Collection<PmfmDTO> pmfms) {
294         return pmfms.stream().filter(pmfm -> !pmfm.getParameter().isQualitative()).collect(Collectors.toList());
295     }
296 
297     /**
298      * Populates PMFMs ans individual PMFMs from existing measurements and individual measurements
299      *
300      * @param bean a {@link MeasurementAware} object.
301      */
302     public static void populatePmfmsFromMeasurements(MeasurementAware bean) {
303 
304         // populate pmfms from ungrouped measurements
305         if (CollectionUtils.isNotEmpty(bean.getMeasurements()) && bean.getPmfms() != null) {
306             for (MeasurementDTO measurement : bean.getMeasurements()) {
307                 // ignore individual measurement
308                 if (measurement.getIndividualId() != null) continue;
309                 if (measurement.getPmfm() != null && !bean.getPmfms().contains(measurement.getPmfm())) {
310                     bean.getPmfms().add(measurement.getPmfm());
311                 }
312             }
313         }
314 
315         // populate individual pmfms from grouped (individual) measurements
316         if (CollectionUtils.isNotEmpty(bean.getIndividualMeasurements()) && bean.getIndividualPmfms() != null) {
317             for (MeasurementDTO measurement : bean.getIndividualMeasurements()) {
318                 // ignore non-individual measurement
319                 if (measurement.getIndividualId() == null) continue;
320                 if (measurement.getPmfm() != null && !bean.getIndividualPmfms().contains(measurement.getPmfm())) {
321                     bean.getIndividualPmfms().add(measurement.getPmfm());
322                 }
323             }
324         }
325 
326     }
327 
328     /**
329      * Populates measurements and individualMeasurements from existing measurements of bean AND create empty measurements from PMFMs of bean<br>
330      * But don't add them to bean
331      *
332      * @param bean                   the source
333      * @param measurements           the output collection of measurements
334      * @param individualMeasurements the output collection of individual measurements
335      */
336     public static void populateMeasurementsFromPmfms(MeasurementAware bean, Collection<MeasurementDTO> measurements, Collection<MeasurementDTO> individualMeasurements) {
337 
338         Multimap<PmfmDTO, MeasurementDTO> existingMeasurementsByPmfm = populateByProperty(bean.getMeasurements(), MeasurementDTO.PROPERTY_PMFM);
339         Multimap<PmfmDTO, MeasurementDTO> existingIndividualMeasurementsByPmfm = populateByProperty(bean.getIndividualMeasurements(), MeasurementDTO.PROPERTY_PMFM);
340         List<Integer> existingIndividualIds = collectProperties(bean.getIndividualMeasurements(), MeasurementDTO.PROPERTY_INDIVIDUAL_ID);
341 
342         for (PmfmDTO pmfm : bean.getPmfms()) {
343             Collection<MeasurementDTO> existingMeasurements = existingMeasurementsByPmfm.get(pmfm);
344             if (CollectionUtils.isEmpty(existingMeasurements)) {
345                 MeasurementDTO measurement = DaliBeanFactory.newMeasurementDTO();
346                 measurement.setPmfm(pmfm);
347                 measurements.add(measurement);
348             } else {
349                 measurements.addAll(existingMeasurements);
350             }
351         }
352 
353         for (PmfmDTO individualPmfm : bean.getIndividualPmfms()) {
354 
355             // get all existing individual measurements
356             List<MeasurementDTO> existingIndividualMeasurements = new ArrayList<>(existingIndividualMeasurementsByPmfm.get(individualPmfm));
357 
358             // build all necessary measurement by individual id (with empty measurements if needed)
359             existingIndividualIds.stream().distinct().forEach(individualId -> {
360 
361                 MeasurementDTO existingIndividualMeasurement = findByProperty(existingIndividualMeasurements, MeasurementDTO.PROPERTY_INDIVIDUAL_ID, individualId);
362                 if (existingIndividualMeasurement == null) {
363                     // create empty measurement
364                     existingIndividualMeasurement = DaliBeanFactory.newMeasurementDTO();
365                     existingIndividualMeasurement.setPmfm(individualPmfm);
366                     existingIndividualMeasurement.setIndividualId(individualId);
367 
368                     // try to get other properties from another measurement in same individualId
369                     MeasurementDTO anotherIndividualMeasurement = findByProperty(bean.getIndividualMeasurements(), MeasurementDTO.PROPERTY_INDIVIDUAL_ID, individualId);
370                     if (anotherIndividualMeasurement != null) {
371                         existingIndividualMeasurement.setTaxonGroup(anotherIndividualMeasurement.getTaxonGroup());
372                         existingIndividualMeasurement.setTaxon(anotherIndividualMeasurement.getTaxon());
373                         existingIndividualMeasurement.setInputTaxonId(anotherIndividualMeasurement.getInputTaxonId());
374                         existingIndividualMeasurement.setInputTaxonName(anotherIndividualMeasurement.getInputTaxonName());
375                         existingIndividualMeasurement.setAnalyst(anotherIndividualMeasurement.getAnalyst());
376                     }
377 
378                     existingIndividualMeasurements.add(existingIndividualMeasurement);
379                 }
380             });
381 
382             // add all measurements (with empty)
383             individualMeasurements.addAll(existingIndividualMeasurements);
384         }
385 
386     }
387 
388     /**
389      * Create empty measurements on bean, based on pmfms ans idividualPmfms of bean.
390      * This will clear existing measurements first
391      *
392      * @param bean a {@link MeasurementAware} object.
393      */
394     public static void createEmptyMeasurements(MeasurementAware bean) {
395 
396         bean.getMeasurements().clear();
397         for (PmfmDTO pmfm : bean.getPmfms()) {
398             MeasurementDTO measurement = DaliBeanFactory.newMeasurementDTO();
399             measurement.setPmfm(pmfm);
400             bean.getMeasurements().add(measurement);
401         }
402 
403         bean.getIndividualMeasurements().clear();
404         for (PmfmDTO individualPmfm : bean.getIndividualPmfms()) {
405             MeasurementDTO individualMeasurement = DaliBeanFactory.newMeasurementDTO();
406             individualMeasurement.setPmfm(individualPmfm);
407             bean.getIndividualMeasurements().add(individualMeasurement);
408         }
409     }
410 
411     /**
412      * <p>isTaxonMeasurement.</p>
413      *
414      * @param measurement a {@link MeasurementDTO} object.
415      * @return a boolean.
416      */
417     public static boolean isTaxonMeasurement(MeasurementDTO measurement) {
418         return measurement.getTaxonGroup() != null || measurement.getTaxon() != null;
419     }
420 
421     /**
422      * <p>isMeasurementEmpty.</p>
423      *
424      * @param measurement a {@link MeasurementDTO} object.
425      * @return a boolean.
426      */
427     public static boolean isMeasurementEmpty(MeasurementDTO measurement) {
428         return measurement == null || (measurement.getNumericalValue() == null && measurement.getQualitativeValue() == null);
429     }
430 
431     public static boolean isNumericalMeasurementEmpty(MeasurementDTO measurement) {
432         return measurement == null || measurement.getNumericalValue() == null;
433     }
434 
435     public static final String PROPERTY_PMFM_ID = MeasurementDTO.PROPERTY_PMFM + '.' + PmfmDTO.PROPERTY_ID;
436 
437     public static MeasurementDTO getMeasurementByPmfmId(MeasurementAware bean, int pmfmId) {
438         if (CollectionUtils.isNotEmpty(bean.getMeasurements())) {
439             return findByProperty(bean.getMeasurements(), PROPERTY_PMFM_ID, pmfmId);
440         }
441         return null;
442     }
443 
444     public static MeasurementDTO getIndividualMeasurementByPmfmId(MeasurementAware bean, int pmfmId) {
445         if (CollectionUtils.isNotEmpty(bean.getIndividualMeasurements())) {
446             return findByProperty(bean.getIndividualMeasurements(), PROPERTY_PMFM_ID, pmfmId);
447         }
448         return null;
449     }
450 
451     /**
452      * Compare measurement values only (not pmfm or other propoerty)
453      *
454      * @param measurement1 first measurement
455      * @param measurement2 second measurement
456      * @return true if their values are equals or null
457      */
458     public static boolean measurementValuesEquals(@Nonnull MeasurementDTO measurement1, @Nonnull MeasurementDTO measurement2) {
459         return (measurement1.getNumericalValue() == null && measurement2.getNumericalValue() == null
460             && measurement1.getQualitativeValue() == null && measurement2.getQualitativeValue() == null)
461             || (measurement1.getNumericalValue() != null && measurement1.getNumericalValue().equals(measurement2.getNumericalValue()))
462             || (measurement1.getQualitativeValue() != null && measurement1.getQualitativeValue().equals(measurement2.getQualitativeValue()));
463     }
464 
465     /**
466      * Get the unified comments from all individual measurements
467      *
468      * @param bean the MeasurementAware bean to proceed
469      * @return the unified comment
470      */
471     public static String getUnifiedCommentFromIndividualMeasurements(MeasurementAware bean) {
472 
473         // collect comments
474         List<String> comments = collectProperties(bean.getIndividualMeasurements(), MeasurementDTO.PROPERTY_COMMENT);
475 
476         return getUnifiedString(comments, DEFAULT_STRING_SEPARATOR);
477     }
478 
479     /**
480      * Method used as external function for HSQLDB
481      * see fr.ifremer.dali.service.extraction.ExtractionServiceImpl#createConcatDistinctFunction(java.lang.String, java.lang.String)
482      *
483      * @param array     the input array (any type)
484      * @param separator the separator to use
485      * @return the unified string
486      */
487     @SuppressWarnings(value = "unused")
488     public static String getUnifiedSQLString(Array array, String separator) {
489 
490         try {
491 
492             return getUnifiedString(
493                     Arrays.stream((Object[]) array.getArray())
494                             .filter(Objects::nonNull)
495                             .map(Object::toString)
496                             .collect(Collectors.toList()),
497                     separator);
498 
499         } catch (SQLException e) {
500 
501             return "";
502         }
503     }
504 
505     /**
506      * Get the unified string from a list of string
507      * <p>
508      * It concat all unique occurrence of string in the list.
509      * If an item already contains a joined string with the separator, the inner items are splitted before.
510      *
511      * @param strings   the list to proceed
512      * @param separator the separator to use
513      * @return the unified string
514      */
515     public static String getUnifiedString(List<String> strings, String separator) {
516 
517         Set<String> stringSet = new LinkedHashSet<>();
518 
519         for (String string : strings) {
520             if (StringUtils.isNotBlank(string)) {
521 
522                 // split already joined comment
523                 if (string.contains(separator)) {
524                     List<String> subComments = split(string, separator);
525                     stringSet.addAll(subComments);
526                 } else {
527                     stringSet.add(string);
528                 }
529             }
530         }
531 
532         return stringSet.stream().filter(Objects::nonNull).collect(Collectors.joining(separator));
533     }
534 
535     /**
536      * return true only if bean has blocking errors (control errors and warnings are not blocking)
537      *
538      * @param bean a {@link ErrorAware} object.
539      * @return a boolean.
540      */
541     public static boolean hasNoBlockingError(ErrorAware bean) {
542         if (bean != null && CollectionUtils.isNotEmpty(bean.getErrors())) {
543             for (ErrorDTO error : bean.getErrors()) {
544                 if (error.isError() && !error.isControl()) {
545                     return false;
546                 }
547             }
548         }
549         return true;
550     }
551 
552     /**
553      * <p>removeBlockingErrors.</p>
554      *
555      * @param bean a {@link ErrorAware} object.
556      */
557     public static void removeBlockingErrors(ErrorAware bean) {
558         bean.getErrors().removeIf(error -> !error.isControl());
559     }
560 
561     /**
562      * <p>getErrors.</p>
563      *
564      * @param bean        a {@link ErrorAware} object.
565      * @param controlOnly a {@link Boolean} object.
566      * @return a {@link List} object.
567      */
568     public static List<ErrorDTO> getErrors(ErrorAware bean, final Boolean controlOnly) {
569         if (bean == null || CollectionUtils.isEmpty(bean.getErrors())) {
570             return new ArrayList<>();
571         }
572         return filterCollection(bean.getErrors(), error -> error.isError() && (controlOnly == null || (controlOnly == error.isControl())));
573     }
574 
575     /**
576      * <p>getErrorMessages.</p>
577      *
578      * @param bean a {@link ErrorAware} object.
579      * @return a {@link List} object.
580      */
581     public static List<String> getErrorMessages(ErrorAware bean) {
582         return collectProperties(getErrors(bean, null), ErrorDTO.PROPERTY_MESSAGE);
583     }
584 
585     /**
586      * <p>getErrors.</p>
587      *
588      * @param bean         a {@link ErrorAware} object.
589      * @param propertyName a {@link String} object.
590      * @param pmfmId       a {@link Integer} object.
591      * @param controlOnly  a {@link Boolean} object.
592      * @return a {@link List} object.
593      */
594     public static List<ErrorDTO> getErrors(ErrorAware bean, final String propertyName, final Integer pmfmId, final Boolean controlOnly) {
595         if (bean == null || CollectionUtils.isEmpty(bean.getErrors())) {
596             return new ArrayList<>();
597         }
598         return filterCollection(bean.getErrors(), error -> error.isError()
599                 && (controlOnly == null || (controlOnly == error.isControl()))
600                 && error.containsPropertyName(propertyName)
601                 && (pmfmId == null || pmfmId.equals(error.getPmfmId()))
602         );
603     }
604 
605     /**
606      * <p>getErrorMessages.</p>
607      *
608      * @param bean         a {@link ErrorAware} object.
609      * @param propertyName a {@link String} object.
610      * @param pmfmId       a {@link Integer} object.
611      * @return a {@link List} object.
612      */
613     public static List<String> getErrorMessages(ErrorAware bean, String propertyName, Integer pmfmId) {
614         return collectProperties(getErrors(bean, propertyName, pmfmId, null), ErrorDTO.PROPERTY_MESSAGE);
615     }
616 
617     /**
618      * <p>getWarnings.</p>
619      *
620      * @param bean        a {@link ErrorAware} object.
621      * @param controlOnly a {@link Boolean} object.
622      * @return a {@link List} object.
623      */
624     public static List<ErrorDTO> getWarnings(ErrorAware bean, final Boolean controlOnly) {
625         if (bean == null || CollectionUtils.isEmpty(bean.getErrors())) {
626             return new ArrayList<>();
627         }
628         return filterCollection(bean.getErrors(), error -> error.isWarning() && (controlOnly == null || (controlOnly == error.isControl())));
629     }
630 
631     /**
632      * <p>getWarningMessages.</p>
633      *
634      * @param bean a {@link ErrorAware} object.
635      * @return a {@link List} object.
636      */
637     public static List<String> getWarningMessages(ErrorAware bean) {
638         return collectProperties(getWarnings(bean, null), ErrorDTO.PROPERTY_MESSAGE);
639     }
640 
641     /**
642      * <p>getWarnings.</p>
643      *
644      * @param bean         a {@link ErrorAware} object.
645      * @param propertyName a {@link String} object.
646      * @param pmfmId       a {@link Integer} object.
647      * @param controlOnly  a {@link Boolean} object.
648      * @return a {@link List} object.
649      */
650     public static List<ErrorDTO> getWarnings(ErrorAware bean, final String propertyName, final Integer pmfmId, final Boolean controlOnly) {
651         if (bean == null || CollectionUtils.isEmpty(bean.getErrors())
652                 // if an error is found, return empty list because error is priority
653                 || !getErrors(bean, propertyName, pmfmId, controlOnly).isEmpty()) {
654             return new ArrayList<>();
655         }
656         return filterCollection(bean.getErrors(), error -> error.isWarning()
657                 && (controlOnly == null || (controlOnly == error.isControl()))
658                 && error.containsPropertyName(propertyName)
659                 && (pmfmId == null || pmfmId.equals(error.getPmfmId())));
660     }
661 
662     /**
663      * <p>getWarningMessages.</p>
664      *
665      * @param bean         a {@link ErrorAware} object.
666      * @param propertyName a {@link String} object.
667      * @param pmfmId       a {@link Integer} object.
668      * @return a {@link List} object.
669      */
670     public static List<String> getWarningMessages(ErrorAware bean, String propertyName, Integer pmfmId) {
671         return collectProperties(getWarnings(bean, propertyName, pmfmId, null), ErrorDTO.PROPERTY_MESSAGE);
672     }
673 
674     /**
675      * <p>addError.</p>
676      *
677      * @param bean       a {@link ErrorAware} object.
678      * @param message    a {@link String} object.
679      * @param properties a {@link String} object.
680      */
681     public static void addError(ErrorAware bean, String message, String... properties) {
682         addError(bean, message, null, properties);
683     }
684 
685     /**
686      * <p>addError.</p>
687      *
688      * @param bean       a {@link ErrorAware} object.
689      * @param message    a {@link String} object.
690      * @param pmfmId     a {@link Integer} object.
691      * @param properties a {@link String} object.
692      */
693     public static void addError(ErrorAware bean, String message, Integer pmfmId, String... properties) {
694         addErrorDTOToBean(bean, false, message, pmfmId, properties);
695     }
696 
697     /**
698      * <p>addWarning.</p>
699      *
700      * @param bean       a {@link ErrorAware} object.
701      * @param message    a {@link String} object.
702      * @param properties a {@link String} object.
703      */
704     public static void addWarning(ErrorAware bean, String message, String... properties) {
705         addWarning(bean, message, null, properties);
706     }
707 
708     /**
709      * <p>addWarning.</p>
710      *
711      * @param bean       a {@link ErrorAware} object.
712      * @param message    a {@link String} object.
713      * @param pmfmId     a {@link Integer} object.
714      * @param properties a {@link String} object.
715      */
716     public static void addWarning(ErrorAware bean, String message, Integer pmfmId, String... properties) {
717         addErrorDTOToBean(bean, true, message, pmfmId, properties);
718     }
719 
720     private static void addErrorDTOToBean(ErrorAware bean, boolean warning, String message, Integer pmfmId, String... properties) {
721         ErrorDTO error = DaliBeanFactory.newErrorDTO();
722         error.setError(!warning);
723         error.setWarning(warning);
724         error.setMessage(message);
725         error.setPropertyName(properties != null ? Arrays.asList(properties) : null);
726         error.setPmfmId(pmfmId);
727         bean.getErrors().add(error);
728     }
729 
730     /**
731      * <p>addUniqueErrors.</p>
732      *
733      * @param bean   a {@link ErrorAware} object.
734      * @param errors a {@link Collection} object.
735      */
736     public static void addUniqueErrors(ErrorAware bean, Collection<ErrorDTO> errors) {
737 
738         if (CollectionUtils.isNotEmpty(errors)) {
739             for (ErrorDTO errorToAdd : errors) {
740                 boolean canAdd = true;
741                 for (ErrorDTO existingError : bean.getErrors()) {
742                     if (isErrorEquals(errorToAdd, existingError)) {
743                         canAdd = false;
744                         break;
745                     }
746                 }
747                 if (canAdd) {
748                     bean.getErrors().add(errorToAdd);
749                 }
750             }
751         }
752 
753     }
754 
755     private static boolean isErrorEquals(ErrorDTO error1, ErrorDTO error2) {
756         return error1.isError() == error2.isError()
757                 && error1.isWarning() == error2.isWarning()
758                 && error1.isControl() == error2.isControl()
759                 && Objects.equals(error1.getControlElementCode(), error2.getControlElementCode())
760                 && Objects.deepEquals(error1.getPropertyName().toArray(), error2.getPropertyName().toArray())
761                 && Objects.equals(error1.getPmfmId(), error2.getPmfmId())
762                 && Objects.equals(error1.getIndividualId(), error2.getIndividualId())
763                 && Objects.equals(error1.getMessage(), error2.getMessage());
764     }
765 
766     /**
767      * <p>isPmfmMandatory.</p>
768      *
769      * @param controlRule a {@link ControlRuleDTO} object.
770      * @return a boolean.
771      */
772     public static boolean isPmfmMandatory(ControlRuleDTO controlRule) {
773         return (ControlElementValues.MEASUREMENT.equals(controlRule.getControlElement())
774                 || ControlElementValues.TAXON_MEASUREMENT.equals(controlRule.getControlElement())) &&
775                 (ControlFeatureMeasurementValues.PMFM.equals(controlRule.getControlFeature())
776                         || ControlFeatureMeasurementValues.NUMERICAL_VALUE.equals(controlRule.getControlFeature())
777                         || ControlFeatureMeasurementValues.QUALITATIVE_VALUE.equals(controlRule.getControlFeature()));
778     }
779 
780     public static boolean isPreconditionRule(ControlRuleDTO controlRule) {
781         return ControlFunctionValues.PRECONDITION.equals(controlRule.getFunction());
782     }
783 
784     public static boolean isQualitativeControlRule(ControlRuleDTO controlRule) {
785         return ControlFunctionValues.IS_AMONG.equals(controlRule.getFunction())
786                 && (
787                 (ControlElementValues.MEASUREMENT.equals(controlRule.getControlElement()) && ControlFeatureMeasurementValues.QUALITATIVE_VALUE.equals(controlRule.getControlFeature()))
788                         ||
789                         (ControlElementValues.TAXON_MEASUREMENT.equals(controlRule.getControlElement()) && ControlFeatureTaxonMeasurementValues.QUALITATIVE_VALUE.equals(controlRule.getControlFeature()))
790         );
791     }
792 
793     /**
794      * <p>isSurveyValidated.</p>
795      *
796      * @param survey a {@link SurveyDTO} object.
797      * @return a boolean.
798      */
799     public static boolean isSurveyValidated(SurveyDTO survey) {
800         return survey.getValidationDate() != null
801                 || SynchronizationStatusValues.SYNCHRONIZED.equals(survey.getSynchronizationStatus())
802                 || SynchronizationStatusValues.READY_TO_SYNCHRONIZE.equals(survey.getSynchronizationStatus());
803     }
804 
805     /**
806      * <p>getFilterOfType.</p>
807      *
808      * @param extraction       a {@link ExtractionDTO} object.
809      * @param extractionFilter a {@link ExtractionFilterTypeValues} object.
810      * @return a {@link FilterDTO} object.
811      */
812     public static FilterDTO getFilterOfType(ExtractionDTO extraction, ExtractionFilterTypeValues extractionFilter) {
813 
814         return CollectionUtils.isNotEmpty(extraction.getFilters())
815                 ? findByProperty(extraction.getFilters(), FilterDTO.PROPERTY_FILTER_TYPE_ID, extractionFilter.getFilterTypeId())
816                 : null;
817     }
818 
819     /**
820      * <p>getFilterElementsIds.</p>
821      *
822      * @param extraction       a {@link ExtractionDTO} object.
823      * @param extractionFilter a {@link ExtractionFilterTypeValues} object.
824      * @param <T>              a T object.
825      * @return a {@link List} object.
826      */
827     public static <T> List<T> getFilterElementsIds(ExtractionDTO extraction, ExtractionFilterTypeValues extractionFilter) {
828 
829         FilterDTO filter = getFilterOfType(extraction, extractionFilter);
830         if (filter != null && CollectionUtils.isNotEmpty(filter.getElements())) {
831             QuadrigeBean firstElement = filter.getElements().get(0);
832             String property = firstElement instanceof CodeOnly ? ProgramDTO.PROPERTY_CODE : QuadrigeBean.PROPERTY_ID;
833             return collectProperties(filter.getElements(), property);
834         }
835         return null;
836     }
837 
838     public static List<String> getIdsAsString(Collection<? extends QuadrigeBean> beans) {
839         if (beans == null)
840             return null;
841 
842         return beans.stream().map(bean -> bean instanceof CodeOnly ? ((CodeOnly)bean).getCode() : bean.getId().toString()).collect(Collectors.toList());
843     }
844 
845     public static List<ExtractionPeriodDTO> getExtractionPeriods(ExtractionDTO extraction) {
846 
847         FilterDTO periodFilter = getFilterOfType(extraction, ExtractionFilterTypeValues.PERIOD);
848         if (periodFilter == null || periodFilter.getElements() == null) return new ArrayList<>();
849         return periodFilter.getElements().stream().map((Function<QuadrigeBean, ExtractionPeriodDTO>) ExtractionPeriodDTO.class::cast).collect(Collectors.toList());
850     }
851 
852 }