55*/
66package edu .ie3 .datamodel .io .source .csv ;
77
8- import edu .ie3 .datamodel .exceptions .ConnectorException ;
98import edu .ie3 .datamodel .exceptions .SourceException ;
109import edu .ie3 .datamodel .io .connectors .CsvFileConnector ;
1110import edu .ie3 .datamodel .io .naming .FileNamingStrategy ;
1211import edu .ie3 .datamodel .io .source .DataSource ;
1312import edu .ie3 .datamodel .models .UniqueEntity ;
13+ import edu .ie3 .datamodel .utils .Try ;
14+ import edu .ie3 .datamodel .utils .Try .Failure ;
15+ import edu .ie3 .datamodel .utils .Try .Success ;
1416import edu .ie3 .datamodel .utils .validation .ValidationUtils ;
1517import edu .ie3 .util .StringUtils ;
1618import java .io .BufferedReader ;
@@ -46,6 +48,8 @@ public class CsvDataSource implements DataSource {
4648 protected final String csvSep ;
4749 protected final CsvFileConnector connector ;
4850
51+ private final FileNamingStrategy fileNamingStrategy ;
52+
4953 /**
5054 * @deprecated ensures downward compatibility with old csv data format. Can be removed when
5155 * support for old csv format is removed. *
@@ -56,41 +60,38 @@ public class CsvDataSource implements DataSource {
5660 public CsvDataSource (String csvSep , Path folderPath , FileNamingStrategy fileNamingStrategy ) {
5761 this .csvSep = csvSep ;
5862 this .connector = new CsvFileConnector (folderPath , fileNamingStrategy );
63+ this .fileNamingStrategy = fileNamingStrategy ;
5964 }
6065
6166 @ Override
6267 public Optional <Set <String >> getSourceFields (Class <? extends UniqueEntity > entityClass )
6368 throws SourceException {
64- return getSourceFields (() -> connector . initReader ( entityClass ));
69+ return getSourceFields (getFilePath ( entityClass ). getOrThrow ( ));
6570 }
6671
67- public Optional <Set <String >> getSourceFields (ReaderSupplier readerSupplier )
68- throws SourceException {
69- try (BufferedReader reader = readerSupplier .get ()) {
72+ /**
73+ * @param filePath path of file starting from base folder, including file name but not file
74+ * extension
75+ * @return The source field names as a set, if file exists
76+ * @throws SourceException on error while reading the source file
77+ */
78+ public Optional <Set <String >> getSourceFields (Path filePath ) throws SourceException {
79+ try (BufferedReader reader = connector .initReader (filePath )) {
7080 return Optional .of (
7181 Arrays .stream (parseCsvRow (reader .readLine (), csvSep )).collect (Collectors .toSet ()));
7282 } catch (FileNotFoundException e ) {
7383 // A file not existing can be acceptable in many cases, and is handled elsewhere.
7484 log .debug ("The source for the given entity couldn't be found! Cause: {}" , e .getMessage ());
7585 return Optional .empty ();
76- } catch (ConnectorException | IOException e ) {
86+ } catch (IOException e ) {
7787 throw new SourceException ("Error while trying to read source" , e );
7888 }
7989 }
8090
81- public interface ReaderSupplier {
82- BufferedReader get () throws FileNotFoundException , ConnectorException ;
83- }
84-
8591 @ Override
86- public Stream <Map <String , String >> getSourceData (Class <? extends UniqueEntity > entityClass ) {
87- return buildStreamWithFieldsToAttributesMap (entityClass , connector );
88- }
89-
90- // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
91-
92- public BufferedReader createReader (Path filePath ) throws FileNotFoundException {
93- return connector .initReader (filePath );
92+ public Stream <Map <String , String >> getSourceData (Class <? extends UniqueEntity > entityClass )
93+ throws SourceException {
94+ return buildStreamWithFieldsToAttributesMap (entityClass , true ).getOrThrow ();
9495 }
9596
9697 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -279,25 +280,24 @@ protected <T extends UniqueEntity> Predicate<Optional<T>> isPresentCollectIfNot(
279280 };
280281 }
281282
283+ public FileNamingStrategy getNamingStrategy () {
284+ return fileNamingStrategy ;
285+ }
286+
282287 /**
283- * Tries to open a file reader from the connector based on the provided entity class and hands it
284- * over for further processing.
288+ * Tries to open a file reader based on the provided entity class and hands it over for further
289+ * processing.
285290 *
286291 * @param entityClass the entity class that should be build and that is used to get the
287292 * corresponding reader
288- * @param connector the connector that should be used to get the reader from
289293 * @return a parallel stream of maps, where each map represents one row of the csv file with the
290294 * mapping (fieldName to fieldValue)
291295 */
292- protected Stream <Map <String , String >> buildStreamWithFieldsToAttributesMap (
293- Class <? extends UniqueEntity > entityClass , CsvFileConnector connector ) {
294- try {
295- return buildStreamWithFieldsToAttributesMap (entityClass , connector .initReader (entityClass ));
296- } catch (FileNotFoundException | ConnectorException e ) {
297- log .warn (
298- "Unable to find file for entity '{}': {}" , entityClass .getSimpleName (), e .getMessage ());
299- }
300- return Stream .empty ();
296+ protected Try <Stream <Map <String , String >>, SourceException > buildStreamWithFieldsToAttributesMap (
297+ Class <? extends UniqueEntity > entityClass , boolean allowFileNotExisting ) {
298+ return getFilePath (entityClass )
299+ .flatMap (
300+ path -> buildStreamWithFieldsToAttributesMap (entityClass , path , allowFileNotExisting ));
301301 }
302302
303303 /**
@@ -306,13 +306,13 @@ protected Stream<Map<String, String>> buildStreamWithFieldsToAttributesMap(
306306 * the returning stream is a parallel stream, the order of the elements cannot be guaranteed.
307307 *
308308 * @param entityClass the entity class that should be build
309- * @param bufferedReader the reader to use
310- * @return a parallel stream of maps, where each map represents one row of the csv file with the
311- * mapping (fieldName to fieldValue)
309+ * @param filePath the path of the file to read
310+ * @return a try containing either a parallel stream of maps, where each map represents one row of
311+ * the csv file with the mapping (fieldName to fieldValue) or an exception
312312 */
313- protected Stream <Map <String , String >> buildStreamWithFieldsToAttributesMap (
314- Class <? extends UniqueEntity > entityClass , BufferedReader bufferedReader ) {
315- try (BufferedReader reader = bufferedReader ) {
313+ protected Try < Stream <Map <String , String >>, SourceException > buildStreamWithFieldsToAttributesMap (
314+ Class <? extends UniqueEntity > entityClass , Path filePath , boolean allowFileNotExisting ) {
315+ try (BufferedReader reader = connector . initReader ( filePath ) ) {
316316 final String [] headline = parseCsvRow (reader .readLine (), csvSep );
317317
318318 // by default try-with-resources closes the reader directly when we leave this method (which
@@ -322,14 +322,31 @@ protected Stream<Map<String, String>> buildStreamWithFieldsToAttributesMap(
322322 Collection <Map <String , String >> allRows = csvRowFieldValueMapping (reader , headline );
323323
324324 return distinctRowsWithLog (
325- allRows , fieldToValues -> fieldToValues .get ("uuid" ), entityClass .getSimpleName (), "UUID" )
326- .parallelStream ();
325+ allRows ,
326+ fieldToValues -> fieldToValues .get ("uuid" ),
327+ entityClass .getSimpleName (),
328+ "UUID" )
329+ .map (Set ::parallelStream );
330+ } catch (FileNotFoundException e ) {
331+ if (allowFileNotExisting ) {
332+ log .warn ("Unable to find file '{}': {}" , filePath , e .getMessage ());
333+ return Success .of (Stream .empty ());
334+ } else {
335+ return Failure .of (new SourceException ("Unable to find file '" + filePath + "'." , e ));
336+ }
327337 } catch (IOException e ) {
328- log .warn (
329- "Cannot read file to build entity '{}': {}" , entityClass .getSimpleName (), e .getMessage ());
338+ return Failure .of (
339+ new SourceException (
340+ "Cannot read file to build entity '" + entityClass .getSimpleName () + "'" , e ));
330341 }
342+ }
331343
332- return Stream .empty ();
344+ private Try <Path , SourceException > getFilePath (Class <? extends UniqueEntity > entityClass ) {
345+ return Try .from (
346+ fileNamingStrategy .getFilePath (entityClass ),
347+ () ->
348+ new SourceException (
349+ "Cannot find a naming strategy for class '" + entityClass .getSimpleName () + "'." ));
333350 }
334351
335352 protected List <Map <String , String >> csvRowFieldValueMapping (
@@ -358,10 +375,10 @@ protected List<Map<String, String>> csvRowFieldValueMapping(
358375 * debug String)
359376 * @param keyDescriptor Colloquial descriptor of the key, that is meant to be unique (for debug
360377 * String)
361- * @return either a set containing only unique rows or an empty set if at least two rows with the
362- * same UUID but different field values exist
378+ * @return a try of either a set containing only unique rows or an exception if at least two rows
379+ * with the same UUID but different field values exist
363380 */
364- protected Set <Map <String , String >> distinctRowsWithLog (
381+ protected Try < Set <Map <String , String >>, SourceException > distinctRowsWithLog (
365382 Collection <Map <String , String >> allRows ,
366383 final Function <Map <String , String >, String > keyExtractor ,
367384 String entityDescriptor ,
@@ -385,18 +402,19 @@ protected Set<Map<String, String>> distinctRowsWithLog(
385402 allRowsSet .removeAll (distinctIdSet );
386403 String affectedCoordinateIds =
387404 allRowsSet .stream ().map (keyExtractor ).collect (Collectors .joining (",\n " ));
388- log .error (
389- """
390- '{}' entities with duplicated {} key, but different field values found! Please review the corresponding input file!
391- Affected primary keys:
392- {}""" ,
393- entityDescriptor ,
394- keyDescriptor ,
395- affectedCoordinateIds );
396- // if this happens, we return an empty set to prevent further processing
397- return new HashSet <>();
405+
406+ // if this happens, we return a failure
407+ return Failure .of (
408+ new SourceException (
409+ "'"
410+ + entityDescriptor
411+ + "' entities with duplicated "
412+ + keyDescriptor
413+ + " key, but different field "
414+ + "values found! Please review the corresponding input file! Affected primary keys: "
415+ + affectedCoordinateIds ));
398416 }
399417
400- return allRowsSet ;
418+ return Success . of ( allRowsSet ) ;
401419 }
402420}
0 commit comments