CSVFormat.with* methods clear the header comments
diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java
index 9a8a81c6..8c71f3af 100644
--- a/src/main/java/org/apache/commons/csv/CSVFormat.java
+++ b/src/main/java/org/apache/commons/csv/CSVFormat.java
@@ -296,7 +296,7 @@ public final class CSVFormat implements Serializable {
*
*
* This is a tab-delimited format with a LF character as the line separator. Values are not quoted and special
- * characters are escaped with '\'.
+ * characters are escaped with {@code '\'}. The default NULL string is {@code "\\N"}.
*
*
*
@@ -308,6 +308,7 @@ public final class CSVFormat implements Serializable {
*
withRecordSeparator('\n')
* withIgnoreEmptyLines(false)
* withEscape('\\')
+ * withNullString("\\N")
*
*
* @see Predefined#MySQL
@@ -315,7 +316,7 @@ public final class CSVFormat implements Serializable {
* http://dev.mysql.com/doc/refman/5.1/en/load-data.html
*/
public static final CSVFormat MYSQL = DEFAULT.withDelimiter(TAB).withEscape(BACKSLASH).withIgnoreEmptyLines(false)
- .withQuote(null).withRecordSeparator(LF);
+ .withQuote(null).withRecordSeparator(LF).withNullString("\\N");
/**
* Returns true if the given character is a line break character.
diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java
index ac4234bb..0e9a2dee 100644
--- a/src/main/java/org/apache/commons/csv/CSVPrinter.java
+++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java
@@ -136,7 +136,9 @@ public final class CSVPrinter implements Flushable, Closeable {
if (!newRecord) {
out.append(format.getDelimiter());
}
- if (format.isQuoteCharacterSet()) {
+ if (object == null) {
+ out.append(value);
+ } else if (format.isQuoteCharacterSet()) {
// the original object is needed so can check for Number
printAndQuote(object, value, offset, len);
} else if (format.isEscapeCharacterSet()) {
diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
index cf49a6d6..2057c40c 100644
--- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
@@ -18,6 +18,7 @@
package org.apache.commons.csv;
import static org.apache.commons.csv.Constants.CR;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -35,6 +36,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Random;
+import org.apache.commons.lang3.ObjectUtils;
import org.junit.Test;
/**
@@ -44,6 +46,8 @@ import org.junit.Test;
*/
public class CSVPrinterTest {
+ private static final int ITERATIONS_FOR_RANDOM_TEST = 50000;
+
private final String recordSeparator = CSVFormat.DEFAULT.getRecordSeparator();
private static String printable(final String s) {
@@ -65,21 +69,14 @@ public class CSVPrinterTest {
final int nLines = r.nextInt(4) + 1;
final int nCol = r.nextInt(3) + 1;
// nLines=1;nCol=2;
- final String[][] lines = new String[nLines][];
- for (int i = 0; i < nLines; i++) {
- final String[] line = new String[nCol];
- lines[i] = line;
- for (int j = 0; j < nCol; j++) {
- line[j] = randStr();
- }
- }
+ final String[][] lines = generateLines(nLines, nCol);
final StringWriter sw = new StringWriter();
final CSVPrinter printer = new CSVPrinter(sw, format);
for (int i = 0; i < nLines; i++) {
// for (int j=0; j parseResult = parser.getRecords();
- Utils.compare("Printer output :" + printable(result), lines, parseResult);
+ String[][] expected = lines.clone();
+ for (int i = 0; i < expected.length; i++) {
+ expected[i] = expectNulls(expected[i], format);
+ }
+ Utils.compare("Printer output :" + printable(result), expected, parseResult);
parser.close();
}
+ private String[][] generateLines(final int nLines, final int nCol) {
+ final String[][] lines = new String[nLines][];
+ for (int i = 0; i < nLines; i++) {
+ final String[] line = new String[nCol];
+ lines[i] = line;
+ for (int j = 0; j < nCol; j++) {
+ line[j] = randStr();
+ }
+ }
+ return lines;
+ }
+
private void doRandom(final CSVFormat format, final int iter) throws Exception {
for (int i = 0; i < iter; i++) {
doOneRandom(format);
@@ -111,37 +124,37 @@ public class CSVPrinterTest {
char ch;
final int what = r.nextInt(20);
switch (what) {
- case 0:
- ch = '\r';
- break;
- case 1:
- ch = '\n';
- break;
- case 2:
- ch = '\t';
- break;
- case 3:
- ch = '\f';
- break;
- case 4:
- ch = ' ';
- break;
- case 5:
- ch = ',';
- break;
- case 6:
- ch = '"';
- break;
- case 7:
- ch = '\'';
- break;
- case 8:
- ch = '\\';
- break;
- default:
- ch = (char) r.nextInt(300);
- break;
- // default: ch = 'a'; break;
+ case 0:
+ ch = '\r';
+ break;
+ case 1:
+ ch = '\n';
+ break;
+ case 2:
+ ch = '\t';
+ break;
+ case 3:
+ ch = '\f';
+ break;
+ case 4:
+ ch = ' ';
+ break;
+ case 5:
+ ch = ',';
+ break;
+ case 6:
+ ch = '"';
+ break;
+ case 7:
+ ch = '\'';
+ break;
+ case 8:
+ ch = '\\';
+ break;
+ default:
+ ch = (char) r.nextInt(300);
+ break;
+ // default: ch = 'a'; break;
}
buf[i] = ch;
}
@@ -189,8 +202,8 @@ public class CSVPrinterTest {
public void testExcelPrintAllIterableOfLists() throws IOException {
final StringWriter sw = new StringWriter();
final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL);
- printer.printRecords(Arrays.asList(new List[] { Arrays.asList("r1c1", "r1c2"),
- Arrays.asList("r2c1", "r2c2") }));
+ printer.printRecords(
+ Arrays.asList(new List[] { Arrays.asList("r1c1", "r1c2"), Arrays.asList("r2c1", "r2c2") }));
assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
printer.close();
}
@@ -301,6 +314,131 @@ public class CSVPrinterTest {
printer.close();
}
+ @Test
+ public void testMySqlNullStringDefault() throws IOException {
+ assertEquals("\\N", CSVFormat.MYSQL.getNullString());
+ }
+
+ @Test
+ public void testMySqlNullOutput() throws IOException {
+ Object[] s = new String[] { "NULL", null };
+ CSVFormat format = CSVFormat.MYSQL.withQuote('"').withNullString("NULL").withQuoteMode(QuoteMode.NON_NUMERIC);
+ StringWriter writer = new StringWriter();
+ CSVPrinter printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ String expected = "\"NULL\"\tNULL\n";
+ assertEquals(expected, writer.toString());
+ String[] record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(new Object[2], record0);
+
+ s = new String[] { "\\N", null };
+ format = CSVFormat.MYSQL.withNullString("\\N");
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\\\\N\t\\N\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "\\N", "A" };
+ format = CSVFormat.MYSQL.withNullString("\\N");
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\\\\N\tA\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "\n", "A" };
+ format = CSVFormat.MYSQL.withNullString("\\N");
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\\n\tA\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "", null };
+ format = CSVFormat.MYSQL.withNullString("NULL");
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\tNULL\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "", null };
+ format = CSVFormat.MYSQL;
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\t\\N\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "\\N", "", "\u000e,\\\r" };
+ format = CSVFormat.MYSQL;
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\\\\N\t\t\u000e,\\\\\\r\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "NULL", "\\\r" };
+ format = CSVFormat.MYSQL;
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "NULL\t\\\\\\r\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+
+ s = new String[] { "\\\r" };
+ format = CSVFormat.MYSQL;
+ writer = new StringWriter();
+ printer = new CSVPrinter(writer, format);
+ printer.printRecord(s);
+ printer.close();
+ expected = "\\\\\\r\n";
+ assertEquals(expected, writer.toString());
+ record0 = toFirstRecordValues(expected, format);
+ assertArrayEquals(expectNulls(s, format), record0);
+ }
+
+ /**
+ * Converts an input CSV array into expected output values WRT NULLs. NULL strings are converted to null values
+ * because the parser will convert these strings to null.
+ */
+ private T[] expectNulls(T[] original, CSVFormat csvFormat) {
+ T[] fixed = original.clone();
+ for (int i = 0; i < fixed.length; i++) {
+ if (ObjectUtils.equals(csvFormat.getNullString(), fixed[i])) {
+ fixed[i] = null;
+ }
+ }
+ return fixed;
+ }
+
+ private String[] toFirstRecordValues(final String expected, CSVFormat format) throws IOException {
+ return CSVParser.parse(expected, format).getRecords().get(0).values();
+ }
+
@Test
public void testPrinter1() throws IOException {
final StringWriter sw = new StringWriter();
@@ -429,11 +567,28 @@ public class CSVPrinterTest {
}
@Test
- public void testRandom() throws Exception {
- final int iter = 10000;
- doRandom(CSVFormat.DEFAULT, iter);
- doRandom(CSVFormat.EXCEL, iter);
- doRandom(CSVFormat.MYSQL, iter);
+ public void testRandomDefault() throws Exception {
+ doRandom(CSVFormat.DEFAULT, ITERATIONS_FOR_RANDOM_TEST);
+ }
+
+ @Test
+ public void testRandomExcel() throws Exception {
+ doRandom(CSVFormat.EXCEL, ITERATIONS_FOR_RANDOM_TEST);
+ }
+
+ @Test
+ public void testRandomMySql() throws Exception {
+ doRandom(CSVFormat.MYSQL, ITERATIONS_FOR_RANDOM_TEST);
+ }
+
+ @Test
+ public void testRandomTdf() throws Exception {
+ doRandom(CSVFormat.TDF, ITERATIONS_FOR_RANDOM_TEST);
+ }
+
+ @Test
+ public void testRandomRfc4180() throws Exception {
+ doRandom(CSVFormat.RFC4180, ITERATIONS_FOR_RANDOM_TEST);
}
@Test
@@ -496,6 +651,42 @@ public class CSVPrinterTest {
printer.close();
}
+ @Test
+ public void testEscapeBackslash() throws IOException {
+ StringWriter sw = new StringWriter();
+ final char quoteChar = '\'';
+ final String eol = "\r\n";
+ CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(quoteChar));
+ printer.print("\\");
+ printer.close();
+ assertEquals("'\\'", sw.toString());
+
+ sw = new StringWriter();
+ printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(quoteChar));
+ printer.print("\\\r");
+ printer.close();
+ assertEquals("'\\\r'", sw.toString());
+
+ sw = new StringWriter();
+ printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(quoteChar));
+ printer.print("X\\\r");
+ printer.close();
+ assertEquals("'X\\\r'", sw.toString());
+
+ sw = new StringWriter();
+ printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(quoteChar));
+ printer.printRecord(new Object[] { "\\\r" });
+ printer.close();
+ assertEquals("'\\\r'" + eol, sw.toString());
+
+ sw = new StringWriter();
+ printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(quoteChar));
+ printer.print("\\\\");
+ printer.close();
+ assertEquals("'\\\\'", sw.toString());
+
+ }
+
@Test
public void testPlainEscaped() throws IOException {
final StringWriter sw = new StringWriter();
@@ -549,8 +740,7 @@ public class CSVPrinterTest {
@Test
public void testHeader() throws IOException {
final StringWriter sw = new StringWriter();
- final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null)
- .withHeader("C1", "C2", "C3"));
+ final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3"));
printer.printRecord("a", "b", "c");
printer.printRecord("x", "y", "z");
assertEquals("C1,C2,C3\r\na,b,c\r\nx,y,z\r\n", sw.toString());
@@ -569,10 +759,10 @@ public class CSVPrinterTest {
@Test
public void testSkipHeaderRecordTrue() throws IOException {
- // functionally identical to testHeaderNotSet, used to test CSV-153
+ // functionally identical to testHeaderNotSet, used to test CSV-153
final StringWriter sw = new StringWriter();
- final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null)
- .withHeader("C1", "C2", "C3").withSkipHeaderRecord(true));
+ final CSVPrinter printer = new CSVPrinter(sw,
+ CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3").withSkipHeaderRecord(true));
printer.printRecord("a", "b", "c");
printer.printRecord("x", "y", "z");
assertEquals("a,b,c\r\nx,y,z\r\n", sw.toString());
@@ -581,10 +771,10 @@ public class CSVPrinterTest {
@Test
public void testSkipHeaderRecordFalse() throws IOException {
- // functionally identical to testHeader, used to test CSV-153
+ // functionally identical to testHeader, used to test CSV-153
final StringWriter sw = new StringWriter();
- final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null)
- .withHeader("C1", "C2", "C3").withSkipHeaderRecord(false));
+ final CSVPrinter printer = new CSVPrinter(sw,
+ CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3").withSkipHeaderRecord(false));
printer.printRecord("a", "b", "c");
printer.printRecord("x", "y", "z");
assertEquals("C1,C2,C3\r\na,b,c\r\nx,y,z\r\n", sw.toString());
@@ -597,7 +787,8 @@ public class CSVPrinterTest {
final Date now = new Date();
final CSVFormat format = CSVFormat.EXCEL;
final CSVPrinter csvPrinter = printWithHeaderComments(sw, now, format);
- assertEquals("# Generated by Apache Commons CSV 1.1\r\n# " + now + "\r\nCol1,Col2\r\nA,B\r\nC,D\r\n", sw.toString());
+ assertEquals("# Generated by Apache Commons CSV 1.1\r\n# " + now + "\r\nCol1,Col2\r\nA,B\r\nC,D\r\n",
+ sw.toString());
csvPrinter.close();
}
@@ -607,7 +798,8 @@ public class CSVPrinterTest {
final Date now = new Date();
final CSVFormat format = CSVFormat.TDF;
final CSVPrinter csvPrinter = printWithHeaderComments(sw, now, format);
- assertEquals("# Generated by Apache Commons CSV 1.1\r\n# " + now + "\r\nCol1\tCol2\r\nA\tB\r\nC\tD\r\n", sw.toString());
+ assertEquals("# Generated by Apache Commons CSV 1.1\r\n# " + now + "\r\nCol1\tCol2\r\nA\tB\r\nC\tD\r\n",
+ sw.toString());
csvPrinter.close();
}