Repository: jtablesaw/tablesaw
Pull request #767 (2020-02-23 21:20:56)

Fixes issue 725
Adds an option to CsvWriteOptions to specify that all fields should be quoted. The result is that all output is quoted, including column headers and any column data regardless of column type.

The implementation simply passes the arguments to the equivalent Univocity code, which does the rest.

Thanks for contributing.

- [x] Tick to sign-off your agreement to the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org)

## Description

What was changed

## Testing

Did you add a unit test?


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=jtablesaw_tablesaw&pullRequest=767&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/100.png' alt='100.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=jtablesaw_tablesaw&pullRequest=767&metric=new_coverage&view=list) [100.0% Coverage](https://sonarcloud.io/component_measures?id=jtablesaw_tablesaw&pullRequest=767&metric=new_coverage&view=list)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=jtablesaw_tablesaw&pullRequest=767&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=jtablesaw_tablesaw&pullRequest=767&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java b/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java
index 64a62b6a..29b9370c 100644
--- a/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java
+++ b/core/src/main/java/tech/tablesaw/io/csv/CsvWriteOptions.java
@@ -17,6 +17,7 @@ public class CsvWriteOptions extends WriteOptions {
   private final char quoteChar;
   private final char escapeChar;
   private final String lineEnd;
+  private final boolean quoteAllFields;
 
   private CsvWriteOptions(Builder builder) {
     super(builder);
@@ -27,6 +28,7 @@ public class CsvWriteOptions extends WriteOptions {
     this.lineEnd = builder.lineEnd;
     this.ignoreLeadingWhitespaces = builder.ignoreLeadingWhitespaces;
     this.ignoreTrailingWhitespaces = builder.ignoreTrailingWhitespaces;
+    this.quoteAllFields = builder.quoteAllFields;
   }
 
   public boolean header() {
@@ -49,6 +51,10 @@ public class CsvWriteOptions extends WriteOptions {
     return escapeChar;
   }
 
+  public boolean quoteAllFields() {
+    return quoteAllFields;
+  }
+
   public char quoteChar() {
     return quoteChar;
   }
@@ -82,6 +88,7 @@ public class CsvWriteOptions extends WriteOptions {
     private boolean header = true;
     private boolean ignoreLeadingWhitespaces = true;
     private boolean ignoreTrailingWhitespaces = true;
+    private boolean quoteAllFields = false;
     private char separator = ',';
     private String lineEnd = System.lineSeparator();
     private char escapeChar = '\\';
@@ -117,6 +124,18 @@ public class CsvWriteOptions extends WriteOptions {
       return this;
     }
 
+    /**
+     * Causes all data exported as a CSV file to be enclosed in quotes. Note that this includes the
+     * headers, and all columns regardless of type
+     *
+     * @param quoteAll {@code} true, to cause all data and column headers to be quoted.
+     * @return this CsvWriteOptionsBuilder
+     */
+    public CsvWriteOptions.Builder quoteAllFields(boolean quoteAll) {
+      this.quoteAllFields = quoteAll;
+      return this;
+    }
+
     public CsvWriteOptions.Builder escapeChar(char escapeChar) {
       this.escapeChar = escapeChar;
       return this;
diff --git a/core/src/main/java/tech/tablesaw/io/csv/CsvWriter.java b/core/src/main/java/tech/tablesaw/io/csv/CsvWriter.java
index 2e618ca9..5868a172 100644
--- a/core/src/main/java/tech/tablesaw/io/csv/CsvWriter.java
+++ b/core/src/main/java/tech/tablesaw/io/csv/CsvWriter.java
@@ -81,6 +81,7 @@ public final class CsvWriter implements DataWriter<CsvWriteOptions> {
     settings.setIgnoreTrailingWhitespaces(options.ignoreTrailingWhitespaces());
     // writes empty lines as well.
     settings.setSkipEmptyLines(false);
+    settings.setQuoteAllFields(options.quoteAllFields());
     return settings;
   }
 


Test patch:
diff --git a/core/src/test/java/tech/tablesaw/io/csv/CsvWriteOptionsTest.java b/core/src/test/java/tech/tablesaw/io/csv/CsvWriteOptionsTest.java
index d1f97453..2e710f0f 100644
--- a/core/src/test/java/tech/tablesaw/io/csv/CsvWriteOptionsTest.java
+++ b/core/src/test/java/tech/tablesaw/io/csv/CsvWriteOptionsTest.java
@@ -19,6 +19,7 @@ public class CsvWriteOptionsTest {
             .lineEnd("\r\n")
             .quoteChar('"')
             .separator('.')
+            .quoteAllFields(true)
             .ignoreLeadingWhitespaces(true)
             .ignoreTrailingWhitespaces(true)
             .build();
@@ -28,9 +29,10 @@ public class CsvWriteOptionsTest {
     assertEquals('.', options.separator());
     assertTrue(options.ignoreLeadingWhitespaces());
     assertTrue(options.ignoreTrailingWhitespaces());
+    assertTrue(options.quoteAllFields());
 
     CsvWriterSettings settings = CsvWriter.createSettings(options);
-
+    assertTrue(settings.getQuoteAllFields());
     assertEquals('~', settings.getFormat().getQuoteEscape());
     assertEquals("\r\n", settings.getFormat().getLineSeparatorString());
     assertEquals('"', settings.getFormat().getQuote());
diff --git a/core/src/test/java/tech/tablesaw/io/csv/CsvWriterTest.java b/core/src/test/java/tech/tablesaw/io/csv/CsvWriterTest.java
index a5c88646..a9ee5669 100644
--- a/core/src/test/java/tech/tablesaw/io/csv/CsvWriterTest.java
+++ b/core/src/test/java/tech/tablesaw/io/csv/CsvWriterTest.java
@@ -20,4 +20,16 @@ public class CsvWriterTest {
     table.write().toWriter(writer, "csv");
     assertEquals("colA,colB\na,1\nb,2\n", writer.toString().replaceAll("\\r\\n", "\n"));
   }
+
+  @Test
+  public void quoteAll() throws IOException {
+    StringColumn colA = StringColumn.create("colA", ImmutableList.of("a", "b"));
+    StringColumn colB = StringColumn.create("colB", ImmutableList.of("1", "2"));
+    Table table = Table.create("testTable", colA, colB);
+    StringWriter writer = new StringWriter();
+    table.write().usingOptions(CsvWriteOptions.builder(writer).quoteAllFields(true).build());
+    assertEquals(
+        "\"colA\",\"colB\"\n\"a\",\"1\"\n\"b\",\"2\"\n",
+        writer.toString().replaceAll("\\r\\n", "\n"));
+  }
 }