Add CSVPrinter.printRecord[s](Stream).

This commit is contained in:
Gary Gregory 2022-08-06 16:40:05 -04:00
parent 0af5d428d6
commit 8f7e3a6682
3 changed files with 2250 additions and 2122 deletions

View File

@ -50,6 +50,7 @@
<action issue="CSV-291" type="add" dev="ggregory" due-to="Gary Gregory">Make CSVRecord#values() public.</action> <action issue="CSV-291" type="add" dev="ggregory" due-to="Gary Gregory">Make CSVRecord#values() public.</action>
<action issue="CSV-264" type="add" dev="ggregory" due-to="Sagar Tiwari, Seth Falco, Alex Herbert, Gary Gregory">Add DuplicateHeaderMode for flexibility with header strictness. #114.</action> <action issue="CSV-264" type="add" dev="ggregory" due-to="Sagar Tiwari, Seth Falco, Alex Herbert, Gary Gregory">Add DuplicateHeaderMode for flexibility with header strictness. #114.</action>
<action issue="CSV-295" type="add" dev="ggregory" due-to="Gary Gregory">Support for parallelism in CSVPrinter.</action> <action issue="CSV-295" type="add" dev="ggregory" due-to="Gary Gregory">Support for parallelism in CSVPrinter.</action>
<action issue="CSV-295" type="add" dev="ggregory" due-to="Gary Gregory">Add CSVPrinter.printRecord[s](Stream).</action>
<action type="add" dev="ggregory">Add github/codeql-action.</action> <action type="add" dev="ggregory">Add github/codeql-action.</action>
<!-- UPDATE --> <!-- UPDATE -->
<action type="update" dev="kinow" due-to="Dependabot, Gary Gregory">Bump actions/cache from 2.1.6 to 3.0.6 #196, #233, #243.</action> <action type="update" dev="kinow" due-to="Dependabot, Gary Gregory">Bump actions/cache from 2.1.6 to 3.0.6 #196, #233, #243.</action>

View File

@ -29,6 +29,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Stream;
/** /**
* Prints values in a {@link CSVFormat CSV format}. * Prints values in a {@link CSVFormat CSV format}.
@ -69,8 +70,21 @@ import java.util.Objects;
*/ */
public final class CSVPrinter implements Flushable, Closeable { public final class CSVPrinter implements Flushable, Closeable {
/**
* Throws the given throwable.
*
* @param <T> The throwable cast type.
* @param throwable The throwable to rethrow.
* @return nothing because we throw.
* @throws T Always thrown.
*/
@SuppressWarnings("unchecked")
private static <T extends Throwable> RuntimeException rethrow(final Throwable throwable) throws T {
throw (T) throwable;
}
/** The place that the values get written. */ /** The place that the values get written. */
private final Appendable appendable; private final Appendable appendable;
private final CSVFormat format; private final CSVFormat format;
/** True if we just began a new record. */ /** True if we just began a new record. */
@ -242,7 +256,7 @@ public final class CSVPrinter implements Flushable, Closeable {
} }
/** /**
* Prints the given values a single record of delimiter separated values followed by the record separator. * Prints the given values as a single record of delimiter separated values followed by the record separator.
* *
* <p> * <p>
* The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
@ -262,7 +276,7 @@ public final class CSVPrinter implements Flushable, Closeable {
} }
/** /**
* Prints the given values a single record of delimiter separated values followed by the record separator. * Prints the given values as a single record of delimiter separated values followed by the record separator.
* *
* <p> * <p>
* The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
@ -279,11 +293,46 @@ public final class CSVPrinter implements Flushable, Closeable {
} }
/** /**
* Prints all the objects in the given collection handling nested collections/arrays as records. * Prints the given values as a single record of delimiter separated values followed by the record separator.
* *
* <p> * <p>
* If the given collection only contains simple objects, this method will print a single record like * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
* {@link #printRecord(Iterable)}. If the given collections contains nested collections/arrays those nested elements * separator to the output after printing the record, so there is no need to call {@link #println()}.
* </p>
*
* @param values
* values to output.
* @throws IOException
* If an I/O error occurs
* @since 1.10.0
*/
public synchronized void printRecord(final Stream<?> values) throws IOException {
values.forEachOrdered(t -> {
try {
print(t);
} catch (IOException e) {
throw rethrow(e);
}
});
println();
}
private void printRecordObject(final Object value) throws IOException {
if (value instanceof Object[]) {
this.printRecord((Object[]) value);
} else if (value instanceof Iterable) {
this.printRecord((Iterable<?>) value);
} else {
this.printRecord(value);
}
}
/**
* Prints all the objects in the given {@link Iterable} handling nested collections/arrays as records.
*
* <p>
* If the given Iterable only contains simple objects, this method will print a single record like
* {@link #printRecord(Iterable)}. If the given Iterable contains nested collections/arrays those nested elements
* will each be printed as records using {@link #printRecord(Object...)}. * will each be printed as records using {@link #printRecord(Object...)}.
* </p> * </p>
* *
@ -293,7 +342,7 @@ public final class CSVPrinter implements Flushable, Closeable {
* *
* <pre> * <pre>
* <code> * <code>
* List&lt;String[]&gt; data = ... * List&lt;String[]&gt; data = new ArrayList&lt;&gt;();
* data.add(new String[]{ "A", "B", "C" }); * data.add(new String[]{ "A", "B", "C" });
* data.add(new String[]{ "1", "2", "3" }); * data.add(new String[]{ "1", "2", "3" });
* data.add(new String[]{ "A1", "B2", "C3" }); * data.add(new String[]{ "A1", "B2", "C3" });
@ -319,13 +368,7 @@ public final class CSVPrinter implements Flushable, Closeable {
*/ */
public void printRecords(final Iterable<?> values) throws IOException { public void printRecords(final Iterable<?> values) throws IOException {
for (final Object value : values) { for (final Object value : values) {
if (value instanceof Object[]) { printRecordObject(value);
this.printRecord((Object[]) value);
} else if (value instanceof Iterable) {
this.printRecord((Iterable<?>) value);
} else {
this.printRecord(value);
}
} }
} }
@ -409,4 +452,56 @@ public final class CSVPrinter implements Flushable, Closeable {
} }
printRecords(resultSet); printRecords(resultSet);
} }
/**
* Prints all the objects in the given {@link Stream} handling nested collections/arrays as records.
*
* <p>
* If the given Stream only contains simple objects, this method will print a single record like
* {@link #printRecord(Iterable)}. If the given Stream contains nested collections/arrays those nested elements
* will each be printed as records using {@link #printRecord(Object...)}.
* </p>
*
* <p>
* Given the following data structure:
* </p>
*
* <pre>
* <code>
* List&lt;String[]&gt; data = new ArrayList&lt;&gt;();
* data.add(new String[]{ "A", "B", "C" });
* data.add(new String[]{ "1", "2", "3" });
* data.add(new String[]{ "A1", "B2", "C3" });
* Stream&lt;String[]&gt; stream = data.stream();
* </code>
* </pre>
*
* <p>
* Calling this method will print:
* </p>
*
* <pre>
* <code>
* A, B, C
* 1, 2, 3
* A1, B2, C3
* </code>
* </pre>
*
* @param values
* the values to print.
* @throws IOException
* If an I/O error occurs
* @since 1.10.0
*/
@SuppressWarnings("unused") // rethrow() throws IOException
public void printRecords(final Stream<?> values) throws IOException {
values.forEachOrdered(t -> {
try {
printRecordObject(t);
} catch (IOException e) {
throw rethrow(e);
}
});
}
} }

View File

@ -56,6 +56,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.Vector; import java.util.Vector;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.NullOutputStream;
@ -588,6 +589,15 @@ public class CSVPrinterTest {
} }
} }
@Test
public void testExcelPrintAllStreamOfArrays() throws IOException {
final StringWriter sw = new StringWriter();
try (final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
printer.printRecords(Stream.of(new String[][] { { "r1c1", "r1c2" }, { "r2c1", "r2c2" } }));
assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
}
}
@Test @Test
public void testExcelPrinter1() throws IOException { public void testExcelPrinter1() throws IOException {
final StringWriter sw = new StringWriter(); final StringWriter sw = new StringWriter();
@ -1456,6 +1466,28 @@ public class CSVPrinterTest {
assertEquals(content, sw.toString()); assertEquals(content, sw.toString());
} }
@Test
public void testPrintRecordStream() throws IOException {
final String code = "a1,b1\n" // 1)
+ "a2,b2\n" // 2)
+ "a3,b3\n" // 3)
+ "a4,b4\n"// 4)
;
final String[][] res = {{"a1", "b1"}, {"a2", "b2"}, {"a3", "b3"}, {"a4", "b4"}};
final CSVFormat format = CSVFormat.DEFAULT;
final StringWriter sw = new StringWriter();
try (final CSVPrinter printer = format.print(sw); final CSVParser parser = CSVParser.parse(code, format)) {
for (final CSVRecord record : parser) {
printer.printRecord(record.stream());
}
}
try (final CSVParser parser = CSVParser.parse(sw.toString(), format)) {
final List<CSVRecord> records = parser.getRecords();
assertFalse(records.isEmpty());
Utils.compare("Fail", res, records);
}
}
@Test @Test
public void testPrintRecordsWithCSVRecord() throws IOException { public void testPrintRecordsWithCSVRecord() throws IOException {
final String[] values = {"A", "B", "C"}; final String[] values = {"A", "B", "C"};