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.Lists;
27  import com.google.common.collect.Maps;
28  import com.google.common.collect.Sets;
29  import fr.ifremer.adagio.core.dao.data.fishingArea.FishingArea;
30  import fr.ifremer.adagio.core.dao.data.fishingArea.FishingArea2RegulationLocation;
31  import fr.ifremer.adagio.core.dao.data.fishingArea.FishingArea2RegulationLocationDao;
32  import fr.ifremer.adagio.core.dao.data.fishingArea.FishingArea2RegulationLocationPK;
33  import fr.ifremer.adagio.core.dao.data.fishingArea.FishingAreaDao;
34  import fr.ifremer.adagio.core.dao.data.fishingArea.FishingAreaImpl;
35  import fr.ifremer.adagio.core.dao.data.measure.GearPhysicalMeasurement;
36  import fr.ifremer.adagio.core.dao.data.measure.GearUseMeasurement;
37  import fr.ifremer.adagio.core.dao.data.measure.VesselUseMeasurement;
38  import fr.ifremer.adagio.core.dao.data.operation.FishingOperationDao;
39  import fr.ifremer.adagio.core.dao.data.operation.Operation;
40  import fr.ifremer.adagio.core.dao.data.operation.OperationImpl;
41  import fr.ifremer.adagio.core.dao.data.operation.OperationVesselAssociation;
42  import fr.ifremer.adagio.core.dao.data.operation.OperationVesselAssociationDao;
43  import fr.ifremer.adagio.core.dao.data.operation.OperationVesselAssociationPK;
44  import fr.ifremer.adagio.core.dao.data.survey.fishingTrip.FishingTrip;
45  import fr.ifremer.adagio.core.dao.data.survey.scientificCruise.ScientificCruise;
46  import fr.ifremer.adagio.core.dao.data.survey.scientificCruise.ScientificCruiseDao;
47  import fr.ifremer.adagio.core.dao.data.vessel.VesselImpl;
48  import fr.ifremer.adagio.core.dao.data.vessel.feature.person.VesselPersonFeatures;
49  import fr.ifremer.adagio.core.dao.data.vessel.feature.physical.GearPhysicalFeatures;
50  import fr.ifremer.adagio.core.dao.data.vessel.feature.use.GearUseFeatures;
51  import fr.ifremer.adagio.core.dao.data.vessel.feature.use.VesselUseFeatures;
52  import fr.ifremer.adagio.core.dao.data.vessel.feature.use.isActive;
53  import fr.ifremer.adagio.core.dao.data.vessel.position.VesselPosition;
54  import fr.ifremer.adagio.core.dao.referential.ObjectTypeCode;
55  import fr.ifremer.adagio.core.dao.referential.QualityFlagCode;
56  import fr.ifremer.adagio.core.dao.referential.QualityFlagImpl;
57  import fr.ifremer.adagio.core.dao.referential.VesselPersonRole;
58  import fr.ifremer.adagio.core.dao.referential.VesselPersonRoleId;
59  import fr.ifremer.adagio.core.dao.referential.gear.GearImpl;
60  import fr.ifremer.adagio.core.dao.referential.location.LocationExtendDao;
61  import fr.ifremer.adagio.core.dao.referential.location.LocationImpl;
62  import fr.ifremer.adagio.core.dao.referential.location.LocationLevelId;
63  import fr.ifremer.adagio.core.dao.referential.pmfm.PmfmId;
64  import fr.ifremer.adagio.core.dao.referential.pmfm.QualitativeValueId;
65  import fr.ifremer.tutti.persistence.dao.GearPhysicalFeaturesDaoTutti;
66  import fr.ifremer.tutti.persistence.entities.CaracteristicMap;
67  import fr.ifremer.tutti.persistence.entities.data.FishingOperation;
68  import fr.ifremer.tutti.persistence.entities.data.FishingOperations;
69  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
70  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
71  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicType;
72  import fr.ifremer.tutti.persistence.entities.referential.Gear;
73  import fr.ifremer.tutti.persistence.entities.referential.GearWithOriginalRankOrders;
74  import fr.ifremer.tutti.persistence.entities.referential.Person;
75  import fr.ifremer.tutti.persistence.entities.referential.TuttiLocation;
76  import fr.ifremer.tutti.persistence.entities.referential.Vessel;
77  import fr.ifremer.tutti.persistence.service.referential.CaracteristicPersistenceService;
78  import fr.ifremer.tutti.persistence.service.referential.GearPersistenceService;
79  import fr.ifremer.tutti.persistence.service.referential.LocationPersistenceService;
80  import fr.ifremer.tutti.persistence.service.referential.PersonPersistenceService;
81  import fr.ifremer.tutti.persistence.service.referential.VesselPersistenceService;
82  import fr.ifremer.tutti.persistence.service.util.MeasurementPersistenceHelper;
83  import fr.ifremer.tutti.persistence.service.util.SynchronizationStatusHelper;
84  import fr.ifremer.tutti.persistence.service.util.VesselPersonFeaturesPersistenceHelper;
85  import org.apache.commons.collections4.CollectionUtils;
86  import org.apache.commons.collections4.MapUtils;
87  import org.apache.commons.lang3.StringUtils;
88  import org.apache.commons.logging.Log;
89  import org.apache.commons.logging.LogFactory;
90  import org.hibernate.FlushMode;
91  import org.hibernate.type.IntegerType;
92  import org.springframework.dao.DataIntegrityViolationException;
93  import org.springframework.dao.DataRetrievalFailureException;
94  import org.springframework.stereotype.Service;
95  
96  import javax.annotation.Resource;
97  import java.io.Serializable;
98  import java.sql.Timestamp;
99  import java.text.MessageFormat;
100 import java.util.Collection;
101 import java.util.Collections;
102 import java.util.Date;
103 import java.util.HashSet;
104 import java.util.Iterator;
105 import java.util.List;
106 import java.util.Map;
107 import java.util.Objects;
108 import java.util.Set;
109 
110 /**
111  * @author Tony Chemit - chemit@codelutin.com
112  * @since 0.3
113  */
114 @Service("fishingOperationPersistenceService")
115 public class FishingOperationPersistenceServiceImpl extends AbstractPersistenceService implements FishingOperationPersistenceService {
116 
117     /** Logger. */
118     private static final Log log =
119             LogFactory.getLog(FishingOperationPersistenceServiceImpl.class);
120 
121     @Resource(name = "caracteristicPersistenceService")
122     private CaracteristicPersistenceService caracteristicService;
123 
124     @Resource(name = "gearPersistenceService")
125     private GearPersistenceService gearService;
126 
127     @Resource(name = "locationPersistenceService")
128     private LocationPersistenceService locationService;
129 
130     @Resource(name = "personPersistenceService")
131     private PersonPersistenceService personService;
132 
133     @Resource(name = "vesselPersistenceService")
134     private VesselPersistenceService vesselService;
135 
136     @Resource(name = "batchPersistenceService")
137     protected CatchBatchPersistenceService catchBatchService;
138 
139     @Resource(name = "attachmentPersistenceService")
140     protected AttachmentPersistenceService attachmentPersistenceService;
141 
142     @Resource(name = "vesselPersonFeaturesPersistenceHelper")
143     protected VesselPersonFeaturesPersistenceHelper vesselPersonFeaturesPersistenceHelper;
144 
145     @Resource(name = "measurementPersistenceHelper")
146     protected MeasurementPersistenceHelper measurementPersistenceHelper;
147 
148     @Resource(name = "scientificCruiseDao")
149     protected ScientificCruiseDao scientificCruiseDao;
150 
151     @Resource(name = "gearPhysicalFeaturesDaoTutti")
152     protected GearPhysicalFeaturesDaoTutti gearPhysicalFeaturesDao;
153 
154     @Resource(name = "fishingArea2RegulationLocationDao")
155     protected FishingArea2RegulationLocationDao fishingArea2RegulationLocationDao;
156 
157     @Resource(name = "fishingOperationDao")
158     protected FishingOperationDao fishingOperationDao;
159 
160     @Resource(name = "fishingAreaDao")
161     protected FishingAreaDao fishingAreaDao;
162 
163     @Resource(name = "operationVesselAssociationDao")
164     protected OperationVesselAssociationDao operationVesselAssociationDao;
165 
166     @Resource(name = "locationDao")
167     protected LocationExtendDao locationDao;
168 
169     @Resource(name = "synchronizationStatusHelper")
170     protected SynchronizationStatusHelper synchronizationStatusHelper;
171 
172     protected static Float DEFAULT_EMPTY_LATITUDE = 0.0001f;
173 
174     protected static Float DEFAULT_EMPTY_LONGITUDE = 0.0001f;
175 
176     @Override
177     public int getFishingOperationCount(Integer cruiseId) {
178         Preconditions.checkNotNull(cruiseId);
179         Iterator<Object[]> list = queryList(
180                 "allFishingOperationIds",
181                 "cruiseId", IntegerType.INSTANCE, cruiseId
182         );
183         int result = 0;
184         while (list.hasNext()) {
185             list.next();
186             result++;
187         }
188         return result;
189     }
190 
191     @Override
192     public List<Integer> getAllFishingOperationIds(Integer cruiseId) {
193         Preconditions.checkNotNull(cruiseId);
194 
195         Iterator list = queryList(
196                 "allFishingOperationIds",
197                 "cruiseId", IntegerType.INSTANCE, cruiseId
198         );
199 
200         List<Integer> result = Lists.newArrayList();
201         while (list.hasNext()) {
202             Integer id = (Integer) list.next();
203             result.add(id);
204         }
205         return result;
206     }
207 
208     @Override
209     public List<FishingOperation> getAllFishingOperation(Integer cruiseId) {
210         Preconditions.checkNotNull(cruiseId);
211 
212         Iterator<Object[]> list = queryList(
213                 "allFishingOperations",
214                 "cruiseId", IntegerType.INSTANCE, cruiseId,
215                 "pmfmIdStationNumber", IntegerType.INSTANCE, PmfmId.STATION_NUMBER.getValue(),
216                 "pmfmIdMultirigAggregation", IntegerType.INSTANCE, PmfmId.MULTIRIG_AGGREGATION.getValue()
217         );
218 
219         List<FishingOperation> result = Lists.newArrayList();
220         int fishingOperationRankOrder = 0;
221         while (list.hasNext()) {
222             Object[] source = list.next();
223             fishingOperationRankOrder++;
224 
225             FishingOperation fishingOperation = FishingOperations.newFishingOperation();
226             int colIndex = 0;
227 
228             // Id
229             fishingOperation.setId(source[colIndex++].toString());
230 
231             // Fishing operation number : trying to retrieve from name
232             String name = (String) source[colIndex++];
233             if (StringUtils.isNotBlank(name)) {
234                 fishingOperation.setFishingOperationNumber(Integer.valueOf(name));
235             }
236 
237             // SynchronizationStatus
238             fishingOperation.setSynchronizationStatus((String) source[colIndex++]);
239 
240             // If not found, compute it using a counter (see "order by startDateTime" in HQL query)
241             if (fishingOperation.getFishingOperationNumber() == null) {
242                 fishingOperation.setFishingOperationNumber(fishingOperationRankOrder);
243             }
244 
245             // Start date time
246             Timestamp startDateTime = (Timestamp) source[colIndex++];
247             fishingOperation.setGearShootingStartDate(convertDatabase2UI(startDateTime));
248 
249             // Station number
250             String stationNumber = (String) source[colIndex++];
251             if (stationNumber != null) {
252                 fishingOperation.setStationNumber(stationNumber);
253             }
254 
255             // Multirig Aggregation
256             String multirigAggregation = (String) source[colIndex];
257             if (multirigAggregation != null) {
258                 fishingOperation.setMultirigAggregation(multirigAggregation);
259             }
260             // Force initialization to 1 initialization (need for UI)
261             else {
262                 log.warn(MessageFormat.format("FishingOperation with id={0} has been load with a default multirigAggregation={1}, because no multirigAggregation were found in database.", fishingOperation.getId(), fishingOperation.getMultirigAggregation()));
263                 fishingOperation.setMultirigAggregation("1");
264             }
265 
266             result.add(fishingOperation);
267         }
268         return Collections.unmodifiableList(result);
269     }
270 
271     @Override
272     public FishingOperation getFishingOperation(Integer id) {
273         Preconditions.checkNotNull(id);
274         Object[] source = queryUnique(
275                 "fishingOperation",
276                 "fishingOperationId", IntegerType.INSTANCE, id,
277                 "locationLevelIdStrata", IntegerType.INSTANCE, LocationLevelId.SCIENTIFIC_CRUISE_STRATA.getValue(),
278                 "locationLevelIdSubStrata", IntegerType.INSTANCE, LocationLevelId.SCIENTIFIC_CRUISE_SUB_STRATA.getValue(),
279                 "locationLevelIdLocalite", IntegerType.INSTANCE, LocationLevelId.SCIENTIFIC_CRUISE_LOCALITE.getValue()
280         );
281 
282         if (source == null) {
283             throw new DataRetrievalFailureException("Could not retrieve fishingOperation with id=" + id);
284         }
285         FishingOperation result = FishingOperations.newFishingOperation();
286         result.setId(id);
287 
288         // Cruise : 
289         // do load load Cruise here, because it will be attach upper in the call stack
290 
291         int colIndex = 0;
292 
293         // Name
294         String name = (String) source[colIndex++];
295         if (StringUtils.isNotBlank(name)) {
296             result.setFishingOperationNumber(Integer.valueOf(name));
297         }
298 
299         // SynchronizationStatus
300         result.setSynchronizationStatus((String) source[colIndex++]);
301 
302         Short rankOrder = (Short) source[colIndex++];
303 
304         // If not found, compute it using a counter (see "order by startDateTime" in HQL query)
305         if (result.getFishingOperationNumber() == null) {
306             Integer fishingOperationRankOrder = queryUniqueTyped(
307                     "fishingOperationRankOrder",
308                     "fishingOperationId", IntegerType.INSTANCE, id
309             );
310             result.setFishingOperationNumber(fishingOperationRankOrder);
311         }
312 
313         // Start date
314         result.setGearShootingStartDate(convertDatabase2UI((Timestamp) source[colIndex++]));
315 
316         // End date
317         result.setGearShootingEndDate(convertDatabase2UI((Timestamp) source[colIndex++]));
318 
319         // RecorderPerson
320         result.setRecorderPerson(Lists.<Person>newArrayList());
321         Iterator<Object[]> vesselPersonFeaturesList = queryList(
322                 "fishingOperationVesselPersonFeatures",
323                 "fishingOperationId", IntegerType.INSTANCE, id);
324         while (vesselPersonFeaturesList.hasNext()) {
325             Object[] vesselPersonFeatures = vesselPersonFeaturesList.next();
326 
327             Integer personId = (Integer) vesselPersonFeatures[0];
328             Person person = personService.getPerson(personId);
329             Integer roleId = (Integer) vesselPersonFeatures[1];
330             if (VesselPersonRoleId.RECORDER_PERSON.getValue().equals(roleId)) {
331                 result.addRecorderPerson(person);
332             }
333         }
334 
335         // Comment :
336         String comment = (String) source[colIndex++];
337         result.setComment(comment);
338 
339         // Gear :
340         Integer gearId = (Integer) source[colIndex++];
341         if (gearId != null) {
342             // get gear from referential
343             Gear gear = gearService.getGear(gearId);
344             Preconditions.checkNotNull(gear);
345             // use a copy of it with rankOrder
346             Gear toSet = GearWithOriginalRankOrders.newGearWithOriginalRankOrder(gear);
347             toSet.setRankOrder(rankOrder);
348 
349             if (log.isDebugEnabled()) {
350                 log.debug("FishingOperation: " + id + ", use gear: " + gearId + " - " + rankOrder);
351             }
352 
353             result.setGear(toSet);
354         }
355 
356         // Start position
357         VesselPosition startVesselPosition = (VesselPosition) source[colIndex++];
358         if (startVesselPosition == null) {
359             result.setGearShootingStartLatitude(null);
360             result.setGearShootingStartLongitude(null);
361         } else {
362             result.setGearShootingStartLatitude(convertLatitude2UI(startVesselPosition.getLatitude()));
363             result.setGearShootingStartLongitude(convertLongitude2UI(startVesselPosition.getLongitude()));
364         }
365 
366         // End position
367         VesselPosition endVesselPosition = (VesselPosition) source[colIndex++];
368         if (endVesselPosition == null) {
369             result.setGearShootingEndLatitude(null);
370             result.setGearShootingEndLongitude(null);
371         } else {
372             result.setGearShootingEndLatitude(DEFAULT_EMPTY_LATITUDE.equals(endVesselPosition.getLatitude()) ? null : endVesselPosition.getLatitude());
373             result.setGearShootingEndLongitude(DEFAULT_EMPTY_LONGITUDE.equals(endVesselPosition.getLongitude()) ? null : endVesselPosition.getLongitude());
374         }
375 
376         // Strata :
377         Integer strataId = (Integer) source[colIndex++];
378         if (strataId != null) {
379             TuttiLocation strata = locationService.getLocation(strataId.toString());
380             result.setStrata(strata);
381         }
382 
383         // Sub Strata :
384         Integer subStrataId = (Integer) source[colIndex++];
385         if (subStrataId != null) {
386             TuttiLocation subStrata = locationService.getLocation(subStrataId.toString());
387             result.setSubStrata(subStrata);
388         }
389 
390         // Localite :
391         Integer localiteId = (Integer) source[colIndex++];
392         if (localiteId != null) {
393             TuttiLocation localite = locationService.getLocation(localiteId.toString());
394             result.setLocation(localite);
395         }
396 
397         // Vessel (the one with the catch batch)
398         String vesselCode = (String) source[colIndex];
399         if (vesselCode != null) {
400             Vessel vessel = vesselService.getVessel(vesselCode);
401             result.setVessel(vessel);
402         }
403 
404         // Retrieve secondary vessels
405         List<Vessel> secondaryVessel = getFishingOperationSecondaryVessel(id);
406         result.setSecondaryVessel(secondaryVessel);
407 
408         // Retrieve gear use features
409         getGearUseCaracteristics(id, result);
410 
411         // Retrieve vessel use features
412         getVesselUseCaracteristics(id, result);
413 
414         return result;
415     }
416 
417     @Override
418     public List<Vessel> getFishingOperationSecondaryVessel(Integer fishingOperationId) {
419         Iterator<Object[]> secondaryVesselList = queryList(
420                 "fishingOperationSecondaryVessel",
421                 "fishingOperationId", IntegerType.INSTANCE, fishingOperationId);
422         List<Vessel> result = Lists.newArrayList();
423         while (secondaryVesselList.hasNext()) {
424 
425             Object[] next = secondaryVesselList.next();
426             String secondaryVesselCode = (String) next[0];
427             if (secondaryVesselCode != null) {
428                 Boolean isCatchOnOperationVessel = (Boolean) next[1];
429                 if (isCatchOnOperationVessel == null || !isCatchOnOperationVessel) {
430 
431                     Vessel vessel = vesselService.getVessel(secondaryVesselCode);
432                     result.add(vessel);
433                 }
434             }
435         }
436         return result;
437     }
438 
439     @Override
440     public FishingOperation createFishingOperation(FishingOperation bean) {
441         Preconditions.checkNotNull(bean);
442         Preconditions.checkArgument(bean.getId() == null);
443         Preconditions.checkNotNull(bean.getCruise());
444         Preconditions.checkNotNull(bean.getCruise().getId());
445         //TODO-TC Voir si il n'y a pas d'autre données obligatoires
446 
447         if (bean.getGearShootingStartDate() != null && bean.getGearShootingEndDate() != null) {
448 
449             //make sure not same date
450             Preconditions.checkArgument(!bean.getGearShootingStartDate().equals(bean.getGearShootingEndDate()));
451         }
452 
453         getCurrentSession().setFlushMode(FlushMode.COMMIT);
454         fr.ifremer.adagio.core.dao.data.operation.FishingOperation fishingOperation = fr.ifremer.adagio.core.dao.data.operation.FishingOperation.Factory.newInstance();
455         beanToEntity(bean, fishingOperation);
456         fishingOperationDao.create(fishingOperation);
457         bean.setId(String.valueOf(fishingOperation.getId()));
458         getCurrentSession().flush();
459 
460         return bean;
461     }
462 
463     @Override
464     public FishingOperation saveFishingOperation(FishingOperation bean) {
465         Preconditions.checkNotNull(bean);
466         Preconditions.checkNotNull(bean.getId());
467         Preconditions.checkNotNull(bean.getCruise());
468         Preconditions.checkNotNull(bean.getCruise().getId());
469         //TODO-TC Voir si il n'y a pas d'autre données obligatoires
470 
471         if (bean.getGearShootingStartDate() != null && bean.getGearShootingEndDate() != null) {
472 
473             //make sure not same date
474             Preconditions.checkArgument(!bean.getGearShootingStartDate().equals(bean.getGearShootingEndDate()));
475         }
476 
477         getCurrentSession().clear();
478         getCurrentSession().setFlushMode(FlushMode.COMMIT);
479         fr.ifremer.adagio.core.dao.data.operation.FishingOperation fishingOperation = fishingOperationDao.load(Integer.valueOf(bean.getId()));
480         if (fishingOperation == null) {
481             throw new DataRetrievalFailureException("Could not retrieve fishing operation with id=" + bean.getId());
482         }
483         beanToEntity(bean, fishingOperation);
484         fishingOperationDao.update(fishingOperation);
485         getCurrentSession().flush();
486         return bean;
487     }
488 
489     @Override
490     public Collection<FishingOperation> saveFishingOperations(Collection<FishingOperation> beans) {
491         getCurrentSession().clear();
492         getCurrentSession().setFlushMode(FlushMode.COMMIT);
493 
494         List<fr.ifremer.adagio.core.dao.data.operation.FishingOperation> operations = Lists.newArrayList();
495         for (FishingOperation bean : beans) {
496             fr.ifremer.adagio.core.dao.data.operation.FishingOperation fishingOperation =
497                     fishingOperationDao.load(Integer.valueOf(bean.getId()));
498             if (fishingOperation == null) {
499                 throw new DataRetrievalFailureException("Could not retrieve fishing operation with id=" + bean.getId());
500             }
501             beanToEntity(bean, fishingOperation);
502             operations.add(fishingOperation);
503         }
504 
505         fishingOperationDao.update(operations);
506         getCurrentSession().flush();
507         return beans;
508     }
509 
510     @Override
511     public void deleteFishingOperation(Integer fishingOperationId) {
512         Preconditions.checkNotNull(fishingOperationId);
513 
514         fr.ifremer.adagio.core.dao.data.operation.FishingOperation fishingOperation = fishingOperationDao.load(fishingOperationId);
515         if (fishingOperation == null) {
516             throw new DataRetrievalFailureException("Could not retrieve fishing operation with id=" + fishingOperationId);
517         }
518 
519         attachmentPersistenceService.deleteAllAttachment(ObjectTypeCode.OPERATION, fishingOperationId);
520         getCurrentSession().flush();
521 
522         // delete catch batch
523         catchBatchService.deleteCatchBatch(fishingOperationId);
524         getCurrentSession().flush();
525 
526         Set<FishingArea> fishingAreas = Sets.newHashSet();
527 
528         // remove gear use features
529         if (CollectionUtils.isNotEmpty(fishingOperation.getGearUseFeatures())) {
530             for (GearUseFeatures gearUseFeatures : fishingOperation.getGearUseFeatures()) {
531 
532                 gearUseFeatures.setOperation(null);
533                 gearUseFeatures.getGearUseMeasurements().clear();
534 
535                 if (CollectionUtils.isNotEmpty(gearUseFeatures.getFishingAreas())) {
536                     for (FishingArea fishingArea : gearUseFeatures.getFishingAreas()) {
537                         fishingArea.setGearUseFeatures(null);
538                     }
539                     fishingAreas.addAll(gearUseFeatures.getFishingAreas());
540                 }
541             }
542             // must remove all features content before removing them (data integrity will then failed otherwise)
543             getCurrentSession().flush();
544             fishingOperation.getGearUseFeatures().clear();
545         }
546 
547         // remove vessel use features
548         if (CollectionUtils.isNotEmpty(fishingOperation.getVesselUseFeatures())) {
549             for (VesselUseFeatures vesselUseFeatures : fishingOperation.getVesselUseFeatures()) {
550 
551                 vesselUseFeatures.setOperation(null);
552                 vesselUseFeatures.getVesselUseMeasurements().clear();
553 
554                 if (CollectionUtils.isNotEmpty(vesselUseFeatures.getFishingAreas())) {
555                     for (FishingArea fishingArea : vesselUseFeatures.getFishingAreas()) {
556                         fishingArea.setVesselUseFeatures(null);
557                     }
558                     fishingAreas.addAll(vesselUseFeatures.getFishingAreas());
559                 }
560             }
561 
562             // must remove all features content before removing them (data integrity will then failed otherwise)
563             getCurrentSession().flush();
564             fishingOperation.getVesselUseFeatures().clear();
565         }
566 
567         // remove vessel position
568         if (CollectionUtils.isNotEmpty(fishingOperation.getVesselPositions())) {
569             fishingOperation.getVesselPositions().clear();
570         }
571 
572         // remove vessel person features
573         if (CollectionUtils.isNotEmpty(fishingOperation.getVesselPersonFeatures())) {
574             fishingOperation.getVesselPersonFeatures().clear();
575         }
576 
577         // remove gear physical features
578         fishingOperation.setGearPhysicalFeatures(null);
579 
580         // remove catch batch
581         fishingOperation.setCatchBatch(null);
582 
583         // remove samples
584         if (CollectionUtils.isNotEmpty(fishingOperation.getSamples())) {
585             fishingOperation.getSamples().clear();
586         }
587 
588         // remove operation vessel associations
589         if (CollectionUtils.isNotEmpty(fishingOperation.getOperationVesselAssociations())) {
590             for (OperationVesselAssociation asso : fishingOperation.getOperationVesselAssociations()) {
591                 operationVesselAssociationDao.remove(asso);
592             }
593             fishingOperation.getOperationVesselAssociations().clear();
594         }
595 
596         // remove fishing areas
597         if (CollectionUtils.isNotEmpty(fishingAreas)) {
598             for (FishingArea fishingArea : fishingAreas) {
599                 fishingArea.setProduce(null);
600                 fishingArea2RegulationLocationDao.remove(fishingArea.getRegulationLocations());
601                 fishingArea.getRegulationLocations().clear();
602             }
603             getCurrentSession().flush();
604             fishingAreaDao.remove(fishingAreas);
605         }
606 
607         getCurrentSession().flush();
608 
609         // remove fishing operation produces
610         if (CollectionUtils.isNotEmpty(fishingOperation.getProduces())) {
611             fishingOperation.getProduces().clear();
612             getCurrentSession().flush();
613         }
614 
615         // remove fishing operations
616         fishingOperationDao.remove(fishingOperation);
617 
618         getCurrentSession().flush();
619 
620 //        attachmentPersistenceService.deleteAllAttachment(
621 //                ObjectTypeCode.OPERATION,
622 //                fishingOperationId);
623     }
624 
625     //------------------------------------------------------------------------//
626     //-- Internal methods                                                   --//
627     //------------------------------------------------------------------------//
628 
629     protected void getVesselUseCaracteristics(Integer fishingOperationId, FishingOperation result) {
630         // retrieve fishing operation caracteristics
631         Iterator<Object[]> list = queryList(
632                 "fishingOperationVesselUseFeatures",
633                 "fishingOperationId", IntegerType.INSTANCE, fishingOperationId
634         );
635 
636         CaracteristicMap vesselUseCaracteristics = new CaracteristicMap();
637         while (list.hasNext()) {
638             int colIndex = 0;
639             Object[] source = list.next();
640             Integer pmfmId = (Integer) source[colIndex++];
641             Float numericalValue = (Float) source[colIndex++];
642             String alphanumericalValue = (String) source[colIndex++];
643             Integer qualitativeValueId = (Integer) source[colIndex];
644 
645             // Trawl distance
646             if (PmfmId.TRAWL_DISTANCE.getValue().equals(pmfmId)) {
647                 result.setTrawlDistance(numericalValue == null ? null : numericalValue.intValue());
648             }
649 
650             // Rectilinear operation ?
651             else if (PmfmId.RECTILINEAR_OPERATION.getValue().equals(pmfmId)) {
652                 result.setFishingOperationRectiligne(QualitativeValueId.RECTILINEAR_OPERATION_YES.getValue().equals(qualitativeValueId));
653             }
654 
655             // Haul valid ?
656             else if (PmfmId.HAUL_VALID.getValue().equals(pmfmId)) {
657                 if (qualitativeValueId != null) {
658                     result.setFishingOperationValid(QualitativeValueId.HAUL_VALID_YES.getValue().equals(qualitativeValueId));
659                 } else {
660                     result.setFishingOperationValid(null);
661                 }
662             }
663 
664             // Station Number :
665             else if (PmfmId.STATION_NUMBER.getValue().equals(pmfmId)) {
666                 result.setStationNumber(alphanumericalValue);
667             }
668 
669             // Vessel Use caracteristic
670             else {
671 
672                 Caracteristic environmentCaracteristic = caracteristicService.getCaracteristic(pmfmId);
673                 Serializable value = null;
674                 if (environmentCaracteristic.getCaracteristicType() == CaracteristicType.NUMBER) {
675                     value = numericalValue;
676                 } else if (environmentCaracteristic.getCaracteristicType() == CaracteristicType.TEXT) {
677                     value = alphanumericalValue;
678                 } else if (environmentCaracteristic.getCaracteristicType() == CaracteristicType.QUALITATIVE) {
679                     for (CaracteristicQualitativeValue qv : environmentCaracteristic.getQualitativeValue()) {
680                         if (Objects.equals(qualitativeValueId ,Integer.parseInt(qv.getId()))) {
681                             value = qv;
682                             break;
683                         }
684                     }
685                 }
686                 vesselUseCaracteristics.put(environmentCaracteristic, value);
687             }
688         }
689 
690         if (vesselUseCaracteristics.size() > 0) {
691             result.setVesselUseFeatures(vesselUseCaracteristics);
692         }
693     }
694 
695     protected void getGearUseCaracteristics(Integer fishingOperationId, FishingOperation result) {
696         // retrieve fishing operation caracteristics
697         Iterator<Object[]> list = queryList(
698                 "fishingOperationGearUseFeatures",
699                 "fishingOperationId", IntegerType.INSTANCE, fishingOperationId
700         );
701 
702         CaracteristicMap gearShootingCaracteristics = new CaracteristicMap();
703         while (list.hasNext()) {
704             int colIndex = 0;
705             Object[] source = list.next();
706             Integer pmfmId = (Integer) source[colIndex++];
707             Float numericalValue = (Float) source[colIndex++];
708             String alphanumericalValue = (String) source[colIndex++];
709             Integer qualitativeValueId = (Integer) source[colIndex];
710 
711             // Trawl net number
712             if (PmfmId.MULTIRIG_AGGREGATION.getValue().equals(pmfmId)
713                 && alphanumericalValue != null
714                 && alphanumericalValue.matches("\\d+")) {
715                 result.setMultirigAggregation(alphanumericalValue);
716             }
717 
718             // Gear Shooting Caracteristics
719             else {
720                 Caracteristic gearShootingCaracteristic = caracteristicService.getCaracteristic(pmfmId);
721                 Serializable value = null;
722                 if (gearShootingCaracteristic.getCaracteristicType() == CaracteristicType.NUMBER) {
723                     value = numericalValue;
724                 } else if (gearShootingCaracteristic.getCaracteristicType() == CaracteristicType.TEXT) {
725                     value = alphanumericalValue;
726                 } else if (gearShootingCaracteristic.getCaracteristicType() == CaracteristicType.QUALITATIVE) {
727                     for (CaracteristicQualitativeValue qv : gearShootingCaracteristic.getQualitativeValue()) {
728                         if (qualitativeValueId == Integer.parseInt(qv.getId())) {
729                             value = qv;
730                             break;
731                         }
732                     }
733                 }
734                 gearShootingCaracteristics.put(gearShootingCaracteristic, value);
735             }
736         }
737 
738         if (gearShootingCaracteristics.size() > 0) {
739             result.setGearUseFeatures(gearShootingCaracteristics);
740         }
741     }
742 
743     protected void beanToEntity(FishingOperation source,
744                                 fr.ifremer.adagio.core.dao.data.operation.FishingOperation target) {
745         // Retrieve entities : FishingTrip and ScientificCruise
746         ScientificCruise scientificCruise;
747         FishingTrip fishingTrip = target.getFishingTrip();
748         if (fishingTrip == null) {
749             scientificCruise = scientificCruiseDao.load(source.getCruise().getIdAsInt());
750             fishingTrip = scientificCruise.getFishingTrips().iterator().next();
751             fishingTrip.getOperations().add(target); // Inverse link
752         } else {
753             scientificCruise = fishingTrip.getScientificCruise();
754         }
755         // Link to parent fishing trip
756         target.setFishingTrip(fishingTrip);
757 
758         // SynchronizationStatus
759         synchronizationStatusHelper.setDirty(fishingTrip);
760 
761 
762         // Retrieve entities : VesselPosition (start and end)
763         VesselPosition startPosition = null;
764         VesselPosition endPosition = null;
765         if (target.getVesselPositions() != null) {
766             for (VesselPosition position : target.getVesselPositions()) {
767                 if (position.getDateTime() != null && position.getDateTime().getTime() == target.getStartDateTime().getTime()) {
768                     startPosition = position;
769                 } else if (position.getDateTime() == null || (target.getEndDateTime() != null && position.getDateTime().getTime() == target.getEndDateTime().getTime())) {
770                     endPosition = position;
771                 }
772             }
773         }
774 
775         // Retrieve entities : Gear Use Features
776         GearUseFeatures gearUseFeatures;
777         if (CollectionUtils.isEmpty(target.getGearUseFeatures())) {
778             gearUseFeatures = GearUseFeatures.Factory.newInstance();
779             gearUseFeatures.setOperation(target);
780             if (target.getGearUseFeatures() == null) {
781                 target.setGearUseFeatures(Sets.newHashSet(gearUseFeatures));
782             } else {
783                 target.getGearUseFeatures().add(gearUseFeatures);
784             }
785         } else {
786             gearUseFeatures = target.getGearUseFeatures().iterator().next();
787         }
788 
789         // Create a list to store all updates, then remove not updated items
790         Set<GearUseMeasurement> notChangedGearUseMeasurements = new HashSet<>();
791         if (gearUseFeatures.getGearUseMeasurements() != null) {
792             notChangedGearUseMeasurements.addAll(gearUseFeatures.getGearUseMeasurements());
793         }
794 
795         // Retrieve entities : Vessel Use Features
796         VesselUseFeatures vesselUseFeatures;
797         if (CollectionUtils.isEmpty(target.getVesselUseFeatures())) {
798             vesselUseFeatures = VesselUseFeatures.Factory.newInstance();
799             if (target.getVesselUseFeatures() == null) {
800                 target.setVesselUseFeatures(Sets.newHashSet(vesselUseFeatures));
801                 vesselUseFeatures.setOperation(target);
802             } else {
803                 target.getVesselUseFeatures().add(vesselUseFeatures);
804                 vesselUseFeatures.setOperation(target);
805             }
806         } else {
807             vesselUseFeatures = target.getVesselUseFeatures().iterator().next();
808         }
809 
810         // Create a list to trace not updated items, to be able to remove them later 
811         Set<VesselUseMeasurement> notChangedVesselUseMeasurements = new HashSet<>();
812         if (vesselUseFeatures.getVesselUseMeasurements() != null) {
813             notChangedVesselUseMeasurements.addAll(vesselUseFeatures.getVesselUseMeasurements());
814         }
815 
816         boolean withGear = source.getGear() != null;
817 
818         Short gearRankOrder = null;
819         if (withGear) {
820             gearRankOrder = source.getGear().getRankOrder();
821         }
822 
823         if (gearRankOrder == null) {
824             gearRankOrder = 1;
825         }
826 
827         if (log.isInfoEnabled()) {
828             log.info("Use gear rankOrder: " + gearRankOrder);
829         }
830 
831         // Retrieve entities : Gear Physical Features
832         GearPhysicalFeatures gearPhysicalFeatures = target.getGearPhysicalFeatures();
833         if (withGear) {
834 
835             // use selected gear
836 
837             if (gearPhysicalFeatures == null) {
838                 gearPhysicalFeatures = gearPhysicalFeaturesDao.getGearPhysicalfeatures(fishingTrip, source.getGear().getIdAsInt(), false);
839                 if (gearPhysicalFeatures == null) {
840                     throw new DataIntegrityViolationException("An operation could not use a gear that is not declared in the cruise.");
841                 }
842                 target.setGearPhysicalFeatures(gearPhysicalFeatures);
843                 if (gearPhysicalFeatures.getOperations() == null) {
844                     gearPhysicalFeatures.setOperations(Sets.newHashSet((Operation) target));
845                 } else {
846                     gearPhysicalFeatures.getOperations().add(target);
847                 }
848             }
849         } else {
850 
851             // no gear
852             gearUseFeatures.setGear(null);
853             gearUseFeatures.setRankOrder((short) 0);
854         }
855 
856         // Retrieve entities : Fishing Area
857         FishingArea fishingArea;
858         List<FishingArea2RegulationLocation> notChangedRegulationLocation;
859 
860         if (CollectionUtils.isEmpty(gearUseFeatures.getFishingAreas())) {
861             fishingArea = FishingArea.Factory.newInstance();
862             if (gearUseFeatures.getFishingAreas() == null) {
863                 gearUseFeatures.setFishingAreas(Sets.newHashSet(fishingArea));
864                 fishingArea.setGearUseFeatures(gearUseFeatures);
865             } else {
866                 gearUseFeatures.getFishingAreas().add(fishingArea);
867                 fishingArea.setGearUseFeatures(gearUseFeatures);
868             }
869             notChangedRegulationLocation = Lists.newArrayList();
870         } else {
871             fishingArea = gearUseFeatures.getFishingAreas().iterator().next();
872 
873             notChangedRegulationLocation = Lists.newArrayList(fishingArea.getRegulationLocations());
874             // Reset all other fishing areas
875         }
876 
877         // Retrieve multirig number, from Gear physical features
878         int cruiseMultirigCount = 1; // default value
879         if (gearPhysicalFeatures != null) {
880             GearPhysicalMeasurement gpmMultirigCount = gearPhysicalFeaturesDao.getGearPhysicalMeasurement(gearPhysicalFeatures, PmfmId.MULTIRIG_NUMBER.getValue());
881             if (gpmMultirigCount != null && gpmMultirigCount.getNumericalValue() != null) {
882                 cruiseMultirigCount = gpmMultirigCount.getNumericalValue().intValue();
883             }
884         }
885 
886         // StationNumber
887         if (source.getStationNumber() != null) {
888             VesselUseMeasurement vum = measurementPersistenceHelper.setVesselUseMeasurement(scientificCruise, vesselUseFeatures, PmfmId.STATION_NUMBER.getValue(), null, source.getStationNumber(), null);
889             notChangedVesselUseMeasurements.remove(vum);
890         }
891 
892         // OP N°
893         if (source.getFishingOperationNumber() != null) {
894             target.setName(source.getFishingOperationNumber().toString());
895         }
896 
897         // Multirig Aggregation
898         if (source.getMultirigAggregation() != null) {
899             if (source.getMultirigAggregation().matches("\\d+")) {
900                 int mutlirigNumber = Integer.valueOf(source.getMultirigAggregation());
901                 if (mutlirigNumber > cruiseMultirigCount) {
902                     throw new DataIntegrityViolationException("An operation could not have a 'multirig number' greater than 'multirig count' defined in the cruise.");
903                 }
904             }
905 
906             // Store into Gear Use Features
907             GearUseMeasurement gum = measurementPersistenceHelper.setGearUseMeasurement(scientificCruise, gearUseFeatures, PmfmId.MULTIRIG_AGGREGATION.getValue(), null, source.getMultirigAggregation(), null);
908             notChangedGearUseMeasurements.remove(gum);
909         }
910 
911         // Start date :
912         if (source.getGearShootingStartDate() == null) {
913             target.setStartDateTime(null);
914             target.setFishingStartDateTime(null);
915         } else if (source.getGearShootingStartDate() != null) {
916             // Reset millisecond (as need for Allegro)
917             Date d = dateWithNoMiliSecond(source.getGearShootingStartDate());
918             target.setStartDateTime(d);
919             target.setFishingStartDateTime(d);
920         }
921 
922         // End date :
923         if (source.getGearShootingEndDate() == null) {
924             target.setEndDateTime(null);
925             target.setFishingEndDateTime(null);
926         } else if (source.getGearShootingEndDate() != null) {
927             // Reset millisecond (as need for Allegro)
928             Date d = dateWithNoMiliSecond(source.getGearShootingEndDate());
929             target.setEndDateTime(d);
930             target.setFishingEndDateTime(d);
931         }
932 
933         // Trawl distance
934         if (source.getTrawlDistance() != null) {
935             VesselUseMeasurement vum = measurementPersistenceHelper.setVesselUseMeasurement(scientificCruise, vesselUseFeatures, PmfmId.TRAWL_DISTANCE.getValue(), source.getTrawlDistance().floatValue(), null, null);
936             notChangedVesselUseMeasurements.remove(vum);
937         }
938 
939         // Rectilinear operation
940         {
941             VesselUseMeasurement vum = measurementPersistenceHelper.setVesselUseMeasurement(scientificCruise, vesselUseFeatures, PmfmId.RECTILINEAR_OPERATION.getValue(), null, null, source.isFishingOperationRectiligne() ? QualitativeValueId.RECTILINEAR_OPERATION_YES.getValue() : QualitativeValueId.RECTILINEAR_OPERATION_NO.getValue());
942             notChangedVesselUseMeasurements.remove(vum);
943         }
944 
945         // Operation is valid ?
946         if (source.getFishingOperationValid() != null) {
947             VesselUseMeasurement vum = measurementPersistenceHelper.setVesselUseMeasurement(scientificCruise, vesselUseFeatures, PmfmId.HAUL_VALID.getValue(), null, null, source.getFishingOperationValid() ? QualitativeValueId.HAUL_VALID_YES.getValue() : QualitativeValueId.HAUL_VALID_NO.getValue());
948             notChangedVesselUseMeasurements.remove(vum);
949         }
950 
951         // Vessel
952         target.setVessel(fishingTrip.getVessel());
953 
954         // Associated vessel
955         List<String> vesselIds = Lists.newArrayList();
956         if (!source.isSecondaryVesselEmpty()) {
957             for (Vessel vessel : source.getSecondaryVessel()) {
958                 vesselIds.add(vessel.getId());
959             }
960         }
961         setOperationVesselAssociation(target, vesselIds);
962 
963         // Quality Flag :
964         if (target.getQualityFlag() == null) {
965             target.setQualityFlag(load(QualityFlagImpl.class, QualityFlagCode.NOTQUALIFIED.getValue()));
966         }
967 
968         // Settings not null properties :
969         if (target.getStartDateTime() == null) {
970             // Generate a fake departureDate (precision=minute) then add 1 millisecond
971             Date d = dateWithNoSecondAndOneMiliSecond(scientificCruise.getDepartureDateTime());
972             target.setStartDateTime(d);
973             target.setFishingStartDateTime(d);
974         }
975 
976         // VesselUseFeatures :
977         vesselUseFeatures.setStartDate(target.getStartDateTime());
978         if (vesselUseFeatures.getStartDate() == null) {
979             vesselUseFeatures.setStartDate(scientificCruise.getDepartureDateTime());
980         }
981         vesselUseFeatures.setEndDate(target.getEndDateTime());
982         vesselUseFeatures.setVessel(target.getVessel());
983         vesselUseFeatures.setProgram(scientificCruise.getProgram());
984         vesselUseFeatures.setIsActive(isActive.ACTIVE.getValue());
985         if (vesselUseFeatures.getCreationDate() == null) {
986             Date d = newCreateDate();
987             vesselUseFeatures.setCreationDate(d);
988         }
989         if (vesselUseFeatures.getQualityFlag() == null) {
990             vesselUseFeatures.setQualityFlag(load(QualityFlagImpl.class, QualityFlagCode.NOTQUALIFIED.getValue()));
991         }
992 
993         // GearUseFeatures :
994         gearUseFeatures.setStartDate(target.getStartDateTime());
995         if (gearUseFeatures.getStartDate() == null) {
996             gearUseFeatures.setStartDate(scientificCruise.getDepartureDateTime());
997         }
998         gearUseFeatures.setEndDate(target.getEndDateTime());
999         gearUseFeatures.setVessel(target.getVessel());
1000         gearUseFeatures.setProgram(scientificCruise.getProgram());
1001         if (gearUseFeatures.getCreationDate() == null) {
1002             Date d = newCreateDate();
1003             gearUseFeatures.setCreationDate(d);
1004         }
1005         if (gearUseFeatures.getQualityFlag() == null) {
1006             gearUseFeatures.setQualityFlag(load(QualityFlagImpl.class, QualityFlagCode.NOTQUALIFIED.getValue()));
1007         }
1008 
1009         // GearUseFeatures.Gear
1010         if (!withGear) {
1011             gearUseFeatures.setGear(null);
1012             gearUseFeatures.setRankOrder((short) 0);
1013         } else {
1014             gearUseFeatures.setGear(load(GearImpl.class, source.getGear().getIdAsInt()));
1015             gearUseFeatures.setRankOrder(gearRankOrder);
1016         }
1017 
1018         // Start position :
1019         // If need to be store
1020         if (source.getGearShootingEndLatitude() != null
1021             || source.getGearShootingEndDate() != null
1022             || source.getGearShootingEndLongitude() != null) {
1023 
1024             if (startPosition == null) {
1025                 startPosition = VesselPosition.Factory.newInstance();
1026                 startPosition.setOperation(target);
1027                 if (target.getVesselPositions() == null) {
1028                     target.setVesselPositions(Sets.newHashSet(startPosition));
1029                 }
1030                 target.getVesselPositions().add(startPosition);
1031             }
1032             startPosition.setDateTime(target.getStartDateTime());
1033             startPosition.setLatitude(source.getGearShootingStartLatitude() == null ? DEFAULT_EMPTY_LATITUDE : source.getGearShootingStartLatitude());
1034             startPosition.setLongitude(source.getGearShootingStartLongitude() == null ? DEFAULT_EMPTY_LONGITUDE : source.getGearShootingStartLongitude());
1035             startPosition.setVessel(target.getVessel());
1036             startPosition.setProgram(scientificCruise.getProgram());
1037             startPosition.setRecorderDepartment(scientificCruise.getRecorderDepartment());
1038             startPosition.setQualityFlag(target.getQualityFlag());
1039         }
1040 
1041         // If not need to be store, delete start position from list 
1042         else if (startPosition != null && target.getVesselPositions() != null) {
1043             target.getVesselPositions().remove(startPosition);
1044         }
1045 
1046         // End position :
1047         // If need to be store
1048         if (source.getGearShootingEndLatitude() != null
1049             || source.getGearShootingEndDate() != null
1050             || source.getGearShootingEndLongitude() != null) {
1051 
1052             if (endPosition == null) {
1053                 endPosition = VesselPosition.Factory.newInstance();
1054                 endPosition.setOperation(target);
1055                 target.getVesselPositions().add(endPosition);
1056             }
1057             Date endDateTime = convertUI2DatabaseMandatoryDate(
1058                     target.getEndDateTime(),
1059                     startPosition.getDateTime(), true);
1060             endPosition.setDateTime(endDateTime);
1061             if (!endDateTime.equals(target.getEndDateTime())) {
1062                 // To link position with the operation end, Allegro need to have exactly the same dates
1063                 target.setEndDateTime(endDateTime);
1064                 target.setFishingEndDateTime(endDateTime);
1065             }
1066             endPosition.setLatitude(convertUI2DatabaseMandatoryLatitude(source.getGearShootingEndLatitude()));
1067             endPosition.setLongitude(convertUI2DatabaseMandatoryLatitude(source.getGearShootingEndLongitude()));
1068             endPosition.setVessel(target.getVessel());
1069             endPosition.setProgram(scientificCruise.getProgram());
1070             endPosition.setRecorderDepartment(scientificCruise.getRecorderDepartment());
1071             endPosition.setQualityFlag(target.getQualityFlag());
1072         }
1073 
1074         // If not need to be store, delete end position from list 
1075         else if (endPosition != null && target.getVesselPositions() != null) {
1076             target.getVesselPositions().remove(endPosition);
1077         }
1078 
1079         // Vessel user features
1080         CaracteristicMap vesselUseCaracteristics = source.getVesselUseFeatures();
1081         if (MapUtils.isNotEmpty(vesselUseCaracteristics)) {
1082             for (Caracteristic caracteristic : vesselUseCaracteristics.keySet()) {
1083                 VesselUseMeasurement vum = measurementPersistenceHelper.setVesselUseMeasurement(scientificCruise, vesselUseFeatures, caracteristic, vesselUseCaracteristics.get(caracteristic));
1084                 notChangedVesselUseMeasurements.remove(vum);
1085             }
1086         }
1087 
1088         // ----------------------------------------------------------------
1089         // Recorder persons                                            ---
1090         // ----------------------------------------------------------------
1091 
1092         Map<Integer, VesselPersonFeatures> vesselPersonFeaturesMap = Maps.newTreeMap();
1093 
1094         VesselPersonRole recorderPersonRole =
1095                 vesselPersonFeaturesPersistenceHelper.getRecorderPersonRole();
1096 
1097         short personRankOrder = 1;
1098         if (CollectionUtils.isNotEmpty(source.getRecorderPerson())) {
1099             for (Person person : source.getRecorderPerson()) {
1100                 Integer personId = person.getIdAsInt();
1101                 vesselPersonFeaturesPersistenceHelper.fillVesselPersonFeatures(
1102                         vesselPersonFeaturesMap,
1103                         personId,
1104                         target,
1105                         recorderPersonRole,
1106                         personRankOrder++);
1107             }
1108         }
1109         if (target.getVesselPersonFeatures() == null) {
1110             target.setVesselPersonFeatures(Sets.<VesselPersonFeatures>newHashSet());
1111         }
1112         target.getVesselPersonFeatures().clear();
1113         target.getVesselPersonFeatures().addAll(vesselPersonFeaturesMap.values());
1114 
1115         // Comment
1116         target.setComments(source.getComment());
1117 
1118         // ----------------------------------------------------------------
1119         // Removed unecessary *UseMeasurement :                         ---
1120         // ----------------------------------------------------------------
1121 
1122         // Gear use Caracteristics
1123         CaracteristicMap gearUseCaracteristics = source.getGearUseFeatures();
1124         if (MapUtils.isNotEmpty(gearUseCaracteristics)) {
1125             for (Caracteristic caracteristic : gearUseCaracteristics.keySet()) {
1126                 GearUseMeasurement gum = measurementPersistenceHelper.setGearUseMeasurement(
1127                         scientificCruise,
1128                         gearUseFeatures,
1129                         caracteristic,
1130                         gearUseCaracteristics.get(caracteristic));
1131                 notChangedGearUseMeasurements.remove(gum);
1132             }
1133         }
1134 
1135         // Removed not changed measurements (in Vessel & Gear Use Measurement lists)
1136         if (vesselUseFeatures.getVesselUseMeasurements() != null &&
1137             notChangedVesselUseMeasurements.size() > 0) {
1138             vesselUseFeatures.getVesselUseMeasurements().removeAll(notChangedVesselUseMeasurements);
1139 
1140         }
1141         if (gearUseFeatures.getGearUseMeasurements() != null &&
1142             notChangedGearUseMeasurements.size() > 0) {
1143             gearUseFeatures.getGearUseMeasurements().removeAll(notChangedGearUseMeasurements);
1144         }
1145 
1146         // ----------------------------------------------------------------
1147         // Fishing Area : Strata, substrata, localite                   ---
1148         // ----------------------------------------------------------------
1149 
1150         // Compute a statistical rectangle, using lat/long (to be used in FishingArea.location)
1151         Integer statisticalLocationId = null;
1152         if (source.getGearShootingStartLatitude() != null && source.getGearShootingStartLongitude() != null) {
1153             statisticalLocationId = locationService.getLocationIdByLatLong(source.getGearShootingStartLatitude(), source.getGearShootingStartLongitude());
1154         }
1155         if (statisticalLocationId == null && source.getGearShootingEndLatitude() != null && source.getGearShootingEndLongitude() != null) {
1156             statisticalLocationId = locationService.getLocationIdByLatLong(source.getGearShootingEndLatitude(), source.getGearShootingEndLongitude());
1157         }
1158 
1159         // Strata :
1160         if (source.getStrata() != null && source.getStrata().getId() != null) {
1161             FishingArea2RegulationLocation fa2rl = getFishingArea2RegulationLocation(fishingArea,
1162                                                                                      source.getStrata().getIdAsInt(),
1163                                                                                      true /*create if need*/);
1164             notChangedRegulationLocation.remove(fa2rl);
1165 
1166             // If no statistical location define yet, then use strata
1167             if (statisticalLocationId == null) {
1168                 statisticalLocationId = source.getStrata().getIdAsInt();
1169             }
1170         }
1171 
1172         // Sub-Strata :
1173         if (source.getSubStrata() != null && source.getSubStrata().getId() != null) {
1174             FishingArea2RegulationLocation fa2rl = getFishingArea2RegulationLocation(fishingArea,
1175                                                                                      source.getSubStrata().getIdAsInt(),
1176                                                                                      true /*create if need*/);
1177             notChangedRegulationLocation.remove(fa2rl);
1178 
1179             // If no statistical location define yet, then use sub-strata
1180             if (statisticalLocationId == null) {
1181                 statisticalLocationId = source.getSubStrata().getIdAsInt();
1182             }
1183         }
1184 
1185         // Localite :
1186         if (source.getLocation() != null && source.getLocation().getId() != null) {
1187             FishingArea2RegulationLocation fa2rl = getFishingArea2RegulationLocation(fishingArea,
1188                                                                                      source.getLocation().getIdAsInt(),
1189                                                                                      true /*create if need*/);
1190             notChangedRegulationLocation.remove(fa2rl);
1191 
1192             // If no statistical location define yet, then use Localité
1193             if (statisticalLocationId == null) {
1194                 statisticalLocationId = source.getLocation().getIdAsInt();
1195             }
1196         }
1197 
1198         // Remove unused regulation locations
1199         fishingArea.getRegulationLocations().removeAll(notChangedRegulationLocation);
1200 
1201         // Fishing Area location (should be a statistical location)
1202         if (statisticalLocationId == null) {
1203             gearUseFeatures.getFishingAreas().remove(fishingArea);
1204             //Nothing to do : a gearUseFeatures.getFishingAreas().clear() has been done before
1205             if (fishingArea.getRegulationLocations() != null) {
1206                 fishingArea.getRegulationLocations().clear();
1207             }
1208         } else if (statisticalLocationId != null) {
1209             fishingArea.setLocation(load(LocationImpl.class, statisticalLocationId));
1210         }
1211     }
1212 
1213     /**
1214      * Test if the latitude is null, and return a default value if yes
1215      *
1216      * @param databaseValue the latitude used in UI (could be null)
1217      * @return null the latitude to store in database (could not be null)
1218      */
1219     protected Float convertUI2DatabaseMandatoryLatitude(Float databaseValue) {
1220         return databaseValue != null ? databaseValue : DEFAULT_EMPTY_LATITUDE;
1221     }
1222 
1223     /**
1224      * Test if the latitude is a fake value. This yes, return null, then return the given value.
1225      *
1226      * @param databaseValue the latitude stored in the database (could be fake date, not null only because of database constraints)
1227      * @return null if the latitude is fake
1228      */
1229     protected Float convertLatitude2UI(Float databaseValue) {
1230         return DEFAULT_EMPTY_LATITUDE.equals(databaseValue) ? null : databaseValue;
1231     }
1232 
1233 //    /**
1234 //     * Test if the latitude is null, and return a default value if yes
1235 //     *
1236 //     * @param databaseValue the latitude used in UI (could be null)
1237 //     * @return null the latitude to store in database (could not be null)
1238 //     */
1239 //    protected Float convertUI2DatabaseMandatoryLongitude(Float databaseValue) {
1240 //        return (databaseValue != null) ? databaseValue : DEFAULT_EMPTY_LONGITUDE;
1241 //    }
1242 
1243     /**
1244      * Test if the longitude is a fake value. This yes, return null, then return the given value.
1245      *
1246      * @param databaseValue the longitude stored in the database (could be fake date, not null only because of database constraints)
1247      * @return null if the longitude is fake
1248      */
1249     protected Float convertLongitude2UI(Float databaseValue) {
1250         return DEFAULT_EMPTY_LONGITUDE.equals(databaseValue) ? null : databaseValue;
1251     }
1252 
1253     protected void setOperationVesselAssociation(Operation target, List<String> vesselCodes) {
1254 
1255         boolean noOperationVesselAssociations =
1256                 target.getOperationVesselAssociations() == null;
1257 
1258         Set<OperationVesselAssociation> toAdd = Sets.newHashSet();
1259         Set<OperationVesselAssociation> notChanged = Sets.newHashSet();
1260 
1261         if (!noOperationVesselAssociations) {
1262             notChanged.addAll(target.getOperationVesselAssociations());
1263         }
1264 
1265         for (String vesselCode : vesselCodes) {
1266 
1267             OperationVesselAssociation ova = null;
1268             OperationVesselAssociationPK ovaPK = new OperationVesselAssociationPK();
1269             ovaPK.setVessel(load(VesselImpl.class, vesselCode));
1270             ovaPK.setOperation((OperationImpl) target);
1271 
1272             // If vessel is equal as cruise vessel : do note store any VesselOperationAssociation entity
1273             if (vesselCode.equals(target.getVessel().getCode())) {
1274                 //removeAllOperationVesselAssociation(target);
1275                 break;
1276             }
1277 
1278             // Retrieve existing association
1279             for (OperationVesselAssociation asso : notChanged) {
1280                 if (asso.getOperationVesselAssociationPk().equals(ovaPK)) {
1281                     ova = asso;
1282                     break;
1283                 }
1284             }
1285 
1286             // Create a new association
1287             if (ova == null) {
1288                 ova = OperationVesselAssociation.Factory.newInstance();
1289                 ova.setOperationVesselAssociationPk(ovaPK);
1290 //                if (target.getOperationVesselAssociations() == null) {
1291 //                    target.setOperationVesselAssociations(Lists.newArrayList(ova));
1292 //                } else {
1293 //                    removeAllOperationVesselAssociation(target);
1294 //                    target.getOperationVesselAssociations().add(ova);
1295 //                }
1296             }
1297 
1298             ova.setIsCatchOnOperationVessel(Boolean.FALSE);
1299             toAdd.add(ova);
1300             notChanged.remove(ova);
1301         }
1302 
1303         if (!noOperationVesselAssociations) {
1304 
1305             // remove all not changed
1306 
1307             for (OperationVesselAssociation operationVesselAssociation : notChanged) {
1308                 operationVesselAssociationDao.remove(operationVesselAssociation);
1309             }
1310             target.getOperationVesselAssociations().removeAll(notChanged);
1311         }
1312 
1313         if (!toAdd.isEmpty()) {
1314 
1315             if (noOperationVesselAssociations) {
1316                 target.setOperationVesselAssociations(Sets.<OperationVesselAssociation>newHashSet());
1317             }
1318             target.getOperationVesselAssociations().addAll(toAdd);
1319         }
1320     }
1321 
1322     /**
1323      * Issue #4995 : use this method to avoid re-create of existing regulation location
1324      *
1325      * @param fishingArea TODO
1326      * @param regulationLocationId TODO
1327      * @param createIfNotExists TODO
1328      * @return a entity FishingArea2RegulationLocation, or {@code null} if not found and createIfNotExists=false
1329      */
1330     protected FishingArea2RegulationLocation getFishingArea2RegulationLocation(FishingArea fishingArea,
1331                                                                                int regulationLocationId,
1332                                                                                boolean createIfNotExists) {
1333         Preconditions.checkNotNull(fishingArea);
1334 
1335         // Create the PK
1336         FishingArea2RegulationLocationPK pk = new FishingArea2RegulationLocationPK();
1337         pk.setFishingArea((FishingAreaImpl) fishingArea);
1338         pk.setLocation(load(LocationImpl.class, regulationLocationId));
1339 
1340         // Retrieve existing regulation location
1341         FishingArea2RegulationLocation fa2rl = null;
1342         if (CollectionUtils.isNotEmpty(fishingArea.getRegulationLocations())) {
1343             for (FishingArea2RegulationLocation existingFa2rl : fishingArea.getRegulationLocations()) {
1344                 FishingArea2RegulationLocationPK existingPk = existingFa2rl.getFishingArea2RegulationLocationPk();
1345                 if (Objects.equals(existingPk, pk)) {
1346                     fa2rl = existingFa2rl;
1347                     break;
1348                 }
1349             }
1350         }
1351 
1352         // If not exists, create if need
1353         if (fa2rl != null || !createIfNotExists) {
1354             return fa2rl;
1355         }
1356 
1357         fa2rl = FishingArea2RegulationLocation.Factory.newInstance();
1358         fa2rl.setFishingArea2RegulationLocationPk(pk);
1359 
1360         if (fishingArea.getRegulationLocations() == null) {
1361             fishingArea.setRegulationLocations(Sets.newHashSet(fa2rl));
1362         } else {
1363             fishingArea.getRegulationLocations().add(fa2rl);
1364         }
1365 
1366         return fa2rl;
1367     }
1368 }