1 package fr.ifremer.quadrige3.batch.shape.service;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 import com.vividsolutions.jts.geom.Envelope;
25 import com.vividsolutions.jts.geom.Geometry;
26 import com.vividsolutions.jts.geom.MultiPoint;
27 import fr.ifremer.quadrige3.batch.BatchesServiceLocator;
28 import fr.ifremer.quadrige3.batch.shape.config.ShapeConfigurationOption;
29 import fr.ifremer.quadrige3.core.config.QuadrigeConfiguration;
30 import fr.ifremer.quadrige3.core.dao.technical.*;
31 import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
32 import fr.ifremer.quadrige3.core.service.referential.monitoringLocation.MonitoringLocationService;
33 import fr.ifremer.quadrige3.core.vo.referential.monitoringLocation.ErrorCodes;
34 import fr.ifremer.quadrige3.core.vo.referential.monitoringLocation.ImportShapeContextVO;
35 import fr.ifremer.quadrige3.core.vo.referential.monitoringLocation.ImportShapeResultVO;
36 import fr.ifremer.quadrige3.core.vo.referential.monitoringLocation.MonitoringLocationVO;
37 import org.apache.commons.collections4.CollectionUtils;
38 import org.apache.commons.io.FileUtils;
39 import org.apache.commons.io.FilenameUtils;
40 import org.apache.commons.lang3.StringUtils;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.geotools.data.FileDataStore;
44 import org.geotools.data.FileDataStoreFinder;
45 import org.geotools.data.shapefile.ShapefileDataStore;
46 import org.geotools.data.simple.SimpleFeatureCollection;
47 import org.geotools.data.simple.SimpleFeatureIterator;
48 import org.geotools.data.simple.SimpleFeatureSource;
49 import org.geotools.geometry.jts.ReferencedEnvelope;
50 import org.geotools.referencing.CRS;
51 import org.geotools.referencing.crs.DefaultGeographicCRS;
52 import org.nuiton.config.ApplicationConfig;
53 import org.opengis.feature.simple.SimpleFeature;
54 import org.opengis.referencing.FactoryException;
55 import org.opengis.referencing.crs.CoordinateReferenceSystem;
56 import org.springframework.beans.factory.InitializingBean;
57 import org.springframework.mail.MailSender;
58 import org.springframework.mail.SimpleMailMessage;
59
60 import java.io.FileNotFoundException;
61 import java.io.IOException;
62 import java.nio.charset.Charset;
63 import java.nio.file.Files;
64 import java.nio.file.Path;
65 import java.text.DecimalFormat;
66 import java.text.ParseException;
67 import java.util.*;
68 import java.util.regex.Matcher;
69 import java.util.regex.Pattern;
70 import java.util.stream.Collectors;
71
72 import static org.nuiton.i18n.I18n.t;
73
74 public class ImportShapeServiceImpl implements ImportShapeService, InitializingBean {
75
76 private static final Log log = LogFactory.getLog(ImportShapeServiceImpl.class);
77 private static final CoordinateReferenceSystem CRS_WGS84 = DefaultGeographicCRS.WGS84;
78 private static final ReferencedEnvelope ENVELOPE_WGS84 = new ReferencedEnvelope(-180, 180, -90, 90, DefaultGeographicCRS.WGS84);
79 private static final String ZIP_EXTENSION = "zip";
80 private static final String SHP_EXTENSION = "shp";
81 private static final String PRJ_EXTENSION = "prj";
82 private static final String FILE_DATE_FORMAT = "yyyyMMdd";
83 private static final String OUTPUT_DATE_FORMAT = "dd/MM/yyyy";
84
85 private String idAttributeName;
86 private String labelAttributeName;
87 private String nameAttributeName;
88 private String bathyAttributeName;
89 private String commentAttributeName;
90 private String posSystemIdAttributeName;
91 private String utFormatAttributeName;
92 private String daylightSavingTimeAttributeName;
93
94 private QuadrigeConfiguration config;
95
96 private MonitoringLocationService monitoringLocationService;
97 private MailSender mailSender;
98
99
100
101
102 @Override
103 public void afterPropertiesSet() {
104
105 this.config = QuadrigeConfiguration.getInstance();
106
107
108 ApplicationConfig applicationConfig = config.getApplicationConfig();
109 idAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_ID.getKey());
110 labelAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_LABEL.getKey());
111 nameAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_NAME.getKey());
112 bathyAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_BATHY.getKey());
113 commentAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_COMMENT.getKey());
114 posSystemIdAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_POS_SYSTEM_ID.getKey());
115 utFormatAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_UT_FORMAT.getKey());
116 daylightSavingTimeAttributeName = applicationConfig.getOption(ShapeConfigurationOption.SHAPE_ATTRIBUTE_DAYLIGHT_SAVING_TIME.getKey());
117
118 }
119
120 @Override
121 public boolean importFromFile(Path inputFile, Path processingFile) {
122 checkInit();
123
124 log.debug(t("quadrige3.batch.shape.process.start.debug", processingFile));
125
126 ImportShapeContextVO context = new ImportShapeContextVO();
127 ImportShapeResultVO result = new ImportShapeResultVO();
128 context.setResult(result);
129 context.setInputFile(inputFile);
130 context.setProcessingFile(processingFile);
131 List<MonitoringLocationVO> importedMonitoringLocations = null;
132 boolean imported = false;
133
134 try {
135
136
137 validateStructure(context);
138
139
140 List<SimpleFeatureCollection> featureCollections = null;
141 if (!result.hasError()) {
142 featureCollections = loadFeatureCollection(context);
143 }
144
145
146 if (!result.hasError()) {
147 validateAttributes(context, featureCollections);
148 }
149
150
151 if (!result.hasError()) {
152 validateData(context, featureCollections);
153 }
154
155
156 if (!result.hasError()) {
157 importedMonitoringLocations = execute(context, featureCollections);
158 }
159
160
161 fr.ifremer.quadrige3.core.dao.technical.Files.deleteQuietly(context.getTempDirs());
162
163
164
165 if (result.hasError()) {
166 String error = getResultErrorAsString(context);
167 log.info(t("quadrige3.batch.shape.process.error", error));
168 sendEmail(context, t("quadrige3.batch.shape.report.email.subject.error"), error);
169 imported = false;
170 } else {
171 String report = getResultReportAsString(context, importedMonitoringLocations);
172 log.info(t("quadrige3.batch.shape.process.report", report));
173 sendEmail(context, t("quadrige3.batch.shape.report.email.subject"), report);
174 imported = true;
175 }
176
177 log.debug(t("quadrige3.batch.shape.process.end.debug", inputFile));
178
179 } catch (Exception e) {
180
181 context.getResult().addError(ErrorCodes.FILE_NAME.name(), e.getLocalizedMessage());
182 String error = getResultErrorAsString(context);
183 log.info(t("quadrige3.batch.shape.process.error", error));
184 sendEmail(context, t("quadrige3.batch.shape.report.email.subject.error"), error);
185 }
186 return imported;
187 }
188
189 private void cleanTempDirectories(ImportShapeContextVO context) throws IOException {
190
191 for (Path tempDir : context.getTempDirs()) {
192
193
194 fr.ifremer.quadrige3.core.dao.technical.Files.cleanDirectory(tempDir, t("quadrige3.batch.shape.error.directory.clean", tempDir));
195
196
197 Files.delete(tempDir);
198 }
199 }
200
201 private void sendEmail(ImportShapeContextVO context, String subject, String content) {
202
203 try {
204
205 String recipients = context.getEmail();
206 SimpleMailMessage message = new SimpleMailMessage();
207 message.setFrom(config.getMailSmtpSender());
208 message.setTo(Beans.split(recipients, ";").toArray(new String[0]));
209 message.setSubject(subject);
210 message.setText(content);
211 message.setSentDate(new Date());
212
213
214 getMailSender().send(message);
215
216 } catch (Exception e) {
217 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.email"), e);
218 }
219
220 }
221
222 private List<SimpleFeatureCollection> loadFeatureCollection(ImportShapeContextVO context) {
223 try {
224 List<SimpleFeatureCollection> featureCollections = new ArrayList<>();
225
226 for (Path file : context.getFiles()) {
227 FileDataStore inputStore = FileDataStoreFinder.getDataStore(file.toFile());
228 if (inputStore instanceof ShapefileDataStore) {
229
230 ((ShapefileDataStore) inputStore).setCharset(Charset.forName("cp1252"));
231 }
232 SimpleFeatureSource inputSource = inputStore.getFeatureSource();
233 SimpleFeatureCollection featureCollection = inputSource.getFeatures();
234
235
236 logFeatureCollection(featureCollection);
237
238 featureCollections.add(featureCollection);
239 }
240
241 return featureCollections;
242
243 } catch (Exception e) {
244 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.feature.read"), e);
245 }
246 }
247
248 private void validateStructure(ImportShapeContextVO context) {
249 try {
250
251 Assert.notNull(context);
252 Assert.notNull(context.getProcessingFile());
253
254 if (!Files.exists(context.getProcessingFile())) {
255 throw new FileNotFoundException(context.getProcessingFile().toString());
256 }
257 if (log.isDebugEnabled())
258 log.debug(String.format("Validating shape file structure ... {%s}", context.getProcessingFile()));
259
260 String fileName = context.getProcessingFile().getFileName().toString();
261 String extension = FilenameUtils.getExtension(fileName);
262
263 if (extension.equalsIgnoreCase(ZIP_EXTENSION)) {
264
265
266 Matcher matcher = Pattern.compile("(.*)#SHP_(\\d+)_(.*)\\.\\w+").matcher(fileName);
267 if (!matcher.matches()) {
268 context.getResult().addError(ErrorCodes.FILE_NAME.name(), t("quadrige3.batch.shape.error.format.file", fileName));
269 return;
270 }
271
272
273 String email = matcher.group(1);
274 String strDate = matcher.group(2);
275 String depositNumber = matcher.group(3);
276
277
278 if (!Pattern.compile("^[A-Za-z0-9+_.-]+@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$").matcher(email).matches()) {
279 context.getResult().addError(ErrorCodes.FILE_NAME.name(), t("quadrige3.batch.shape.error.format.email", email));
280 return;
281 }
282
283
284 Date date = Dates.safeParseDate(strDate, FILE_DATE_FORMAT);
285 if (date == null) {
286 context.getResult().addError(ErrorCodes.FILE_NAME.name(), t("quadrige3.batch.shape.error.format.date", strDate, FILE_DATE_FORMAT));
287 return;
288 }
289
290
291 context.setEmail(email);
292 context.setDepositDate(date);
293 context.setDepositNumber(depositNumber);
294
295
296 uncompressInputFile(context);
297
298 } else if (extension.equalsIgnoreCase(SHP_EXTENSION)) {
299
300
301 context.addFile(context.getProcessingFile());
302
303 } else {
304
305
306 context.getResult().addError(ErrorCodes.FILE_NAME.name(), t("quadrige3.batch.shape.error.unknown.file", fileName));
307 }
308
309
310 checkPrjFiles(context);
311
312 } catch (Exception e) {
313 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.structure"), e);
314 }
315 }
316
317 private void uncompressInputFile(ImportShapeContextVO context) {
318
319 try {
320 Path tempPath = config.getTempDirectory().toPath().resolve("shape_" + System.currentTimeMillis());
321 context.addTempDir(tempPath);
322 Files.createDirectories(tempPath);
323
324
325 ZipUtils.uncompressFileToPath(context.getProcessingFile(), tempPath, false);
326
327
328 Files.walk(tempPath)
329 .filter(path -> FilenameUtils.isExtension(path.getFileName().toString().toLowerCase(), SHP_EXTENSION))
330 .forEach(context::addFile);
331
332 } catch (Exception e) {
333 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.uncompress"), e);
334 }
335 }
336
337 private void validateAttributes(ImportShapeContextVO context, List<SimpleFeatureCollection> featureCollections) {
338
339 try {
340
341 if (log.isDebugEnabled())
342 log.debug(String.format("Validating shape file attributes ... {%s}", context.getProcessingFile()));
343 ImportShapeResultVO result = context.getResult();
344
345 for (SimpleFeatureCollection featureCollection : featureCollections) {
346
347 try (SimpleFeatureIterator ite = featureCollection.features()) {
348 while (ite.hasNext()) {
349
350 SimpleFeature feature = ite.next();
351
352
353 checkCoordinate(feature, result);
354
355
356 checkAttributes(feature, result);
357 }
358 }
359 }
360 } catch (Exception e) {
361 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.attribute"), e);
362 }
363 }
364
365 private void validateData(ImportShapeContextVO context, List<SimpleFeatureCollection> featureCollections) {
366
367 try {
368
369
370 if (log.isDebugEnabled())
371 log.debug(String.format("Validating shape file data ... {%s}", context.getProcessingFile()));
372
373 ImportShapeResultVO result = context.getResult();
374 List<MonitoringLocationVO> existingMonitoringLocations = getMonitoringLocationService().getLightMonitoringLocations();
375 List<Integer> existingPosIds = getMonitoringLocationService().getPositioningSystemIds();
376
377 for (SimpleFeatureCollection featureCollection : featureCollections) {
378 try (SimpleFeatureIterator ite = featureCollection.features()) {
379 while (ite.hasNext()) {
380 SimpleFeature feature = ite.next();
381
382 Integer id = getIntegerAttribute(feature, idAttributeName);
383
384 if (id == null) {
385
386
387 validateDataForInsert(feature, existingMonitoringLocations, result);
388
389 } else {
390
391
392 validateDataForUpdate(feature, id, existingMonitoringLocations, result);
393
394 }
395
396
397 Integer posId = getIntegerAttribute(feature, posSystemIdAttributeName);
398 if (!existingPosIds.contains(posId)) {
399 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.notExists", posSystemIdAttributeName, posId));
400 }
401 }
402 }
403 }
404 } catch (Exception e) {
405 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.attribute.data"), e);
406 }
407 }
408
409 private void validateDataForUpdate(SimpleFeature feature, Integer id, List<MonitoringLocationVO> existingMonitoringLocations, ImportShapeResultVO result) {
410
411
412 if (existingMonitoringLocations.stream()
413 .noneMatch(monitoringLocationVO -> monitoringLocationVO.getId().equals(id))) {
414 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.notExists", idAttributeName, id));
415 }
416
417
418 String label = getStringAttribute(feature, labelAttributeName);
419 if (StringUtils.isNotBlank(label)
420 && existingMonitoringLocations.stream().filter(monitoringLocationVO -> !monitoringLocationVO.getId().equals(id))
421 .map(MonitoringLocationVO::getLabel).filter(Objects::nonNull)
422 .anyMatch(label::equalsIgnoreCase)) {
423 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.alreadyExists", labelAttributeName, label));
424 }
425
426
427 String name = getStringAttribute(feature, nameAttributeName);
428 assert name != null;
429 if (existingMonitoringLocations.stream().filter(monitoringLocationVO -> !monitoringLocationVO.getId().equals(id))
430 .map(MonitoringLocationVO::getName)
431 .anyMatch(name::equalsIgnoreCase)) {
432 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.alreadyExists", nameAttributeName, name));
433 }
434
435 }
436
437 private void validateDataForInsert(SimpleFeature feature, List<MonitoringLocationVO> existingMonitoringLocations, ImportShapeResultVO result) {
438
439
440 String label = getStringAttribute(feature, labelAttributeName);
441 if (StringUtils.isNotBlank(label)
442 && existingMonitoringLocations.stream()
443 .map(MonitoringLocationVO::getLabel).filter(Objects::nonNull)
444 .anyMatch(label::equalsIgnoreCase)) {
445 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.alreadyExists", labelAttributeName, label));
446 }
447
448
449 String name = getStringAttribute(feature, nameAttributeName);
450 assert name != null;
451 if (existingMonitoringLocations.stream()
452 .map(MonitoringLocationVO::getName)
453 .anyMatch(name::equalsIgnoreCase)) {
454 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.alreadyExists", nameAttributeName, name));
455 }
456
457 }
458
459 private List<MonitoringLocationVO> execute(ImportShapeContextVO context, List<SimpleFeatureCollection> featureCollections) {
460
461 try {
462
463
464 List<MonitoringLocationVO> list = new ArrayList<>();
465
466 for (SimpleFeatureCollection featureCollection : featureCollections) {
467 try (SimpleFeatureIterator ite = featureCollection.features()) {
468 while (ite.hasNext()) {
469 SimpleFeature feature = ite.next();
470
471 MonitoringLocationVO vo = new MonitoringLocationVO();
472 vo.setFeatureId(feature.getID());
473 vo.setId(getIntegerAttribute(feature, idAttributeName, 0));
474 vo.setLabel(getStringAttribute(feature, labelAttributeName));
475 vo.setName(getStringAttribute(feature, nameAttributeName));
476 vo.setBathy(getDoubleAttribute(feature, bathyAttributeName));
477 vo.setComment(getStringAttribute(feature, commentAttributeName));
478 vo.setPosId(getIntegerAttribute(feature, posSystemIdAttributeName));
479 vo.setUtFormat(getIntegerAttribute(feature, utFormatAttributeName));
480 vo.setDaylightSavingTime(getIntegerAttribute(feature, daylightSavingTimeAttributeName) == 1);
481 vo.setGeometry(Geometries.getWKTString((Geometry) feature.getDefaultGeometry()));
482
483 list.add(vo);
484 }
485 }
486 }
487
488 if (CollectionUtils.isNotEmpty(list)) {
489
490
491 List<Integer> entityToUpdateIds = list.stream().map(MonitoringLocationVO::getId).filter(Objects::nonNull).collect(Collectors.toList());
492
493
494 getMonitoringLocationService().importLocationsWithShapes(list);
495
496
497 list.forEach(monitoringLocation ->
498 {
499 String i18nMessage = entityToUpdateIds.contains(monitoringLocation.getId()) ? "quadrige3.batch.shape.report.update" : "quadrige3.batch.shape.report.add";
500 context.getResult().addMessage(
501 monitoringLocation.getFeatureId(),
502 t(i18nMessage,
503 idAttributeName, monitoringLocation.getId(),
504 labelAttributeName, monitoringLocation.getLabel()));
505 });
506
507 } else {
508
509 context.getResult().addError(ErrorCodes.NO_DATA.name(), t("quadrige3.batch.shape.error.noData"));
510 }
511
512 return list;
513
514 } catch (Exception e) {
515 throw new QuadrigeTechnicalException(t("quadrige3.batch.shape.error.import"), e);
516 }
517 }
518
519
520
521
522
523
524 private MonitoringLocationService getMonitoringLocationService() {
525 if (monitoringLocationService == null) {
526 monitoringLocationService = BatchesServiceLocator.getMonitoringLocationService();
527 }
528 return monitoringLocationService;
529 }
530
531 private MailSender getMailSender() {
532 if (mailSender == null) {
533 mailSender = BatchesServiceLocator.getMailSender();
534 }
535 return mailSender;
536 }
537
538 private void logFeatureCollection(SimpleFeatureCollection featureCollection) {
539 if (log.isDebugEnabled()) {
540 try (SimpleFeatureIterator ite = featureCollection.features()) {
541 while (ite.hasNext()) {
542
543 SimpleFeature feature = ite.next();
544 StringJoiner stringJoiner = new StringJoiner(" | ", feature.getID() + " : ", "");
545 feature.getType().getAttributeDescriptors().forEach(
546 attributeDescriptor -> stringJoiner.add(attributeDescriptor.getLocalName() + " = " + feature.getAttribute(attributeDescriptor.getLocalName())));
547 log.debug(stringJoiner.toString());
548 }
549 }
550 }
551 }
552
553 private void checkPrjFiles(ImportShapeContextVO context) {
554
555 ImportShapeResultVO result = context.getResult();
556
557 for (Path file : context.getFiles()) {
558
559
560 Path prjFile = file.resolveSibling(FilenameUtils.getBaseName(file.getFileName().toString()) + "." + PRJ_EXTENSION);
561 if (!Files.exists(prjFile)) {
562 result.addError(ErrorCodes.PROJECTION.name(), t("quadrige3.batch.shape.error.projection.missing"));
563 return;
564 }
565
566
567 try {
568 String projection = FileUtils.readFileToString(prjFile.toFile(), "UTF8");
569 CoordinateReferenceSystem crs = CRS.parseWKT(projection);
570
571 if (!CRS.equalsIgnoreMetadata(CRS_WGS84, crs)) {
572 result.addError(ErrorCodes.PROJECTION.name(), t("quadrige3.batch.shape.error.projection.notWGS84", crs.getName()));
573 }
574
575 } catch (FactoryException e) {
576 if (log.isDebugEnabled()) log.debug(e);
577 result.addError(ErrorCodes.PROJECTION.name(), t("quadrige3.batch.shape.error.projection.badFormat"));
578 } catch (IOException e) {
579 if (log.isDebugEnabled()) log.debug(e);
580 result.addError(ErrorCodes.PROJECTION.name(), t("quadrige3.batch.shape.error.projection.read"));
581 }
582 }
583 }
584
585 private void checkCoordinate(SimpleFeature feature, ImportShapeResultVO result) {
586
587 Geometry geometry = (Geometry) feature.getDefaultGeometry();
588 Envelope envelope = geometry.getEnvelopeInternal();
589
590 if (!ENVELOPE_WGS84.contains(envelope)) {
591 result.addError(feature.getID(), t("quadrige3.batch.shape.error.coordinate.oversized", envelope.toString(), ENVELOPE_WGS84.toString()));
592 }
593
594 if (geometry instanceof MultiPoint) {
595 int nbPoints = geometry.getNumPoints();
596 if (nbPoints > 1) {
597 result.addError(feature.getID(), t("quadrige3.batch.shape.error.coordinate.moreThanOnePoint", nbPoints, 1));
598 }
599 }
600 }
601
602 private void checkAttributes(final SimpleFeature feature, final ImportShapeResultVO result) {
603
604 boolean isValid;
605
606
607 checkIntegerAttribute(feature, idAttributeName, false, result);
608
609
610 checkStringAttribute(feature, labelAttributeName, false, 50, result);
611 String label = getStringAttribute(feature, labelAttributeName);
612 if (StringUtils.isNotBlank(label) && !label.contains("P") && !label.contains("L") && !label.contains("S")) {
613 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.format", labelAttributeName, label));
614 }
615
616
617 checkStringAttribute(feature, nameAttributeName, true, 100, result);
618
619
620 checkDecimalAttribute(feature, bathyAttributeName, false, 2, result);
621
622
623 checkStringAttribute(feature, commentAttributeName, false, 2000, result);
624
625
626 checkIntegerAttribute(feature, posSystemIdAttributeName, true, result);
627
628
629 isValid = checkIntegerAttribute(feature, utFormatAttributeName, true, result);
630 if (isValid) {
631 int utFormat = getIntegerAttribute(feature, utFormatAttributeName);
632 if (utFormat < -12 || utFormat > 12) {
633 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.minMax", utFormatAttributeName, -12, 12));
634 }
635 }
636
637
638 isValid = checkIntegerAttribute(feature, daylightSavingTimeAttributeName, true, result);
639 if (isValid) {
640 int daylightSavingTime = getIntegerAttribute(feature, daylightSavingTimeAttributeName);
641 if (daylightSavingTime < 0 || daylightSavingTime > 1) {
642 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.intBoolean", daylightSavingTimeAttributeName));
643 }
644 }
645
646 }
647
648 private void checkInit() {
649 if (config == null) {
650 try {
651 afterPropertiesSet();
652 } catch (Exception e) {
653 throw new QuadrigeTechnicalException(e);
654 }
655 }
656 }
657
658 private boolean checkStringAttribute(final SimpleFeature feature,
659 final String attributeName,
660 final boolean mandatory,
661 final int maxLength,
662 final ImportShapeResultVO result) {
663
664 Object value = feature.getAttribute(attributeName);
665 String strValue = value != null ? value.toString().trim() : null;
666
667
668 if (StringUtils.isBlank(strValue)) {
669 if (mandatory) {
670 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.missingMandatory", attributeName));
671 }
672 return !mandatory;
673 }
674
675
676 if (strValue.length() > maxLength) {
677 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.maxLength", attributeName, maxLength));
678 return false;
679 }
680
681 return true;
682 }
683
684 private boolean checkDecimalAttribute(final SimpleFeature feature,
685 final String attributeName,
686 final boolean mandatory,
687 final int maxDecimal,
688 final ImportShapeResultVO result) {
689
690 Object value = feature.getAttribute(attributeName);
691 String strValue = value != null ? value.toString().trim() : null;
692 if (StringUtils.isBlank(strValue)) {
693 if (mandatory) {
694 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.missingMandatory", attributeName));
695 }
696 return !mandatory;
697 }
698
699
700 try {
701 Number number = DecimalFormat.getInstance().parse(strValue);
702
703
704 double tmpValue = number.doubleValue() * Math.pow(10, maxDecimal);
705 if (Math.ceil(tmpValue) != tmpValue) {
706 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.maxDecimal", attributeName, maxDecimal));
707 return false;
708 }
709 } catch (ParseException e) {
710 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.decimal", attributeName, strValue));
711 return false;
712 }
713
714 return true;
715 }
716
717 private boolean checkIntegerAttribute(final SimpleFeature feature,
718 final String attributeName,
719 final boolean mandatory,
720 final ImportShapeResultVO result) {
721
722 Object value = feature.getAttribute(attributeName);
723
724 if (value instanceof CharSequence) {
725 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.type", attributeName,
726 String.format("%s as %s", value.toString(), value.getClass().getName())));
727 return false;
728 }
729
730 if (value == null) {
731 if (mandatory) {
732 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.missingMandatory", attributeName));
733 }
734 return !mandatory;
735 }
736
737
738 Number number = (Number) value;
739 if (number.doubleValue() - number.intValue() > 0) {
740 result.addError(feature.getID(), t("quadrige3.batch.shape.error.attribute.integer", attributeName, number.toString()));
741 }
742
743 return true;
744 }
745
746
747
748
749
750
751
752
753 private Integer getIntegerAttribute(final SimpleFeature feature, final String attributeName) {
754 return getIntegerAttribute(feature, attributeName, null);
755 }
756
757
758
759
760
761
762
763
764
765 private Integer getIntegerAttribute(final SimpleFeature feature, final String attributeName, final Integer valueIsNull) {
766 Object value = feature.getAttribute(attributeName);
767 Integer integer = value != null ? ((Number) value).intValue() : null;
768
769 if (integer != null && integer.equals(valueIsNull)) integer = null;
770 return integer;
771 }
772
773 private Double getDoubleAttribute(final SimpleFeature feature, final String attributeName) {
774 Object value = feature.getAttribute(attributeName);
775
776 if (value instanceof String) {
777 if (StringUtils.isBlank((CharSequence) value)) return null;
778 try {
779 return DecimalFormat.getInstance().parse((String) value).doubleValue();
780 } catch (ParseException e) {
781 log.error(e.getMessage());
782 return null;
783 }
784 } else if (value instanceof Number) {
785 return ((Number) value).doubleValue();
786 }
787
788 return null;
789 }
790
791 private String getStringAttribute(final SimpleFeature feature, final String attributeName) {
792 Object value = feature.getAttribute(attributeName);
793 return value != null ? (String) value : null;
794 }
795
796 private String getResultErrorAsString(ImportShapeContextVO context) {
797
798 StringBuilder sb = new StringBuilder();
799
800 sb.append(t("quadrige3.batch.shape.report.file", context.getInputFile())).append('\n');
801 sb.append(t("quadrige3.batch.shape.report.deposit", Dates.formatDate(context.getDepositDate(), OUTPUT_DATE_FORMAT), context.getEmail())).append('\n');
802
803 for (String featureId : context.getResult().getErrors().keySet()) {
804
805 sb.append(t("quadrige3.batch.shape.report.location", featureId)).append('\n');
806 for (String error : context.getResult().getErrors().get(featureId)) {
807 sb.append(" - ").append(error).append('\n');
808 }
809 }
810
811 return sb.toString();
812 }
813
814 private String getResultReportAsString(ImportShapeContextVO context, List<MonitoringLocationVO> list) {
815
816 StringBuilder sb = new StringBuilder();
817
818 sb.append(t("quadrige3.batch.shape.report.file", context.getInputFile())).append('\n');
819 sb.append(t("quadrige3.batch.shape.report.deposit", Dates.formatDate(context.getDepositDate(), OUTPUT_DATE_FORMAT), context.getEmail())).append('\n');
820 sb.append(t("quadrige3.batch.shape.report.nbImport", CollectionUtils.size(list))).append("\n\n");
821
822 for (String featureId : context.getResult().getMessages().keySet()) {
823
824 sb.append(t("quadrige3.batch.shape.report.location", featureId)).append('\n');
825 for (String message : context.getResult().getMessages().get(featureId)) {
826 sb.append(" - ").append(message).append('\n');
827 }
828 }
829
830 return sb.toString();
831 }
832
833 }