1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.portal.service.csv;
35
36 import java.io.ByteArrayInputStream;
37 import java.io.FileReader;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.InputStreamReader;
41 import java.io.Reader;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Locale;
47
48 import org.apache.commons.collections.CollectionUtils;
49 import org.apache.commons.fileupload.FileItem;
50
51 import au.com.bytecode.opencsv.CSVReader;
52 import fr.paris.lutece.portal.business.file.File;
53 import fr.paris.lutece.portal.business.physicalfile.PhysicalFile;
54 import fr.paris.lutece.portal.business.physicalfile.PhysicalFileHome;
55 import fr.paris.lutece.portal.service.i18n.I18nService;
56 import fr.paris.lutece.portal.service.util.AppLogService;
57 import fr.paris.lutece.portal.service.util.AppPropertiesService;
58 import fr.paris.lutece.util.stream.StreamUtil;
59
60
61
62
63
64 public abstract class CSVReaderService
65 {
66 private static final String MESSAGE_NO_FILE_FOUND = "portal.util.message.noFileFound";
67 private static final String MESSAGE_ERROR_READING_FILE = "portal.util.message.errorReadingFile";
68 private static final String MESSAGE_ERROR_NUMBER_COLUMNS = "portal.xsl.message.errorNumberColumns";
69 private static final String MESSAGE_UNKOWN_ERROR = "portal.xsl.message.errorUnknown";
70 private static final String PROPERTY_DEFAULT_CSV_SEPARATOR = "lutece.csvReader.defaultCSVSeparator";
71 private static final String PROPERTY_DEFAULT_CSV_ESCAPE_CHARACTER = "lutece.csvReader.defaultCSVEscapeCharacter";
72 private static final String CONSTANT_DEFAULT_CSV_SEPARATOR = ";";
73 private static final String CONSTANT_DEFAULT_CSV_ESCAPE_CHARACTER = "\"";
74 private Character _strCSVSeparator;
75 private Character _strCSVEscapeCharacter;
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 protected abstract List<CSVMessageDescriptor> readLineOfCSVFile( String [ ] strLineDataArray, int nLineNumber, Locale locale, String strBaseUrl );
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 protected abstract List<CSVMessageDescriptor> checkLineOfCSVFile( String [ ] strLineDataArray, int nLineNumber, Locale locale );
106
107
108
109
110
111
112
113
114
115
116
117
118 protected abstract List<CSVMessageDescriptor> getEndOfProcessMessages( int nNbLineParses, int nNbLinesWithoutErrors, Locale locale );
119
120
121
122
123
124
125 public static Character getDefaultCSVSeparator( )
126 {
127 return AppPropertiesService.getProperty( PROPERTY_DEFAULT_CSV_SEPARATOR, CONSTANT_DEFAULT_CSV_SEPARATOR ).charAt( 0 );
128 }
129
130
131
132
133
134
135 public static Character getDefaultCSVEscapeCharacter( )
136 {
137 return AppPropertiesService.getProperty( PROPERTY_DEFAULT_CSV_ESCAPE_CHARACTER, CONSTANT_DEFAULT_CSV_ESCAPE_CHARACTER ).charAt( 0 );
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 public List<CSVMessageDescriptor> readCSVFile( FileItem fileItem, int nColumnNumber, boolean bCheckFileBeforeProcessing, boolean bExitOnError,
163 boolean bSkipFirstLine, Locale locale, String strBaseUrl )
164 {
165 if ( fileItem != null )
166 {
167 InputStreamReader inputStreamReader = null;
168
169 try
170 {
171 inputStreamReader = new InputStreamReader( fileItem.getInputStream( ) );
172 }
173 catch( IOException e )
174 {
175 AppLogService.error( e.getMessage( ), e );
176 }
177
178 if ( inputStreamReader != null )
179 {
180 CSVReader csvReader = new CSVReader( inputStreamReader, getCSVSeparator( ), getCSVEscapeCharacter( ) );
181
182 return readCSVFile( inputStreamReader, csvReader, nColumnNumber, bCheckFileBeforeProcessing, bExitOnError, bSkipFirstLine, locale, strBaseUrl );
183 }
184 }
185
186 List<CSVMessageDescriptor> listErrors = new ArrayList<>( );
187 CSVMessageDescriptorescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor errorDescription = new CSVMessageDescriptor( CSVMessageLevel.ERROR, 0,
188 I18nService.getLocalizedString( MESSAGE_NO_FILE_FOUND, locale ) );
189 listErrors.add( errorDescription );
190
191 return listErrors;
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 public List<CSVMessageDescriptor> readCSVFile( String strPath, int nColumnNumber, boolean bCheckFileBeforeProcessing, boolean bExitOnError,
216 boolean bSkipFirstLine, Locale locale, String strBaseUrl )
217 {
218 java.io.File file = new java.io.File( strPath );
219
220 try ( FileReader fileReader = new FileReader( file ) )
221 {
222 CSVReader csvReader = new CSVReader( fileReader, getCSVSeparator( ), getCSVEscapeCharacter( ) );
223
224 return readCSVFile( fileReader, csvReader, nColumnNumber, bCheckFileBeforeProcessing, bExitOnError, bSkipFirstLine, locale, strBaseUrl );
225 }
226 catch( IOException e )
227 {
228 AppLogService.error( e.getMessage( ), e );
229 }
230
231 List<CSVMessageDescriptor> listErrors = new ArrayList<>( );
232 CSVMessageDescriptorescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor errorDescription = new CSVMessageDescriptor( CSVMessageLevel.ERROR, 0,
233 I18nService.getLocalizedString( MESSAGE_NO_FILE_FOUND, locale ) );
234 listErrors.add( errorDescription );
235
236 return listErrors;
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 public List<CSVMessageDescriptor> readCSVFile( File file, int nColumnNumber, boolean bCheckFileBeforeProcessing, boolean bExitOnError,
261 boolean bSkipFirstLine, Locale locale, String strBaseUrl )
262 {
263 return readCSVFile( file.getPhysicalFile( ), nColumnNumber, bCheckFileBeforeProcessing, bExitOnError, bSkipFirstLine, locale, strBaseUrl );
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 public List<CSVMessageDescriptor> readCSVFile( PhysicalFile physicalFile, int nColumnNumber, boolean bCheckFileBeforeProcessing, boolean bExitOnError,
288 boolean bSkipFirstLine, Locale locale, String strBaseUrl )
289 {
290 PhysicalFile importedPhysicalFile = physicalFile;
291
292 if ( ( importedPhysicalFile != null ) && ( importedPhysicalFile.getValue( ) == null ) )
293 {
294 if ( importedPhysicalFile.getValue( ) == null )
295 {
296 importedPhysicalFile = PhysicalFileHome.findByPrimaryKey( importedPhysicalFile.getIdPhysicalFile( ) );
297 }
298
299 if ( ( importedPhysicalFile != null ) && ( importedPhysicalFile.getValue( ) == null ) )
300 {
301 InputStream inputStream = new ByteArrayInputStream( importedPhysicalFile.getValue( ) );
302 InputStreamReader inputStreamReader = new InputStreamReader( inputStream );
303 CSVReader csvReader = new CSVReader( inputStreamReader, getCSVSeparator( ), getCSVEscapeCharacter( ) );
304
305 return readCSVFile( inputStreamReader, csvReader, nColumnNumber, bCheckFileBeforeProcessing, bExitOnError, bSkipFirstLine, locale, strBaseUrl );
306 }
307 }
308
309 List<CSVMessageDescriptor> listErrors = new ArrayList<>( );
310 CSVMessageDescriptorescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor errorDescription = new CSVMessageDescriptor( CSVMessageLevel.ERROR, 0,
311 I18nService.getLocalizedString( MESSAGE_NO_FILE_FOUND, locale ) );
312 listErrors.add( errorDescription );
313
314 return listErrors;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 protected List<CSVMessageDescriptor> readCSVFile( Reader reader, CSVReader csvReader, int nColumnNumber, boolean bCheckFileBeforeProcessing,
341 boolean bExitOnError, boolean bSkipFirstLine, Locale locale, String strBaseUrl )
342 {
343 List<CSVMessageDescriptor> listMessages = new ArrayList<>( );
344 int nLineNumber = 0;
345
346 if ( bSkipFirstLine )
347 {
348 try
349 {
350 nLineNumber++;
351 csvReader.readNext( );
352 }
353 catch( IOException e )
354 {
355 AppLogService.error( e.getMessage( ), e );
356
357 CSVMessageDescriptorCSVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, 1,
358 I18nService.getLocalizedString( MESSAGE_ERROR_READING_FILE, locale ) );
359 listMessages.add( error );
360
361 if ( bExitOnError )
362 {
363 StreamUtil.safeClose( csvReader );
364 StreamUtil.safeClose( reader );
365
366 return listMessages;
367 }
368 }
369 }
370
371 List<String [ ]> listLines = null;
372
373 if ( bCheckFileBeforeProcessing )
374 {
375 listLines = new ArrayList<>( );
376
377 String [ ] strLine = null;
378
379 do
380 {
381 try
382 {
383 nLineNumber++;
384 strLine = csvReader.readNext( );
385 }
386 catch( IOException e )
387 {
388 AppLogService.error( e.getMessage( ), e );
389
390 CSVMessageDescriptorCSVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
391 I18nService.getLocalizedString( MESSAGE_ERROR_READING_FILE, locale ) );
392 listMessages.add( error );
393
394 if ( bExitOnError )
395 {
396 StreamUtil.safeClose( csvReader );
397 StreamUtil.safeClose( reader );
398
399 Collections.sort( listMessages );
400
401 return listMessages;
402 }
403 }
404
405 if ( strLine != null )
406 {
407 listLines.add( strLine );
408 }
409 }
410 while ( strLine != null );
411
412 List<CSVMessageDescriptor> listCheckErrors = checkCSVFileValidity( listLines, nColumnNumber, bSkipFirstLine, locale );
413
414 if ( CollectionUtils.isNotEmpty( listCheckErrors ) && doesListMessageContainError( listCheckErrors ) )
415 {
416 listCheckErrors.addAll( 0, listMessages );
417 StreamUtil.safeClose( csvReader );
418 StreamUtil.safeClose( reader );
419
420 Collections.sort( listMessages );
421
422 return listCheckErrors;
423 }
424
425 nLineNumber = 0;
426 }
427
428 boolean bHasMoreLines = true;
429 int nNbLinesWithoutErrors = 0;
430 String [ ] strLine = null;
431 Iterator<String [ ]> iterator = null;
432
433 if ( listLines != null )
434 {
435 iterator = listLines.iterator( );
436 }
437
438 while ( bHasMoreLines )
439 {
440 nLineNumber++;
441
442 if ( iterator != null )
443 {
444 if ( iterator.hasNext( ) )
445 {
446 strLine = iterator.next( );
447 }
448 else
449 {
450 strLine = null;
451 bHasMoreLines = false;
452 }
453 }
454 else
455 {
456 try
457 {
458 strLine = csvReader.readNext( );
459 }
460 catch( IOException e )
461 {
462 strLine = null;
463 AppLogService.error( e.getMessage( ), e );
464
465 CSVMessageDescriptorCSVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
466 I18nService.getLocalizedString( MESSAGE_ERROR_READING_FILE, locale ) );
467 listMessages.add( error );
468
469 if ( bExitOnError )
470 {
471 bHasMoreLines = false;
472 }
473 }
474 }
475
476 if ( strLine != null )
477 {
478 try
479 {
480 List<CSVMessageDescriptor> listLinesMessages = null;
481
482 if ( !bCheckFileBeforeProcessing )
483 {
484 listLinesMessages = checkCSVLineColumnNumber( strLine, nColumnNumber, nLineNumber, locale );
485
486 if ( !doesListMessageContainError( listLinesMessages ) )
487 {
488 List<CSVMessageDescriptor> listFileCheckMessages = checkLineOfCSVFile( strLine, nLineNumber, locale );
489
490 if ( CollectionUtils.isNotEmpty( listFileCheckMessages ) )
491 {
492 if ( CollectionUtils.isNotEmpty( listLinesMessages ) )
493 {
494 listLinesMessages.addAll( listFileCheckMessages );
495 }
496 else
497 {
498 listLinesMessages = listFileCheckMessages;
499 }
500 }
501 }
502
503 if ( CollectionUtils.isNotEmpty( listLinesMessages ) )
504 {
505 listMessages.addAll( listLinesMessages );
506 }
507 }
508
509
510 if ( !doesListMessageContainError( listLinesMessages ) )
511 {
512 List<CSVMessageDescriptor> listMessagesOfCurrentLine = readLineOfCSVFile( strLine, nLineNumber, locale, strBaseUrl );
513
514 if ( CollectionUtils.isNotEmpty( listMessagesOfCurrentLine ) )
515 {
516 listMessages.addAll( listMessagesOfCurrentLine );
517 }
518
519 if ( doesListMessageContainError( listMessagesOfCurrentLine ) )
520 {
521 if ( bExitOnError )
522 {
523 bHasMoreLines = false;
524 }
525 }
526 else
527 {
528 nNbLinesWithoutErrors++;
529 }
530 }
531 }
532 catch( Exception e )
533 {
534 AppLogService.error( e.getMessage( ), e );
535
536 CSVMessageDescriptorCSVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber,
537 I18nService.getLocalizedString( MESSAGE_UNKOWN_ERROR, locale ) );
538 listMessages.add( error );
539
540 if ( bExitOnError )
541 {
542 bHasMoreLines = false;
543 }
544 }
545 }
546 else
547 {
548 bHasMoreLines = false;
549 }
550 }
551 StreamUtil.safeClose( csvReader );
552 StreamUtil.safeClose( reader );
553
554
555 nLineNumber--;
556
557 if ( bSkipFirstLine )
558 {
559 nLineNumber--;
560 }
561
562 List<CSVMessageDescriptor> listMessagesEndOfProcess = getEndOfProcessMessages( nLineNumber, nNbLinesWithoutErrors, locale );
563
564 if ( CollectionUtils.isNotEmpty( listMessagesEndOfProcess ) )
565 {
566 listMessages.addAll( 0, listMessagesEndOfProcess );
567 }
568
569 Collections.sort( listMessages );
570
571 return listMessages;
572 }
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587 protected List<CSVMessageDescriptor> checkCSVFileValidity( List<String [ ]> listLines, int nColumnNumber, boolean bSkipFirstLine, Locale locale )
588 {
589 List<CSVMessageDescriptor> listErrors = new ArrayList<>( );
590 int nLineNumber = 0;
591
592 if ( bSkipFirstLine )
593 {
594 nLineNumber++;
595 }
596
597 for ( String [ ] strLine : listLines )
598 {
599 nLineNumber++;
600
601 List<CSVMessageDescriptor> listMessages = checkCSVLineColumnNumber( strLine, nColumnNumber, nLineNumber, locale );
602
603 if ( CollectionUtils.isNotEmpty( listMessages ) )
604 {
605 listErrors.addAll( listMessages );
606 }
607
608 if ( !doesListMessageContainError( listMessages ) )
609 {
610 listMessages = checkLineOfCSVFile( strLine, nLineNumber, locale );
611
612 if ( CollectionUtils.isNotEmpty( listMessages ) )
613 {
614 listErrors.addAll( listMessages );
615 }
616 }
617 }
618
619 return listErrors;
620 }
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635 protected List<CSVMessageDescriptor> checkCSVLineColumnNumber( String [ ] strLine, int nColumnNumber, int nLineNumber, Locale locale )
636 {
637 if ( ( strLine == null ) || ( ( nColumnNumber > 0 ) && ( strLine.length != nColumnNumber ) ) )
638 {
639 List<CSVMessageDescriptor> listMessages = new ArrayList<>( );
640 Object [ ] args = {
641 ( strLine == null ) ? 0 : strLine.length, nColumnNumber
642 };
643 String strErrorMessage = I18nService.getLocalizedString( MESSAGE_ERROR_NUMBER_COLUMNS, args, locale );
644 CSVMessageDescriptorCSVMessageDescriptor.html#CSVMessageDescriptor">CSVMessageDescriptor error = new CSVMessageDescriptor( CSVMessageLevel.ERROR, nLineNumber, strErrorMessage );
645 listMessages.add( error );
646
647 return listMessages;
648 }
649
650 return null;
651 }
652
653
654
655
656
657
658 public Character getCSVSeparator( )
659 {
660 if ( this._strCSVSeparator == null )
661 {
662 this._strCSVSeparator = getDefaultCSVSeparator( );
663 }
664
665 return _strCSVSeparator;
666 }
667
668
669
670
671
672
673
674 public void setCSVSeparator( Character strCSVSeparator )
675 {
676 this._strCSVSeparator = strCSVSeparator;
677 }
678
679
680
681
682
683
684 public Character getCSVEscapeCharacter( )
685 {
686 if ( this._strCSVEscapeCharacter == null )
687 {
688 this._strCSVEscapeCharacter = getDefaultCSVEscapeCharacter( );
689 }
690
691 return _strCSVEscapeCharacter;
692 }
693
694
695
696
697
698
699
700 public void setCSVEscapeCharacter( Character strCSVEscapeCharacter )
701 {
702 this._strCSVEscapeCharacter = strCSVEscapeCharacter;
703 }
704
705
706
707
708
709
710
711
712 private boolean doesListMessageContainError( List<CSVMessageDescriptor> listMessageOfCurrentLine )
713 {
714 if ( CollectionUtils.isNotEmpty( listMessageOfCurrentLine ) )
715 {
716 for ( CSVMessageDescriptor message : listMessageOfCurrentLine )
717 {
718 if ( message.getMessageLevel( ) == CSVMessageLevel.ERROR )
719 {
720 return true;
721 }
722 }
723 }
724
725 return false;
726 }
727 }