View Javadoc
1   package net.sumaris.core.dao.data;
2   
3   /*-
4    * #%L
5    * SUMARiS:: Core
6    * %%
7    * Copyright (C) 2018 SUMARiS Consortium
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 lombok.AllArgsConstructor;
27  import lombok.Data;
28  import net.sumaris.core.config.SumarisConfiguration;
29  import net.sumaris.core.dao.referential.ReferentialDao;
30  import net.sumaris.core.dao.referential.location.LocationDao;
31  import net.sumaris.core.dao.technical.SortDirection;
32  import net.sumaris.core.model.QualityFlagEnum;
33  import net.sumaris.core.model.administration.programStrategy.Program;
34  import net.sumaris.core.model.administration.programStrategy.ProgramEnum;
35  import net.sumaris.core.model.data.Vessel;
36  import net.sumaris.core.model.data.VesselFeatures;
37  import net.sumaris.core.model.data.VesselRegistrationPeriod;
38  import net.sumaris.core.model.referential.QualityFlag;
39  import net.sumaris.core.model.referential.Status;
40  import net.sumaris.core.model.referential.VesselType;
41  import net.sumaris.core.model.referential.location.Location;
42  import net.sumaris.core.util.Beans;
43  import net.sumaris.core.vo.administration.user.DepartmentVO;
44  import net.sumaris.core.vo.data.VesselFeaturesVO;
45  import net.sumaris.core.vo.data.VesselRegistrationVO;
46  import net.sumaris.core.vo.data.VesselVO;
47  import net.sumaris.core.vo.filter.VesselFilterVO;
48  import net.sumaris.core.vo.referential.LocationVO;
49  import net.sumaris.core.vo.referential.ReferentialVO;
50  import org.apache.commons.collections4.CollectionUtils;
51  import org.apache.commons.lang3.StringUtils;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  import org.springframework.beans.factory.annotation.Autowired;
55  import org.springframework.stereotype.Repository;
56  
57  import javax.persistence.TypedQuery;
58  import javax.persistence.criteria.*;
59  import java.sql.Timestamp;
60  import java.util.Collection;
61  import java.util.Date;
62  import java.util.List;
63  import java.util.Objects;
64  import java.util.stream.Collectors;
65  
66  @Repository("vesselDao")
67  public class VesselDaoImpl extends BaseDataDaoImpl implements VesselDao {
68  
69      /**
70       * Logger.
71       */
72      private static final Logger log = LoggerFactory.getLogger(VesselDaoImpl.class);
73  
74      @Autowired
75      private LocationDao locationDao;
76  
77      @Autowired
78      private ReferentialDao referentialDao;
79  
80      @Override
81      public VesselVO get(int id) {
82  
83          CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
84          CriteriaQuery<VesselResult> query = builder.createQuery(VesselResult.class);
85  
86          Root<Vessel> vesselRoot = query.from(Vessel.class);
87          Join<Vessel, VesselFeatures> featuresJoin = vesselRoot.join(Vessel.Fields.VESSEL_FEATURES, JoinType.LEFT);
88          Join<Vessel, VesselRegistrationPeriod> vrpJoin = vesselRoot.join(Vessel.Fields.VESSEL_REGISTRATION_PERIODS, JoinType.LEFT);
89  
90          query.multiselect(vesselRoot, featuresJoin, vrpJoin);
91  
92          // filter by active features and registration
93          query.where(builder.and(
94              builder.equal(vesselRoot.get(Vessel.Fields.ID), id),
95              builder.isNull(featuresJoin.get(VesselFeatures.Fields.END_DATE)),
96              builder.isNull(vrpJoin.get(VesselRegistrationPeriod.Fields.END_DATE))
97          ));
98  
99          TypedQuery<VesselResult> q = getEntityManager().createQuery(query);
100         VesselResult result = q.getSingleResult();
101 
102         // TODO: maybe remove filter if no result
103         return toVesselVO(result);
104     }
105 
106     @Override
107     public List<VesselVO> findByFilter(VesselFilterVO filter, int offset, int size, String sortAttribute, SortDirection sortDirection) {
108         Preconditions.checkArgument(offset >= 0);
109         Preconditions.checkArgument(size > 0);
110 
111         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
112         CriteriaQuery<VesselResult> query = builder.createQuery(VesselResult.class);
113         Root<Vessel> vesselRoot = query.from(Vessel.class);
114         Join<Vessel, VesselFeatures> featuresJoin = vesselRoot.join(Vessel.Fields.VESSEL_FEATURES, JoinType.LEFT);
115         Join<Vessel, VesselRegistrationPeriod> vrpJoin = vesselRoot.join(Vessel.Fields.VESSEL_REGISTRATION_PERIODS, JoinType.LEFT);
116 
117         // Select 3 entities
118         query.multiselect(vesselRoot, featuresJoin, vrpJoin);
119 
120         // Apply sorting
121         if (StringUtils.isNotBlank(sortAttribute)) {
122             // replace some field aliases
123             sortAttribute = sortAttribute.replaceFirst(VesselVO.Fields.FEATURES, Vessel.Fields.VESSEL_FEATURES);
124             sortAttribute = sortAttribute.replaceFirst(VesselVO.Fields.REGISTRATION, Vessel.Fields.VESSEL_REGISTRATION_PERIODS);
125             sortAttribute = sortAttribute.replaceFirst(VesselVO.Fields.STATUS_ID, Vessel.Fields.STATUS + "." + Status.Fields.ID);
126         }
127         addSorting(query, builder, vesselRoot, sortAttribute, sortDirection);
128 
129         // Create query
130         TypedQuery<VesselResult> typedQuery = createVesselQuery(builder, query, vesselRoot, featuresJoin, vrpJoin, filter)
131             .setFirstResult(offset)
132             .setMaxResults(size);
133         return toVesselVOs(typedQuery.getResultList());
134 
135     }
136 
137     @Override
138     public Long countByFilter(VesselFilterVO filter) {
139 
140         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
141         CriteriaQuery<Long> query = builder.createQuery(Long.class);
142         Root<Vessel> vesselRoot = query.from(Vessel.class);
143         Join<Vessel, VesselFeatures> featuresJoin = vesselRoot.join(Vessel.Fields.VESSEL_FEATURES, JoinType.LEFT);
144         Join<Vessel, VesselRegistrationPeriod> vrpJoin = vesselRoot.join(Vessel.Fields.VESSEL_REGISTRATION_PERIODS, JoinType.LEFT);
145 
146         query.select(builder.count(vesselRoot));
147 
148         return createVesselQuery(builder, query, vesselRoot, featuresJoin, vrpJoin, filter).getSingleResult();
149     }
150 
151     @Override
152     public List<VesselFeaturesVO> getFeaturesByVesselId(int vesselId, int offset, int size, String sortAttribute, SortDirection sortDirection) {
153         Preconditions.checkArgument(offset >= 0);
154         Preconditions.checkArgument(size > 0);
155 
156         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); //getEntityManager().getCriteriaBuilder();
157         CriteriaQuery<VesselFeatures> query = builder.createQuery(VesselFeatures.class);
158         Root<VesselFeatures> root = query.from(VesselFeatures.class);
159         query.select(root);
160 
161         // Apply filter
162         ParameterExpression<Integer> vesselIdParam = builder.parameter(Integer.class);
163         query.where(builder.equal(root.get(VesselFeatures.Fields.VESSEL).get(Vessel.Fields.ID), vesselIdParam));
164 
165         // Apply sorting
166         addSorting(query, builder, root, sortAttribute, sortDirection);
167 
168         TypedQuery<VesselFeatures> q = getEntityManager().createQuery(query)
169             .setParameter(vesselIdParam, vesselId)
170             .setFirstResult(offset)
171             .setMaxResults(size);
172         List<VesselFeatures> result = q.getResultList();
173         return result.stream().map(this::toVesselFeaturesVO).collect(Collectors.toList());
174     }
175 
176     @Override
177     public List<VesselRegistrationVO> getRegistrationsByVesselId(int vesselId, int offset, int size, String sortAttribute, SortDirection sortDirection) {
178         Preconditions.checkArgument(offset >= 0);
179         Preconditions.checkArgument(size > 0);
180 
181         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
182         CriteriaQuery<VesselRegistrationPeriod> query = builder.createQuery(VesselRegistrationPeriod.class);
183         Root<VesselRegistrationPeriod> root = query.from(VesselRegistrationPeriod.class);
184         query.select(root);
185 
186         // Apply filter
187         ParameterExpression<Integer> vesselIdParam = builder.parameter(Integer.class);
188         query.where(builder.equal(root.get(VesselRegistrationPeriod.Fields.VESSEL).get(Vessel.Fields.ID), vesselIdParam));
189 
190         // Apply sorting
191         addSorting(query, builder, root, sortAttribute, sortDirection);
192 
193         TypedQuery<VesselRegistrationPeriod> q = getEntityManager().createQuery(query)
194             .setParameter(vesselIdParam, vesselId)
195             .setFirstResult(offset)
196             .setMaxResults(size);
197         List<VesselRegistrationPeriod> result = q.getResultList();
198         return result.stream().map(this::toVesselRegistrationVO).collect(Collectors.toList());
199     }
200 
201     @Override
202     public VesselVOf="../../../../../net/sumaris/core/vo/data/VesselVO.html#VesselVO">VesselVO save(VesselVO vessel, boolean checkUpdateDate) {
203         Preconditions.checkNotNull(vessel);
204 
205         Vessel vesselEntity = null;
206         if (vessel.getId() != null) {
207             vesselEntity = get(Vessel.class, vessel.getId());
208         }
209         boolean isNew = vesselEntity == null;
210 
211         if (isNew) {
212             vesselEntity = new Vessel();
213         }
214 
215         if (!isNew) {
216 
217             if (checkUpdateDate) {
218                 // Check update date
219                 checkUpdateDateForUpdate(vessel, vesselEntity);
220             }
221 
222             // Lock entityName
223             lockForUpdate(vesselEntity);
224         }
225 
226         // VO -> Entity
227         vesselVOToEntity(vessel, vesselEntity, true);
228 
229         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
230         if (isNew) {
231             // Force creation date
232             vesselEntity.setCreationDate(newUpdateDate);
233             vessel.setCreationDate(newUpdateDate);
234         }
235         // Update update_dt
236         vesselEntity.setUpdateDate(newUpdateDate);
237         vessel.setUpdateDate(newUpdateDate);
238 
239         // Save Vessel features
240         VesselFeaturesVO features = vessel.getFeatures();
241         if (features != null) {
242             if (features.getId() == null) {
243                 // New features
244                 VesselFeaturess.html#VesselFeatures">VesselFeatures featuresEntity = new VesselFeatures();
245                 vesselFeaturesVOToEntity(features, featuresEntity, false);
246                 featuresEntity.setCreationDate(newUpdateDate);
247                 featuresEntity.setUpdateDate(newUpdateDate);
248                 // Affect the vessel
249                 featuresEntity.setVessel(vesselEntity);
250                 // Create new entity
251                 getEntityManager().persist(featuresEntity);
252                 // Get new Id
253                 features.setId(featuresEntity.getId());
254             } else {
255                 // Update features
256                 VesselFeatures featuresEntity = get(VesselFeatures.class, features.getId());
257                 lockForUpdate(featuresEntity);
258                 vesselFeaturesVOToEntity(features, featuresEntity, true);
259                 featuresEntity.setUpdateDate(newUpdateDate);
260                 // Update entity
261                 getEntityManager().merge(featuresEntity);
262             }
263             // update source feature update also
264             features.setUpdateDate(newUpdateDate);
265         }
266 
267         // Save Registration period
268         VesselRegistrationVO registration = vessel.getRegistration();
269         if (registration != null) {
270             if (registration.getId() == null) {
271                 // New period
272                 VesselRegistrationPeriodiod.html#VesselRegistrationPeriod">VesselRegistrationPeriod periodEntity = new VesselRegistrationPeriod();
273                 vesselRegistrationPeriodVOToEntity(registration, periodEntity, false);
274                 // Affect Vessel
275                 periodEntity.setVessel(vesselEntity);
276                 // Create new entity
277                 getEntityManager().persist(periodEntity);
278                 // Get new Id
279                 vessel.getRegistration().setId(periodEntity.getId());
280             } else {
281                 // Update period
282                 VesselRegistrationPeriod registrationEntity = get(VesselRegistrationPeriod.class, registration.getId());
283                 lockForUpdate(registrationEntity);
284                 vesselRegistrationPeriodVOToEntity(registration, registrationEntity, true);
285                 // Update entity
286                 getEntityManager().merge(registrationEntity);
287             }
288         }
289 
290         // Save entity
291         if (isNew) {
292             getEntityManager().persist(vesselEntity);
293             vessel.setId(vesselEntity.getId());
294         } else {
295             getEntityManager().merge(vesselEntity);
296         }
297 
298         getEntityManager().flush();
299         getEntityManager().clear();
300 
301         return vessel;
302     }
303 
304     @Override
305     public void delete(int id) {
306 
307         // Get the entity
308         VesselFeatures entity = get(VesselFeatures.class, id);
309 
310         boolean deleteParentVessel = CollectionUtils.size(entity.getVessel().getVesselFeatures()) == 1;
311 
312         // Vessel features will be deleted by cascade - see Vessel mapping
313         if (deleteParentVessel) {
314 
315             log.debug(String.format("Deleting vessel {id=%s}...", entity.getVessel().getId()));
316             delete(Vessel.class, entity.getVessel().getId());
317         } else {
318 
319             log.debug(String.format("Deleting vessel features {id=%s}...", id));
320             delete(VesselFeatures.class, id);
321         }
322     }
323 
324     /* -- protected methods -- */
325 
326     private <R> TypedQuery<R> createVesselQuery(CriteriaBuilder builder, CriteriaQuery<R> query,
327                                                 Root<Vessel> vesselRoot,
328                                                 Join<Vessel, VesselFeatures> featuresJoin,
329                                                 Join<Vessel, VesselRegistrationPeriod> vrpJoin,
330                                                 VesselFilterVO filter) {
331 
332         if (filter != null) {
333             // Apply vessel Filter
334             ParameterExpression<Date> dateParam = builder.parameter(Date.class);
335             ParameterExpression<Integer> vesselIdParam = builder.parameter(Integer.class);
336             ParameterExpression<Integer> vesselFeaturesIdParam = builder.parameter(Integer.class);
337             ParameterExpression<String> searchNameParam = builder.parameter(String.class);
338             ParameterExpression<String> searchExteriorMarkingParam = builder.parameter(String.class);
339             ParameterExpression<String> searchRegistrationCodeParam = builder.parameter(String.class);
340             ParameterExpression<Boolean> hasStatusIdsParam = builder.parameter(Boolean.class);
341             ParameterExpression<Collection> statusIdsParam = builder.parameter(Collection.class);
342 
343             query.where(builder.and(
344                 // Filter: date
345                 builder.or(
346                     builder.and(
347                         // if no date in filter, will return only active period
348                         builder.isNull(dateParam),
349                         builder.isNull(featuresJoin.get(VesselFeatures.Fields.END_DATE)),
350                         builder.isNull(vrpJoin.get(VesselRegistrationPeriod.Fields.END_DATE))
351                     ),
352                     builder.and(
353                         builder.isNotNull(dateParam),
354                         builder.and(
355                             builder.or(
356                                 builder.isNull(featuresJoin.get(VesselFeatures.Fields.END_DATE)),
357                                 builder.greaterThan(featuresJoin.get(VesselFeatures.Fields.END_DATE), dateParam)
358                             ),
359                             builder.lessThan(featuresJoin.get(VesselFeatures.Fields.START_DATE), dateParam)
360                         ),
361                         builder.and(
362                             builder.or(
363                                 builder.isNull(vrpJoin.get(VesselRegistrationPeriod.Fields.END_DATE)),
364                                 builder.greaterThan(vrpJoin.get(VesselRegistrationPeriod.Fields.END_DATE), dateParam)
365                             ),
366                             builder.lessThan(vrpJoin.get(VesselRegistrationPeriod.Fields.START_DATE), dateParam)
367                         )
368                     )
369                 ),
370 
371                 // Filter: vessel features id
372                 builder.or(
373                     builder.isNull(vesselFeaturesIdParam),
374                     builder.equal(featuresJoin.get(VesselFeatures.Fields.ID), vesselFeaturesIdParam)
375                 ),
376 
377                 // Filter: vessel id
378                 builder.or(
379                     builder.isNull(vesselIdParam),
380                     builder.equal(vesselRoot.get(Vessel.Fields.ID), vesselIdParam))
381                 ),
382 
383                 // Filter: search text (on exterior marking OR id)
384                 builder.or(
385                     builder.isNull(searchNameParam),
386                     builder.like(builder.lower(featuresJoin.get(VesselFeatures.Fields.NAME)), builder.lower(searchNameParam)),
387                     builder.like(builder.lower(featuresJoin.get(VesselFeatures.Fields.EXTERIOR_MARKING)), builder.lower(searchExteriorMarkingParam)),
388                     builder.like(builder.lower(vrpJoin.get(VesselRegistrationPeriod.Fields.REGISTRATION_CODE)), builder.lower(searchRegistrationCodeParam))
389                 ),
390 
391                 // Status
392                 builder.or(
393                     builder.isFalse(hasStatusIdsParam),
394                     builder.in(vesselRoot.get(Vessel.Fields.STATUS).get(Status.Fields.ID)).value(statusIdsParam)
395                 )
396             );
397 
398             String searchText = StringUtils.trimToNull(filter.getSearchText());
399             String searchTextAsPrefix = null;
400             if (StringUtils.isNotBlank(searchText)) {
401                 searchTextAsPrefix = (searchText + "*"); // add trailing escape char
402                 searchTextAsPrefix = searchTextAsPrefix.replaceAll("[*]+", "*"); // group escape chars
403                 searchTextAsPrefix = searchTextAsPrefix.replaceAll("[%]", "\\%"); // protected '%' chars
404                 searchTextAsPrefix = searchTextAsPrefix.replaceAll("[*]", "%"); // replace asterix
405             }
406             String searchTextAnyMatch = StringUtils.isNotBlank(searchTextAsPrefix) ? ("%" + searchTextAsPrefix) : null;
407 
408             List<Integer> statusIds = CollectionUtils.isEmpty(filter.getStatusIds())
409                 ? null
410                 : filter.getStatusIds();
411 
412             return getEntityManager().createQuery(query)
413                 .setParameter(dateParam, filter.getDate())
414                 .setParameter(vesselFeaturesIdParam, filter.getVesselFeaturesId())
415                 .setParameter(vesselIdParam, filter.getVesselId())
416                 .setParameter(searchExteriorMarkingParam, searchTextAsPrefix)
417                 .setParameter(searchRegistrationCodeParam, searchTextAsPrefix)
418                 .setParameter(searchNameParam, searchTextAnyMatch)
419                 .setParameter(hasStatusIdsParam, CollectionUtils.isNotEmpty(statusIds))
420                 .setParameter(statusIdsParam, statusIds);
421 
422         } else {
423 
424             // if no date in filter, will return only active period
425             query.where(
426                 builder.and(
427                     builder.isNull(featuresJoin.get(VesselFeatures.Fields.END_DATE)),
428                     builder.isNull(vrpJoin.get(VesselRegistrationPeriod.Fields.END_DATE))
429                 )
430             );
431 
432             return getEntityManager().createQuery(query);
433 
434         }
435     }
436 
437     private List<VesselVO> toVesselVOs(List<VesselResult> source) {
438         return source.stream()
439             .map(this::toVesselVO)
440             .filter(Objects::nonNull)
441             .collect(Collectors.toList());
442     }
443 
444     private VesselVO toVesselVO(VesselResult source) {
445         if (source == null)
446             return null;
447 
448         VesselVOsselVO.html#VesselVO">VesselVO target = new VesselVO();
449         Beans.copyProperties(source.getVessel(), target);
450 
451         // Status
452         target.setStatusId(source.getVessel().getStatus().getId());
453 
454         // Vessel type
455         ReferentialVO vesselType = referentialDao.toReferentialVO(source.getVessel().getVesselType());
456         target.setVesselType(vesselType);
457 
458         // Recorder department
459         DepartmentVO recorderDepartment = referentialDao.toTypedVO(source.getVessel().getRecorderDepartment(), DepartmentVO.class).orElse(null);
460         target.setRecorderDepartment(recorderDepartment);
461 
462         // Vessel features
463         target.setFeatures(toVesselFeaturesVO(source.getVesselFeatures()));
464 
465         // Vessel registration period
466         target.setRegistration(toVesselRegistrationVO(source.getVesselRegistrationPeriod()));
467 
468         return target;
469     }
470 
471     private VesselFeaturesVO toVesselFeaturesVO(VesselFeatures source) {
472         if (source == null) return null;
473 
474         VesselFeaturesVOuresVO.html#VesselFeaturesVO">VesselFeaturesVO target = new VesselFeaturesVO();
475 
476         Beans.copyProperties(source, target);
477 
478         // Convert from cm to m
479         if (source.getLengthOverAll() != null) {
480             target.setLengthOverAll(source.getLengthOverAll().doubleValue() / 100);
481         }
482         // Convert tonnage (divide by 100)
483         if (source.getGrossTonnageGrt() != null) {
484             target.setGrossTonnageGrt(source.getGrossTonnageGrt().doubleValue() / 100);
485         }
486         if (source.getGrossTonnageGt() != null) {
487             target.setGrossTonnageGt(source.getGrossTonnageGt().doubleValue() / 100);
488         }
489 
490         target.setQualityFlagId(source.getQualityFlag().getId());
491 
492         // base port location
493         LocationVO basePortLocation = locationDao.toLocationVO(source.getBasePortLocation());
494         target.setBasePortLocation(basePortLocation);
495 
496         // Recorder department
497         DepartmentVO recorderDepartment = referentialDao.toTypedVO(source.getRecorderDepartment(), DepartmentVO.class).orElse(null);
498         target.setRecorderDepartment(recorderDepartment);
499 
500         return target;
501     }
502 
503     private VesselRegistrationVO toVesselRegistrationVO(VesselRegistrationPeriod source) {
504         if (source == null)
505             return null;
506 
507         VesselRegistrationVOtionVO.html#VesselRegistrationVO">VesselRegistrationVO target = new VesselRegistrationVO();
508 
509         Beans.copyProperties(source, target);
510 
511         // Registration location
512         LocationVO registrationLocation = locationDao.toLocationVO(source.getRegistrationLocation());
513         target.setRegistrationLocation(registrationLocation);
514 
515         return target;
516     }
517 
518     private void vesselVOToEntity(VesselVO source, Vessel target, boolean copyIfNull) {
519 
520         copyDataProperties(source, target, copyIfNull);
521 
522         // Recorder person
523         copyRecorderPerson(source, target, copyIfNull);
524 
525         // Vessel type
526         if (copyIfNull || source.getVesselType() != null) {
527             if (source.getVesselType() == null) {
528                 target.setVesselType(null);
529             } else {
530                 target.setVesselType(load(VesselType.class, source.getVesselType().getId()));
531             }
532         }
533 
534         // Vessel status
535         if (copyIfNull || source.getStatusId() != null) {
536             if (source.getStatusId() == null) {
537                 target.setStatus(null);
538             } else {
539                 target.setStatus(load(Status.class, source.getStatusId()));
540             }
541         }
542 
543         // Default program
544         if (copyIfNull && target.getProgram() == null) {
545             target.setProgram(load(Program.class, ProgramEnum.SIH.getId()));
546         }
547     }
548 
549     private void vesselFeaturesVOToEntity(VesselFeaturesVO source, VesselFeatures target, boolean copyIfNull) {
550 
551         copyDataProperties(source, target, copyIfNull);
552 
553         // Recorder department and person
554         copyRecorderPerson(source, target, copyIfNull);
555 
556         // Convert from meter to centimeter
557         if (source.getLengthOverAll() != null) {
558             target.setLengthOverAll((int) (source.getLengthOverAll() * 100));
559         }
560         // Convert tonnage (x100)
561         if (source.getGrossTonnageGrt() != null) {
562             target.setGrossTonnageGrt((int) (source.getGrossTonnageGrt() * 100));
563         }
564         if (source.getGrossTonnageGt() != null) {
565             target.setGrossTonnageGt((int) (source.getGrossTonnageGt() * 100));
566         }
567 
568         // Base port location
569         if (copyIfNull || source.getBasePortLocation() != null) {
570             if (source.getBasePortLocation() == null || source.getBasePortLocation().getId() == null) {
571                 target.setBasePortLocation(null);
572             } else {
573                 target.setBasePortLocation(load(Location.class, source.getBasePortLocation().getId()));
574             }
575         }
576 
577         // Quality flag
578         if (copyIfNull || source.getQualityFlagId() != null) {
579             if (source.getQualityFlagId() == null) {
580                 target.setQualityFlag(null);
581             } else {
582                 target.setQualityFlag(load(QualityFlag.class, source.getQualityFlagId()));
583             }
584         }
585         else if (copyIfNull) {
586             // Set default
587             target.setQualityFlag(load(QualityFlag.class, QualityFlagEnum.NOT_QUALIFED.getId()));
588         }
589     }
590 
591     private void vesselRegistrationPeriodVOToEntity(VesselRegistrationVO source, VesselRegistrationPeriod target, boolean copyIfNull) {
592 
593         // Registration start date
594         if (copyIfNull || source.getStartDate() != null) {
595             target.setStartDate(source.getStartDate());
596         }
597 
598         // Registration end date
599         if (copyIfNull || source.getEndDate() != null) {
600             target.setEndDate(source.getEndDate());
601         }
602 
603         // Registration code
604         if (copyIfNull || source.getRegistrationCode() != null) {
605             target.setRegistrationCode(source.getRegistrationCode());
606         }
607 
608         // Registration location
609         if (copyIfNull || source.getRegistrationLocation() != null) {
610             if (source.getRegistrationLocation() == null || source.getRegistrationLocation().getId() == null) {
611                 target.setRegistrationLocation(null);
612             } else {
613                 target.setRegistrationLocation(get(Location.class, source.getRegistrationLocation().getId()));
614             }
615         }
616 
617         // default quality flag
618         if (target.getQualityFlag() == null) {
619             target.setQualityFlag(load(QualityFlag.class, SumarisConfiguration.getInstance().getDefaultQualityFlagId()));
620         }
621 
622         // default rank order
623         if (target.getRankOrder() == null) {
624             target.setRankOrder(1);
625         }
626     }
627 
628     @Data
629     @AllArgsConstructor
630     private static class VesselResult {
631         private Vessel vessel;
632         private VesselFeatures vesselFeatures;
633         private VesselRegistrationPeriod vesselRegistrationPeriod;
634     }
635 }