Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/main/java/org/apache/commons/csv/CSVParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import java.util.stream.StreamSupport;

import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.build.AbstractStreamBuilder;
import org.apache.commons.io.function.Uncheck;

Expand Down Expand Up @@ -389,10 +390,16 @@ public static CSVParser parse(final InputStream inputStream, final Charset chars
* @throws NullPointerException if {@code path} is {@code null}.
* @since 1.5
*/
@SuppressWarnings("resource")
public static CSVParser parse(final Path path, final Charset charset, final CSVFormat format) throws IOException {
Objects.requireNonNull(path, "path");
return parse(Files.newInputStream(path), charset, format);
final InputStream inputStream = Files.newInputStream(path);
try {
return parse(inputStream, charset, format);
} catch (final IOException | RuntimeException e) {
// This method allocated the stream and the caller never gets a parser to close, so close it here.
IOUtils.closeQuietlySuppress(inputStream, e);
throw e;
}
}

/**
Expand Down Expand Up @@ -461,10 +468,16 @@ public static CSVParser parse(final String string, final CSVFormat format) throw
* @throws CSVException Thrown on invalid CSV input data.
* @throws NullPointerException if {@code url} is {@code null}.
*/
@SuppressWarnings("resource")
public static CSVParser parse(final URL url, final Charset charset, final CSVFormat format) throws IOException {
Objects.requireNonNull(url, "url");
return parse(url.openStream(), charset, format);
final InputStream inputStream = url.openStream();
try {
return parse(inputStream, charset, format);
} catch (final IOException | RuntimeException e) {
// This method allocated the stream and the caller never gets a parser to close, so close it here.
IOUtils.closeQuietlySuppress(inputStream, e);
throw e;
}
}

private String headerComment;
Expand Down
48 changes: 48 additions & 0 deletions src/test/java/org/apache/commons/csv/CSVParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
Expand Down Expand Up @@ -1667,6 +1669,18 @@ void testParsePathCharsetNullFormat() throws IOException {
}
}

@Test
void testParsePathClosesInputStreamWhenHeaderIsInvalid() throws IOException {
final Path path = Files.createTempFile(getClass().getName(), ".csv");
try {
Files.write(path, "A,,C\n1,2,3\n".getBytes(UTF_8));
final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader().get();
assertThrows(IllegalArgumentException.class, () -> CSVParser.parse(path, UTF_8, format));
} finally {
Files.delete(path);
}
}

@Test
void testParserUrlNullCharsetFormat() throws IOException {
final URL url = ClassLoader.getSystemClassLoader().getResource("org/apache/commons/csv/CSVFileParser/test.csv");
Expand Down Expand Up @@ -1699,6 +1713,40 @@ void testParseUrlCharsetNullFormat() throws IOException {
}
}

@Test
void testParseUrlClosesInputStreamWhenHeaderIsInvalid() throws IOException {
final AtomicBoolean closed = new AtomicBoolean();
final URLStreamHandler handler = new URLStreamHandler() {

@Override
protected URLConnection openConnection(final URL u) {
return new URLConnection(u) {

@Override
public void connect() {
// noop
}

@Override
public InputStream getInputStream() {
return new FilterInputStream(new ByteArrayInputStream("A,,C\n1,2,3\n".getBytes(UTF_8))) {

@Override
public void close() throws IOException {
closed.set(true);
super.close();
}
};
}
};
}
};
final URL url = new URL("csv", null, -1, "test.csv", handler);
final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader().get();
assertThrows(IllegalArgumentException.class, () -> CSVParser.parse(url, UTF_8, format));
assertTrue(closed.get(), "The stream opened from the URL must be closed when the parser cannot be constructed");
}

@Test
void testParseWithDelimiterStringFromChunkedReader() throws IOException {
final CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setEscape('!').get();
Expand Down
Loading