001package io.konik.csv.mapper;
002
003import io.konik.csv.converter.RowToInvoiceConverter;
004import io.konik.csv.model.Row;
005import io.konik.zugferd.Invoice;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008import org.supercsv.cellprocessor.ift.CellProcessor;
009import org.supercsv.io.dozer.CsvDozerBeanReader;
010
011import java.io.File;
012import java.io.IOException;
013import java.util.LinkedList;
014import java.util.List;
015import java.util.concurrent.atomic.AtomicInteger;
016
017/**
018 * Reader class used for reading CSV files and returning a {@link List} of {@see Invoice}s.
019 *
020 * This is high-level component that should be used to import invoices from CSV files.
021 */
022public class CsvInvoicesReader {
023
024        private static final Logger log = LoggerFactory.getLogger(CsvInvoicesReader.class);
025
026        private final ColumnsConfigurer columnsConfigurer;
027
028        public CsvInvoicesReader() {
029                this(new CsvMapperHeaderColumnsConfigurer());
030        }
031
032        public CsvInvoicesReader(ColumnsConfigurer columnsConfigurer) {
033                this.columnsConfigurer = columnsConfigurer;
034        }
035
036        /**
037         * Loads invoices from given CSV file.
038         * @param csvFile
039         * @return
040         */
041        public Result read(File csvFile) {
042                CsvMapperBuilder mapperBuilder = CsvMapperBuilder.withHeadersFromCsvFile(csvFile, columnsConfigurer);
043                CellProcessor[] processors = mapperBuilder.getCellProcessors();
044                CsvDozerBeanReader reader = mapperBuilder.getBeanReader(csvFile, Row.class);
045
046                List<ConvertedRow> convertedRows = new LinkedList<ConvertedRow>();
047                List<RowError> rowErrors = new LinkedList<RowError>();
048                Row currentRow = null;
049                final AtomicInteger rowNumber = new AtomicInteger(1);
050
051                try {
052                        do {
053                                try {
054                                        currentRow = reader.read(Row.class, processors);
055                                        if (currentRow != null) {
056                                                Invoice invoice = RowToInvoiceConverter.convert(currentRow);
057                                                convertedRows.add(new ConvertedRow(rowNumber.getAndIncrement(), currentRow, invoice));
058                                        }
059                                } catch (Exception e) {
060                                        log.warn("Exception caught during reading a row");
061                                        rowErrors.add(new RowError(rowNumber.getAndIncrement(), currentRow, e.getMessage(), e));
062                                }
063                        } while (currentRow != null);
064
065                        return new Result(convertedRows, rowErrors);
066
067                } catch (Exception e) {
068                        throw new RuntimeException(e);
069
070                } finally {
071                        if (reader != null) {
072                                try {
073                                        reader.close();
074                                } catch (IOException e) {
075                                        log.error(e.getMessage(), e);
076                                }
077                        }
078                }
079        }
080
081        static public class ConvertedRow {
082                private final int rowNumber;
083                private final Row row;
084                private final Invoice invoice;
085
086                public ConvertedRow(int rowNumber, Row row, Invoice invoice) {
087                        this.rowNumber = rowNumber;
088                        this.row = row;
089                        this.invoice = invoice;
090                }
091
092                public int getRowNumber() {
093                        return rowNumber;
094                }
095
096                public Row getRow() {
097                        return row;
098                }
099
100                public Invoice getInvoice() {
101                        return invoice;
102                }
103        }
104
105        static public class RowError {
106                private final int rowNumber;
107                private final Row row;
108                private final String errorMessage;
109                private final Exception exception;
110
111                public RowError(int rowNumber, Row row, String errorMessage, Exception exception) {
112                        this.rowNumber = rowNumber;
113                        this.row = row;
114                        this.errorMessage = errorMessage;
115                        this.exception = exception;
116                }
117
118                public int getRowNumber() {
119                        return rowNumber;
120                }
121
122                public Row getRow() {
123                        return row;
124                }
125
126                public String getErrorMessage() {
127                        return errorMessage;
128                }
129
130                public Exception getException() {
131                        return exception;
132                }
133        }
134
135        static public class Result {
136                private final List<ConvertedRow> convertedRows;
137                private final List<RowError> rowErrors;
138
139                public Result(List<ConvertedRow> convertedRows, List<RowError> rowErrors) {
140                        this.convertedRows = convertedRows;
141                        this.rowErrors = rowErrors;
142                }
143
144                public List<ConvertedRow> getConvertedRows() {
145                        return convertedRows;
146                }
147
148                public List<RowError> getRowErrors() {
149                        return rowErrors;
150                }
151        }
152}