Skip to content

Commit 77ce996

Browse files
committed
Decode multiline CSV. (#769)
Surfacing the actual CSV syntax error, if any, instead of printing the stack trace.
1 parent 7dbdd75 commit 77ce996

2 files changed

Lines changed: 40 additions & 39 deletions

File tree

metafacture-csv/src/main/java/org/metafacture/csv/CsvDecoder.java

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.metafacture.csv;
1818

1919
import org.metafacture.framework.FluxCommand;
20+
import org.metafacture.framework.MetafactureException;
2021
import org.metafacture.framework.StreamReceiver;
2122
import org.metafacture.framework.annotations.Description;
2223
import org.metafacture.framework.annotations.In;
@@ -31,7 +32,6 @@
3132

3233
import java.io.IOException;
3334
import java.io.StringReader;
34-
import java.util.List;
3535

3636
/**
3737
* Decodes lines of CSV files. First line may be interpreted as header.
@@ -91,50 +91,51 @@ private void initializeCsvParser() {
9191
@Override
9292
public void process(final String string) {
9393
assert !isClosed();
94-
final String[] parts = parseCsv(string);
95-
if (hasHeader) {
96-
if (header.length == 0) {
97-
header = parts;
98-
}
99-
else if (parts.length == header.length) {
100-
getReceiver().startRecord(String.valueOf(++count));
101-
for (int i = 0; i < parts.length; ++i) {
102-
getReceiver().literal(header[i], parts[i]);
94+
try (
95+
StringReader sr = new StringReader(string);
96+
CSVReader reader = new CSVReaderBuilder(sr).withCSVParser(parser).build()
97+
) {
98+
String[] parts;
99+
while ((parts = parseCsv(reader)) != null) {
100+
if (hasHeader) {
101+
if (header.length == 0) {
102+
header = parts;
103+
}
104+
else if (parts.length == header.length) {
105+
getReceiver().startRecord(String.valueOf(++count));
106+
for (int i = 0; i < parts.length; ++i) {
107+
getReceiver().literal(header[i], parts[i]);
108+
}
109+
getReceiver().endRecord();
110+
}
111+
else {
112+
throw new IllegalArgumentException(
113+
String.format(
114+
"wrong number of columns (expected %s, was %s) in input line: %s",
115+
header.length, parts.length, string));
116+
}
117+
}
118+
else {
119+
getReceiver().startRecord(String.valueOf(++count));
120+
for (int i = 0; i < parts.length; ++i) {
121+
getReceiver().literal(String.valueOf(i), parts[i]);
122+
}
123+
getReceiver().endRecord();
103124
}
104-
getReceiver().endRecord();
105-
}
106-
else {
107-
throw new IllegalArgumentException(
108-
String.format(
109-
"wrong number of columns (expected %s, was %s) in input line: %s",
110-
header.length, parts.length, string));
111125
}
112126
}
113-
else {
114-
getReceiver().startRecord(String.valueOf(++count));
115-
for (int i = 0; i < parts.length; ++i) {
116-
getReceiver().literal(String.valueOf(i), parts[i]);
117-
}
118-
getReceiver().endRecord();
127+
catch (final IOException e) {
128+
throw new MetafactureException(e);
119129
}
120130
}
121131

122-
private String[] parseCsv(final String csv) {
123-
String[] parts = new String[0];
132+
private String[] parseCsv(final CSVReader reader) {
124133
try {
125-
final CSVReader reader = new CSVReaderBuilder(new StringReader(csv))
126-
.withCSVParser(parser)
127-
.build();
128-
final List<String[]> lines = reader.readAll();
129-
if (lines.size() > 0) {
130-
parts = lines.get(0);
131-
}
132-
reader.close();
134+
return reader.readNext();
133135
}
134136
catch (final IOException | CsvException e) {
135-
e.printStackTrace();
137+
throw new MetafactureException(e);
136138
}
137-
return parts;
138139
}
139140

140141
/**

metafacture-csv/src/test/java/org/metafacture/csv/CsvDecoderTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package org.metafacture.csv;
1818

19+
import org.metafacture.framework.MetafactureException;
1920
import org.metafacture.framework.StreamReceiver;
2021

22+
import com.opencsv.exceptions.CsvMalformedLineException;
2123
import org.hamcrest.CoreMatchers;
2224
import org.hamcrest.MatcherAssert;
2325
import org.junit.After;
@@ -83,8 +85,8 @@ public void testQuoted() {
8385

8486
@Test
8587
public void testInvalidSyntax() {
86-
assertException(IllegalArgumentException.class, null,
87-
"wrong number of columns (expected 3, was 0)", "a,\"b1,b2,b3,c"); // missing closing "
88+
assertException(MetafactureException.class, CsvMalformedLineException.class,
89+
"Unterminated quoted field at end of CSV line", "a,\"b1,b2,b3,c"); // missing closing "
8890
}
8991

9092
@Test
@@ -115,13 +117,11 @@ public void testMultilineMultipleRows() {
115117
ordered.verify(receiver).literal("h2", "b1,b2,\nb3");
116118
ordered.verify(receiver).literal("h3", "c");
117119
ordered.verify(receiver).endRecord();
118-
/* skipped:
119120
ordered.verify(receiver).startRecord("2");
120121
ordered.verify(receiver).literal("h1", "a2");
121122
ordered.verify(receiver).literal("h2", "b4");
122123
ordered.verify(receiver).literal("h3", "c2");
123124
ordered.verify(receiver).endRecord();
124-
*/
125125
ordered.verifyNoMoreInteractions();
126126
Mockito.verifyNoMoreInteractions(receiver);
127127
}

0 commit comments

Comments
 (0)