View Javadoc
1   package fr.ifremer.tutti.persistence.service;
2   
3   /*
4    * #%L
5    * Tutti :: Persistence
6    * %%
7    * Copyright (C) 2012 - 2014 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Preconditions;
26  import com.google.common.collect.ArrayListMultimap;
27  import com.google.common.collect.Iterables;
28  import com.google.common.collect.Lists;
29  import com.google.common.collect.Multimap;
30  import com.google.common.collect.Sets;
31  import fr.ifremer.adagio.core.dao.data.batch.Batch;
32  import fr.ifremer.adagio.core.dao.data.batch.CatchBatch;
33  import fr.ifremer.adagio.core.dao.data.batch.SortingBatch;
34  import fr.ifremer.adagio.core.dao.data.measure.SortingMeasurement;
35  import fr.ifremer.adagio.core.dao.referential.QualityFlag;
36  import fr.ifremer.adagio.core.dao.referential.QualityFlagCode;
37  import fr.ifremer.adagio.core.dao.referential.QualityFlagImpl;
38  import fr.ifremer.adagio.core.dao.referential.pmfm.Pmfm;
39  import fr.ifremer.adagio.core.dao.referential.pmfm.PmfmId;
40  import fr.ifremer.adagio.core.dao.referential.taxon.ReferenceTaxon;
41  import fr.ifremer.adagio.core.dao.referential.taxon.ReferenceTaxonImpl;
42  import fr.ifremer.tutti.persistence.InvalidBatchModelException;
43  import fr.ifremer.tutti.persistence.entities.TuttiEntities;
44  import fr.ifremer.tutti.persistence.entities.data.BatchContainer;
45  import fr.ifremer.tutti.persistence.entities.data.FishingOperation;
46  import fr.ifremer.tutti.persistence.entities.data.SampleCategoryModel;
47  import fr.ifremer.tutti.persistence.entities.data.SpeciesBatch;
48  import fr.ifremer.tutti.persistence.entities.data.SpeciesBatchFrequency;
49  import fr.ifremer.tutti.persistence.entities.data.SpeciesBatchs;
50  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
51  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
52  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicType;
53  import fr.ifremer.tutti.persistence.entities.referential.Species;
54  import fr.ifremer.tutti.persistence.service.referential.CaracteristicPersistenceService;
55  import fr.ifremer.tutti.persistence.service.referential.SpeciesPersistenceService;
56  import fr.ifremer.tutti.persistence.service.util.BatchPersistenceHelper;
57  import fr.ifremer.tutti.persistence.service.util.MeasurementPersistenceHelper;
58  import fr.ifremer.tutti.persistence.service.util.tree.SpeciesBatchTreeHelperSupport;
59  import fr.ifremer.tutti.persistence.service.util.SynchronizationStatusHelper;
60  import org.apache.commons.collections4.CollectionUtils;
61  import org.apache.commons.logging.Log;
62  import org.apache.commons.logging.LogFactory;
63  import org.springframework.dao.DataIntegrityViolationException;
64  
65  import javax.annotation.Resource;
66  import java.io.Serializable;
67  import java.text.DateFormat;
68  import java.text.SimpleDateFormat;
69  import java.util.ArrayList;
70  import java.util.Collection;
71  import java.util.Collections;
72  import java.util.List;
73  import java.util.Objects;
74  import java.util.Set;
75  import java.util.function.Supplier;
76  import java.util.stream.Collectors;
77  
78  import static org.nuiton.i18n.I18n.t;
79  
80  /**
81   * Default implementation of {@link SpeciesBatchPersistenceService}.
82   *
83   * @author Tony Chemit - chemit@codelutin.com
84   * @since 1.2
85   */
86  public abstract class SpeciesBatchPersistenceServiceSupport extends AbstractPersistenceService {
87  
88      /** Logger. */
89      private static final Log log = LogFactory.getLog(SpeciesBatchPersistenceServiceSupport.class);
90  
91      @Resource(name = "speciesPersistenceService")
92      private SpeciesPersistenceService speciesService;
93  
94      @Resource(name = "fishingOperationPersistenceService")
95      private FishingOperationPersistenceService fishingOperationPersistenceService;
96  
97      @Resource(name = "caracteristicPersistenceService")
98      private CaracteristicPersistenceService caracteristicService;
99  
100     @Resource(name = "batchPersistenceHelper")
101     protected BatchPersistenceHelper batchHelper;
102 
103     @Resource(name = "measurementPersistenceHelper")
104     protected MeasurementPersistenceHelper measurementPersistenceHelper;
105 
106     @Resource(name = "synchronizationStatusHelper")
107     private SynchronizationStatusHelper synchronizationStatusHelper;
108 
109     private final String prefix;
110 
111     private final Supplier<SpeciesBatch> batchFactory;
112     private final Supplier<SpeciesBatchFrequency> frequencyFactory;
113 
114     protected SpeciesBatchPersistenceServiceSupport(String prefix, Supplier<SpeciesBatch> batchFactory, Supplier<SpeciesBatchFrequency> frequencyFactory) {
115         this.prefix = prefix;
116         this.batchFactory = batchFactory;
117         this.frequencyFactory = frequencyFactory;
118     }
119 
120     public Set<Integer> getBatchChildIds(Integer id) {
121         return batchHelper.getBatchIds(id);
122     }
123 
124     //------------------------------------------------------------------------//
125     //-- SpeciesBatch methods                                               --//
126     //------------------------------------------------------------------------//
127 
128     protected abstract SpeciesBatchTreeHelperSupport getBatchTreeHelper();
129 
130     protected abstract void validate(BatchPersistenceHelper batchHelper, SampleCategoryModel sampleCategoryModel, BatchContainer<SpeciesBatch> result);
131 
132     protected final BatchContainer<SpeciesBatch> getRootSpeciesBatch0(Integer fishingOperationId, boolean validateTree) throws InvalidBatchModelException {
133 
134         Objects.requireNonNull(fishingOperationId);
135 
136         DateFormat df = new SimpleDateFormat("dd/MM/yyy");
137 
138         CatchBatch catchBatch = batchHelper.getRootCatchBatchByFishingOperationId(fishingOperationId, false);
139 
140         // -- Vrac > Species > Alive Itemized
141         SortingBatch vracSpeciesBatch = getBatchTreeHelper().getVracAliveItemizedRootBatch(catchBatch);
142 
143         // container of speciesBatch is arbitraty put on vrac type (there is
144         // no common ancestor for all species batch).
145         BatchContainer<SpeciesBatch> result = new BatchContainer<>();
146 
147         SampleCategoryModel sampleCategoryModel = getSampleCategoryModel();
148 
149         if (vracSpeciesBatch != null) {
150 
151             result.setId(vracSpeciesBatch.getId());
152 
153             for (Batch batch : vracSpeciesBatch.getChildBatchs()) {
154                 SortingBatch source = (SortingBatch) batch;
155                 ReferenceTaxon referenceTaxon = source.getReferenceTaxon();
156                 Objects.requireNonNull(referenceTaxon, "Can't have a rootSpeciesBatch with a null taxon, but was for " + batch.getId());
157                 if (log.isTraceEnabled()) {
158                     log.trace("Loading CatchBatch Vrac > " + prefix + " > Alive Itemized > " + referenceTaxon.getId() + " - " + " (batch:" + source.getId() + ")");
159                 }
160                 Species species = speciesService.getSpeciesByReferenceTaxonId(referenceTaxon.getId());
161                 if (species == null) {
162                     FishingOperation fishingOperation = fishingOperationPersistenceService.getFishingOperation(fishingOperationId);
163                     String fishingOperationName = fishingOperation.getStationNumber() + " - " + fishingOperation.getFishingOperationNumber() + " - " + df.format(fishingOperation.getGearShootingStartDate());
164                     throw new InvalidBatchModelException(t("tutti.persistence.speciesBatch.validation.unkonwn.taxon", fishingOperationName, source.getId(), referenceTaxon.getId()));
165                 }
166                 SpeciesBatch target = batchFactory.get();
167                 target.setSpecies(species);
168 
169                 entityToBean(sampleCategoryModel, source, target);
170                 result.addChildren(target);
171 
172                 if (log.isDebugEnabled()) {
173                     log.debug("Loaded CatchBatch Vrac > " + prefix + " > Alive Itemized > " + target.getSpecies().getReferenceTaxonId() + ": " + target.getId());
174                 }
175             }
176         }
177 
178         // -- Hors Vrac > Species
179         SortingBatch horsVracSpeciesBatch = getBatchTreeHelper().getHorsVracRootBatch(catchBatch);
180 
181         if (horsVracSpeciesBatch != null) {
182             for (Batch batch : horsVracSpeciesBatch.getChildBatchs()) {
183                 SortingBatch source = (SortingBatch) batch;
184                 ReferenceTaxon referenceTaxon = source.getReferenceTaxon();
185                 Objects.requireNonNull(referenceTaxon, "Can't have a rootSpeciesBatch with a null taxon, but was for " + source.getId());
186                 if (log.isTraceEnabled()) {
187                     log.trace("Loading CatchBatch Hors Vrac > " + prefix + " > " + referenceTaxon.getId() + " - " + " (batch:" + source.getId() + ")");
188                 }
189                 Species species = speciesService.getSpeciesByReferenceTaxonId(referenceTaxon.getId());
190                 if (species == null) {
191                     FishingOperation fishingOperation = fishingOperationPersistenceService.getFishingOperation(fishingOperationId);
192                     String fishingOperationName = fishingOperation.getStationNumber() + " - " + fishingOperation.getFishingOperationNumber() + " - " + df.format(fishingOperation.getGearShootingStartDate());
193                     throw new InvalidBatchModelException(t("tutti.persistence.speciesBatch.validation.unkonwn.taxon", fishingOperationName, source.getId(), referenceTaxon.getId()));
194                 }
195                 SpeciesBatch target = batchFactory.get();
196                 target.setSpecies(species);
197                 entityToBean(sampleCategoryModel, source, target);
198                 result.addChildren(target);
199                 if (log.isDebugEnabled()) {
200                     log.debug("Loaded CatchBatch Hors Vrac > " + prefix + " > " + target.getSpecies().getReferenceTaxonId() + ": " + target.getId());
201                 }
202             }
203         }
204 
205         if (validateTree) {
206 
207             // validate with given sample category model
208             validate(batchHelper, sampleCategoryModel, result);
209         }
210 
211         return result;
212     }
213 
214     protected SpeciesBatch createSpeciesBatch0(SpeciesBatch bean, Integer parentBatchId, boolean computeRankOrder) {
215         Objects.requireNonNull(bean);
216         Preconditions.checkArgument(TuttiEntities.isNew(bean));
217         Objects.requireNonNull(bean.getSpecies());
218         Objects.requireNonNull(bean.getSpecies().getId());
219         Objects.requireNonNull(bean.getFishingOperation());
220         Objects.requireNonNull(bean.getFishingOperation().getId());
221 
222         CatchBatch catchBatch = batchHelper.getRootCatchBatchByFishingOperationId(bean.getFishingOperation().getIdAsInt(), false);
223 
224         return createSpeciesBatch0(bean, parentBatchId, catchBatch, computeRankOrder);
225     }
226 
227 
228     protected Collection<SpeciesBatch> createSpeciesBatches0(Integer fishingOperationId, Collection<SpeciesBatch> beans) {
229 
230         Objects.requireNonNull(beans);
231         Objects.requireNonNull(fishingOperationId);
232 
233         CatchBatch catchBatch = batchHelper.getRootCatchBatchByFishingOperationId(fishingOperationId, false);
234 
235         Collection<SpeciesBatch> result = new ArrayList<>();
236         for (SpeciesBatch bean : beans) {
237 
238             SpeciesBatch created = createSpeciesBatch0(bean, null, catchBatch, true);
239             result.add(created);
240 
241         }
242         return result;
243 
244     }
245 
246     protected SpeciesBatch createSpeciesBatch0(SpeciesBatch bean, Integer parentBatchId, CatchBatch catchBatch, boolean computeRankOrder) {
247 
248         Objects.requireNonNull(bean);
249         Preconditions.checkArgument(TuttiEntities.isNew(bean));
250         Objects.requireNonNull(bean.getSpecies());
251         Objects.requireNonNull(bean.getSpecies().getId());
252         Objects.requireNonNull(bean.getFishingOperation());
253         Objects.requireNonNull(bean.getFishingOperation().getId());
254 
255         SortingBatch batch = SortingBatch.Factory.newInstance();
256         beanToEntity0(bean, batch, parentBatchId, catchBatch, computeRankOrder);
257         bean = batchHelper.createSortingBatch(bean, catchBatch, batch);
258 
259         return bean;
260     }
261 
262     protected SpeciesBatch saveSpeciesBatch0(SpeciesBatch bean) {
263         Objects.requireNonNull(bean);
264         Preconditions.checkArgument(!TuttiEntities.isNew(bean));
265 
266         Integer batchId = bean.getIdAsInt();
267 
268         CatchBatch catchBatch = batchHelper.getRootCatchBatchByBatchId(batchId);
269         SortingBatch batch = batchHelper.getSortingBatchById(catchBatch, batchId);
270         Integer parentBatchId = null;
271         if (bean.getParentBatch() != null) {
272             parentBatchId = bean.getParentBatch().getIdAsInt();
273         }
274         beanToEntity0(bean, batch, parentBatchId, catchBatch, true);
275         batchHelper.updateSortingBatch(batch, catchBatch);
276 
277         return bean;
278     }
279 
280     protected void deleteSpeciesBatch0(Integer id) {
281         Objects.requireNonNull(id);
282         batchHelper.deleteBatch(id);
283     }
284 
285     protected void deleteSpeciesSubBatch0(Integer id) {
286         Objects.requireNonNull(id);
287         deleteSpeciesSubBatch(id);
288     }
289 
290     protected void changeSpeciesBatchSpecies0(Integer id, Species species) {
291         Objects.requireNonNull(id);
292         Objects.requireNonNull(species);
293         Objects.requireNonNull(species.getReferenceTaxonId());
294         changeBatchSpecies(id, species);
295     }
296 
297     protected List<SpeciesBatch> getAllSpeciesBatchToConfirm0(Integer fishingOperationId) throws InvalidBatchModelException {
298         List<SpeciesBatch> batchesToConfirm = new ArrayList<>();
299 
300         BatchContainer<SpeciesBatch> rootSpeciesBatch = getRootSpeciesBatch0(fishingOperationId, false);
301         for (SpeciesBatch speciesBatch : rootSpeciesBatch.getChildren()) {
302             findSpeciesBatchesToConfirm(speciesBatch, batchesToConfirm);
303         }
304 
305         return batchesToConfirm;
306     }
307 
308     private void findSpeciesBatchesToConfirm(SpeciesBatch speciesBatch, List<SpeciesBatch> batchesToConfirm) {
309         if (speciesBatch.isSpeciesToConfirm()) {
310             batchesToConfirm.add(speciesBatch);
311 
312         } else if (!speciesBatch.isChildBatchsEmpty()) {
313             for (SpeciesBatch batch : speciesBatch.getChildBatchs()) {
314                 findSpeciesBatchesToConfirm(batch, batchesToConfirm);
315             }
316         }
317     }
318 
319     //------------------------------------------------------------------------//
320     //-- SpeciesBatchFrequency methods                                      --//
321     //------------------------------------------------------------------------//
322 
323     protected List<SpeciesBatchFrequency> getAllSpeciesBatchFrequency0(Integer speciesBatchId) {
324         Objects.requireNonNull(speciesBatchId);
325 
326         List<SortingBatch> frequencyChilds = getFrequencies(speciesBatchId);
327         List<SpeciesBatchFrequency> results = new ArrayList<>();
328         for (SortingBatch child : frequencyChilds) {
329             SpeciesBatchFrequency target = frequencyFactory.get();
330 
331             entityToBatchFrequency(child, target);
332             results.add(target);
333         }
334         return Collections.unmodifiableList(results);
335     }
336 
337     protected Multimap<Species, SpeciesBatchFrequency> getAllSpeciesBatchFrequencyForBatch0(BatchContainer<SpeciesBatch> batchContainer) {
338         Multimap<Species, SpeciesBatchFrequency> result = ArrayListMultimap.create();
339         for (SpeciesBatch speciesBatch : batchContainer.getChildren()) {
340             getAllSpeciesBatchFrequencyForBatch(speciesBatch, result);
341         }
342         return result;
343     }
344 
345     private void getAllSpeciesBatchFrequencyForBatch(SpeciesBatch batch, Multimap<Species, SpeciesBatchFrequency> result) {
346 
347         List<SpeciesBatchFrequency> speciesBatchFrequency = getAllSpeciesBatchFrequency0(batch.getIdAsInt());
348         result.putAll(batch.getSpecies(), speciesBatchFrequency);
349 
350         if (!batch.isChildBatchsEmpty()) {
351             for (SpeciesBatch child : batch.getChildBatchs()) {
352                 getAllSpeciesBatchFrequencyForBatch(child, result);
353             }
354         }
355     }
356 
357     protected List<SpeciesBatchFrequency> saveSpeciesBatchFrequency0(Integer speciesBatchId, List<SpeciesBatchFrequency> frequencies) {
358         Objects.requireNonNull(speciesBatchId);
359         Objects.requireNonNull(frequencies);
360 
361         List<SpeciesBatchFrequency> notNullFrequencies = new ArrayList<>();
362 
363         // Check that all frequencies have the same length PMFM (before doing any db call)
364         // and remove null frequencies
365         String pmfmId = null;
366         for (SpeciesBatchFrequency source : frequencies) {
367 
368             if (source.getLengthStepCaracteristic() != null) {
369                 if (pmfmId == null) {
370                     pmfmId = source.getLengthStepCaracteristic().getId();
371                 } else if (!pmfmId.equals(source.getLengthStepCaracteristic().getId())) {
372                     throw new DataIntegrityViolationException("Batch frequencies under one SpeciesBatch must have all the same lengthStepCaracteristic");
373                 }
374                 notNullFrequencies.add(source);
375             }
376         }
377 
378         CatchBatch catchBatch = batchHelper.getRootCatchBatchByBatchId(speciesBatchId);
379 
380         if (catchBatch == null) {
381             return notNullFrequencies;
382         }
383 
384         // Synchronization status
385         synchronizationStatusHelper.setDirty(catchBatch);
386 
387         // Retrieve parent
388         SortingBatch parentBatch = batchHelper.getSortingBatchById(catchBatch, speciesBatchId);
389 
390         // Remember child ids, to remove unchanged item (see at bottom in this method)
391         List<Integer> notUpdatedChildIds = new ArrayList<>();
392         List<SortingBatch> frequencyChilds = getFrequencyChilds(parentBatch);
393         notUpdatedChildIds.addAll(frequencyChilds.stream().map(SortingBatch::getId).collect(Collectors.toList()));
394 
395         short rankOrder = 0;
396         List<SortingBatch> batchsToUpdate = new ArrayList<>();
397         for (SpeciesBatchFrequency source : notNullFrequencies) {
398             rankOrder++;
399 
400             SortingBatch target;
401             if (source.getId() == null) {
402 
403                 // Not existing batch
404                 target = SortingBatch.Factory.newInstance();
405 
406                 // Fill the sorting batch from the source
407                 beanToEntity0(source, target, parentBatch, rankOrder);
408 
409                 // Create the targeted batch, then update the source id
410                 batchHelper.createSortingBatch(source, catchBatch, target);
411 
412                 // push back id of created sortingBatch
413                 source.setId(target.getId());
414 
415                 if (log.isDebugEnabled()) {
416                     log.debug("Create frequency sortingBatch(" + rankOrder + "): " + target.getId());
417                 }
418             } else {
419 
420                 // Existing batch
421                 target = batchHelper.loadSortingBatch(source.getIdAsInt(), catchBatch);
422 
423                 // Fill the sorting batch from the source
424                 beanToEntity0(source, target, parentBatch, rankOrder);
425 
426                 // Add the batch into a list (will be update later, using this list)
427                 batchsToUpdate.add(target);
428 
429                 // Remove id from id to remove
430                 notUpdatedChildIds.remove(target.getId());
431 
432                 if (log.isDebugEnabled()) {
433                     log.debug("Update frequency sortingBatch(" + rankOrder + "): " + target.getId());
434                 }
435             }
436         }
437 
438         if (CollectionUtils.isNotEmpty(batchsToUpdate)) {
439 
440             // update some batchs
441             batchHelper.updateSortingBatch(batchsToUpdate, catchBatch);
442         }
443 
444         if (CollectionUtils.isNotEmpty(notUpdatedChildIds)) {
445 
446             // Remove obsolete frequencies
447             for (Integer batchId : notUpdatedChildIds) {
448 
449                 if (log.isDebugEnabled()) {
450                     log.debug("Remove obsolete frequency sortingBatch: " + batchId);
451                 }
452                 batchHelper.removeWithChildren(batchId, catchBatch);
453             }
454         }
455 
456         return Collections.unmodifiableList(notNullFrequencies);
457     }
458 
459     //------------------------------------------------------------------------//
460     //-- Internal methods                                                   --//
461     //------------------------------------------------------------------------//
462 
463     protected void beanToEntity0(SpeciesBatch source,
464                                  SortingBatch target,
465                                  Integer parentBatchId,
466                                  CatchBatch catchBatch,
467                                  boolean computeRankOrder) {
468 
469         Objects.requireNonNull(source.getFishingOperation());
470         Objects.requireNonNull(source.getFishingOperation().getId());
471 
472         // If parent and root need to be set
473         if (target.getId() == null
474                 || target.getRootBatch() == null
475                 || (target.getParentBatch() != null && !target.getParentBatch().getId().equals(parentBatchId))) {
476 
477             getBatchTreeHelper().setBatchParents(source.getSampleCategoryId(),
478                                                  source.getSampleCategoryValue(),
479                                                  target,
480                                                  parentBatchId,
481                                                  catchBatch);
482         }
483 
484         // --- RankOrder (initialize once, at creation) --- //
485         {
486             if (target.getRankOrder() == null) {
487 
488                 short rankOrder;
489 
490                 if (computeRankOrder) {
491 
492                     // Start rank order at 1
493                     rankOrder = (short) 1;
494                     //FIXME : tchemit-2015-04-04 This code can not be used to save multiple batches at the same time, since it will always give the
495                     //FIXME : tchemit-2015-04-04 same values for all batches
496                     if (source.getParentBatch() != null && CollectionUtils.isNotEmpty(source.getParentBatch().getChildBatchs())) {
497                         int maxRankOrder = 0;
498                         for (SpeciesBatch batch : source.getParentBatch().getChildBatchs()) {
499                             Integer r = batch.getRankOrder();
500                             if (r != null && r > maxRankOrder) {
501                                 maxRankOrder = r;
502                             }
503                         }
504                         rankOrder += (short) maxRankOrder;
505 
506                     } else {
507 
508                         rankOrder = batchHelper.computeRankOrder(target);
509 
510                     }
511 
512                 } else {
513 
514                     Preconditions.checkState(source.getRankOrder() != null, "Not using computeRankOrder requires source rankOrder to be not null, but was on batch: " + source);
515                     rankOrder = (short) (int) source.getRankOrder();
516 
517                 }
518 
519                 target.setRankOrder(rankOrder);
520 
521             }
522         }
523 
524         // --- Force subgroup count to '1', as Allegro --- //
525         target.setSubgroupCount(1f);
526 
527         // --- Individual count --- //
528         target.setIndividualCount(source.getNumber());
529 
530         // --- Comments --- //
531         target.setComments(source.getComment());
532 
533         // --- Exhaustive inventory (always true under a species batch) --- //
534         target.setExhaustiveInventory(true);
535 
536         // --- Species --- //
537         {
538             ReferenceTaxon referenceTaxon;
539             if (source.getSpecies() == null || parentBatchId != null) {
540                 referenceTaxon = null;
541             } else {
542                 referenceTaxon = load(ReferenceTaxonImpl.class, source.getSpecies().getReferenceTaxonId());
543             }
544             target.setReferenceTaxon(referenceTaxon);
545         }
546 
547         // --- QualityFlag --- //
548         {
549             String qualityFlag;
550             if (source.isSpeciesToConfirm()) {
551                 qualityFlag = QualityFlagCode.DOUBTFUL.getValue();
552             } else {
553                 qualityFlag = QualityFlagCode.NOTQUALIFIED.getValue();
554             }
555             target.setQualityFlag(load(QualityFlagImpl.class, qualityFlag));
556         }
557 
558         Float weight = source.getWeight();
559         Float sampleCategoryWeight = source.getSampleCategoryWeight();
560 
561         // --- Sampling Ratio + QuantificationMeasurement --- //
562         getBatchTreeHelper().setWeightAndSampleRatio(target, weight, sampleCategoryWeight);
563 
564         // --- Sorting measurement --- //
565         {
566             Collection<SortingMeasurement> sortingMeasurements = target.getSortingMeasurements();
567             Set<SortingMeasurement> notChangedSortingMeasurements = Sets.newHashSet();
568             if (sortingMeasurements != null) {
569                 notChangedSortingMeasurements.addAll(sortingMeasurements);
570             }
571 
572             if (source.getSampleCategoryId() != null && source.getSampleCategoryValue() != null) {
573                 Integer pmfmId = source.getSampleCategoryId();
574                 // Do not store sorting measurement if pmfm = SORTED (already store in an ancestor batch)
575                 if (!pmfmId.equals(PmfmId.SORTED_UNSORTED.getValue())) {
576                     SortingMeasurement sortingMeasurement = measurementPersistenceHelper.setSortingMeasurement(
577                             target,
578                             pmfmId,
579                             source.getSampleCategoryValue());
580                     notChangedSortingMeasurements.remove(sortingMeasurement);
581                 }
582             }
583             if (sortingMeasurements != null) {
584                 sortingMeasurements.removeAll(notChangedSortingMeasurements);
585             }
586         }
587 
588     }
589 
590     private void beanToEntity0(SpeciesBatchFrequency source,
591                                SortingBatch target,
592                                SortingBatch parentBatch,
593                                short rankOrder) {
594         Preconditions.checkNotNull(source.getBatch());
595         Preconditions.checkNotNull(source.getBatch().getId());
596 
597         // If parent and root need to be set
598         if (target.getId() == null
599                 || target.getRootBatch() == null
600                 || (target.getParentBatch() != null && !target.getParentBatch().getId().equals(parentBatch.getId()))) {
601 
602             target.setParentBatch(parentBatch);
603             target.setRootBatch(parentBatch.getRootBatch());
604         }
605 
606         // --- RankOrder --- //
607         target.setRankOrder(rankOrder);
608 
609         // --- Individual count --- //
610         target.setIndividualCount(source.getNumber());
611 
612         // --- Species --- //
613         target.setReferenceTaxon(null);
614 
615         // --- QualityFlag --- //
616         target.setQualityFlag(parentBatch.getQualityFlag());
617 
618         // --- Exhaustive inventory (always true under a species batch) --- //
619         target.setExhaustiveInventory(true);
620 
621         // --- Sampling Ratio + QuantificationMeasurement --- //
622         getBatchTreeHelper().setWeightAndSampleRatio(target, source.getWeight(), null);
623 
624         // --- Sorting measurement --- //
625         {
626             Collection<SortingMeasurement> sortingMeasurements = target.getSortingMeasurements();
627             Set<SortingMeasurement> notChangedSortingMeasurements = Sets.newHashSet();
628             if (sortingMeasurements != null) {
629                 notChangedSortingMeasurements.addAll(sortingMeasurements);
630             }
631             if ((source.getLengthStepCaracteristic() != null && source.getLengthStep() != null)) {
632                 Integer pmfmId = source.getLengthStepCaracteristic().getIdAsInt();
633                 SortingMeasurement sortingMeasurement = measurementPersistenceHelper.setSortingMeasurement(target, pmfmId,
634                                                                                                            source.getLengthStep());
635                 notChangedSortingMeasurements.remove(sortingMeasurement);
636             }
637             if (sortingMeasurements != null) {
638                 sortingMeasurements.removeAll(notChangedSortingMeasurements);
639             }
640         }
641 
642     }
643 
644 
645     private SpeciesBatch entityToBean(SampleCategoryModel sampleCategoryModel,
646                                       SortingBatch source,
647                                       SpeciesBatch target) {
648 
649         Preconditions.checkNotNull(target.getSpecies());
650 
651         target.setId(source.getId().toString());
652 
653         // Rank order
654         target.setRankOrder(Integer.valueOf(source.getRankOrder()));
655 
656         // Individual count
657         target.setNumber(source.getIndividualCount());
658 
659         // Convert database weight (and sampling ratio) into UI weight and sampleCategoryWeight
660         if (source.getWeight() != null && source.getWeightBeforeSampling() == null) {
661             target.setSampleCategoryWeight(source.getWeight());
662         } else {
663             target.setWeight(source.getWeight());
664             target.setSampleCategoryWeight(source.getWeightBeforeSampling());
665 
666 //            if (Objects.equals(source.getWeight(), source.getWeightBeforeSampling())) {
667 //
668 //                // after a allegro synchronize, can happen, we do not use quantification measurement on a not leaf node
669 //                // the weight comes from sampleRatioText, but in facts there only one weight...
670 //                target.setWeight(null);
671 //
672 //            }
673         }
674 
675 //        if (CollectionUtils.isNotEmpty(source.getChildBatchs()) && target.getWeight() != null) {
676 //
677 //            // can't use this sample weight on a node
678 //            // the weight comes from sampleRatioText, but must NOT be used here
679 //            target.setWeight(null);
680 //
681 //        }
682 
683 
684         // Comments
685         target.setComment(source.getComments());
686 
687         // Sample category type (only one is applied)
688         SortingMeasurement sm = null;
689         if (source.getSortingMeasurements().size() == 1) {
690             sm = source.getSortingMeasurements().iterator().next();
691         } else if (source.getReferenceTaxon() != null && source.getReferenceTaxon().getId() != null) {
692             sm = measurementPersistenceHelper.getInheritedSortingMeasurement(source);
693         }
694         if (sm != null) {
695 
696             boolean isFrequency = isFrequencyBatch(sampleCategoryModel, source);
697 
698             if (!isFrequency) {
699                 Integer qualitativeId = null;
700                 if (sm.getQualitativeValue() != null) {
701                     qualitativeId = sm.getQualitativeValue().getId();
702                 }
703                 setSampleCategoryQualitative(
704                         target,
705                         sm.getPmfm().getId(),
706                         sm.getNumericalValue(),
707                         sm.getAlphanumericalValue(),
708                         qualitativeId);
709             }
710         }
711 
712         if (target.getSampleCategoryId() != null) {
713             List<SpeciesBatch> targetChilds = Lists.newArrayList();
714             for (Batch batch : source.getChildBatchs()) {
715                 SortingBatch sourceChild = (SortingBatch) batch;
716                 SpeciesBatch targetChild = SpeciesBatchs.newInstance(target);
717                 targetChild.setSpecies(target.getSpecies());
718                 entityToBean(sampleCategoryModel, sourceChild, targetChild);
719                 if (log.isDebugEnabled()) {
720                     log.debug("Loaded CatchBatch (Vrac|Hors Vrac) > Species > " + targetChild.getSpecies().getReferenceTaxonId() + " : " + target.getId());
721                 }
722                 if (targetChild.getSampleCategoryValue() != null) {
723                     targetChilds.add(targetChild);
724                     targetChild.setParentBatch(target);
725                 }
726             }
727             target.setChildBatchs(targetChilds);
728 
729         }
730 
731         //FIXME tchemit-2014-08-29 We can only do this if not an a leaf node (means with no frequencies...)
732         //FIXME tchemit-2014-08-29 But need to see if this is really need to do that .
733         // see https://forge.codelutin.com/issues/5698
734 
735         if (CollectionUtils.isNotEmpty(source.getChildBatchs()) && target.getWeight() != null) {
736 
737             SortingBatch childBatch = (SortingBatch) Iterables.get(source.getChildBatchs(), 0);
738 
739             boolean isFrequency = isFrequencyBatch(sampleCategoryModel, childBatch);
740 
741             if (!isFrequency) {
742 
743                 // can't use this sample weight on a node
744                 // the weight comes from sampleRatioText, but must NOT be used here
745                 // but we can only do this if childs are not frequencies
746 
747                 target.setWeight(null);
748 
749             }
750 
751         }
752 
753         QualityFlag qualityFlag = source.getQualityFlag();
754         target.setSpeciesToConfirm(qualityFlag != null && QualityFlagCode.DOUBTFUL.getValue().equals(qualityFlag.getCode()));
755 
756         return target;
757 
758     }
759 
760 //    private  short computeRankOrder(SortingBatch target) {
761 //
762 //        // Start rank order at 1, nothing before it
763 //        short rankOrder = (short) 1;
764 //        if (target.getParentBatch() != null && target.getParentBatch().getChildBatchs() != null) {
765 //            int maxRankOrder = 0;
766 //            for (Batch batch : target.getParentBatch().getChildBatchs()) {
767 //                Short r = batch.getRankOrder();
768 //                if (r != null && r > maxRankOrder) {
769 //                    maxRankOrder = r;
770 //                }
771 //            }
772 //            rankOrder += maxRankOrder;
773 //        }
774 //        return rankOrder;
775 //
776 //    }
777 
778     private void setSampleCategoryQualitative(SpeciesBatch target,
779                                               Integer pmfmId,
780                                               Float numericalvalue,
781                                               String alphanumericalValue,
782                                               Integer qualitativeValueId) {
783         // skip if null or corresponding to the SORTING_TYPE PMFM (Espèce, Benthos, Plancton, etc.)
784         if (pmfmId == null || pmfmId.equals(SpeciesBatchTreeHelperSupport.SORTING_TYPE_ID)) {
785             return;
786         }
787         SampleCategoryModel sampleCategoryModel = getSampleCategoryModel();
788 
789         boolean isSamplingCategory = sampleCategoryModel.containsCategoryId(pmfmId);
790         Preconditions.checkNotNull(isSamplingCategory, "Unable to find corresponding SampleCategoryEnum for PMFM.ID : " + pmfmId);
791 
792         target.setSampleCategoryId(pmfmId);
793         Serializable categoryValue = getSampleCategoryQualitative(
794                 pmfmId,
795                 numericalvalue,
796                 alphanumericalValue,
797                 qualitativeValueId);
798         target.setSampleCategoryValue(categoryValue);
799     }
800 
801     /**
802      * Check if the given {@code sortingBatch} is a frequency one.
803      *
804      * We test that:
805      * <ul>
806      * <li>batch has exactly one measurement</li>
807      * <li>the measurement pmfm is not a sample category</li>
808      * </ul>
809      *
810      * @param sampleCategoryModel model of authorized sample categories
811      * @param sortingBatch        batch to check
812      * @return {@code true} if given batch is a frequency batch,
813      * {@code false} otherwise.
814      */
815     private boolean isFrequencyBatch(SampleCategoryModel sampleCategoryModel, SortingBatch sortingBatch) {
816         boolean result = false;
817         if (sortingBatch.getSortingMeasurements().size() == 1) {
818             SortingMeasurement sm
819                     = sortingBatch.getSortingMeasurements().iterator().next();
820             Pmfm pmfm = sm.getPmfm();
821 
822             result = sortingBatch.getIndividualCount() != null &&
823                     !sampleCategoryModel.containsCategoryId(pmfm.getId());
824         }
825         return result;
826     }
827 
828     private Serializable getSampleCategoryQualitative(Integer pmfmId,
829                                                       Float numericalvalue,
830                                                       String alphanumericalValue,
831                                                       Integer qualitativeValueId) {
832 
833         if (numericalvalue != null) {
834             return numericalvalue;
835         }
836         if (alphanumericalValue != null) {
837             return alphanumericalValue;
838         }
839 
840         Caracteristic caracteristic = caracteristicService.getCaracteristic(pmfmId);
841         if (caracteristic == null || caracteristic.getCaracteristicType() != CaracteristicType.QUALITATIVE) {
842             return null;
843         }
844         CaracteristicQualitativeValue value = null;
845         for (CaracteristicQualitativeValue qv : caracteristic.getQualitativeValue()) {
846             if (qualitativeValueId.equals(qv.getIdAsInt())) {
847                 value = qv;
848                 break;
849             }
850         }
851 
852         return value;
853     }
854 
855     private void entityToBatchFrequency(SortingBatch source,
856                                         SpeciesBatchFrequency target) {
857 
858         target.setId(source.getId());
859 
860         // Rank order
861         target.setRankOrder(Integer.valueOf(source.getRankOrder()));
862 
863         target.setNumber(source.getIndividualCount());
864         target.setWeight(source.getWeight());
865 
866         Preconditions.checkState(source.getSortingMeasurements().size() == 1, "SortingBatch [" + source.getId() + "] need exactly one sortingMeasurement (to store the length step category), but had " + source.getSortingMeasurements().size());
867         SortingMeasurement sm = source.getSortingMeasurements().iterator().next();
868         Preconditions.checkNotNull(sm.getPmfm(), "SortingMeasurement [" + sm.getId() + "] can not have a null pmfm");
869         Preconditions.checkNotNull(sm.getPmfm().getId(), "SortingMeasurement [" + sm.getId() + "] can not have a pmfm with null id");
870 
871         // Length step category
872         Caracteristic lengthStepCaracteristic = caracteristicService.getCaracteristic(sm.getPmfm().getId());
873         target.setLengthStepCaracteristic(lengthStepCaracteristic);
874 
875         // Length
876         target.setLengthStep(sm.getNumericalValue());
877     }
878 
879     private List<SortingBatch> getFrequencyChilds(SortingBatch sortingBatch) {
880         List<SortingBatch> result = Lists.newArrayList();
881 
882         SampleCategoryModel sampleCategoryModel = getSampleCategoryModel();
883 
884         for (Batch batch : sortingBatch.getChildBatchs()) {
885             SortingBatch child = (SortingBatch) batch;
886             if (isFrequencyBatch(sampleCategoryModel, child)) {
887                 result.add(child);
888             }
889         }
890         return result;
891     }
892 
893     private List<SortingBatch> getFrequencies(Integer batchId) {
894         Preconditions.checkNotNull(batchId);
895         CatchBatch catchBatch = batchHelper.getRootCatchBatchByBatchId(batchId);
896         SortingBatch sortingBatch = batchHelper.getSortingBatchById(catchBatch, batchId);
897 
898         return getFrequencyChilds(sortingBatch);
899     }
900 
901     private void deleteSpeciesSubBatch(Integer speciesBatchId) {
902         Preconditions.checkNotNull(speciesBatchId);
903 
904         CatchBatch catchBatch = batchHelper.getRootCatchBatchByBatchId(speciesBatchId);
905         synchronizationStatusHelper.setDirty(catchBatch);
906 
907         SortingBatch sortingBatch = batchHelper.getSortingBatchById(catchBatch, speciesBatchId);
908 
909         // get his children
910         Collection<Batch> childBatchs = sortingBatch.getChildBatchs();
911 
912         if (CollectionUtils.isNotEmpty(childBatchs)) {
913 
914             for (Batch childBatch : childBatchs) {
915 
916                 // delete this child and all his children
917                 Integer childBatchId = childBatch.getId();
918 
919                 if (log.isDebugEnabled()) {
920                     log.debug("Delete child [" + childBatchId + "] of species batch: " + speciesBatchId);
921                 }
922                 batchHelper.removeWithChildren(childBatchId);
923             }
924         }
925     }
926 
927     private void changeBatchSpecies(Integer batchId, Species species) {
928 
929         Preconditions.checkNotNull(batchId);
930         Preconditions.checkNotNull(species);
931         Preconditions.checkNotNull(species.getReferenceTaxonId());
932 
933         CatchBatch catchBatch = batchHelper.getRootCatchBatchByBatchId(batchId);
934         synchronizationStatusHelper.setDirty(catchBatch);
935 
936         batchHelper.setSortingBatchReferenceTaxon(batchId, species);
937     }
938 }