You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2025/01/18 22:31:15 UTC

(commons-csv) branch master updated: Normalise EOL

This is an automated email from the ASF dual-hosted git repository.

sebb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git


The following commit(s) were added to refs/heads/master by this push:
     new 669023ac Normalise EOL
669023ac is described below

commit 669023ac454642c34181d82b678539f2a020b3a0
Author: Sebb <se...@apache.org>
AuthorDate: Sat Jan 18 22:30:57 2025 +0000

    Normalise EOL
---
 .gitattributes                                     |   16 +
 .github/workflows/codeql-analysis.yml              |  160 +-
 .github/workflows/maven.yml                        |   98 +-
 src/assembly/bin.xml                               |  108 +-
 src/assembly/src.xml                               |   86 +-
 src/changes/changes.xml                            |  772 ++--
 .../java/org/apache/commons/csv/CSVPrinter.java    | 1040 +++---
 .../java/org/apache/commons/csv/Constants.java     |  180 +-
 .../java/org/apache/commons/csv/CSVFormatTest.java | 3066 ++++++++--------
 .../java/org/apache/commons/csv/CSVParserTest.java | 3624 +++++++++---------
 .../org/apache/commons/csv/CSVPrinterTest.java     | 3852 ++++++++++----------
 .../org/apache/commons/csv/PerformanceTest.java    |  690 ++--
 .../apache/commons/csv/issues/JiraCsv198Test.java  |  108 +-
 .../apache/commons/csv/issues/JiraCsv211Test.java  |  108 +-
 .../apache/commons/csv/issues/JiraCsv288Test.java  |  432 +--
 15 files changed, 7178 insertions(+), 7162 deletions(-)

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..bec231c1
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,16 @@
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+* text=auto
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index d6f2c05d..1f42db3b 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,85 +1,85 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: "CodeQL"
-
-on:
-  push:
-    branches: [ master ]
-  pull_request:
-    # The branches below must be a subset of the branches above
-    branches: [ master ]
-  schedule:
-    - cron: '33 9 * * 4'
-
-permissions:
-  contents: read
-
-jobs:
-  analyze:
-    name: Analyze
-    runs-on: ubuntu-latest
-    permissions:
-      actions: read
-      contents: read
-      security-events: write
-
-    strategy:
-      fail-fast: false
-      matrix:
-        language: [ 'java' ]
-        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
-        # Learn more about CodeQL language support at https://git.io/codeql-language-support
-
-    steps:
-    - name: Checkout repository
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: "CodeQL"
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [ master ]
+  schedule:
+    - cron: '33 9 * * 4'
+
+permissions:
+  contents: read
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+    permissions:
+      actions: read
+      contents: read
+      security-events: write
+
+    strategy:
+      fail-fast: false
+      matrix:
+        language: [ 'java' ]
+        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+        # Learn more about CodeQL language support at https://git.io/codeql-language-support
+
+    steps:
+    - name: Checkout repository
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
-      with:
-        persist-credentials: false
+      with:
+        persist-credentials: false
     - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
-      with:
-        path: ~/.m2/repository
-        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
-        restore-keys: |
-          ${{ runner.os }}-maven-
-
-    # Initializes the CodeQL tools for scanning.
-    - name: Initialize CodeQL
+      with:
+        path: ~/.m2/repository
+        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+        restore-keys: |
+          ${{ runner.os }}-maven-
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
       uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c    # 3.28.1
-      with:
-        languages: ${{ matrix.language }}
-        # If you wish to specify custom queries, you can do so here or in a config file.
-        # By default, queries listed here will override any specified in a config file.
-        # Prefix the list here with "+" to use these queries and those in the config file.
-        # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
-    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
-    # If this step fails, then you should remove it and run the build manually (see below)
-    - name: Autobuild
+      with:
+        languages: ${{ matrix.language }}
+        # If you wish to specify custom queries, you can do so here or in a config file.
+        # By default, queries listed here will override any specified in a config file.
+        # Prefix the list here with "+" to use these queries and those in the config file.
+        # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
       uses: github/codeql-action/autobuild@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c    # 3.28.1
-
-    # ℹ️ Command-line programs to run using the OS shell.
-    # 📚 https://git.io/JvXDl
-
-    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
-    #    and modify them (or add more) to build your code if your project
-    #    uses a compiled language
-
-    #- run: |
-    #   make bootstrap
-    #   make release
-
-    - name: Perform CodeQL Analysis
+
+    # ℹ️ Command-line programs to run using the OS shell.
+    # 📚 https://git.io/JvXDl
+
+    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+    #    and modify them (or add more) to build your code if your project
+    #    uses a compiled language
+
+    #- run: |
+    #   make bootstrap
+    #   make release
+
+    - name: Perform CodeQL Analysis
       uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c    # 3.28.1
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 9aec002d..71887b86 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -1,52 +1,52 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: Java CI
-
-on: [push, pull_request]
-
-permissions:
-  contents: read
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-    continue-on-error: ${{ matrix.experimental }}
-    strategy:
-      matrix:
-        java: [ 8, 11, 17, 21, 23 ]
-        experimental: [false]
-        include:
-            - java: 24-ea
-              experimental: true
-        
-    steps:
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Java CI
+
+on: [push, pull_request]
+
+permissions:
+  contents: read
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    continue-on-error: ${{ matrix.experimental }}
+    strategy:
+      matrix:
+        java: [ 8, 11, 17, 21, 23 ]
+        experimental: [false]
+        include:
+            - java: 24-ea
+              experimental: true
+        
+    steps:
     - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
-      with:
-        persist-credentials: false
+      with:
+        persist-credentials: false
     - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
-      with:
-        path: ~/.m2/repository
-        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
-        restore-keys: |
-          ${{ runner.os }}-maven-
-    - name: Set up JDK ${{ matrix.java }}
+      with:
+        path: ~/.m2/repository
+        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+        restore-keys: |
+          ${{ runner.os }}-maven-
+    - name: Set up JDK ${{ matrix.java }}
       uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
-      with:
-        distribution: 'temurin'
-        java-version: ${{ matrix.java }}
-    - name: Build with Maven
-      run: mvn -Ddoclint=all --show-version --batch-mode --no-transfer-progress
+      with:
+        distribution: 'temurin'
+        java-version: ${{ matrix.java }}
+    - name: Build with Maven
+      run: mvn -Ddoclint=all --show-version --batch-mode --no-transfer-progress
diff --git a/src/assembly/bin.xml b/src/assembly/bin.xml
index 82301463..f73d62f6 100644
--- a/src/assembly/bin.xml
+++ b/src/assembly/bin.xml
@@ -1,56 +1,56 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
 <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
-    <id>bin</id>
-    <formats>
-        <format>tar.gz</format>
-        <format>zip</format>
-    </formats>
-    <fileSets>
-        <fileSet>
-            <includes>
-                <include>LICENSE.txt</include>
-                <include>NOTICE.txt</include>
-                <include>RELEASE-NOTES.txt</include>
-            </includes>
-        </fileSet>
-        <fileSet>
-            <directory>target</directory>
-            <outputDirectory></outputDirectory>
-            <includes>
-                <include>${artifactId}-${version}.jar</include>
-            </includes>
-        </fileSet>
-        <fileSet>
-            <directory>target/site/apidocs</directory>
-            <outputDirectory>apidocs</outputDirectory>
-            <includes>
-                <include>**/*</include>
-            </includes>
-        </fileSet>
-        <!-- Include sources jar for IDEs -->
-        <fileSet>
-            <directory>target</directory>
-            <outputDirectory></outputDirectory>
-            <includes>
-                <include>${artifactId}-${version}-sources.jar</include>
-            </includes>
-        </fileSet>
-    </fileSets>
-</assembly>
+    xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
+    <id>bin</id>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>LICENSE.txt</include>
+                <include>NOTICE.txt</include>
+                <include>RELEASE-NOTES.txt</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>${artifactId}-${version}.jar</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target/site/apidocs</directory>
+            <outputDirectory>apidocs</outputDirectory>
+            <includes>
+                <include>**/*</include>
+            </includes>
+        </fileSet>
+        <!-- Include sources jar for IDEs -->
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>${artifactId}-${version}-sources.jar</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/src/assembly/src.xml b/src/assembly/src.xml
index 81955342..9f33f58f 100644
--- a/src/assembly/src.xml
+++ b/src/assembly/src.xml
@@ -1,45 +1,45 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
 <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
-  <id>src</id>
-  <formats>
-    <format>tar.gz</format>
-    <format>zip</format>
-  </formats>
-  <baseDirectory>${artifactId}-${version}-src</baseDirectory>
-  <fileSets>
-    <fileSet>
-      <includes>
-        <include>LICENSE*</include>
-        <include>NOTICE*</include>
-        <include>RELEASE-NOTES.txt</include>
-        <include>pom.xml</include>
-        <include>findbugs-exclude-filter.xml</include>
-        <include>checkstyle*.xml</include>
-      </includes>
-    </fileSet>
-    <fileSet>
-      <directory>src</directory>
-      <excludes>
-        <exclude>**/*Benchmark.java</exclude>
-      </excludes>
-    </fileSet>
-  </fileSets>
-</assembly>
-
+    xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
+  <id>src</id>
+  <formats>
+    <format>tar.gz</format>
+    <format>zip</format>
+  </formats>
+  <baseDirectory>${artifactId}-${version}-src</baseDirectory>
+  <fileSets>
+    <fileSet>
+      <includes>
+        <include>LICENSE*</include>
+        <include>NOTICE*</include>
+        <include>RELEASE-NOTES.txt</include>
+        <include>pom.xml</include>
+        <include>findbugs-exclude-filter.xml</include>
+        <include>checkstyle*.xml</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>src</directory>
+      <excludes>
+        <exclude>**/*Benchmark.java</exclude>
+      </excludes>
+    </fileSet>
+  </fileSets>
+</assembly>
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b5359533..a05e5d52 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -1,386 +1,386 @@
-<?xml version="1.0"?>
-<!--
-  Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements. See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License. You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<!--
-  This file is used by the maven-changes-plugin to generate the release notes.
-  Useful ways of finding items to add to this file are:
-
-  1. Add items when you fix a bug or add a feature (this makes the
-  release process easy :-).
-
-  2. Do a JIRA search for tickets closed since the previous release.
-
-  3. Use the report generated by the maven-changelog-plugin to see all
-  SVN commits. Set the project.properties' maven.changelog.range
-  property to the number of days since the last release.
-
-
-  The <action> type attribute can be add,update,fix,remove.
--->
-
-<document xmlns="http://maven.apache.org/changes/2.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-    xsi:schemaLocation="http://maven.apache.org/changes/2.0.0 https://maven.apache.org/xsd/changes-2.0.0.xsd">
-  <properties>
-    <title>Apache Commons CSV Release Notes</title>
-  </properties>
-  <body>
-    <release version="1.13.1" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <!-- FIX -->
-      <action type="fix" issue="CSV-317" dev="ggregory" due-to="Filipe Roque">Release history link changed from changes-report.html to changes.html #516.</action>
-      <!-- ADD -->
-      <!-- UPDATE -->
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump com.opencsv:opencsv from 5.9 to 5.10.</action>
-    </release>
-    <release version="1.13.0" date="2025-01-08" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <!-- FIX -->
-      <action type="fix" issue="CSV-314" dev="ggregory" due-to="Gary Gregory">Required OSGi Import-Package version numbers in MANIFEST.MF #504.</action>
-      <action type="fix" issue="CSV-314" dev="ggregory" due-to="Gary Gregory">CSVParser.nextRecord() should throw CSVException (an IOException subclass) instead of IOException and IllegalStateException, no method signature changes needed.</action>
-      <!-- ADD -->
-      <action type="add" issue="CSV-313" dev="ggregory" due-to="Gary Gregory">Add CSVPrinter.getRecordCount().</action>
-      <action type="add" dev="ggregory" due-to="Gary Gregory">Add and use CSVParser.Builder and builder() and deprecate CSVParser constructors.</action>
-      <action type="add" dev="ggregory" due-to="Gary Gregory">CSVFormat.Builder implements Supplier&lt;CSVFormat&gt;.</action>
-      <action type="add" dev="ggregory" due-to="Gary Gregory">Deprecate CSVFormat.Builder.build() for get().</action>
-      <action type="add" issue="CSV-196" dev="ggregory" due-to="Yuzhan Jiang, Gary Gregory">Track byte position #502.</action>
-      <!-- UPDATE -->
-      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 76 to 78 #486, #495.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #493.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump commons-io:commons-io from 2.17.0 to 2.18.0 #505.</action> 
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-codec:commons-codec from 1.17.1 to 1.17.2.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 78 to 79.</action>
-    </release>
-    <release version="1.12.0" date="2024-09-21" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <!-- ADD -->
-      <action type="add" issue="CSV-270" dev="ggregory" due-to="Thomas Kamps, Gary Gregory">Add CSVException that extends IOException thrown on invalid input instead of IOException.</action>
-      <!-- FIX -->
-      <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix PMD issues for port to PMD 7.1.0.</action>
-      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán, Gary Gregory">Fix some Javadoc links #442.</action>
-      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Extract duplicated code into a method #444.</action>
-      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Migrate CSVFormat#print(File, Charset) to NIO #445.</action>
-      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Fix documentation for CSVFormat private constructor #466.</action>
-      <action type="fix" issue="CSV-294" dev="ggregory" due-to="Joern Huxhorn, Gary Gregory">CSVFormat does not support explicit " as escape char.</action>
-      <action type="fix" issue="CSV-150" dev="ggregory" due-to="dota17, Gary Gregory, Jörn Huxhorn">Escaping is not disableable.</action>
-      <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix Javadoc warnings on Java 23.</action>
-      <action type="fix" dev="ggregory" due-to="Gary Gregory">Improve parser performance by up to 20%, YMMV.</action>
-      <!-- UPDATE -->
-      <action type="update" dev="ggregory" due-to="Dependabot">Bump commons-codec:commons-codec from 1.16.1 to 1.17.1 #422, #449.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 69 to 76 #435, #452, #465, #468, #475, #482.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.codehaus.mojo:taglist-maven-plugin from 3.0.0 to 3.1.0 #441.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-lang3 from 3.14.0 to 3.17.0 #450, #459, #470.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.hamcrest:hamcrest from 2.2 to 3.0 #455.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump commons-io:commons-io from 2.16.1 to 2.17.0 #476.</action>
-    </release>
-    <release version="1.11.0" date="2024-04-28" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <!-- ADD -->
-      <action issue="CSV-308" type="add" dev="ggregory" due-to="Buddhi De Silva, Gary Gregory">[Javadoc] Add example to CSVFormat#setHeaderComments() #344.</action>
-      <action type="add" dev="ggregory" due-to="DamjanJovanovic, Gary Gregory">Add and use CSVFormat#setTrailingData(boolean) in CSVFormat.EXCEL for Excel compatibility #303.</action>
-      <action type="add" dev="ggregory" due-to="DamjanJovanovic, Gary Gregory">Add and use CSVFormat#setLenientEof(boolean) in CSVFormat.EXCEL for Excel compatibility #303.</action>
-      <!-- FIX -->
-      <action type="fix" issue="CSV-306" dev="ggregory" due-to="Sam Ng, Bruno P. Kinoshita">Replace deprecated method in user guide, update external link #324, #325.</action>
-      <action type="fix" dev="ggregory" due-to="Seth Falco, Bruno P. Kinoshita">Document duplicate header behavior #309.</action>
-      <action type="fix" dev="ggregory" due-to="jkbkupczyk">Add missing docs #328.</action>
-      <action type="fix" dev="ggregory" due-to="step-security-bot">[StepSecurity] CI: Harden GitHub Actions #329, #330.</action>
-      <action type="fix" issue="CSV-147" dev="ggregory" due-to="Steven Peterson, Benedikt Ritter, Gary Gregory, Joerg Schaible, Buddhi De Silva, Elliotte Rusty Harold">Better error message during faulty CSV record read #347.</action>
-      <action type="fix" issue="CSV-310" dev="ggregory" due-to="Buddhi De Silva">Misleading error message when QuoteMode set to None #352.</action>
-      <action type="fix" issue="CSV-311" dev="ggregory" due-to="Christian Feuersaenger, Gary Gregory">OutOfMemory for very long rows despite using column value of type Reader.</action>
-      <action type="fix" dev="ggregory" due-to="Gary Gregory">Use try-with-resources to manage JDBC CLOB in CSVPrinter.printRecords(ResultSet).</action>
-      <action type="fix" dev="ggregory" due-to="Gary Gregory">JDBC Blob columns are now output as Base64 instead of Object#toString(), which usually is InputStream#toString().</action>
-      <action type="fix" dev="ggregory" due-to="DamjanJovanovic, Gary Gregory">Support unusual Excel use cases: Add support for trailing data after the closing quote, and EOF without a final closing quote #303.</action>
-      <action type="fix" dev="ggregory" due-to="Igor Kamyshnikov, Gary Gregory">MongoDB CSV empty first column parsing fix #412.</action>
-      <!-- UPDATE -->
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-io:commons-io: from 2.11.0 to 2.16.1 #408, #413.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump commons-parent from 57 to 69 #410.</action>
-      <action type="update" dev="ggregory" due-to="Dependabot">Bump h2 from 2.1.214 to 2.2.224 #333, #349, #359.</action>
-      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-lang3 from 3.12.0 to 3.14.0.</action>
-      <action type="update" dev="ggregory" due-to="Buddhi De Silva, Michael Osipov, Gary Gregory">Update exception message in CSVRecord#getNextRecord() #348.</action>
-      <action type="update" dev="ggregory" due-to="Dependabot">Bump tests using com.opencsv:opencsv from 5.8 to 5.9 #373.</action>
-    </release>
-    <release version="1.10.0" date="2023-01-28" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <!-- FIX -->
-      <action                 type="fix" dev="ggregory" due-to="Arturo Bernal">Minor changes #172.</action>
-      <action issue="CSV-292" type="fix" dev="kinow" due-to="Rob Vesse">No Automatic-Module-Name prevents usage in JPMS projects without repacking the JAR.</action>
-      <action issue="CSV-288" type="fix" dev="ggregory" due-to="Santhsoh, Angus">Fix for multi-char delimiter not working as expected #218.</action>
-      <action issue="CSV-269" type="fix" dev="ggregory" due-to="Auke te Winkel, Gary Gregory">CSVRecord.get(Enum) should use Enum.name() instead of Enum.toString().</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">Allow org.apache.commons.csv.IOUtils.copy(Reader, Appendable, CharBuffer) to compile on Java 11 and run on Java 8.</action>
-      <action issue="CSV-300" type="fix" dev="ggregory" due-to="Markus Spann, Gary Gregory">CSVRecord.toList() does not give write access to the new List.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">CSVParser.getRecords() now throws UncheckedIOException instead of IOException.</action>
-      <action issue="CSV-274" type="fix" dev="ggregory" due-to="Peter Hull, Bruno P. Kinoshita, Gary Gregory">Add comments to iterator() and stream() #270.</action>
-      <action issue="CSV-290" type="fix" dev="ggregory" due-to="angusdev, Gary Gregory">Fix wrong assumptions in PostgreSQL formats #265.</action>
-      <action                 type="fix" dev="ggregory" due-to="Mykola Faryma">Validate input to setDelimiter(String) for empty string #266.</action>
-      <action                 type="fix" dev="ggregory" due-to="Dependabot">Bump CSVFormat#serialVersionUID from 1 to 2.</action>
-      <action                 type="fix" dev="ggregory" due-to="Alex Herbert">CSVParser: Identify duplicates in null, empty and blank header names #279.</action>
-      <!-- REMOVE -->
-      <action                 type="remove" dev="ggregory">Serialization in CSVFormat is not supported from one version to the next.</action>
-      <!-- ADD -->
-      <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-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 issue="CSV-304" type="add" dev="ggregory" due-to="Peter Hull, Bruno P. Kinoshita, Gary Gregory">Add accessors for header/trailer comments #257.</action>
-      <action type="add" dev="ggregory">Add github/codeql-action.</action>
-      <!-- UPDATE -->
-      <action                 type="update" dev="kinow" due-to="Dependabot, Gary Gregory">Bump actions/cache from 2.1.6 to 3.0.10 #196, #233, #243, #267, #271.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump actions/checkout from 2.3.4 to 3.1.0 #188, #195, #220, #272.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump actions/setup-java from 2 to 3.5.1.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump actions/upload-artifact from 3.1.0 to 3.1.1 #280.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-parent from 52 to 57 #264, #288, #298, #323.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump checkstyle from 8.44 to 9.2.1 #180, #190, #194, #202, #207.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump junit-jupiter from 5.8.0-M1 to 5.9.1 #179, #186, #201, #244, #263.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump jmh-core from 1.32 to 1.36 #176, #208, #229, #285.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump jmh-generator-annprocess from 1.32 to 1.36 #175, #206, #226, #283.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump mockito-core from 3.11.2 to 4.11.0 #187, #197, #204, #212, #230, #237, #251, #259, #284, #292, #297.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump maven-pmd-plugin from 3.14.0 to 3.19.0 #184, #219, #238, #254, #258.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump pmd from 6.36.0 to 6.52.0 #173, #189, #193, #199, #227, #233, #214, #236, #240, #247, #255, #273.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump opencsv from 5.5.1 to 5.7.1 #182, #221, #260, #281.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump spotbugs-maven-plugin from 4.3.0 to 4.7.3.0 #192, #198, #203, #211, #225, #234, #242, #245, #261, #275, #282.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump com.github.spotbugs:spotbugs from 4.5.3 to 4.7.2.</action>
-      <action                 type="update" dev="kinow" due-to="Dependabot">Bump h2 from 1.4.200 to 2.1.214 #200, #205, #213, #239.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump maven-javadoc-plugin from 3.3.0 to 3.4.1.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump biz.aQute.bnd:biz.aQute.bndlib from 5.3.0 to 6.3.1.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump jacoco-maven-plugin from 0.8.7 to 0.8.8.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump japicmp-maven-plugin from 0.15.3 to 0.16.0.</action>
-      <action                 type="update" dev="kinow" due-to="Dependabot">Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #253.</action>
-    </release>
-    <release version="1.9.0" date="2021-07-24" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <!-- FIX -->
-      <action                 type="fix" dev="ggregory" due-to="Amey Jadiye">Replace FindBugs with SpotBugs #56.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Javadoc typo in CSVFormat let's -> lets #57.</action>
-      <action issue="CSV-259" type="fix" dev="ggregory" due-to="Chen">CSVFormat.printWithEscapes throws StringIndexOutOfBoundsException when value is Reader #61.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Improve CSVFormat test coverage #63.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Fix CSVFileParserTest.java to allow for a null return value from record.getComment() #62.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Improve test coverage in CSVFormatTest #65.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Removed invalid Javadoc markup for CSVFormat EXCEL #64.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Improve CSVRecord and CSVPrinter code coverage #66.</action>
-      <action                 type="fix" dev="ggregory" due-to="Chen">Improve lexer and token coverage #67.</action>
-      <action issue="CSV-211" type="fix" dev="ggregory" due-to="Alpesh Kulkarni, Chen">CSVFormat.format trims last delimiter if the delimiter is a white space #71.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">Replace org.apache.commons.csv.Assertions.notNull() with Objects.requireNonNull().</action>
-      <action issue="CSV-149" type="fix" dev="ggregory" due-to="Kranthi, Gary Gregory, Brent Worden, dota17">Line number is not proper at EOF.</action>
-      <action issue="CSV-195" type="fix" dev="ggregory" due-to="Rodolfo Duldulao, Rodolfo Duldulao, Michael Vitz, dota17">Parser iterates over the last CSV Record twice.</action>
-      <action issue="CSV-267" type="fix" dev="ggregory" due-to="Arturo Bernal">Minor improvements #126, #127, #130.</action>
-      <action issue="CSV-123" type="fix" dev="ggregory" due-to="Emmanuel Bourg, Benedikt Ritter, shivakrishnaah, Gary Gregory">Add possibility to use ResultSet header meta data as CSV header #11.</action>
-      <action issue="CSV-148" type="fix" dev="ggregory" due-to="dota17">Add test cases for withIgnoreSurroundingSpaces() and withTrim() #70.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">Update CSVParser.parse(File, Charset, CSVFormat) from IO to NIO.</action>
-      <action issue="CSV-271" type="fix" dev="ggregory" due-to="Amar Prakash Pandey">Missing separator with print(object) followed by printRecord(Object[]) #157.</action>
-      <action issue="CSV-158" type="fix" dev="ggregory" due-to="Alexander Bondarev, Benedikt Ritter, Gary Gregory, Chen">Fix EOL checking for read array in ExtendedBufferedReader #5.</action>
-      <action issue="CSV-263" type="fix" dev="ggregory" due-to="Jason A. Guild, Gary Gregory">Print from Reader with embedded quotes generates incorrect output #78.</action>
-      <action                 type="fix" dev="ggregory" due-to="Arturo Bernal">Replace JUnit assert by simpler but equivalent calls. #159.</action>
-      <action                 type="fix" dev="ggregory" due-to="Seth Falco">Update gitignore to ignore idea and vscode #160.</action>
-      <action issue="CSV-281" type="fix" dev="ggregory" due-to="belugabehr">Update CSVBenchmark #165.</action>
-      <action issue="CSV-283" type="fix" dev="ggregory" due-to="belugabehr">Remove Whitespace Check Determines Delimiter Twice #167.</action>
-      <action issue="CSV-283" type="fix" dev="ggregory" due-to="belugabehr">Document and Automate CSV Benchmark Harness #166.</action>
-      <action issue="CSV-279" type="fix" dev="ggregory" due-to="belugabehr">Optimize Lexer Delimiter Check for One Character Delimiter #163.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: org.apache.commons.csv.CSVParser.getHeaderNames() may expose internal representation by returning CSVParser.headerNames [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 599] EI_EXPOSE_REP.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVParser(Reader, CSVFormat, long, long) may expose internal representation by storing an externally mutable object into CSVParser.format [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 433] EI_EXPOSE_REP2.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVParser(Reader, CSVFormat, long, long) may expose internal representation by storing an externally mutable object into CSVParser.headerMap [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 437] EI_EXPOSE_REP2.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVParser(Reader, CSVFormat, long, long) may expose internal representation by storing an externally mutable object into CSVParser.headerNames [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 438] EI_EXPOSE_REP2.</action>
-      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVPrinter(Appendable, CSVFormat) may expose internal representation by storing an externally mutable object into CSVPrinter.format [org.apache.commons.csv.CSVPrinter] At CSVPrinter.java:[line 100] EI_EXPOSE_REP2.</action>
-      <action issue="CSV-284" type="fix" dev="ggregory" due-to="belugabehr">Formalize PerformanceTest #168.</action>
-      <action issue="CSV-278" type="fix" dev="ggregory" due-to="belugabehr">Reuse Buffers in Lexer for Delimiter Detection #162.</action>
-      <action issue="CSV-286" type="fix" dev="ggregory" due-to="belugabehr">Cleanup and Document Performance Test Harness #170.</action>
-      <action issue="CSV-265" type="fix" dev="ggregory" due-to="belugabehr">Update buffer position when reading line comment #120.</action>
-      <!-- ADD -->
-      <action issue="CSV-275" type="add" dev="ggregory" due-to="Michael Wyraz, Gary Gregory">Make CSVRecord#toList() public.</action>
-      <action                 type="add" dev="ggregory" due-to="Gary Gregory">Add CSVRecord#stream().</action>
-      <action                 type="add" dev="ggregory" due-to="Gary Gregory">Add CSVParser#stream().</action>
-      <action issue="CSV-184" type="add" dev="ggregory" due-to="Gaurav Agarwal, M. Steiger, Gary Gregory">Make the method CSVRecord.putIn(Map) public.</action>
-      <action                 type="add" dev="ggregory" due-to="dota17">Add test cases for CSVRecord with get(Enum) and toString. #54.</action>
-      <action                 type="add" dev="ggregory" due-to="Gary Gregory, dota17">Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73.</action>
-      <action issue="CSV-206" type="add" dev="ggregory" due-to="Gary Gregory, dota17">Add support for String delimiters #76.</action>
-      <!-- UPDATE -->
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update org.junit.jupiter:junit-jupiter from 5.6.0 to 5.7.0, #84 #109</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Apache Commons Lang 3.9 to 3.12.0.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from commons-io:commons-io 2.6 to 2.11.0, #108.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump actions/checkout from v1 to v2.3.4, #79, #92, #121.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump commons-parent from 50 to 51 #80.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump tests from opencsv from 3.1 to 5.5.1 #81, #137, #158.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from super-csv from 2.2.1 to 2.4.0 #86.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump build actions/setup-java from v1.4.0 to v2, #101, #113.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #122.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump tests from org.mockito:mockito-core 3.2.4 -> 3.11.2; #88, #107, #110, #123, #128, #129, #156.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump actions/cache from v2 to v2.1.6 #132, #153.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump maven-checkstyle-plugin from 3.0.0 to 3.1.2 #131.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump checkstyle from 8.29 to 8.44.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump junit-jupiter from 5.7.0 to 5.8.0-M1 #133, #149.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump commons.jacoco.version from 0.8.5 to 0.8.7 (Java 16).</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump commons.spotbugs.version from 4.0.4 to 4.3.0 (Java 16).</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump maven-javadoc-plugin from 3.2.0 to 3.3.0.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump jmh-generator-annprocess from 1.5.2 to 1.32 #151.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump PMD core from 6.29.0 to 6.36.0.</action>
-      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump biz.aQute.bnd:biz.aQute.bndlib from 5.1.2 to 5.3.0.</action>
-    </release>
-    <release version="1.8" date="2020-02-01" description="This is a feature and maintenance release. Java 8 or later is required.
-
-This release fixes serialization compatibility of CSVRecord with versions 1.0 to 1.6.
-New fields added since 1.7 are not serialized. Support for Serializable is scheduled to be
-removed in version 2.0.
-">
-      <action issue="CSV-255" type="add" dev="ggregory" due-to="0x100">Add CSVRecord.isSet(int) method #52.</action>
-      <action issue="CSV-135" type="fix" dev="sebb" due-to="Mateusz Zakarczemny">Char escape doesn't work properly with quoting.</action>
-      <action issue="CSV-244" type="fix" dev="sebb">Test case failures following CSVFormat#equals() update.</action>
-      <action issue="CSV-243" type="fix" dev="sebb">CSVFormat withTrim() and withIgnoreSurroundingSpaces() need better docs.</action>
-      <action issue="CSV-242" type="fix" dev="sebb">CSVFormat equals() and hashCode() don't use all fields.</action>
-      <action issue="CSV-241" type="fix" dev="ggregory" due-to="LuckyIlam, Gary Gregory">CSVFormat#validate() does not account for allowDuplicateHeaderNames #43.</action>
-      <action issue="CSV-245" type="fix" dev="ggregory" due-to="Alex Herbert">Post 1.7 release fixes.</action>
-      <action issue="CSV-252" type="fix" dev="ggregory" due-to= "Alex Herbert">Upgrade test framework to JUnit 5 Jupiter #49, #50.</action>
-      <action issue="CSV-247" type="fix" dev="ggregory" due-to="Alex Herbert, Gary Gregory">A single empty header is allowed when not allowing empty column headers. #47.</action>
-      <action issue="CSV-248" type="fix" dev="ggregory" due-to="Alex Herbert">CSVRecord is not Serializable.</action>
-      <action                 type="fix" dev="ggregory" due-to="Alex Herbert">Use test scope for supercsv #48.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from H2 1.4.199 to 1.4.200.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Hamcrest 2.1 to 2.2.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Mockito 3.1.0 to 3.2.4.</action>
-      <action                 type="update" dev="ggregory" due-to="Chen">Fix typos in site and test #53.</action>
-      <action                 type="update" dev="ggregory" due-to="Chen">Fix typo performance test #55.</action>
-    </release>
-    <release version="1.7" date="2019-06-01" description="This is a feature and maintenance release. Java 8 or later is required.">
-      <action issue="CSV-233" type="add" dev="ggregory" due-to="Gary Gregory">Add predefined CSVFormats for printing MongoDB CSV and TSV.</action>
-      <action issue="CSV-208" type="fix" dev="ggregory" due-to="Jurrie Overgoor">Fix escape character for POSTGRESQL_TEXT and POSTGRESQL_CSV formats.</action>
-      <action issue="CSV-232" type="fix" dev="ggregory" due-to="Jurrie Overgoor, Gary Gregory">Site link "Source Repository" does not work.</action>
-      <action issue="CSV-234" type="add" dev="ggregory" due-to="Roberto Benedetti, Gary Gregory">Add support for java.sql.Clob.</action>
-      <action issue="CSV-237" type="update" dev="ggregory" due-to="Gary Gregory">Update to Java 8.</action>
-      <action issue="CSV-238" type="fix" dev="ggregory" due-to="Stephen Olander-Waters">Escape quotes in CLOBs #39.</action>
-      <action issue="CSV-239" type="add" dev="ggregory" due-to="Gary Gregory, Dave Moten">Cannot get headers in column order from CSVRecord.</action>
-      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from H2 1.4.198 to 1.4.199.</action>
-    </release>
-    <release version="1.6" date="2018-09-22" description="Feature and bug fix release (Java 7 or above)">
-      <action issue="CSV-231" type="update" dev="britter">Add more documentation to CSVPrinter.</action>
-      <action issue="CSV-217" type="add" dev="ggregory" due-to="Korolyov Alexei">Add autoFlush option for CsvPrinter. PR #24.</action>
-      <action issue="CSV-219" type="fix" dev="ggregory" due-to="Zhang Hongda">The behavior of quote char using is not similar as Excel does when the first string contains CJK char(s).</action>
-      <action issue="CSV-172" type="fix" dev="ggregory" due-to="Andrew Pennebaker">Don't quote cells just because they have UTF-8 encoded characters.</action>
-      <action issue="CSV-220" type="add" dev="ggregory" due-to="Gary Gregory">Add API org.apache.commons.csv.CSVFormat.withSystemRecordSeparator().</action>
-      <action issue="CSV-223" type="fix" dev="ggregory" due-to="Samuel Martin">Inconsistency between Javadoc of CSVFormat DEFAULT EXCEL.</action>
-      <action issue="CSV-209" type="fix" dev="ggregory" due-to="Gary Gregory">Create CSVFormat.ORACLE preset.</action>
-      <action issue="CSV-224" type="fix" dev="ggregory" due-to="David Warshaw">Some multi-iterator parsing peek sequences incorrectly consume elements.</action>
-      <action issue="CSV-225" type="fix" dev="ggregory" due-to="Anson Schwabecher">Parse method should avoid creating a redundant BufferedReader.</action>
-      <action issue="CSV-233" type="fix" dev="ggregory" due-to="Gary Gregory">Add predefined CSVFormats for printing MongoDB CSV and TSV.</action>
-    </release>
-    <release version="1.5" date="2017-09-03" description="Feature and bug fix release (Java 7 or above)">
-      <action issue="CSV-203" type="fix" dev="ggregory" due-to="Richard Wheeldon, Kai Paroth">withNullString value is printed without quotes when QuoteMode.ALL is specified; add QuoteMode.ALL_NON_NULL. PR #17.</action>
-      <action issue="CSV-194" type="fix" dev="ggregory" due-to="Marc Prud'hommeaux">Fix outdated comments about FileReader in CSVParser #13</action>
-      <action issue="CSV-193" type="fix" dev="ggregory" due-to="Matthias Wiehl">Fix incorrect method name 'withFirstRowAsHeader' in user guide.</action>
-      <action issue="CSV-171" type="fix" dev="ggregory" due-to="Gary Gregory, Michael Graessle, Adrian Bridgett">Negative numeric values in the first column are always quoted in minimal mode.</action>
-      <action issue="CSV-187" type="update" dev="ggregory" due-to="Gary Gregory">Update platform requirement from Java 6 to 7.</action>
-      <action issue="CSV-201" type="update" dev="ggregory" due-to="Benedikt Ritter, Gary Gregory">Do not use RuntimeException in CSVParser.iterator().new Iterator() {...}.getNextRecord()</action>
-      <action issue="CSV-189" type="add" dev="ggregory" due-to="Peter Holzwarth, Gary Gregory">CSVParser: Add factory method accepting InputStream.</action>
-      <action issue="CSV-190" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat.print(File, Charset)</action>
-      <action issue="CSV-191" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat.print(Path, Charset)</action>
-      <action issue="CSV-192" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVParser.parse(Path, Charset, CSVFormat)</action>
-      <action issue="CSV-205" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat#printer() to print to System.out</action>
-      <action issue="CSV-207" type="add" dev="ggregory" due-to="Gary Gregory">Provide a CSV Format for printing PostgreSQL CSV and Text formats.</action>
-      <action issue="CSV-214" type="add" dev="ggregory" due-to="Nitin Mahendru, Gary Gregory">Adding a placeholder in the Lexer and CSV parser to store the end-of-line string.</action>
-    </release>
-    <release version="1.4" date="2016-05-28" description="Feature and bug fix release (Java 6 or above)">
-      <action issue="CSV-181" type="update" dev="ggregory" due-to="Gary Gregory">Make CSVPrinter.print(Object) GC-free.</action>
-      <action issue="CSV-182" type="add" dev="ggregory" due-to="Gary Gregory">Allow some printing operations directly from CSVFormat.</action>
-      <action issue="CSV-183" type="update" dev="ggregory">Drop ferc.gov tests.</action>
-    </release>
-    <release version="1.3" date="2016-05-09" description="Feature and bug fix release (Java 6 or above)">
-      <action issue="CSV-179" type="add" dev="britter">Add shortcut method for using first record as header to CSVFormat</action>
-      <action issue="CSV-180" type="add" dev="britter">Add withHeader(Class&lt;? extends Enum&gt;) to CSVFormat</action>
-      <action issue="CSV-167" type="update" dev="sebb" due-to="Rene">Comment line hides next record; update Javadoc to make behavior clear</action>
-      <action issue="CSV-153" type="update" dev="britter" due-to="Wren">CSVPrinter doesn't skip creation of header record if skipHeaderRecord is set to true</action>
-      <action issue="CSV-159" type="add" dev="ggregory" due-to="Yamil Medina">Add IgnoreCase option for accessing header names</action>
-      <action issue="CSV-169" type="add" dev="ggregory" due-to="Gary Gregory">The null string should be case-sensitive when reading records</action>
-      <action issue="CSV-168" type="fix" dev="ggregory" due-to="Gary Gregory, cornel creanga">CSVFormat.nullString should not be escaped</action>
-      <action issue="CSV-170" type="fix" dev="ggregory" due-to="Gary Gregory, cornel creanga">CSVFormat.MYSQL nullString should be "\N"</action>
-      <action issue="CSV-161" type="fix" dev="ggregory" due-to="Gary Gregory, Kristof Meixner, Emmanuel Bourg">Fix Javadoc to say CSVFormat with() methods return a new CSVFormat</action>
-      <action issue="CSV-175" type="add" dev="ggregory" due-to="Gary Gregory, Chris Jones">Support for ignoring trailing delimiter.</action>
-      <action issue="CSV-177" type="add" dev="ggregory" due-to="Gary Gregory">Support trimming leading and trailing blanks.</action>
-      <action issue="CSV-178" type="add" dev="ggregory" due-to="Gary Gregory">Create default formats for Informix UNLOAD and UNLOAD CSV.</action>
-    </release>
-    <release version="1.2" date="2015-08-24" description="Feature and bug fix release (Java 6 or above)">
-      <action issue="CSV-145" type="fix" dev="ggregory" due-to="Frank Ulbricht">CSVFormat.with* methods clear the header comments</action>
-      <action issue="CSV-156" type="fix" dev="ggregory" due-to="Jason Steenstra-Pickens">Incorrect Javadoc on QuoteMode.NONE</action>
-      <action issue="CSV-157" type="add" dev="ggregory">Add enum CSVFormat.Predefined that contains the default CSVFormat values.</action>
-    </release>
-    <release version="1.1" date="2014-11-16" description="Feature and bug fix release (Java 6 or above)">
-      <action issue="CSV-140" type="fix" dev="ggregory" due-to="Damjan Jovanovic">QuoteMode.NON_NUMERIC doesn't work with CSVPrinter.printRecords(ResultSet)</action>
-      <action issue="CSV-130" type="fix" dev="ggregory" due-to="Sergei Lebedev">CSVFormat#withHeader doesn't work well with #printComment, add withHeaderComments(String...)</action>
-      <action issue="CSV-128" type="fix" dev="ggregory">CSVFormat.EXCEL should ignore empty header names</action>
-      <action issue="CSV-132" type="fix" dev="ggregory" due-to="Sascha Szott">Incorrect Javadoc referencing org.apache.commons.csv.CSVFormat withQuote()</action>
-      <action issue="CSV-124" type="update" dev="brentworden" due-to="Kalyan">Improve toString() implementation of CSVRecord</action>
-      <action issue="CSV-134" type="update" dev="ggregory" due-to="wu wen">Unified parameter validation</action>
-      <action issue="CSV-129" type="add" dev="ggregory">Add CSVFormat#with 0-arg methods matching boolean arg methods</action>
-      <action issue="CSV-131" type="add" dev="ggregory" due-to="Holger Stratmann">Save positions of records to enable random access</action>
-      <action issue="CSV-139" type="add" dev="ggregory">CSVPrinter.printRecord(ResultSet) with metadata</action>
-    </release>
-    <release version="1.0" date="2014-08-14" description="First release (Java 6 or above)">
-      <action issue="CSV-125" type="fix" dev="britter">No longer works with Java 6</action>
-      <action issue="CSV-122" type="fix" dev="britter" due-to="Mike Lewis">NullPointerException when empty header string and null string of ""</action>
-      <action issue="CSV-117" type="update" dev="sebb">Validate format parameters in constructor</action>
-      <action issue="CSV-121" type="add" dev="ggregory" due-to="Sebastian Hardt">IllegalArgumentException thrown when the header contains duplicate names when the column names are empty.</action>
-      <action issue="CSV-120" type="add" dev="ggregory" due-to="Sergei Lebedev">CSVFormat#withHeader doesn't work with CSVPrinter</action>
-      <action issue="CSV-119" type="add" dev="ggregory" due-to="Sergei Lebedev">CSVFormat is missing a print(...) method</action>
-      <action issue="CSV-118" type="fix" dev="ggregory" due-to="Enrique Lara">CSVRecord.toMap() throws NPE on formats with no
-        headers.</action>
-      <action issue="CSV-113" type="fix" dev="sebb">Check whether ISE/IAE are being used appropriately</action>
-      <action issue="CSV-114" type="fix" dev="sebb">CSVFormat constructor should reject a header array with duplicate
-        entries
-      </action>
-      <action issue="CSV-112" type="fix" dev="britter">HeaderMap is inconsistent when it is parsed from an input with
-        duplicate columns names
-      </action>
-      <action issue="CSV-111" type="fix" dev="ggregory">CSVRecord.toMap() fails if row length shorter than header length
-      </action>
-      <action issue="CSV-106" type="fix" dev="ggregory">CSVFormat.format allways append null</action>
-      <action issue="CSV-105" type="add" dev="ggregory">Add Map conversion API to CSVRecord</action>
-      <action issue="CSV-100" type="fix" dev="ggregory">CSVParser: getHeaderMap throws NPE</action>
-      <action issue="CSV-42" type="update" dev="ebourg" due-to="Bob Smith">Lots of possible changes</action>
-      <action issue="CSV-78" type="update" dev="sebb">Use Character instead of char for char fields except delimiter
-      </action>
-      <action issue="CSV-99" type="update" dev="britter">Revert Builder implementation in CSVFormat</action>
-      <action issue="CSV-53" type="fix" dev="britter">CSVRecord does not verify that the length of the header mapping
-        matches the number of values
-      </action>
-      <action issue="CSV-93" type="update" dev="ggregory">Allow the handling of NULL values</action>
-      <action issue="CSV-68" type="update" dev="ggregory">Use the Builder pattern for CSVFormat</action>
-      <action issue="CSV-84" type="update" dev="sebb">Clarify comment handling</action>
-      <action issue="CSV-25" type="update" dev="ebourg">CSVParser.nextValue() seems pointless</action>
-      <action issue="CSV-97" type="update" dev="ggregory">Allow the String value for null to be customized for the CSV
-        printer
-      </action>
-      <action issue="CSV-88" type="update" dev="ggregory">Not possible to create a CSVFormat from scratch</action>
-      <action issue="CSV-52" type="add" dev="ggregory">Keep track of record number</action>
-      <action issue="CSV-94" type="update" dev="sebb">Lexer should only use char fields</action>
-      <action issue="CSV-92" type="add" dev="ggregory">Need a way to extract parsed headers, e.g. for use in formatting
-        output
-      </action>
-      <action issue="CSV-65" type="add" dev="ebourg">Header support</action>
-      <action issue="CSV-54" type="fix" dev="sebb">Confusing semantic of the ignore leading/trailing spaces parameters
-      </action>
-      <action issue="CSV-71" type="update" dev="sebb">Add convenience methods to CSVLexer</action>
-      <action issue="CSV-59" type="update" dev="ebourg">Is CharBuffer really needed, now that StringBuilder is available?
-      </action>
-      <action issue="CSV-55" type="update" dev="britter">Replace while(true)-loop in CSVParser.getRecord with do-while-loop
-      </action>
-      <action issue="CSV-34" type="fix" dev="sebb">CSVFormat describes itself as immutable, but it is not - in
-        particular it is not thread-safe
-      </action>
-      <action issue="CSV-36" type="fix" dev="yonik">Endless loops in CSV parser</action>
-      <action issue="CSV-13" type="fix" dev="ebourg">NullPointerException in CSVPrinter.print()/println()</action>
-      <action issue="CSV-45" type="update" dev="yonik">CSVPrinter overhaul</action>
-      <action issue="CSV-23" type="fix" dev="ebourg">Excel strategy uses wrong separator</action>
-      <action issue="CSV-49" type="update" dev="ebourg" due-to="Bob Smith">CSVStrategy has modifiable public static variables
-      </action>
-      <action issue="CSV-48" type="add" dev="ebourg">Predefined format for MYSQL</action>
-      <action issue="CSV-46" type="update" dev="ebourg">Reduce visibility of methods in internal classes</action>
-      <action issue="CSV-26" type="update" dev="jacopoc">ExtendedBufferedReader does too much</action>
-      <action issue="CSV-27" type="update" dev="ebourg">Decide whether to keep the csv.writer subpackage</action>
-    </release>
-
-  </body>
-</document>
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements. See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!--
+  This file is used by the maven-changes-plugin to generate the release notes.
+  Useful ways of finding items to add to this file are:
+
+  1. Add items when you fix a bug or add a feature (this makes the
+  release process easy :-).
+
+  2. Do a JIRA search for tickets closed since the previous release.
+
+  3. Use the report generated by the maven-changelog-plugin to see all
+  SVN commits. Set the project.properties' maven.changelog.range
+  property to the number of days since the last release.
+
+
+  The <action> type attribute can be add,update,fix,remove.
+-->
+
+<document xmlns="http://maven.apache.org/changes/2.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+    xsi:schemaLocation="http://maven.apache.org/changes/2.0.0 https://maven.apache.org/xsd/changes-2.0.0.xsd">
+  <properties>
+    <title>Apache Commons CSV Release Notes</title>
+  </properties>
+  <body>
+    <release version="1.13.1" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <!-- FIX -->
+      <action type="fix" issue="CSV-317" dev="ggregory" due-to="Filipe Roque">Release history link changed from changes-report.html to changes.html #516.</action>
+      <!-- ADD -->
+      <!-- UPDATE -->
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump com.opencsv:opencsv from 5.9 to 5.10.</action>
+    </release>
+    <release version="1.13.0" date="2025-01-08" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <!-- FIX -->
+      <action type="fix" issue="CSV-314" dev="ggregory" due-to="Gary Gregory">Required OSGi Import-Package version numbers in MANIFEST.MF #504.</action>
+      <action type="fix" issue="CSV-314" dev="ggregory" due-to="Gary Gregory">CSVParser.nextRecord() should throw CSVException (an IOException subclass) instead of IOException and IllegalStateException, no method signature changes needed.</action>
+      <!-- ADD -->
+      <action type="add" issue="CSV-313" dev="ggregory" due-to="Gary Gregory">Add CSVPrinter.getRecordCount().</action>
+      <action type="add" dev="ggregory" due-to="Gary Gregory">Add and use CSVParser.Builder and builder() and deprecate CSVParser constructors.</action>
+      <action type="add" dev="ggregory" due-to="Gary Gregory">CSVFormat.Builder implements Supplier&lt;CSVFormat&gt;.</action>
+      <action type="add" dev="ggregory" due-to="Gary Gregory">Deprecate CSVFormat.Builder.build() for get().</action>
+      <action type="add" issue="CSV-196" dev="ggregory" due-to="Yuzhan Jiang, Gary Gregory">Track byte position #502.</action>
+      <!-- UPDATE -->
+      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 76 to 78 #486, #495.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #493.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump commons-io:commons-io from 2.17.0 to 2.18.0 #505.</action> 
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-codec:commons-codec from 1.17.1 to 1.17.2.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 78 to 79.</action>
+    </release>
+    <release version="1.12.0" date="2024-09-21" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <!-- ADD -->
+      <action type="add" issue="CSV-270" dev="ggregory" due-to="Thomas Kamps, Gary Gregory">Add CSVException that extends IOException thrown on invalid input instead of IOException.</action>
+      <!-- FIX -->
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix PMD issues for port to PMD 7.1.0.</action>
+      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán, Gary Gregory">Fix some Javadoc links #442.</action>
+      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Extract duplicated code into a method #444.</action>
+      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Migrate CSVFormat#print(File, Charset) to NIO #445.</action>
+      <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Fix documentation for CSVFormat private constructor #466.</action>
+      <action type="fix" issue="CSV-294" dev="ggregory" due-to="Joern Huxhorn, Gary Gregory">CSVFormat does not support explicit " as escape char.</action>
+      <action type="fix" issue="CSV-150" dev="ggregory" due-to="dota17, Gary Gregory, Jörn Huxhorn">Escaping is not disableable.</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix Javadoc warnings on Java 23.</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Improve parser performance by up to 20%, YMMV.</action>
+      <!-- UPDATE -->
+      <action type="update" dev="ggregory" due-to="Dependabot">Bump commons-codec:commons-codec from 1.16.1 to 1.17.1 #422, #449.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 69 to 76 #435, #452, #465, #468, #475, #482.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.codehaus.mojo:taglist-maven-plugin from 3.0.0 to 3.1.0 #441.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-lang3 from 3.14.0 to 3.17.0 #450, #459, #470.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.hamcrest:hamcrest from 2.2 to 3.0 #455.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump commons-io:commons-io from 2.16.1 to 2.17.0 #476.</action>
+    </release>
+    <release version="1.11.0" date="2024-04-28" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <!-- ADD -->
+      <action issue="CSV-308" type="add" dev="ggregory" due-to="Buddhi De Silva, Gary Gregory">[Javadoc] Add example to CSVFormat#setHeaderComments() #344.</action>
+      <action type="add" dev="ggregory" due-to="DamjanJovanovic, Gary Gregory">Add and use CSVFormat#setTrailingData(boolean) in CSVFormat.EXCEL for Excel compatibility #303.</action>
+      <action type="add" dev="ggregory" due-to="DamjanJovanovic, Gary Gregory">Add and use CSVFormat#setLenientEof(boolean) in CSVFormat.EXCEL for Excel compatibility #303.</action>
+      <!-- FIX -->
+      <action type="fix" issue="CSV-306" dev="ggregory" due-to="Sam Ng, Bruno P. Kinoshita">Replace deprecated method in user guide, update external link #324, #325.</action>
+      <action type="fix" dev="ggregory" due-to="Seth Falco, Bruno P. Kinoshita">Document duplicate header behavior #309.</action>
+      <action type="fix" dev="ggregory" due-to="jkbkupczyk">Add missing docs #328.</action>
+      <action type="fix" dev="ggregory" due-to="step-security-bot">[StepSecurity] CI: Harden GitHub Actions #329, #330.</action>
+      <action type="fix" issue="CSV-147" dev="ggregory" due-to="Steven Peterson, Benedikt Ritter, Gary Gregory, Joerg Schaible, Buddhi De Silva, Elliotte Rusty Harold">Better error message during faulty CSV record read #347.</action>
+      <action type="fix" issue="CSV-310" dev="ggregory" due-to="Buddhi De Silva">Misleading error message when QuoteMode set to None #352.</action>
+      <action type="fix" issue="CSV-311" dev="ggregory" due-to="Christian Feuersaenger, Gary Gregory">OutOfMemory for very long rows despite using column value of type Reader.</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">Use try-with-resources to manage JDBC CLOB in CSVPrinter.printRecords(ResultSet).</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory">JDBC Blob columns are now output as Base64 instead of Object#toString(), which usually is InputStream#toString().</action>
+      <action type="fix" dev="ggregory" due-to="DamjanJovanovic, Gary Gregory">Support unusual Excel use cases: Add support for trailing data after the closing quote, and EOF without a final closing quote #303.</action>
+      <action type="fix" dev="ggregory" due-to="Igor Kamyshnikov, Gary Gregory">MongoDB CSV empty first column parsing fix #412.</action>
+      <!-- UPDATE -->
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-io:commons-io: from 2.11.0 to 2.16.1 #408, #413.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump commons-parent from 57 to 69 #410.</action>
+      <action type="update" dev="ggregory" due-to="Dependabot">Bump h2 from 2.1.214 to 2.2.224 #333, #349, #359.</action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-lang3 from 3.12.0 to 3.14.0.</action>
+      <action type="update" dev="ggregory" due-to="Buddhi De Silva, Michael Osipov, Gary Gregory">Update exception message in CSVRecord#getNextRecord() #348.</action>
+      <action type="update" dev="ggregory" due-to="Dependabot">Bump tests using com.opencsv:opencsv from 5.8 to 5.9 #373.</action>
+    </release>
+    <release version="1.10.0" date="2023-01-28" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <!-- FIX -->
+      <action                 type="fix" dev="ggregory" due-to="Arturo Bernal">Minor changes #172.</action>
+      <action issue="CSV-292" type="fix" dev="kinow" due-to="Rob Vesse">No Automatic-Module-Name prevents usage in JPMS projects without repacking the JAR.</action>
+      <action issue="CSV-288" type="fix" dev="ggregory" due-to="Santhsoh, Angus">Fix for multi-char delimiter not working as expected #218.</action>
+      <action issue="CSV-269" type="fix" dev="ggregory" due-to="Auke te Winkel, Gary Gregory">CSVRecord.get(Enum) should use Enum.name() instead of Enum.toString().</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">Allow org.apache.commons.csv.IOUtils.copy(Reader, Appendable, CharBuffer) to compile on Java 11 and run on Java 8.</action>
+      <action issue="CSV-300" type="fix" dev="ggregory" due-to="Markus Spann, Gary Gregory">CSVRecord.toList() does not give write access to the new List.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">CSVParser.getRecords() now throws UncheckedIOException instead of IOException.</action>
+      <action issue="CSV-274" type="fix" dev="ggregory" due-to="Peter Hull, Bruno P. Kinoshita, Gary Gregory">Add comments to iterator() and stream() #270.</action>
+      <action issue="CSV-290" type="fix" dev="ggregory" due-to="angusdev, Gary Gregory">Fix wrong assumptions in PostgreSQL formats #265.</action>
+      <action                 type="fix" dev="ggregory" due-to="Mykola Faryma">Validate input to setDelimiter(String) for empty string #266.</action>
+      <action                 type="fix" dev="ggregory" due-to="Dependabot">Bump CSVFormat#serialVersionUID from 1 to 2.</action>
+      <action                 type="fix" dev="ggregory" due-to="Alex Herbert">CSVParser: Identify duplicates in null, empty and blank header names #279.</action>
+      <!-- REMOVE -->
+      <action                 type="remove" dev="ggregory">Serialization in CSVFormat is not supported from one version to the next.</action>
+      <!-- ADD -->
+      <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-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 issue="CSV-304" type="add" dev="ggregory" due-to="Peter Hull, Bruno P. Kinoshita, Gary Gregory">Add accessors for header/trailer comments #257.</action>
+      <action type="add" dev="ggregory">Add github/codeql-action.</action>
+      <!-- UPDATE -->
+      <action                 type="update" dev="kinow" due-to="Dependabot, Gary Gregory">Bump actions/cache from 2.1.6 to 3.0.10 #196, #233, #243, #267, #271.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump actions/checkout from 2.3.4 to 3.1.0 #188, #195, #220, #272.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump actions/setup-java from 2 to 3.5.1.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump actions/upload-artifact from 3.1.0 to 3.1.1 #280.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-parent from 52 to 57 #264, #288, #298, #323.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump checkstyle from 8.44 to 9.2.1 #180, #190, #194, #202, #207.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump junit-jupiter from 5.8.0-M1 to 5.9.1 #179, #186, #201, #244, #263.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump jmh-core from 1.32 to 1.36 #176, #208, #229, #285.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump jmh-generator-annprocess from 1.32 to 1.36 #175, #206, #226, #283.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump mockito-core from 3.11.2 to 4.11.0 #187, #197, #204, #212, #230, #237, #251, #259, #284, #292, #297.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump maven-pmd-plugin from 3.14.0 to 3.19.0 #184, #219, #238, #254, #258.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump pmd from 6.36.0 to 6.52.0 #173, #189, #193, #199, #227, #233, #214, #236, #240, #247, #255, #273.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump opencsv from 5.5.1 to 5.7.1 #182, #221, #260, #281.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump spotbugs-maven-plugin from 4.3.0 to 4.7.3.0 #192, #198, #203, #211, #225, #234, #242, #245, #261, #275, #282.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump com.github.spotbugs:spotbugs from 4.5.3 to 4.7.2.</action>
+      <action                 type="update" dev="kinow" due-to="Dependabot">Bump h2 from 1.4.200 to 2.1.214 #200, #205, #213, #239.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump maven-javadoc-plugin from 3.3.0 to 3.4.1.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump biz.aQute.bnd:biz.aQute.bndlib from 5.3.0 to 6.3.1.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump jacoco-maven-plugin from 0.8.7 to 0.8.8.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump japicmp-maven-plugin from 0.15.3 to 0.16.0.</action>
+      <action                 type="update" dev="kinow" due-to="Dependabot">Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #253.</action>
+    </release>
+    <release version="1.9.0" date="2021-07-24" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <!-- FIX -->
+      <action                 type="fix" dev="ggregory" due-to="Amey Jadiye">Replace FindBugs with SpotBugs #56.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Javadoc typo in CSVFormat let's -> lets #57.</action>
+      <action issue="CSV-259" type="fix" dev="ggregory" due-to="Chen">CSVFormat.printWithEscapes throws StringIndexOutOfBoundsException when value is Reader #61.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Improve CSVFormat test coverage #63.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Fix CSVFileParserTest.java to allow for a null return value from record.getComment() #62.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Improve test coverage in CSVFormatTest #65.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Removed invalid Javadoc markup for CSVFormat EXCEL #64.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Improve CSVRecord and CSVPrinter code coverage #66.</action>
+      <action                 type="fix" dev="ggregory" due-to="Chen">Improve lexer and token coverage #67.</action>
+      <action issue="CSV-211" type="fix" dev="ggregory" due-to="Alpesh Kulkarni, Chen">CSVFormat.format trims last delimiter if the delimiter is a white space #71.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">Replace org.apache.commons.csv.Assertions.notNull() with Objects.requireNonNull().</action>
+      <action issue="CSV-149" type="fix" dev="ggregory" due-to="Kranthi, Gary Gregory, Brent Worden, dota17">Line number is not proper at EOF.</action>
+      <action issue="CSV-195" type="fix" dev="ggregory" due-to="Rodolfo Duldulao, Rodolfo Duldulao, Michael Vitz, dota17">Parser iterates over the last CSV Record twice.</action>
+      <action issue="CSV-267" type="fix" dev="ggregory" due-to="Arturo Bernal">Minor improvements #126, #127, #130.</action>
+      <action issue="CSV-123" type="fix" dev="ggregory" due-to="Emmanuel Bourg, Benedikt Ritter, shivakrishnaah, Gary Gregory">Add possibility to use ResultSet header meta data as CSV header #11.</action>
+      <action issue="CSV-148" type="fix" dev="ggregory" due-to="dota17">Add test cases for withIgnoreSurroundingSpaces() and withTrim() #70.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">Update CSVParser.parse(File, Charset, CSVFormat) from IO to NIO.</action>
+      <action issue="CSV-271" type="fix" dev="ggregory" due-to="Amar Prakash Pandey">Missing separator with print(object) followed by printRecord(Object[]) #157.</action>
+      <action issue="CSV-158" type="fix" dev="ggregory" due-to="Alexander Bondarev, Benedikt Ritter, Gary Gregory, Chen">Fix EOL checking for read array in ExtendedBufferedReader #5.</action>
+      <action issue="CSV-263" type="fix" dev="ggregory" due-to="Jason A. Guild, Gary Gregory">Print from Reader with embedded quotes generates incorrect output #78.</action>
+      <action                 type="fix" dev="ggregory" due-to="Arturo Bernal">Replace JUnit assert by simpler but equivalent calls. #159.</action>
+      <action                 type="fix" dev="ggregory" due-to="Seth Falco">Update gitignore to ignore idea and vscode #160.</action>
+      <action issue="CSV-281" type="fix" dev="ggregory" due-to="belugabehr">Update CSVBenchmark #165.</action>
+      <action issue="CSV-283" type="fix" dev="ggregory" due-to="belugabehr">Remove Whitespace Check Determines Delimiter Twice #167.</action>
+      <action issue="CSV-283" type="fix" dev="ggregory" due-to="belugabehr">Document and Automate CSV Benchmark Harness #166.</action>
+      <action issue="CSV-279" type="fix" dev="ggregory" due-to="belugabehr">Optimize Lexer Delimiter Check for One Character Delimiter #163.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: org.apache.commons.csv.CSVParser.getHeaderNames() may expose internal representation by returning CSVParser.headerNames [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 599] EI_EXPOSE_REP.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVParser(Reader, CSVFormat, long, long) may expose internal representation by storing an externally mutable object into CSVParser.format [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 433] EI_EXPOSE_REP2.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVParser(Reader, CSVFormat, long, long) may expose internal representation by storing an externally mutable object into CSVParser.headerMap [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 437] EI_EXPOSE_REP2.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVParser(Reader, CSVFormat, long, long) may expose internal representation by storing an externally mutable object into CSVParser.headerNames [org.apache.commons.csv.CSVParser] At CSVParser.java:[line 438] EI_EXPOSE_REP2.</action>
+      <action                 type="fix" dev="ggregory" due-to="Gary Gregory">SpotBugs Error: Medium: new org.apache.commons.csv.CSVPrinter(Appendable, CSVFormat) may expose internal representation by storing an externally mutable object into CSVPrinter.format [org.apache.commons.csv.CSVPrinter] At CSVPrinter.java:[line 100] EI_EXPOSE_REP2.</action>
+      <action issue="CSV-284" type="fix" dev="ggregory" due-to="belugabehr">Formalize PerformanceTest #168.</action>
+      <action issue="CSV-278" type="fix" dev="ggregory" due-to="belugabehr">Reuse Buffers in Lexer for Delimiter Detection #162.</action>
+      <action issue="CSV-286" type="fix" dev="ggregory" due-to="belugabehr">Cleanup and Document Performance Test Harness #170.</action>
+      <action issue="CSV-265" type="fix" dev="ggregory" due-to="belugabehr">Update buffer position when reading line comment #120.</action>
+      <!-- ADD -->
+      <action issue="CSV-275" type="add" dev="ggregory" due-to="Michael Wyraz, Gary Gregory">Make CSVRecord#toList() public.</action>
+      <action                 type="add" dev="ggregory" due-to="Gary Gregory">Add CSVRecord#stream().</action>
+      <action                 type="add" dev="ggregory" due-to="Gary Gregory">Add CSVParser#stream().</action>
+      <action issue="CSV-184" type="add" dev="ggregory" due-to="Gaurav Agarwal, M. Steiger, Gary Gregory">Make the method CSVRecord.putIn(Map) public.</action>
+      <action                 type="add" dev="ggregory" due-to="dota17">Add test cases for CSVRecord with get(Enum) and toString. #54.</action>
+      <action                 type="add" dev="ggregory" due-to="Gary Gregory, dota17">Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73.</action>
+      <action issue="CSV-206" type="add" dev="ggregory" due-to="Gary Gregory, dota17">Add support for String delimiters #76.</action>
+      <!-- UPDATE -->
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update org.junit.jupiter:junit-jupiter from 5.6.0 to 5.7.0, #84 #109</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Apache Commons Lang 3.9 to 3.12.0.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from commons-io:commons-io 2.6 to 2.11.0, #108.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump actions/checkout from v1 to v2.3.4, #79, #92, #121.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump commons-parent from 50 to 51 #80.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump tests from opencsv from 3.1 to 5.5.1 #81, #137, #158.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from super-csv from 2.2.1 to 2.4.0 #86.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump build actions/setup-java from v1.4.0 to v2, #101, #113.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #122.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump tests from org.mockito:mockito-core 3.2.4 -> 3.11.2; #88, #107, #110, #123, #128, #129, #156.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump actions/cache from v2 to v2.1.6 #132, #153.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump maven-checkstyle-plugin from 3.0.0 to 3.1.2 #131.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump checkstyle from 8.29 to 8.44.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump junit-jupiter from 5.7.0 to 5.8.0-M1 #133, #149.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump commons.jacoco.version from 0.8.5 to 0.8.7 (Java 16).</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump commons.spotbugs.version from 4.0.4 to 4.3.0 (Java 16).</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Bump maven-javadoc-plugin from 3.2.0 to 3.3.0.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump jmh-generator-annprocess from 1.5.2 to 1.32 #151.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump PMD core from 6.29.0 to 6.36.0.</action>
+      <action                 type="update" dev="ggregory" due-to="Dependabot">Bump biz.aQute.bnd:biz.aQute.bndlib from 5.1.2 to 5.3.0.</action>
+    </release>
+    <release version="1.8" date="2020-02-01" description="This is a feature and maintenance release. Java 8 or later is required.
+
+This release fixes serialization compatibility of CSVRecord with versions 1.0 to 1.6.
+New fields added since 1.7 are not serialized. Support for Serializable is scheduled to be
+removed in version 2.0.
+">
+      <action issue="CSV-255" type="add" dev="ggregory" due-to="0x100">Add CSVRecord.isSet(int) method #52.</action>
+      <action issue="CSV-135" type="fix" dev="sebb" due-to="Mateusz Zakarczemny">Char escape doesn't work properly with quoting.</action>
+      <action issue="CSV-244" type="fix" dev="sebb">Test case failures following CSVFormat#equals() update.</action>
+      <action issue="CSV-243" type="fix" dev="sebb">CSVFormat withTrim() and withIgnoreSurroundingSpaces() need better docs.</action>
+      <action issue="CSV-242" type="fix" dev="sebb">CSVFormat equals() and hashCode() don't use all fields.</action>
+      <action issue="CSV-241" type="fix" dev="ggregory" due-to="LuckyIlam, Gary Gregory">CSVFormat#validate() does not account for allowDuplicateHeaderNames #43.</action>
+      <action issue="CSV-245" type="fix" dev="ggregory" due-to="Alex Herbert">Post 1.7 release fixes.</action>
+      <action issue="CSV-252" type="fix" dev="ggregory" due-to= "Alex Herbert">Upgrade test framework to JUnit 5 Jupiter #49, #50.</action>
+      <action issue="CSV-247" type="fix" dev="ggregory" due-to="Alex Herbert, Gary Gregory">A single empty header is allowed when not allowing empty column headers. #47.</action>
+      <action issue="CSV-248" type="fix" dev="ggregory" due-to="Alex Herbert">CSVRecord is not Serializable.</action>
+      <action                 type="fix" dev="ggregory" due-to="Alex Herbert">Use test scope for supercsv #48.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from H2 1.4.199 to 1.4.200.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Hamcrest 2.1 to 2.2.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Mockito 3.1.0 to 3.2.4.</action>
+      <action                 type="update" dev="ggregory" due-to="Chen">Fix typos in site and test #53.</action>
+      <action                 type="update" dev="ggregory" due-to="Chen">Fix typo performance test #55.</action>
+    </release>
+    <release version="1.7" date="2019-06-01" description="This is a feature and maintenance release. Java 8 or later is required.">
+      <action issue="CSV-233" type="add" dev="ggregory" due-to="Gary Gregory">Add predefined CSVFormats for printing MongoDB CSV and TSV.</action>
+      <action issue="CSV-208" type="fix" dev="ggregory" due-to="Jurrie Overgoor">Fix escape character for POSTGRESQL_TEXT and POSTGRESQL_CSV formats.</action>
+      <action issue="CSV-232" type="fix" dev="ggregory" due-to="Jurrie Overgoor, Gary Gregory">Site link "Source Repository" does not work.</action>
+      <action issue="CSV-234" type="add" dev="ggregory" due-to="Roberto Benedetti, Gary Gregory">Add support for java.sql.Clob.</action>
+      <action issue="CSV-237" type="update" dev="ggregory" due-to="Gary Gregory">Update to Java 8.</action>
+      <action issue="CSV-238" type="fix" dev="ggregory" due-to="Stephen Olander-Waters">Escape quotes in CLOBs #39.</action>
+      <action issue="CSV-239" type="add" dev="ggregory" due-to="Gary Gregory, Dave Moten">Cannot get headers in column order from CSVRecord.</action>
+      <action                 type="update" dev="ggregory" due-to="Gary Gregory">Update tests from H2 1.4.198 to 1.4.199.</action>
+    </release>
+    <release version="1.6" date="2018-09-22" description="Feature and bug fix release (Java 7 or above)">
+      <action issue="CSV-231" type="update" dev="britter">Add more documentation to CSVPrinter.</action>
+      <action issue="CSV-217" type="add" dev="ggregory" due-to="Korolyov Alexei">Add autoFlush option for CsvPrinter. PR #24.</action>
+      <action issue="CSV-219" type="fix" dev="ggregory" due-to="Zhang Hongda">The behavior of quote char using is not similar as Excel does when the first string contains CJK char(s).</action>
+      <action issue="CSV-172" type="fix" dev="ggregory" due-to="Andrew Pennebaker">Don't quote cells just because they have UTF-8 encoded characters.</action>
+      <action issue="CSV-220" type="add" dev="ggregory" due-to="Gary Gregory">Add API org.apache.commons.csv.CSVFormat.withSystemRecordSeparator().</action>
+      <action issue="CSV-223" type="fix" dev="ggregory" due-to="Samuel Martin">Inconsistency between Javadoc of CSVFormat DEFAULT EXCEL.</action>
+      <action issue="CSV-209" type="fix" dev="ggregory" due-to="Gary Gregory">Create CSVFormat.ORACLE preset.</action>
+      <action issue="CSV-224" type="fix" dev="ggregory" due-to="David Warshaw">Some multi-iterator parsing peek sequences incorrectly consume elements.</action>
+      <action issue="CSV-225" type="fix" dev="ggregory" due-to="Anson Schwabecher">Parse method should avoid creating a redundant BufferedReader.</action>
+      <action issue="CSV-233" type="fix" dev="ggregory" due-to="Gary Gregory">Add predefined CSVFormats for printing MongoDB CSV and TSV.</action>
+    </release>
+    <release version="1.5" date="2017-09-03" description="Feature and bug fix release (Java 7 or above)">
+      <action issue="CSV-203" type="fix" dev="ggregory" due-to="Richard Wheeldon, Kai Paroth">withNullString value is printed without quotes when QuoteMode.ALL is specified; add QuoteMode.ALL_NON_NULL. PR #17.</action>
+      <action issue="CSV-194" type="fix" dev="ggregory" due-to="Marc Prud'hommeaux">Fix outdated comments about FileReader in CSVParser #13</action>
+      <action issue="CSV-193" type="fix" dev="ggregory" due-to="Matthias Wiehl">Fix incorrect method name 'withFirstRowAsHeader' in user guide.</action>
+      <action issue="CSV-171" type="fix" dev="ggregory" due-to="Gary Gregory, Michael Graessle, Adrian Bridgett">Negative numeric values in the first column are always quoted in minimal mode.</action>
+      <action issue="CSV-187" type="update" dev="ggregory" due-to="Gary Gregory">Update platform requirement from Java 6 to 7.</action>
+      <action issue="CSV-201" type="update" dev="ggregory" due-to="Benedikt Ritter, Gary Gregory">Do not use RuntimeException in CSVParser.iterator().new Iterator() {...}.getNextRecord()</action>
+      <action issue="CSV-189" type="add" dev="ggregory" due-to="Peter Holzwarth, Gary Gregory">CSVParser: Add factory method accepting InputStream.</action>
+      <action issue="CSV-190" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat.print(File, Charset)</action>
+      <action issue="CSV-191" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat.print(Path, Charset)</action>
+      <action issue="CSV-192" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVParser.parse(Path, Charset, CSVFormat)</action>
+      <action issue="CSV-205" type="add" dev="ggregory" due-to="Gary Gregory">Add convenience API CSVFormat#printer() to print to System.out</action>
+      <action issue="CSV-207" type="add" dev="ggregory" due-to="Gary Gregory">Provide a CSV Format for printing PostgreSQL CSV and Text formats.</action>
+      <action issue="CSV-214" type="add" dev="ggregory" due-to="Nitin Mahendru, Gary Gregory">Adding a placeholder in the Lexer and CSV parser to store the end-of-line string.</action>
+    </release>
+    <release version="1.4" date="2016-05-28" description="Feature and bug fix release (Java 6 or above)">
+      <action issue="CSV-181" type="update" dev="ggregory" due-to="Gary Gregory">Make CSVPrinter.print(Object) GC-free.</action>
+      <action issue="CSV-182" type="add" dev="ggregory" due-to="Gary Gregory">Allow some printing operations directly from CSVFormat.</action>
+      <action issue="CSV-183" type="update" dev="ggregory">Drop ferc.gov tests.</action>
+    </release>
+    <release version="1.3" date="2016-05-09" description="Feature and bug fix release (Java 6 or above)">
+      <action issue="CSV-179" type="add" dev="britter">Add shortcut method for using first record as header to CSVFormat</action>
+      <action issue="CSV-180" type="add" dev="britter">Add withHeader(Class&lt;? extends Enum&gt;) to CSVFormat</action>
+      <action issue="CSV-167" type="update" dev="sebb" due-to="Rene">Comment line hides next record; update Javadoc to make behavior clear</action>
+      <action issue="CSV-153" type="update" dev="britter" due-to="Wren">CSVPrinter doesn't skip creation of header record if skipHeaderRecord is set to true</action>
+      <action issue="CSV-159" type="add" dev="ggregory" due-to="Yamil Medina">Add IgnoreCase option for accessing header names</action>
+      <action issue="CSV-169" type="add" dev="ggregory" due-to="Gary Gregory">The null string should be case-sensitive when reading records</action>
+      <action issue="CSV-168" type="fix" dev="ggregory" due-to="Gary Gregory, cornel creanga">CSVFormat.nullString should not be escaped</action>
+      <action issue="CSV-170" type="fix" dev="ggregory" due-to="Gary Gregory, cornel creanga">CSVFormat.MYSQL nullString should be "\N"</action>
+      <action issue="CSV-161" type="fix" dev="ggregory" due-to="Gary Gregory, Kristof Meixner, Emmanuel Bourg">Fix Javadoc to say CSVFormat with() methods return a new CSVFormat</action>
+      <action issue="CSV-175" type="add" dev="ggregory" due-to="Gary Gregory, Chris Jones">Support for ignoring trailing delimiter.</action>
+      <action issue="CSV-177" type="add" dev="ggregory" due-to="Gary Gregory">Support trimming leading and trailing blanks.</action>
+      <action issue="CSV-178" type="add" dev="ggregory" due-to="Gary Gregory">Create default formats for Informix UNLOAD and UNLOAD CSV.</action>
+    </release>
+    <release version="1.2" date="2015-08-24" description="Feature and bug fix release (Java 6 or above)">
+      <action issue="CSV-145" type="fix" dev="ggregory" due-to="Frank Ulbricht">CSVFormat.with* methods clear the header comments</action>
+      <action issue="CSV-156" type="fix" dev="ggregory" due-to="Jason Steenstra-Pickens">Incorrect Javadoc on QuoteMode.NONE</action>
+      <action issue="CSV-157" type="add" dev="ggregory">Add enum CSVFormat.Predefined that contains the default CSVFormat values.</action>
+    </release>
+    <release version="1.1" date="2014-11-16" description="Feature and bug fix release (Java 6 or above)">
+      <action issue="CSV-140" type="fix" dev="ggregory" due-to="Damjan Jovanovic">QuoteMode.NON_NUMERIC doesn't work with CSVPrinter.printRecords(ResultSet)</action>
+      <action issue="CSV-130" type="fix" dev="ggregory" due-to="Sergei Lebedev">CSVFormat#withHeader doesn't work well with #printComment, add withHeaderComments(String...)</action>
+      <action issue="CSV-128" type="fix" dev="ggregory">CSVFormat.EXCEL should ignore empty header names</action>
+      <action issue="CSV-132" type="fix" dev="ggregory" due-to="Sascha Szott">Incorrect Javadoc referencing org.apache.commons.csv.CSVFormat withQuote()</action>
+      <action issue="CSV-124" type="update" dev="brentworden" due-to="Kalyan">Improve toString() implementation of CSVRecord</action>
+      <action issue="CSV-134" type="update" dev="ggregory" due-to="wu wen">Unified parameter validation</action>
+      <action issue="CSV-129" type="add" dev="ggregory">Add CSVFormat#with 0-arg methods matching boolean arg methods</action>
+      <action issue="CSV-131" type="add" dev="ggregory" due-to="Holger Stratmann">Save positions of records to enable random access</action>
+      <action issue="CSV-139" type="add" dev="ggregory">CSVPrinter.printRecord(ResultSet) with metadata</action>
+    </release>
+    <release version="1.0" date="2014-08-14" description="First release (Java 6 or above)">
+      <action issue="CSV-125" type="fix" dev="britter">No longer works with Java 6</action>
+      <action issue="CSV-122" type="fix" dev="britter" due-to="Mike Lewis">NullPointerException when empty header string and null string of ""</action>
+      <action issue="CSV-117" type="update" dev="sebb">Validate format parameters in constructor</action>
+      <action issue="CSV-121" type="add" dev="ggregory" due-to="Sebastian Hardt">IllegalArgumentException thrown when the header contains duplicate names when the column names are empty.</action>
+      <action issue="CSV-120" type="add" dev="ggregory" due-to="Sergei Lebedev">CSVFormat#withHeader doesn't work with CSVPrinter</action>
+      <action issue="CSV-119" type="add" dev="ggregory" due-to="Sergei Lebedev">CSVFormat is missing a print(...) method</action>
+      <action issue="CSV-118" type="fix" dev="ggregory" due-to="Enrique Lara">CSVRecord.toMap() throws NPE on formats with no
+        headers.</action>
+      <action issue="CSV-113" type="fix" dev="sebb">Check whether ISE/IAE are being used appropriately</action>
+      <action issue="CSV-114" type="fix" dev="sebb">CSVFormat constructor should reject a header array with duplicate
+        entries
+      </action>
+      <action issue="CSV-112" type="fix" dev="britter">HeaderMap is inconsistent when it is parsed from an input with
+        duplicate columns names
+      </action>
+      <action issue="CSV-111" type="fix" dev="ggregory">CSVRecord.toMap() fails if row length shorter than header length
+      </action>
+      <action issue="CSV-106" type="fix" dev="ggregory">CSVFormat.format allways append null</action>
+      <action issue="CSV-105" type="add" dev="ggregory">Add Map conversion API to CSVRecord</action>
+      <action issue="CSV-100" type="fix" dev="ggregory">CSVParser: getHeaderMap throws NPE</action>
+      <action issue="CSV-42" type="update" dev="ebourg" due-to="Bob Smith">Lots of possible changes</action>
+      <action issue="CSV-78" type="update" dev="sebb">Use Character instead of char for char fields except delimiter
+      </action>
+      <action issue="CSV-99" type="update" dev="britter">Revert Builder implementation in CSVFormat</action>
+      <action issue="CSV-53" type="fix" dev="britter">CSVRecord does not verify that the length of the header mapping
+        matches the number of values
+      </action>
+      <action issue="CSV-93" type="update" dev="ggregory">Allow the handling of NULL values</action>
+      <action issue="CSV-68" type="update" dev="ggregory">Use the Builder pattern for CSVFormat</action>
+      <action issue="CSV-84" type="update" dev="sebb">Clarify comment handling</action>
+      <action issue="CSV-25" type="update" dev="ebourg">CSVParser.nextValue() seems pointless</action>
+      <action issue="CSV-97" type="update" dev="ggregory">Allow the String value for null to be customized for the CSV
+        printer
+      </action>
+      <action issue="CSV-88" type="update" dev="ggregory">Not possible to create a CSVFormat from scratch</action>
+      <action issue="CSV-52" type="add" dev="ggregory">Keep track of record number</action>
+      <action issue="CSV-94" type="update" dev="sebb">Lexer should only use char fields</action>
+      <action issue="CSV-92" type="add" dev="ggregory">Need a way to extract parsed headers, e.g. for use in formatting
+        output
+      </action>
+      <action issue="CSV-65" type="add" dev="ebourg">Header support</action>
+      <action issue="CSV-54" type="fix" dev="sebb">Confusing semantic of the ignore leading/trailing spaces parameters
+      </action>
+      <action issue="CSV-71" type="update" dev="sebb">Add convenience methods to CSVLexer</action>
+      <action issue="CSV-59" type="update" dev="ebourg">Is CharBuffer really needed, now that StringBuilder is available?
+      </action>
+      <action issue="CSV-55" type="update" dev="britter">Replace while(true)-loop in CSVParser.getRecord with do-while-loop
+      </action>
+      <action issue="CSV-34" type="fix" dev="sebb">CSVFormat describes itself as immutable, but it is not - in
+        particular it is not thread-safe
+      </action>
+      <action issue="CSV-36" type="fix" dev="yonik">Endless loops in CSV parser</action>
+      <action issue="CSV-13" type="fix" dev="ebourg">NullPointerException in CSVPrinter.print()/println()</action>
+      <action issue="CSV-45" type="update" dev="yonik">CSVPrinter overhaul</action>
+      <action issue="CSV-23" type="fix" dev="ebourg">Excel strategy uses wrong separator</action>
+      <action issue="CSV-49" type="update" dev="ebourg" due-to="Bob Smith">CSVStrategy has modifiable public static variables
+      </action>
+      <action issue="CSV-48" type="add" dev="ebourg">Predefined format for MYSQL</action>
+      <action issue="CSV-46" type="update" dev="ebourg">Reduce visibility of methods in internal classes</action>
+      <action issue="CSV-26" type="update" dev="jacopoc">ExtendedBufferedReader does too much</action>
+      <action issue="CSV-27" type="update" dev="ebourg">Decide whether to keep the csv.writer subpackage</action>
+    </release>
+
+  </body>
+</document>
diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java
index dce94692..67088c38 100644
--- a/src/main/java/org/apache/commons/csv/CSVPrinter.java
+++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java
@@ -1,520 +1,520 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.commons.csv;
-
-import static org.apache.commons.csv.Constants.CR;
-import static org.apache.commons.csv.Constants.LF;
-import static org.apache.commons.csv.Constants.SP;
-
-import java.io.Closeable;
-import java.io.Flushable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.sql.Blob;
-import java.sql.Clob;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.stream.Stream;
-
-import org.apache.commons.io.function.IOStream;
-
-/**
- * Prints values in a {@link CSVFormat CSV format}.
- *
- * <p>Values can be appended to the output by calling the {@link #print(Object)} method.
- * Values are printed according to {@link String#valueOf(Object)}.
- * To complete a record the {@link #println()} method has to be called.
- * Comments can be appended by calling {@link #printComment(String)}.
- * However a comment will only be written to the output if the {@link CSVFormat} supports comments.
- * </p>
- *
- * <p>The printer also supports appending a complete record at once by calling {@link #printRecord(Object...)}
- * or {@link #printRecord(Iterable)}.
- * Furthermore {@link #printRecords(Object...)}, {@link #printRecords(Iterable)} and {@link #printRecords(ResultSet)}
- * methods can be used to print several records at once.
- * </p>
- *
- * <p>Example:</p>
- *
- * <pre>
- * try (CSVPrinter printer = new CSVPrinter(new FileWriter("csv.txt"), CSVFormat.EXCEL)) {
- *     printer.printRecord("id", "userName", "firstName", "lastName", "birthday");
- *     printer.printRecord(1, "john73", "John", "Doe", LocalDate.of(1973, 9, 15));
- *     printer.println();
- *     printer.printRecord(2, "mary", "Mary", "Meyer", LocalDate.of(1985, 3, 29));
- * } catch (IOException ex) {
- *     ex.printStackTrace();
- * }
- * </pre>
- *
- * <p>This code will write the following to csv.txt:</p>
- * <pre>
- * id,userName,firstName,lastName,birthday
- * 1,john73,John,Doe,1973-09-15
- *
- * 2,mary,Mary,Meyer,1985-03-29
- * </pre>
- */
-public final class CSVPrinter implements Flushable, Closeable {
-
-    /** The place that the values get written. */
-    private final Appendable appendable;
-
-    private final CSVFormat format;
-
-    /** True if we just began a new record. */
-    private boolean newRecord = true;
-
-    private long recordCount;
-
-    /**
-     * Creates a printer that will print values to the given stream following the CSVFormat.
-     * <p>
-     * Currently, only a pure encapsulation format or a pure escaping format is supported. Hybrid formats (encapsulation
-     * and escaping with a different character) are not supported.
-     * </p>
-     *
-     * @param appendable
-     *            stream to which to print. Must not be null.
-     * @param format
-     *            the CSV format. Must not be null.
-     * @throws IOException
-     *             thrown if the optional header cannot be printed.
-     * @throws IllegalArgumentException
-     *             thrown if the parameters of the format are inconsistent or if either out or format are null.
-     */
-    public CSVPrinter(final Appendable appendable, final CSVFormat format) throws IOException {
-        Objects.requireNonNull(appendable, "appendable");
-        Objects.requireNonNull(format, "format");
-
-        this.appendable = appendable;
-        this.format = format.copy();
-        // TODO: Is it a good idea to do this here instead of on the first call to a print method?
-        // It seems a pain to have to track whether the header has already been printed or not.
-        final String[] headerComments = format.getHeaderComments();
-        if (headerComments != null) {
-            for (final String line : headerComments) {
-                printComment(line);
-            }
-        }
-        if (format.getHeader() != null && !format.getSkipHeaderRecord()) {
-            this.printRecord((Object[]) format.getHeader());
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        close(false);
-    }
-
-    /**
-     * Closes the underlying stream with an optional flush first.
-     * @param flush whether to flush before the actual close.
-     * @throws IOException
-     *             If an I/O error occurs
-     * @since 1.6
-     */
-    public void close(final boolean flush) throws IOException {
-        if (flush || format.getAutoFlush()) {
-            flush();
-        }
-        if (appendable instanceof Closeable) {
-            ((Closeable) appendable).close();
-        }
-    }
-
-    /**
-     * Outputs the record separator and increments the record count.
-     *
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    private synchronized void endOfRecord() throws IOException {
-        println();
-        recordCount++;
-    }
-
-    /**
-     * Flushes the underlying stream.
-     *
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    @Override
-    public void flush() throws IOException {
-        if (appendable instanceof Flushable) {
-            ((Flushable) appendable).flush();
-        }
-    }
-
-    /**
-     * Gets the target Appendable.
-     *
-     * @return the target Appendable.
-     */
-    public Appendable getOut() {
-        return this.appendable;
-    }
-
-    /**
-     * Gets the record count printed, this does not include comments or headers.
-     *
-     * @return the record count, this does not include comments or headers.
-     * @since 1.13.0
-     */
-    public long getRecordCount() {
-        return recordCount;
-    }
-
-    /**
-     * Prints the string as the next value on the line. The value will be escaped or encapsulated as needed.
-     *
-     * @param value
-     *            value to be output.
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    public synchronized void print(final Object value) throws IOException {
-        format.print(value, appendable, newRecord);
-        newRecord = false;
-    }
-
-    /**
-     * Prints a comment on a new line among the delimiter-separated values.
-     *
-     * <p>
-     * Comments will always begin on a new line and occupy at least one full line. The character specified to start
-     * comments and a space will be inserted at the beginning of each new line in the comment.
-     * </p>
-     *
-     * <p>
-     * If comments are disabled in the current CSV format this method does nothing.
-     * </p>
-     *
-     * <p>This method detects line breaks inside the comment string and inserts {@link CSVFormat#getRecordSeparator()}
-     * to start a new line of the comment. Note that this might produce unexpected results for formats that do not use
-     * line breaks as record separators.</p>
-     *
-     * @param comment
-     *            the comment to output
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    public synchronized void printComment(final String comment) throws IOException {
-        if (comment == null || !format.isCommentMarkerSet()) {
-            return;
-        }
-        if (!newRecord) {
-            println();
-        }
-        appendable.append(format.getCommentMarker().charValue()); // N.B. Explicit (un)boxing is intentional
-        appendable.append(SP);
-        for (int i = 0; i < comment.length(); i++) {
-            final char c = comment.charAt(i);
-            switch (c) {
-            case CR:
-                if (i + 1 < comment.length() && comment.charAt(i + 1) == LF) {
-                    i++;
-                }
-                // falls-through: break intentionally excluded.
-            case LF:
-                println();
-                appendable.append(format.getCommentMarker().charValue()); // N.B. Explicit (un)boxing is intentional
-                appendable.append(SP);
-                break;
-            default:
-                appendable.append(c);
-                break;
-            }
-        }
-        println();
-    }
-
-    /**
-     * Prints headers for a result set based on its metadata.
-     *
-     * @param resultSet The ResultSet to query for metadata.
-     * @throws IOException If an I/O error occurs.
-     * @throws SQLException If a database access error occurs or this method is called on a closed result set.
-     * @since 1.9.0
-     */
-    public synchronized void printHeaders(final ResultSet resultSet) throws IOException, SQLException {
-        try (IOStream<String> stream = IOStream.of(format.builder().setHeader(resultSet).get().getHeader())) {
-            stream.forEachOrdered(this::print);
-        }
-        println();
-    }
-
-    /**
-     * Outputs the record separator.
-     *
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    public synchronized void println() throws IOException {
-        format.println(appendable);
-        newRecord = true;
-    }
-
-    /**
-     * Prints the given values as a single record of delimiter-separated values followed by the record separator.
-     *
-     * <p>
-     * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
-     * 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
-     */
-    @SuppressWarnings("resource")
-    public synchronized void printRecord(final Iterable<?> values) throws IOException {
-        IOStream.of(values).forEachOrdered(this::print);
-        endOfRecord();
-    }
-
-    /**
-     * Prints the given values as a single record of delimiter-separated values followed by the record separator.
-     *
-     * <p>
-     * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
-     * 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
-     */
-    public void printRecord(final Object... values) throws IOException {
-        printRecord(Arrays.asList(values));
-    }
-
-    /**
-     * Prints the given values as a single record of delimiter-separated values followed by the record separator.
-     *
-     * <p>
-     * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
-     * 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
-     */
-    @SuppressWarnings("resource") // caller closes.
-    public synchronized void printRecord(final Stream<?> values) throws IOException {
-        IOStream.adapt(values).forEachOrdered(this::print);
-        endOfRecord();
-    }
-
-    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...)}.
-     * </p>
-     *
-     * <p>
-     * Given the following data structure:
-     * </p>
-     *
-     * <pre>{@code
-     * List<String[]> data = new ArrayList<>();
-     * data.add(new String[]{ "A", "B", "C" });
-     * data.add(new String[]{ "1", "2", "3" });
-     * data.add(new String[]{ "A1", "B2", "C3" });
-     * }
-     * </pre>
-     *
-     * <p>
-     * Calling this method will print:
-     * </p>
-     *
-     * <pre>
-     * {@code
-     * A, B, C
-     * 1, 2, 3
-     * A1, B2, C3
-     * }
-     * </pre>
-     *
-     * @param values
-     *            the values to print.
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    @SuppressWarnings("resource")
-    public void printRecords(final Iterable<?> values) throws IOException {
-        IOStream.of(values).forEachOrdered(this::printRecordObject);
-    }
-
-    /**
-     * Prints all the objects in the given array handling nested collections/arrays as records.
-     *
-     * <p>
-     * If the given array only contains simple objects, this method will print a single record like
-     * {@link #printRecord(Object...)}. If the given collections contain nested collections or arrays, those nested
-     * elements will each be printed as records using {@link #printRecord(Object...)}.
-     * </p>
-     *
-     * <p>
-     * Given the following data structure:
-     * </p>
-     *
-     * <pre>{@code
-     * String[][] data = new String[3][]
-     * data[0] = String[]{ "A", "B", "C" };
-     * data[1] = new String[]{ "1", "2", "3" };
-     * data[2] = new String[]{ "A1", "B2", "C3" };
-     * }
-     * </pre>
-     *
-     * <p>
-     * Calling this method will print:
-     * </p>
-     *
-     * <pre>{@code
-     * A, B, C
-     * 1, 2, 3
-     * A1, B2, C3
-     * }
-     * </pre>
-     *
-     * @param values
-     *            the values to print.
-     * @throws IOException
-     *             If an I/O error occurs
-     */
-    public void printRecords(final Object... values) throws IOException {
-        printRecords(Arrays.asList(values));
-    }
-
-    /**
-     * Prints all the objects in the given JDBC result set.
-     *
-     * @param resultSet
-     *             The values to print.
-     * @throws IOException
-     *             If an I/O error occurs.
-     * @throws SQLException
-     *             Thrown when a database access error occurs.
-     */
-    public void printRecords(final ResultSet resultSet) throws SQLException, IOException {
-        final int columnCount = resultSet.getMetaData().getColumnCount();
-        while (resultSet.next()) {
-            for (int i = 1; i <= columnCount; i++) {
-                final Object object = resultSet.getObject(i);
-                if (object instanceof Clob) {
-                    try (Reader reader = ((Clob) object).getCharacterStream()) {
-                        print(reader);
-                    }
-                } else if (object instanceof Blob) {
-                    try (InputStream inputStream = ((Blob) object).getBinaryStream()) {
-                        print(inputStream);
-                    }
-                } else {
-                    print(object);
-                }
-            }
-            endOfRecord();
-        }
-    }
-
-    /**
-     * Prints all the objects with metadata in the given JDBC result set based on the header boolean.
-     *
-     * @param resultSet source of row data.
-     * @param printHeader whether to print headers.
-     * @throws IOException If an I/O error occurs
-     * @throws SQLException if a database access error occurs
-     * @since 1.9.0
-     */
-    public void printRecords(final ResultSet resultSet, final boolean printHeader) throws SQLException, IOException {
-        if (printHeader) {
-            printHeaders(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<String[]> data = new ArrayList<>();
-     * data.add(new String[]{ "A", "B", "C" });
-     * data.add(new String[]{ "1", "2", "3" });
-     * data.add(new String[]{ "A1", "B2", "C3" });
-     * Stream<String[]> stream = data.stream();
-     * }
-     * </pre>
-     *
-     * <p>
-     * Calling this method will print:
-     * </p>
-     *
-     * <pre>
-     * {@code
-     * A, B, C
-     * 1, 2, 3
-     * A1, B2, C3
-     * }
-     * </pre>
-     *
-     * @param values
-     *            the values to print.
-     * @throws IOException
-     *             If an I/O error occurs
-     * @since 1.10.0
-     */
-    @SuppressWarnings({ "resource" }) // Caller closes.
-    public void printRecords(final Stream<?> values) throws IOException {
-        IOStream.adapt(values).forEachOrdered(this::printRecordObject);
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.commons.csv;
+
+import static org.apache.commons.csv.Constants.CR;
+import static org.apache.commons.csv.Constants.LF;
+import static org.apache.commons.csv.Constants.SP;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import org.apache.commons.io.function.IOStream;
+
+/**
+ * Prints values in a {@link CSVFormat CSV format}.
+ *
+ * <p>Values can be appended to the output by calling the {@link #print(Object)} method.
+ * Values are printed according to {@link String#valueOf(Object)}.
+ * To complete a record the {@link #println()} method has to be called.
+ * Comments can be appended by calling {@link #printComment(String)}.
+ * However a comment will only be written to the output if the {@link CSVFormat} supports comments.
+ * </p>
+ *
+ * <p>The printer also supports appending a complete record at once by calling {@link #printRecord(Object...)}
+ * or {@link #printRecord(Iterable)}.
+ * Furthermore {@link #printRecords(Object...)}, {@link #printRecords(Iterable)} and {@link #printRecords(ResultSet)}
+ * methods can be used to print several records at once.
+ * </p>
+ *
+ * <p>Example:</p>
+ *
+ * <pre>
+ * try (CSVPrinter printer = new CSVPrinter(new FileWriter("csv.txt"), CSVFormat.EXCEL)) {
+ *     printer.printRecord("id", "userName", "firstName", "lastName", "birthday");
+ *     printer.printRecord(1, "john73", "John", "Doe", LocalDate.of(1973, 9, 15));
+ *     printer.println();
+ *     printer.printRecord(2, "mary", "Mary", "Meyer", LocalDate.of(1985, 3, 29));
+ * } catch (IOException ex) {
+ *     ex.printStackTrace();
+ * }
+ * </pre>
+ *
+ * <p>This code will write the following to csv.txt:</p>
+ * <pre>
+ * id,userName,firstName,lastName,birthday
+ * 1,john73,John,Doe,1973-09-15
+ *
+ * 2,mary,Mary,Meyer,1985-03-29
+ * </pre>
+ */
+public final class CSVPrinter implements Flushable, Closeable {
+
+    /** The place that the values get written. */
+    private final Appendable appendable;
+
+    private final CSVFormat format;
+
+    /** True if we just began a new record. */
+    private boolean newRecord = true;
+
+    private long recordCount;
+
+    /**
+     * Creates a printer that will print values to the given stream following the CSVFormat.
+     * <p>
+     * Currently, only a pure encapsulation format or a pure escaping format is supported. Hybrid formats (encapsulation
+     * and escaping with a different character) are not supported.
+     * </p>
+     *
+     * @param appendable
+     *            stream to which to print. Must not be null.
+     * @param format
+     *            the CSV format. Must not be null.
+     * @throws IOException
+     *             thrown if the optional header cannot be printed.
+     * @throws IllegalArgumentException
+     *             thrown if the parameters of the format are inconsistent or if either out or format are null.
+     */
+    public CSVPrinter(final Appendable appendable, final CSVFormat format) throws IOException {
+        Objects.requireNonNull(appendable, "appendable");
+        Objects.requireNonNull(format, "format");
+
+        this.appendable = appendable;
+        this.format = format.copy();
+        // TODO: Is it a good idea to do this here instead of on the first call to a print method?
+        // It seems a pain to have to track whether the header has already been printed or not.
+        final String[] headerComments = format.getHeaderComments();
+        if (headerComments != null) {
+            for (final String line : headerComments) {
+                printComment(line);
+            }
+        }
+        if (format.getHeader() != null && !format.getSkipHeaderRecord()) {
+            this.printRecord((Object[]) format.getHeader());
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        close(false);
+    }
+
+    /**
+     * Closes the underlying stream with an optional flush first.
+     * @param flush whether to flush before the actual close.
+     * @throws IOException
+     *             If an I/O error occurs
+     * @since 1.6
+     */
+    public void close(final boolean flush) throws IOException {
+        if (flush || format.getAutoFlush()) {
+            flush();
+        }
+        if (appendable instanceof Closeable) {
+            ((Closeable) appendable).close();
+        }
+    }
+
+    /**
+     * Outputs the record separator and increments the record count.
+     *
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    private synchronized void endOfRecord() throws IOException {
+        println();
+        recordCount++;
+    }
+
+    /**
+     * Flushes the underlying stream.
+     *
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    @Override
+    public void flush() throws IOException {
+        if (appendable instanceof Flushable) {
+            ((Flushable) appendable).flush();
+        }
+    }
+
+    /**
+     * Gets the target Appendable.
+     *
+     * @return the target Appendable.
+     */
+    public Appendable getOut() {
+        return this.appendable;
+    }
+
+    /**
+     * Gets the record count printed, this does not include comments or headers.
+     *
+     * @return the record count, this does not include comments or headers.
+     * @since 1.13.0
+     */
+    public long getRecordCount() {
+        return recordCount;
+    }
+
+    /**
+     * Prints the string as the next value on the line. The value will be escaped or encapsulated as needed.
+     *
+     * @param value
+     *            value to be output.
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    public synchronized void print(final Object value) throws IOException {
+        format.print(value, appendable, newRecord);
+        newRecord = false;
+    }
+
+    /**
+     * Prints a comment on a new line among the delimiter-separated values.
+     *
+     * <p>
+     * Comments will always begin on a new line and occupy at least one full line. The character specified to start
+     * comments and a space will be inserted at the beginning of each new line in the comment.
+     * </p>
+     *
+     * <p>
+     * If comments are disabled in the current CSV format this method does nothing.
+     * </p>
+     *
+     * <p>This method detects line breaks inside the comment string and inserts {@link CSVFormat#getRecordSeparator()}
+     * to start a new line of the comment. Note that this might produce unexpected results for formats that do not use
+     * line breaks as record separators.</p>
+     *
+     * @param comment
+     *            the comment to output
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    public synchronized void printComment(final String comment) throws IOException {
+        if (comment == null || !format.isCommentMarkerSet()) {
+            return;
+        }
+        if (!newRecord) {
+            println();
+        }
+        appendable.append(format.getCommentMarker().charValue()); // N.B. Explicit (un)boxing is intentional
+        appendable.append(SP);
+        for (int i = 0; i < comment.length(); i++) {
+            final char c = comment.charAt(i);
+            switch (c) {
+            case CR:
+                if (i + 1 < comment.length() && comment.charAt(i + 1) == LF) {
+                    i++;
+                }
+                // falls-through: break intentionally excluded.
+            case LF:
+                println();
+                appendable.append(format.getCommentMarker().charValue()); // N.B. Explicit (un)boxing is intentional
+                appendable.append(SP);
+                break;
+            default:
+                appendable.append(c);
+                break;
+            }
+        }
+        println();
+    }
+
+    /**
+     * Prints headers for a result set based on its metadata.
+     *
+     * @param resultSet The ResultSet to query for metadata.
+     * @throws IOException If an I/O error occurs.
+     * @throws SQLException If a database access error occurs or this method is called on a closed result set.
+     * @since 1.9.0
+     */
+    public synchronized void printHeaders(final ResultSet resultSet) throws IOException, SQLException {
+        try (IOStream<String> stream = IOStream.of(format.builder().setHeader(resultSet).get().getHeader())) {
+            stream.forEachOrdered(this::print);
+        }
+        println();
+    }
+
+    /**
+     * Outputs the record separator.
+     *
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    public synchronized void println() throws IOException {
+        format.println(appendable);
+        newRecord = true;
+    }
+
+    /**
+     * Prints the given values as a single record of delimiter-separated values followed by the record separator.
+     *
+     * <p>
+     * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
+     * 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
+     */
+    @SuppressWarnings("resource")
+    public synchronized void printRecord(final Iterable<?> values) throws IOException {
+        IOStream.of(values).forEachOrdered(this::print);
+        endOfRecord();
+    }
+
+    /**
+     * Prints the given values as a single record of delimiter-separated values followed by the record separator.
+     *
+     * <p>
+     * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
+     * 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
+     */
+    public void printRecord(final Object... values) throws IOException {
+        printRecord(Arrays.asList(values));
+    }
+
+    /**
+     * Prints the given values as a single record of delimiter-separated values followed by the record separator.
+     *
+     * <p>
+     * The values will be quoted if needed. Quotes and newLine characters will be escaped. This method adds the record
+     * 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
+     */
+    @SuppressWarnings("resource") // caller closes.
+    public synchronized void printRecord(final Stream<?> values) throws IOException {
+        IOStream.adapt(values).forEachOrdered(this::print);
+        endOfRecord();
+    }
+
+    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...)}.
+     * </p>
+     *
+     * <p>
+     * Given the following data structure:
+     * </p>
+     *
+     * <pre>{@code
+     * List<String[]> data = new ArrayList<>();
+     * data.add(new String[]{ "A", "B", "C" });
+     * data.add(new String[]{ "1", "2", "3" });
+     * data.add(new String[]{ "A1", "B2", "C3" });
+     * }
+     * </pre>
+     *
+     * <p>
+     * Calling this method will print:
+     * </p>
+     *
+     * <pre>
+     * {@code
+     * A, B, C
+     * 1, 2, 3
+     * A1, B2, C3
+     * }
+     * </pre>
+     *
+     * @param values
+     *            the values to print.
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    @SuppressWarnings("resource")
+    public void printRecords(final Iterable<?> values) throws IOException {
+        IOStream.of(values).forEachOrdered(this::printRecordObject);
+    }
+
+    /**
+     * Prints all the objects in the given array handling nested collections/arrays as records.
+     *
+     * <p>
+     * If the given array only contains simple objects, this method will print a single record like
+     * {@link #printRecord(Object...)}. If the given collections contain nested collections or arrays, those nested
+     * elements will each be printed as records using {@link #printRecord(Object...)}.
+     * </p>
+     *
+     * <p>
+     * Given the following data structure:
+     * </p>
+     *
+     * <pre>{@code
+     * String[][] data = new String[3][]
+     * data[0] = String[]{ "A", "B", "C" };
+     * data[1] = new String[]{ "1", "2", "3" };
+     * data[2] = new String[]{ "A1", "B2", "C3" };
+     * }
+     * </pre>
+     *
+     * <p>
+     * Calling this method will print:
+     * </p>
+     *
+     * <pre>{@code
+     * A, B, C
+     * 1, 2, 3
+     * A1, B2, C3
+     * }
+     * </pre>
+     *
+     * @param values
+     *            the values to print.
+     * @throws IOException
+     *             If an I/O error occurs
+     */
+    public void printRecords(final Object... values) throws IOException {
+        printRecords(Arrays.asList(values));
+    }
+
+    /**
+     * Prints all the objects in the given JDBC result set.
+     *
+     * @param resultSet
+     *             The values to print.
+     * @throws IOException
+     *             If an I/O error occurs.
+     * @throws SQLException
+     *             Thrown when a database access error occurs.
+     */
+    public void printRecords(final ResultSet resultSet) throws SQLException, IOException {
+        final int columnCount = resultSet.getMetaData().getColumnCount();
+        while (resultSet.next()) {
+            for (int i = 1; i <= columnCount; i++) {
+                final Object object = resultSet.getObject(i);
+                if (object instanceof Clob) {
+                    try (Reader reader = ((Clob) object).getCharacterStream()) {
+                        print(reader);
+                    }
+                } else if (object instanceof Blob) {
+                    try (InputStream inputStream = ((Blob) object).getBinaryStream()) {
+                        print(inputStream);
+                    }
+                } else {
+                    print(object);
+                }
+            }
+            endOfRecord();
+        }
+    }
+
+    /**
+     * Prints all the objects with metadata in the given JDBC result set based on the header boolean.
+     *
+     * @param resultSet source of row data.
+     * @param printHeader whether to print headers.
+     * @throws IOException If an I/O error occurs
+     * @throws SQLException if a database access error occurs
+     * @since 1.9.0
+     */
+    public void printRecords(final ResultSet resultSet, final boolean printHeader) throws SQLException, IOException {
+        if (printHeader) {
+            printHeaders(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<String[]> data = new ArrayList<>();
+     * data.add(new String[]{ "A", "B", "C" });
+     * data.add(new String[]{ "1", "2", "3" });
+     * data.add(new String[]{ "A1", "B2", "C3" });
+     * Stream<String[]> stream = data.stream();
+     * }
+     * </pre>
+     *
+     * <p>
+     * Calling this method will print:
+     * </p>
+     *
+     * <pre>
+     * {@code
+     * A, B, C
+     * 1, 2, 3
+     * A1, B2, C3
+     * }
+     * </pre>
+     *
+     * @param values
+     *            the values to print.
+     * @throws IOException
+     *             If an I/O error occurs
+     * @since 1.10.0
+     */
+    @SuppressWarnings({ "resource" }) // Caller closes.
+    public void printRecords(final Stream<?> values) throws IOException {
+        IOStream.adapt(values).forEachOrdered(this::printRecordObject);
+    }
+}
diff --git a/src/main/java/org/apache/commons/csv/Constants.java b/src/main/java/org/apache/commons/csv/Constants.java
index 5f8a5cf4..e8557846 100644
--- a/src/main/java/org/apache/commons/csv/Constants.java
+++ b/src/main/java/org/apache/commons/csv/Constants.java
@@ -1,90 +1,90 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.commons.csv;
-
-/**
- * Private constants to this package.
- */
-final class Constants {
-
-    static final char BACKSLASH = '\\';
-
-    static final char BACKSPACE = '\b';
-
-    static final String COMMA = ",";
-
-    /**
-     * Starts a comment, the remainder of the line is the comment.
-     */
-    static final char COMMENT = '#';
-
-    static final char CR = '\r';
-
-    /** RFC 4180 defines line breaks as CRLF */
-    static final String CRLF = "\r\n";
-
-    static final Character DOUBLE_QUOTE_CHAR = Character.valueOf('"');  // N.B. Explicit (un)boxing is intentional
-
-    static final String EMPTY = "";
-
-    static final String[] EMPTY_STRING_ARRAY = {};
-
-    static final char FF = '\f';
-
-    static final char LF = '\n';
-
-    /**
-     * Unicode line separator.
-     */
-    static final String LINE_SEPARATOR = "\u2028";
-
-    /**
-     * Unicode next line.
-     */
-    static final String NEXT_LINE = "\u0085";
-
-    /**
-     * Unicode paragraph separator.
-     */
-    static final String PARAGRAPH_SEPARATOR = "\u2029";
-
-    static final char PIPE = '|';
-
-    /** ASCII record separator */
-    static final char RS = 30;
-
-    static final char SP = ' ';
-
-    static final String SQL_NULL_STRING = "\\N";
-
-    static final char TAB = '\t';
-
-    /** Undefined state for the lookahead char */
-    static final int UNDEFINED = -2;
-
-    /** ASCII unit separator */
-    static final char US = 31;
-
-    /** No instances. */
-    private Constants() {
-        // noop
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.commons.csv;
+
+/**
+ * Private constants to this package.
+ */
+final class Constants {
+
+    static final char BACKSLASH = '\\';
+
+    static final char BACKSPACE = '\b';
+
+    static final String COMMA = ",";
+
+    /**
+     * Starts a comment, the remainder of the line is the comment.
+     */
+    static final char COMMENT = '#';
+
+    static final char CR = '\r';
+
+    /** RFC 4180 defines line breaks as CRLF */
+    static final String CRLF = "\r\n";
+
+    static final Character DOUBLE_QUOTE_CHAR = Character.valueOf('"');  // N.B. Explicit (un)boxing is intentional
+
+    static final String EMPTY = "";
+
+    static final String[] EMPTY_STRING_ARRAY = {};
+
+    static final char FF = '\f';
+
+    static final char LF = '\n';
+
+    /**
+     * Unicode line separator.
+     */
+    static final String LINE_SEPARATOR = "\u2028";
+
+    /**
+     * Unicode next line.
+     */
+    static final String NEXT_LINE = "\u0085";
+
+    /**
+     * Unicode paragraph separator.
+     */
+    static final String PARAGRAPH_SEPARATOR = "\u2029";
+
+    static final char PIPE = '|';
+
+    /** ASCII record separator */
+    static final char RS = 30;
+
+    static final char SP = ' ';
+
+    static final String SQL_NULL_STRING = "\\N";
+
+    static final char TAB = '\t';
+
+    /** Undefined state for the lookahead char */
+    static final int UNDEFINED = -2;
+
+    /** ASCII unit separator */
+    static final char US = 31;
+
+    /** No instances. */
+    private Constants() {
+        // noop
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java
index 50dfd0f8..9677d8ec 100644
--- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java
@@ -1,1533 +1,1533 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.commons.csv;
-
-import static org.apache.commons.csv.CSVFormat.RFC4180;
-import static org.apache.commons.csv.Constants.CR;
-import static org.apache.commons.csv.Constants.CRLF;
-import static org.apache.commons.csv.Constants.LF;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNotSame;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Objects;
-
-import org.apache.commons.csv.CSVFormat.Builder;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests {@link CSVFormat}.
- */
-public class CSVFormatTest {
-
-    public enum EmptyEnum {
-        // empty enum.
-    }
-
-    public enum Header {
-        Name, Email, Phone
-    }
-
-    private static void assertNotEquals(final Object right, final Object left) {
-        Assertions.assertNotEquals(right, left);
-        Assertions.assertNotEquals(left, right);
-    }
-
-    private static CSVFormat copy(final CSVFormat format) {
-        return format.builder().setDelimiter(format.getDelimiter()).get();
-    }
-
-    private void assertNotEquals(final String name, final String type, final Object left, final Object right) {
-        if (left.equals(right) || right.equals(left)) {
-            fail("Objects must not compare equal for " + name + "(" + type + ")");
-        }
-        if (left.hashCode() == right.hashCode()) {
-            fail("Hash code should not be equal for " + name + "(" + type + ")");
-        }
-    }
-
-    @Test
-    public void testBuildVsGet() {
-        final Builder builder = CSVFormat.DEFAULT.builder();
-        assertNotSame(builder.get(), builder.build());
-    }
-
-    @Test
-    public void testDelimiterEmptyStringThrowsException1() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter("").get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testDelimiterSameAsCommentStartThrowsException_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter('!').withCommentMarker('!'));
-    }
-
-    @Test
-    public void testDelimiterSameAsCommentStartThrowsException1() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter('!').setCommentMarker('!').get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testDelimiterSameAsEscapeThrowsException_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter('!').withEscape('!'));
-    }
-
-    @Test
-    public void testDelimiterSameAsEscapeThrowsException1() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter('!').setEscape('!').get());
-    }
-
-    @Test
-    public void testDelimiterSameAsRecordSeparatorThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat(CR));
-    }
-
-    @Test
-    public void testDuplicateHeaderElements() {
-        final String[] header = { "A", "A" };
-        final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader(header).get();
-        assertEquals(2, format.getHeader().length);
-        assertArrayEquals(header, format.getHeader());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testDuplicateHeaderElements_Deprecated() {
-        final String[] header = { "A", "A" };
-        final CSVFormat format = CSVFormat.DEFAULT.withHeader(header);
-        assertEquals(2, format.getHeader().length);
-        assertArrayEquals(header, format.getHeader());
-    }
-
-    @Test
-    public void testDuplicateHeaderElementsFalse() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setHeader("A", "A").get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testDuplicateHeaderElementsFalse_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(false).withHeader("A", "A"));
-    }
-
-    @Test
-    public void testDuplicateHeaderElementsTrue() {
-        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(true).setHeader("A", "A").get();
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testDuplicateHeaderElementsTrue_Deprecated() {
-        CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(true).withHeader("A", "A");
-    }
-
-    @Test
-    public void testDuplicateHeaderElementsTrueContainsEmpty1() {
-        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setHeader("A", "", "B", "").get();
-    }
-
-    @Test
-    public void testDuplicateHeaderElementsTrueContainsEmpty2() {
-        CSVFormat.DEFAULT.builder().setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).setHeader("A", "", "B", "").get();
-    }
-
-    @Test
-    public void testDuplicateHeaderElementsTrueContainsEmpty3() {
-        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setAllowMissingColumnNames(true).setHeader("A", "", "B", "").get();
-    }
-
-    @Test
-    public void testEquals() {
-        final CSVFormat right = CSVFormat.DEFAULT;
-        final CSVFormat left = copy(right);
-        Assertions.assertNotEquals(null, right);
-        Assertions.assertNotEquals("A String Instance", right);
-        assertEquals(right, right);
-        assertEquals(right, left);
-        assertEquals(left, right);
-        assertEquals(right.hashCode(), right.hashCode());
-        assertEquals(right.hashCode(), left.hashCode());
-    }
-
-    @Test
-    public void testEqualsCommentStart() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setCommentMarker('#').setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setCommentMarker('!').get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsCommentStart_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withCommentMarker('#').withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withCommentMarker('!');
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsDelimiter() {
-        final CSVFormat right = CSVFormat.newFormat('!');
-        final CSVFormat left = CSVFormat.newFormat('?');
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsEscape() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setCommentMarker('#').setEscape('+').setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setEscape('!').get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsEscape_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withCommentMarker('#').withEscape('+').withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withEscape('!');
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsHash() throws Exception {
-        final Method[] methods = CSVFormat.class.getDeclaredMethods();
-        for (final Method method : methods) {
-            if (Modifier.isPublic(method.getModifiers())) {
-                final String name = method.getName();
-                if (name.startsWith("with")) {
-                    for (final Class<?> cls : method.getParameterTypes()) {
-                        final String type = cls.getCanonicalName();
-                        switch (type) {
-                        case "boolean": {
-                            final Object defTrue = method.invoke(CSVFormat.DEFAULT, Boolean.TRUE);
-                            final Object defFalse = method.invoke(CSVFormat.DEFAULT, Boolean.FALSE);
-                            assertNotEquals(name, type, defTrue, defFalse);
-                            break;
-                        }
-                        case "char": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, 'a');
-                            final Object b = method.invoke(CSVFormat.DEFAULT, 'b');
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        case "java.lang.Character": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { null });
-                            final Object b = method.invoke(CSVFormat.DEFAULT, Character.valueOf('d'));
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        case "java.lang.String": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { null });
-                            final Object b = method.invoke(CSVFormat.DEFAULT, "e");
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        case "java.lang.String[]": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { new String[] { null, null } });
-                            final Object b = method.invoke(CSVFormat.DEFAULT, new Object[] { new String[] { "f", "g" } });
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        case "org.apache.commons.csv.QuoteMode": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, QuoteMode.MINIMAL);
-                            final Object b = method.invoke(CSVFormat.DEFAULT, QuoteMode.ALL);
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        case "org.apache.commons.csv.DuplicateHeaderMode": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, DuplicateHeaderMode.ALLOW_ALL);
-                            final Object b = method.invoke(CSVFormat.DEFAULT, DuplicateHeaderMode.DISALLOW);
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        case "java.lang.Object[]": {
-                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { new Object[] { null, null } });
-                            final Object b = method.invoke(CSVFormat.DEFAULT, new Object[] { new Object[] { new Object(), new Object() } });
-                            assertNotEquals(name, type, a, b);
-                            break;
-                        }
-                        default:
-                            if ("withHeader".equals(name)) { // covered above by String[]
-                                // ignored
-                            } else {
-                                fail("Unhandled method: " + name + "(" + type + ")");
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testEqualsHeader() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setHeader("One", "Two", "Three")
-                .setIgnoreEmptyLines(true).setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setHeader("Three", "Two", "One").get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsHeader_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withHeader("One", "Two", "Three")
-                .withIgnoreEmptyLines().withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withHeader("Three", "Two", "One");
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsIgnoreEmptyLines() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
-                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setIgnoreEmptyLines(false).get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsIgnoreEmptyLines_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withCommentMarker('#').withEscape('+').withIgnoreEmptyLines().withIgnoreSurroundingSpaces()
-                .withQuote('"').withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withIgnoreEmptyLines(false);
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsIgnoreSurroundingSpaces() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setCommentMarker('#').setEscape('+').setIgnoreSurroundingSpaces(true).setQuote('"')
-                .setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setIgnoreSurroundingSpaces(false).get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsIgnoreSurroundingSpaces_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withCommentMarker('#').withEscape('+').withIgnoreSurroundingSpaces().withQuote('"')
-                .withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withIgnoreSurroundingSpaces(false);
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsLeftNoQuoteRightQuote() {
-        final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).get();
-        final CSVFormat right = left.builder().setQuote('#').get();
-
-        assertNotEquals(left, right);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsLeftNoQuoteRightQuote_Deprecated() {
-        final CSVFormat left = CSVFormat.newFormat(',').withQuote(null);
-        final CSVFormat right = left.withQuote('#');
-
-        assertNotEquals(left, right);
-    }
-
-    @Test
-    public void testEqualsNoQuotes() {
-        final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).get();
-        final CSVFormat right = left.builder().setQuote(null).get();
-
-        assertEquals(left, right);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsNoQuotes_Deprecated() {
-        final CSVFormat left = CSVFormat.newFormat(',').withQuote(null);
-        final CSVFormat right = left.withQuote(null);
-
-        assertEquals(left, right);
-    }
-
-    @Test
-    public void testEqualsNullString() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
-                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).setNullString("null").get();
-        final CSVFormat left = right.builder().setNullString("---").get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsNullString_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withIgnoreEmptyLines()
-                .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL).withNullString("null");
-        final CSVFormat left = right.withNullString("---");
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsOne() {
-
-        final CSVFormat csvFormatOne = CSVFormat.INFORMIX_UNLOAD;
-        final CSVFormat csvFormatTwo = CSVFormat.MYSQL;
-
-        assertEquals('\\', (char) csvFormatOne.getEscapeCharacter());
-        assertEquals('\\', csvFormatOne.getEscapeChar());
-        assertNull(csvFormatOne.getQuoteMode());
-
-        assertTrue(csvFormatOne.getIgnoreEmptyLines());
-        assertFalse(csvFormatOne.getSkipHeaderRecord());
-
-        assertFalse(csvFormatOne.getIgnoreHeaderCase());
-        assertNull(csvFormatOne.getCommentMarker());
-
-        assertFalse(csvFormatOne.isCommentMarkerSet());
-        assertTrue(csvFormatOne.isQuoteCharacterSet());
-
-        assertEquals('|', csvFormatOne.getDelimiter());
-        assertFalse(csvFormatOne.getAllowMissingColumnNames());
-
-        assertTrue(csvFormatOne.isEscapeCharacterSet());
-        assertEquals("\n", csvFormatOne.getRecordSeparator());
-
-        assertEquals('\"', (char) csvFormatOne.getQuoteCharacter());
-        assertFalse(csvFormatOne.getTrailingDelimiter());
-
-        assertFalse(csvFormatOne.getTrim());
-        assertFalse(csvFormatOne.isNullStringSet());
-
-        assertNull(csvFormatOne.getNullString());
-        assertFalse(csvFormatOne.getIgnoreSurroundingSpaces());
-
-        assertTrue(csvFormatTwo.isEscapeCharacterSet());
-        assertNull(csvFormatTwo.getQuoteCharacter());
-
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-        assertEquals(QuoteMode.ALL_NON_NULL, csvFormatTwo.getQuoteMode());
-
-        assertEquals('\t', csvFormatTwo.getDelimiter());
-        assertArrayEquals(new char[] { '\t' }, csvFormatTwo.getDelimiterCharArray());
-        assertEquals("\t", csvFormatTwo.getDelimiterString());
-        assertEquals("\n", csvFormatTwo.getRecordSeparator());
-
-        assertFalse(csvFormatTwo.isQuoteCharacterSet());
-        assertTrue(csvFormatTwo.isNullStringSet());
-
-        assertEquals('\\', (char) csvFormatTwo.getEscapeCharacter());
-        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
-
-        assertFalse(csvFormatTwo.getTrim());
-        assertFalse(csvFormatTwo.getIgnoreEmptyLines());
-
-        assertEquals("\\N", csvFormatTwo.getNullString());
-        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-        assertFalse(csvFormatTwo.getSkipHeaderRecord());
-
-        assertNull(csvFormatTwo.getCommentMarker());
-        assertFalse(csvFormatTwo.isCommentMarkerSet());
-
-        assertNotSame(csvFormatTwo, csvFormatOne);
-        Assertions.assertNotEquals(csvFormatTwo, csvFormatOne);
-
-        assertEquals('\\', (char) csvFormatOne.getEscapeCharacter());
-        assertNull(csvFormatOne.getQuoteMode());
-
-        assertTrue(csvFormatOne.getIgnoreEmptyLines());
-        assertFalse(csvFormatOne.getSkipHeaderRecord());
-
-        assertFalse(csvFormatOne.getIgnoreHeaderCase());
-        assertNull(csvFormatOne.getCommentMarker());
-
-        assertFalse(csvFormatOne.isCommentMarkerSet());
-        assertTrue(csvFormatOne.isQuoteCharacterSet());
-
-        assertEquals('|', csvFormatOne.getDelimiter());
-        assertFalse(csvFormatOne.getAllowMissingColumnNames());
-
-        assertTrue(csvFormatOne.isEscapeCharacterSet());
-        assertEquals("\n", csvFormatOne.getRecordSeparator());
-
-        assertEquals('\"', (char) csvFormatOne.getQuoteCharacter());
-        assertFalse(csvFormatOne.getTrailingDelimiter());
-
-        assertFalse(csvFormatOne.getTrim());
-        assertFalse(csvFormatOne.isNullStringSet());
-
-        assertNull(csvFormatOne.getNullString());
-        assertFalse(csvFormatOne.getIgnoreSurroundingSpaces());
-
-        assertTrue(csvFormatTwo.isEscapeCharacterSet());
-        assertNull(csvFormatTwo.getQuoteCharacter());
-
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-        assertEquals(QuoteMode.ALL_NON_NULL, csvFormatTwo.getQuoteMode());
-
-        assertEquals('\t', csvFormatTwo.getDelimiter());
-        assertEquals("\n", csvFormatTwo.getRecordSeparator());
-
-        assertFalse(csvFormatTwo.isQuoteCharacterSet());
-        assertTrue(csvFormatTwo.isNullStringSet());
-
-        assertEquals('\\', (char) csvFormatTwo.getEscapeCharacter());
-        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
-
-        assertFalse(csvFormatTwo.getTrim());
-        assertFalse(csvFormatTwo.getIgnoreEmptyLines());
-
-        assertEquals("\\N", csvFormatTwo.getNullString());
-        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-        assertFalse(csvFormatTwo.getSkipHeaderRecord());
-
-        assertNull(csvFormatTwo.getCommentMarker());
-        assertFalse(csvFormatTwo.isCommentMarkerSet());
-
-        assertNotSame(csvFormatOne, csvFormatTwo);
-        assertNotSame(csvFormatTwo, csvFormatOne);
-
-        Assertions.assertNotEquals(csvFormatOne, csvFormatTwo);
-        Assertions.assertNotEquals(csvFormatTwo, csvFormatOne);
-
-        Assertions.assertNotEquals(csvFormatTwo, csvFormatOne);
-
-    }
-
-    @Test
-    public void testEqualsQuoteChar() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').get();
-        final CSVFormat left = right.builder().setQuote('!').get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsQuoteChar_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"');
-        final CSVFormat left = right.withQuote('!');
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsQuotePolicy() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setQuoteMode(QuoteMode.MINIMAL).get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsQuotePolicy_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withQuoteMode(QuoteMode.MINIMAL);
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsRecordSeparator() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
-                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get();
-        final CSVFormat left = right.builder().setRecordSeparator(LF).get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsRecordSeparator_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withIgnoreEmptyLines()
-                .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL);
-        final CSVFormat left = right.withRecordSeparator(LF);
-
-        assertNotEquals(right, left);
-    }
-
-    public void testEqualsSkipHeaderRecord() {
-        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
-                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).setNullString("null").setSkipHeaderRecord(true).get();
-        final CSVFormat left = right.builder().setSkipHeaderRecord(false).get();
-
-        assertNotEquals(right, left);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEqualsSkipHeaderRecord_Deprecated() {
-        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withIgnoreEmptyLines()
-                .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL).withNullString("null").withSkipHeaderRecord();
-        final CSVFormat left = right.withSkipHeaderRecord(false);
-
-        assertNotEquals(right, left);
-    }
-
-    @Test
-    public void testEqualsWithNull() {
-
-        final CSVFormat csvFormat = CSVFormat.POSTGRESQL_TEXT;
-
-        assertEquals('\\', (char) csvFormat.getEscapeCharacter());
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormat.getTrailingDelimiter());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isQuoteCharacterSet());
-        assertEquals("\\N", csvFormat.getNullString());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertTrue(csvFormat.isEscapeCharacterSet());
-
-        assertFalse(csvFormat.isCommentMarkerSet());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertEquals(QuoteMode.ALL_NON_NULL, csvFormat.getQuoteMode());
-
-        assertEquals('\t', csvFormat.getDelimiter());
-        assertFalse(csvFormat.getSkipHeaderRecord());
-
-        assertEquals("\n", csvFormat.getRecordSeparator());
-        assertFalse(csvFormat.getIgnoreEmptyLines());
-
-        assertNull(csvFormat.getQuoteCharacter());
-        assertTrue(csvFormat.isNullStringSet());
-
-        assertEquals('\\', (char) csvFormat.getEscapeCharacter());
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormat.getTrailingDelimiter());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isQuoteCharacterSet());
-        assertEquals("\\N", csvFormat.getNullString());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertTrue(csvFormat.isEscapeCharacterSet());
-
-        assertFalse(csvFormat.isCommentMarkerSet());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertEquals(QuoteMode.ALL_NON_NULL, csvFormat.getQuoteMode());
-
-        assertEquals('\t', csvFormat.getDelimiter());
-        assertFalse(csvFormat.getSkipHeaderRecord());
-
-        assertEquals("\n", csvFormat.getRecordSeparator());
-        assertFalse(csvFormat.getIgnoreEmptyLines());
-
-        assertNull(csvFormat.getQuoteCharacter());
-        assertTrue(csvFormat.isNullStringSet());
-
-        Assertions.assertNotEquals(null, csvFormat);
-
-    }
-
-    @Test
-    public void testEscapeSameAsCommentStartThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setEscape('!').setCommentMarker('!').get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEscapeSameAsCommentStartThrowsException_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape('!').withCommentMarker('!'));
-    }
-
-    @Test
-    public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType() {
-        // Cannot assume that callers won't use different Character objects
-        assertThrows(IllegalArgumentException.class,
-                () -> CSVFormat.DEFAULT.builder().setEscape(Character.valueOf('!')).setCommentMarker(Character.valueOf('!')).get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType_Deprecated() {
-        // Cannot assume that callers won't use different Character objects
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape(Character.valueOf('!')).withCommentMarker(Character.valueOf('!')));
-    }
-
-    @Test
-    public void testFormat() {
-        final CSVFormat format = CSVFormat.DEFAULT;
-
-        assertEquals("", format.format());
-        assertEquals("a,b,c", format.format("a", "b", "c"));
-        assertEquals("\"x,y\",z", format.format("x,y", "z"));
-    }
-
-    @Test // I assume this to be a defect.
-    public void testFormatThrowsNullPointerException() {
-
-        final CSVFormat csvFormat = CSVFormat.MYSQL;
-
-        final NullPointerException e = assertThrows(NullPointerException.class, () -> csvFormat.format((Object[]) null));
-        assertEquals(Objects.class.getName(), e.getStackTrace()[0].getClassName());
-    }
-
-    @Test
-    public void testFormatToString() {
-        // @formatter:off
-        final CSVFormat format = CSVFormat.RFC4180
-                .withEscape('?')
-                .withDelimiter(',')
-                .withQuoteMode(QuoteMode.MINIMAL)
-                .withRecordSeparator(CRLF)
-                .withQuote('"')
-                .withNullString("")
-                .withIgnoreHeaderCase(true)
-                .withHeaderComments("This is HeaderComments")
-                .withHeader("col1", "col2", "col3");
-        // @formatter:on
-        assertEquals(
-                "Delimiter=<,> Escape=<?> QuoteChar=<\"> QuoteMode=<MINIMAL> NullString=<> RecordSeparator=<" + CRLF +
-                        "> IgnoreHeaderCase:ignored SkipHeaderRecord:false HeaderComments:[This is HeaderComments] Header:[col1, col2, col3]",
-                format.toString());
-    }
-
-    @Test
-    public void testGetAllowDuplicateHeaderNames() {
-        final Builder builder = CSVFormat.DEFAULT.builder();
-        assertTrue(builder.get().getAllowDuplicateHeaderNames());
-        assertTrue(builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL).get().getAllowDuplicateHeaderNames());
-        assertFalse(builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).get().getAllowDuplicateHeaderNames());
-        assertFalse(builder.setDuplicateHeaderMode(DuplicateHeaderMode.DISALLOW).get().getAllowDuplicateHeaderNames());
-    }
-
-    @Test
-    public void testGetDuplicateHeaderMode() {
-        final Builder builder = CSVFormat.DEFAULT.builder();
-
-        assertEquals(DuplicateHeaderMode.ALLOW_ALL, builder.get().getDuplicateHeaderMode());
-        assertEquals(DuplicateHeaderMode.ALLOW_ALL, builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL).get().getDuplicateHeaderMode());
-        assertEquals(DuplicateHeaderMode.ALLOW_EMPTY, builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).get().getDuplicateHeaderMode());
-        assertEquals(DuplicateHeaderMode.DISALLOW, builder.setDuplicateHeaderMode(DuplicateHeaderMode.DISALLOW).get().getDuplicateHeaderMode());
-    }
-
-    @Test
-    public void testGetHeader() {
-        final String[] header = { "one", "two", "three" };
-        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(header);
-        // getHeader() makes a copy of the header array.
-        final String[] headerCopy = formatWithHeader.getHeader();
-        headerCopy[0] = "A";
-        headerCopy[1] = "B";
-        headerCopy[2] = "C";
-        assertFalse(Arrays.equals(formatWithHeader.getHeader(), headerCopy));
-        assertNotSame(formatWithHeader.getHeader(), headerCopy);
-    }
-
-    @Test
-    public void testHashCodeAndWithIgnoreHeaderCase() {
-
-        final CSVFormat csvFormat = CSVFormat.INFORMIX_UNLOAD_CSV;
-        final CSVFormat csvFormatTwo = csvFormat.withIgnoreHeaderCase();
-        csvFormatTwo.hashCode();
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertTrue(csvFormatTwo.getIgnoreHeaderCase()); // now different
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-
-        Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-
-        assertFalse(csvFormatTwo.getTrim());
-
-    }
-
-    @Test
-    public void testJiraCsv236() {
-        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(true).setHeader("CC", "VV", "VV").get();
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testJiraCsv236__Deprecated() {
-        CSVFormat.DEFAULT.withAllowDuplicateHeaderNames().withHeader("CC", "VV", "VV");
-    }
-
-    @Test
-    public void testNewFormat() {
-
-        final CSVFormat csvFormat = CSVFormat.newFormat('X');
-
-        assertFalse(csvFormat.getSkipHeaderRecord());
-        assertFalse(csvFormat.isEscapeCharacterSet());
-
-        assertNull(csvFormat.getRecordSeparator());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertNull(csvFormat.getCommentMarker());
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertNull(csvFormat.getEscapeCharacter());
-
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-        assertFalse(csvFormat.getTrailingDelimiter());
-
-        assertEquals('X', csvFormat.getDelimiter());
-        assertNull(csvFormat.getNullString());
-
-        assertFalse(csvFormat.isQuoteCharacterSet());
-        assertFalse(csvFormat.isCommentMarkerSet());
-
-        assertNull(csvFormat.getQuoteCharacter());
-        assertFalse(csvFormat.getIgnoreEmptyLines());
-
-        assertFalse(csvFormat.getSkipHeaderRecord());
-        assertFalse(csvFormat.isEscapeCharacterSet());
-
-        assertNull(csvFormat.getRecordSeparator());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertNull(csvFormat.getCommentMarker());
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertNull(csvFormat.getEscapeCharacter());
-
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-        assertFalse(csvFormat.getTrailingDelimiter());
-
-        assertEquals('X', csvFormat.getDelimiter());
-        assertNull(csvFormat.getNullString());
-
-        assertFalse(csvFormat.isQuoteCharacterSet());
-        assertFalse(csvFormat.isCommentMarkerSet());
-
-        assertNull(csvFormat.getQuoteCharacter());
-        assertFalse(csvFormat.getIgnoreEmptyLines());
-
-    }
-
-    @Test
-    public void testNullRecordSeparatorCsv106() {
-        final CSVFormat format = CSVFormat.newFormat(';').builder().setSkipHeaderRecord(true).setHeader("H1", "H2").get();
-        final String formatStr = format.format("A", "B");
-        assertNotNull(formatStr);
-        assertFalse(formatStr.endsWith("null"));
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testNullRecordSeparatorCsv106__Deprecated() {
-        final CSVFormat format = CSVFormat.newFormat(';').withSkipHeaderRecord().withHeader("H1", "H2");
-        final String formatStr = format.format("A", "B");
-        assertNotNull(formatStr);
-        assertFalse(formatStr.endsWith("null"));
-    }
-
-    @Test
-    public void testPrintRecord() throws IOException {
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180;
-        format.printRecord(out, "a", "b", "c");
-        assertEquals("a,b,c" + format.getRecordSeparator(), out.toString());
-    }
-
-    @Test
-    public void testPrintRecordEmpty() throws IOException {
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180;
-        format.printRecord(out);
-        assertEquals(format.getRecordSeparator(), out.toString());
-    }
-
-    @Test
-    public void testPrintWithEscapesEndWithCRLF() throws IOException {
-        final Reader in = new StringReader("x,y,x\r\na,?b,c\r\n");
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180.withEscape('?').withDelimiter(',').withQuote(null).withRecordSeparator(CRLF);
-        format.print(in, out, true);
-        assertEquals("x?,y?,x?r?na?,??b?,c?r?n", out.toString());
-    }
-
-    @Test
-    public void testPrintWithEscapesEndWithoutCRLF() throws IOException {
-        final Reader in = new StringReader("x,y,x");
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180.withEscape('?').withDelimiter(',').withQuote(null).withRecordSeparator(CRLF);
-        format.print(in, out, true);
-        assertEquals("x?,y?,x", out.toString());
-    }
-
-    @Test
-    public void testPrintWithoutQuotes() throws IOException {
-        final Reader in = new StringReader("");
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180.withDelimiter(',').withQuote('"').withEscape('?').withQuoteMode(QuoteMode.NON_NUMERIC);
-        format.print(in, out, true);
-        assertEquals("\"\"", out.toString());
-    }
-
-    @Test
-    public void testPrintWithQuoteModeIsNONE() throws IOException {
-        final Reader in = new StringReader("a,b,c");
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180.withDelimiter(',').withQuote('"').withEscape('?').withQuoteMode(QuoteMode.NONE);
-        format.print(in, out, true);
-        assertEquals("a?,b?,c", out.toString());
-    }
-
-    @Test
-    public void testPrintWithQuotes() throws IOException {
-        final Reader in = new StringReader("\"a,b,c\r\nx,y,z");
-        final Appendable out = new StringBuilder();
-        final CSVFormat format = CSVFormat.RFC4180.withDelimiter(',').withQuote('"').withEscape('?').withQuoteMode(QuoteMode.NON_NUMERIC);
-        format.print(in, out, true);
-        assertEquals("\"\"\"a,b,c\r\nx,y,z\"", out.toString());
-    }
-
-    @Test
-    public void testQuoteCharSameAsCommentStartThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setCommentMarker('!').get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testQuoteCharSameAsCommentStartThrowsException_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withCommentMarker('!'));
-    }
-
-    @Test
-    public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType() {
-        // Cannot assume that callers won't use different Character objects
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote(Character.valueOf('!')).setCommentMarker('!').get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType_Deprecated() {
-        // Cannot assume that callers won't use different Character objects
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote(Character.valueOf('!')).withCommentMarker('!'));
-    }
-
-    @Test
-    public void testQuoteCharSameAsDelimiterThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setDelimiter('!').get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testQuoteCharSameAsDelimiterThrowsException_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withDelimiter('!'));
-    }
-
-    @Test
-    public void testQuoteModeNoneShouldReturnMeaningfulExceptionMessage() {
-        final Exception exception = assertThrows(IllegalArgumentException.class, () ->
-        // @formatter:off
-            CSVFormat.DEFAULT.builder()
-                    .setHeader("Col1", "Col2", "Col3", "Col4")
-                    .setQuoteMode(QuoteMode.NONE)
-                    .get()
-        // @formatter:on
-        );
-        final String actualMessage = exception.getMessage();
-        final String expectedMessage = "Quote mode set to NONE but no escape character is set";
-        assertEquals(expectedMessage, actualMessage);
-    }
-
-    @Test
-    public void testQuotePolicyNoneWithoutEscapeThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat('!').builder().setQuoteMode(QuoteMode.NONE).get());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Test
-    public void testQuotePolicyNoneWithoutEscapeThrowsException_Deprecated() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat('!').withQuoteMode(QuoteMode.NONE));
-    }
-
-    @Test
-    public void testRFC4180() {
-        assertNull(RFC4180.getCommentMarker());
-        assertEquals(',', RFC4180.getDelimiter());
-        assertNull(RFC4180.getEscapeCharacter());
-        assertFalse(RFC4180.getIgnoreEmptyLines());
-        assertEquals(Character.valueOf('"'), RFC4180.getQuoteCharacter());
-        assertNull(RFC4180.getQuoteMode());
-        assertEquals("\r\n", RFC4180.getRecordSeparator());
-    }
-
-    @SuppressWarnings("boxing") // no need to worry about boxing here
-    @Test
-    public void testSerialization() throws Exception {
-        final ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-        try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
-            oos.writeObject(CSVFormat.DEFAULT);
-            oos.flush();
-        }
-
-        final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
-        final CSVFormat format = (CSVFormat) in.readObject();
-
-        assertNotNull(format);
-        assertEquals(CSVFormat.DEFAULT.getDelimiter(), format.getDelimiter(), "delimiter");
-        assertEquals(CSVFormat.DEFAULT.getQuoteCharacter(), format.getQuoteCharacter(), "encapsulator");
-        assertEquals(CSVFormat.DEFAULT.getCommentMarker(), format.getCommentMarker(), "comment start");
-        assertEquals(CSVFormat.DEFAULT.getRecordSeparator(), format.getRecordSeparator(), "record separator");
-        assertEquals(CSVFormat.DEFAULT.getEscapeCharacter(), format.getEscapeCharacter(), "escape");
-        assertEquals(CSVFormat.DEFAULT.getIgnoreSurroundingSpaces(), format.getIgnoreSurroundingSpaces(), "trim");
-        assertEquals(CSVFormat.DEFAULT.getIgnoreEmptyLines(), format.getIgnoreEmptyLines(), "empty lines");
-    }
-
-    @Test
-    public void testToString() {
-
-        final String string = CSVFormat.INFORMIX_UNLOAD.toString();
-
-        assertEquals("Delimiter=<|> Escape=<\\> QuoteChar=<\"> RecordSeparator=<\n> EmptyLines:ignored SkipHeaderRecord:false", string);
-
-    }
-
-    @Test
-    public void testToStringAndWithCommentMarkerTakingCharacter() {
-
-        final CSVFormat.Predefined csvFormatPredefined = CSVFormat.Predefined.Default;
-        final CSVFormat csvFormat = csvFormatPredefined.getFormat();
-
-        assertNull(csvFormat.getEscapeCharacter());
-        assertTrue(csvFormat.isQuoteCharacterSet());
-
-        assertFalse(csvFormat.getTrim());
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormat.getTrailingDelimiter());
-        assertEquals(',', csvFormat.getDelimiter());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertEquals("\r\n", csvFormat.getRecordSeparator());
-
-        assertFalse(csvFormat.isCommentMarkerSet());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-
-        assertFalse(csvFormat.isEscapeCharacterSet());
-        assertFalse(csvFormat.getSkipHeaderRecord());
-
-        assertNull(csvFormat.getNullString());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertTrue(csvFormat.getIgnoreEmptyLines());
-        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
-
-        final Character character = Character.valueOf('n');
-
-        final CSVFormat csvFormatTwo = csvFormat.withCommentMarker(character);
-
-        assertNull(csvFormat.getEscapeCharacter());
-        assertTrue(csvFormat.isQuoteCharacterSet());
-
-        assertFalse(csvFormat.getTrim());
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormat.getTrailingDelimiter());
-        assertEquals(',', csvFormat.getDelimiter());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertEquals("\r\n", csvFormat.getRecordSeparator());
-
-        assertFalse(csvFormat.isCommentMarkerSet());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-
-        assertFalse(csvFormat.isEscapeCharacterSet());
-        assertFalse(csvFormat.getSkipHeaderRecord());
-
-        assertNull(csvFormat.getNullString());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertTrue(csvFormat.getIgnoreEmptyLines());
-        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
-
-        assertFalse(csvFormatTwo.isNullStringSet());
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-
-        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
-        assertNull(csvFormatTwo.getNullString());
-
-        assertEquals(',', csvFormatTwo.getDelimiter());
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-
-        assertTrue(csvFormatTwo.isCommentMarkerSet());
-        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
-
-        assertFalse(csvFormatTwo.getTrim());
-        assertNull(csvFormatTwo.getEscapeCharacter());
-
-        assertTrue(csvFormatTwo.isQuoteCharacterSet());
-        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
-
-        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
-        assertNull(csvFormatTwo.getQuoteMode());
-
-        assertEquals('n', (char) csvFormatTwo.getCommentMarker());
-        assertFalse(csvFormatTwo.getSkipHeaderRecord());
-
-        assertFalse(csvFormatTwo.isEscapeCharacterSet());
-        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
-
-        assertNotSame(csvFormat, csvFormatTwo);
-        assertNotSame(csvFormatTwo, csvFormat);
-
-        Assertions.assertNotEquals(csvFormatTwo, csvFormat);
-
-        assertNull(csvFormat.getEscapeCharacter());
-        assertTrue(csvFormat.isQuoteCharacterSet());
-
-        assertFalse(csvFormat.getTrim());
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-
-        assertFalse(csvFormat.getTrailingDelimiter());
-        assertEquals(',', csvFormat.getDelimiter());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertEquals("\r\n", csvFormat.getRecordSeparator());
-
-        assertFalse(csvFormat.isCommentMarkerSet());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-
-        assertFalse(csvFormat.isEscapeCharacterSet());
-        assertFalse(csvFormat.getSkipHeaderRecord());
-
-        assertNull(csvFormat.getNullString());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertTrue(csvFormat.getIgnoreEmptyLines());
-        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
-
-        assertFalse(csvFormatTwo.isNullStringSet());
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-
-        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
-        assertNull(csvFormatTwo.getNullString());
-
-        assertEquals(',', csvFormatTwo.getDelimiter());
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-
-        assertTrue(csvFormatTwo.isCommentMarkerSet());
-        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
-
-        assertFalse(csvFormatTwo.getTrim());
-        assertNull(csvFormatTwo.getEscapeCharacter());
-
-        assertTrue(csvFormatTwo.isQuoteCharacterSet());
-        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
-
-        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
-        assertNull(csvFormatTwo.getQuoteMode());
-
-        assertEquals('n', (char) csvFormatTwo.getCommentMarker());
-        assertFalse(csvFormatTwo.getSkipHeaderRecord());
-
-        assertFalse(csvFormatTwo.isEscapeCharacterSet());
-        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
-
-        assertNotSame(csvFormat, csvFormatTwo);
-        assertNotSame(csvFormatTwo, csvFormat);
-
-        Assertions.assertNotEquals(csvFormat, csvFormatTwo);
-
-        Assertions.assertNotEquals(csvFormatTwo, csvFormat);
-        assertEquals("Delimiter=<,> QuoteChar=<\"> CommentStart=<n> " + "RecordSeparator=<\r\n> EmptyLines:ignored SkipHeaderRecord:false",
-                csvFormatTwo.toString());
-
-    }
-
-    @Test
-    public void testTrim() throws IOException {
-        final CSVFormat formatWithTrim = CSVFormat.DEFAULT.withDelimiter(',').withTrim().withQuote(null).withRecordSeparator(CRLF);
-
-        CharSequence in = "a,b,c";
-        final StringBuilder out = new StringBuilder();
-        formatWithTrim.print(in, out, true);
-        assertEquals("a,b,c", out.toString());
-
-        in = new StringBuilder(" x,y,z");
-        out.setLength(0);
-        formatWithTrim.print(in, out, true);
-        assertEquals("x,y,z", out.toString());
-
-        in = new StringBuilder("");
-        out.setLength(0);
-        formatWithTrim.print(in, out, true);
-        assertEquals("", out.toString());
-
-        in = new StringBuilder("header\r\n");
-        out.setLength(0);
-        formatWithTrim.print(in, out, true);
-        assertEquals("header", out.toString());
-    }
-
-    @Test
-    public void testWithCommentStart() {
-        final CSVFormat formatWithCommentStart = CSVFormat.DEFAULT.withCommentMarker('#');
-        assertEquals(Character.valueOf('#'), formatWithCommentStart.getCommentMarker());
-    }
-
-    @Test
-    public void testWithCommentStartCRThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withCommentMarker(CR));
-    }
-
-    @Test
-    public void testWithDelimiter() {
-        final CSVFormat formatWithDelimiter = CSVFormat.DEFAULT.withDelimiter('!');
-        assertEquals('!', formatWithDelimiter.getDelimiter());
-    }
-
-    @Test
-    public void testWithDelimiterLFThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter(LF));
-    }
-
-    @Test
-    public void testWithEmptyDuplicates() {
-        final CSVFormat formatWithEmptyDuplicates = CSVFormat.DEFAULT.builder().setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).get();
-
-        assertEquals(DuplicateHeaderMode.ALLOW_EMPTY, formatWithEmptyDuplicates.getDuplicateHeaderMode());
-        assertFalse(formatWithEmptyDuplicates.getAllowDuplicateHeaderNames());
-    }
-
-    @Test
-    public void testWithEmptyEnum() {
-        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(EmptyEnum.class);
-        assertEquals(0, formatWithHeader.getHeader().length);
-    }
-
-    @Test
-    public void testWithEscape() {
-        final CSVFormat formatWithEscape = CSVFormat.DEFAULT.withEscape('&');
-        assertEquals(Character.valueOf('&'), formatWithEscape.getEscapeCharacter());
-    }
-
-    @Test
-    public void testWithEscapeCRThrowsExceptions() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape(CR));
-    }
-
-    @Test
-    public void testWithFirstRecordAsHeader() {
-        final CSVFormat formatWithFirstRecordAsHeader = CSVFormat.DEFAULT.withFirstRecordAsHeader();
-        assertTrue(formatWithFirstRecordAsHeader.getSkipHeaderRecord());
-        assertEquals(0, formatWithFirstRecordAsHeader.getHeader().length);
-    }
-
-    @Test
-    public void testWithHeader() {
-        final String[] header = { "one", "two", "three" };
-        // withHeader() makes a copy of the header array.
-        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(header);
-        assertArrayEquals(header, formatWithHeader.getHeader());
-        assertNotSame(header, formatWithHeader.getHeader());
-    }
-
-    @Test
-    public void testWithHeaderComments() {
-
-        final CSVFormat csvFormat = CSVFormat.DEFAULT;
-
-        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
-        assertFalse(csvFormat.isCommentMarkerSet());
-
-        assertFalse(csvFormat.isEscapeCharacterSet());
-        assertTrue(csvFormat.isQuoteCharacterSet());
-
-        assertFalse(csvFormat.getSkipHeaderRecord());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertEquals(',', csvFormat.getDelimiter());
-        assertTrue(csvFormat.getIgnoreEmptyLines());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertEquals("\r\n", csvFormat.getRecordSeparator());
-        assertFalse(csvFormat.getTrailingDelimiter());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertNull(csvFormat.getNullString());
-
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-        assertNull(csvFormat.getEscapeCharacter());
-
-        final Object[] objectArray = new Object[8];
-        final CSVFormat csvFormatTwo = csvFormat.withHeaderComments(objectArray);
-
-        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
-        assertFalse(csvFormat.isCommentMarkerSet());
-
-        assertFalse(csvFormat.isEscapeCharacterSet());
-        assertTrue(csvFormat.isQuoteCharacterSet());
-
-        assertFalse(csvFormat.getSkipHeaderRecord());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertEquals(',', csvFormat.getDelimiter());
-        assertTrue(csvFormat.getIgnoreEmptyLines());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertEquals("\r\n", csvFormat.getRecordSeparator());
-        assertFalse(csvFormat.getTrailingDelimiter());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertNull(csvFormat.getNullString());
-
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-        assertNull(csvFormat.getEscapeCharacter());
-
-        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
-        assertNull(csvFormatTwo.getQuoteMode());
-
-        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
-        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
-
-        assertNull(csvFormatTwo.getEscapeCharacter());
-        assertFalse(csvFormatTwo.getTrim());
-
-        assertFalse(csvFormatTwo.isEscapeCharacterSet());
-        assertTrue(csvFormatTwo.isQuoteCharacterSet());
-
-        assertFalse(csvFormatTwo.getSkipHeaderRecord());
-        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
-
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-        assertNull(csvFormatTwo.getNullString());
-
-        assertFalse(csvFormatTwo.isNullStringSet());
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-
-        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
-        assertEquals(',', csvFormatTwo.getDelimiter());
-
-        assertNull(csvFormatTwo.getCommentMarker());
-        assertFalse(csvFormatTwo.isCommentMarkerSet());
-
-        assertNotSame(csvFormat, csvFormatTwo);
-        assertNotSame(csvFormatTwo, csvFormat);
-
-        Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal
-
-        final String string = csvFormatTwo.format(objectArray);
-
-        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
-        assertFalse(csvFormat.isCommentMarkerSet());
-
-        assertFalse(csvFormat.isEscapeCharacterSet());
-        assertTrue(csvFormat.isQuoteCharacterSet());
-
-        assertFalse(csvFormat.getSkipHeaderRecord());
-        assertNull(csvFormat.getQuoteMode());
-
-        assertEquals(',', csvFormat.getDelimiter());
-        assertTrue(csvFormat.getIgnoreEmptyLines());
-
-        assertFalse(csvFormat.getIgnoreHeaderCase());
-        assertNull(csvFormat.getCommentMarker());
-
-        assertEquals("\r\n", csvFormat.getRecordSeparator());
-        assertFalse(csvFormat.getTrailingDelimiter());
-
-        assertFalse(csvFormat.getAllowMissingColumnNames());
-        assertFalse(csvFormat.getTrim());
-
-        assertFalse(csvFormat.isNullStringSet());
-        assertNull(csvFormat.getNullString());
-
-        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
-        assertNull(csvFormat.getEscapeCharacter());
-
-        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
-        assertNull(csvFormatTwo.getQuoteMode());
-
-        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
-        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
-
-        assertNull(csvFormatTwo.getEscapeCharacter());
-        assertFalse(csvFormatTwo.getTrim());
-
-        assertFalse(csvFormatTwo.isEscapeCharacterSet());
-        assertTrue(csvFormatTwo.isQuoteCharacterSet());
-
-        assertFalse(csvFormatTwo.getSkipHeaderRecord());
-        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
-
-        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
-        assertNull(csvFormatTwo.getNullString());
-
-        assertFalse(csvFormatTwo.isNullStringSet());
-        assertFalse(csvFormatTwo.getTrailingDelimiter());
-
-        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
-        assertEquals(',', csvFormatTwo.getDelimiter());
-
-        assertNull(csvFormatTwo.getCommentMarker());
-        assertFalse(csvFormatTwo.isCommentMarkerSet());
-
-        assertNotSame(csvFormat, csvFormatTwo);
-        assertNotSame(csvFormatTwo, csvFormat);
-
-        assertNotNull(string);
-        Assertions.assertNotEquals(csvFormat, csvFormatTwo); // CSV-244 - should not be equal
-
-        Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal
-        assertEquals(",,,,,,,", string);
-
-    }
-
-    @Test
-    public void testWithHeaderEnum() {
-        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(Header.class);
-        assertArrayEquals(new String[] { "Name", "Email", "Phone" }, formatWithHeader.getHeader());
-    }
-
-    @Test
-    public void testWithHeaderEnumNull() {
-        final CSVFormat format = CSVFormat.DEFAULT;
-        final Class<Enum<?>> simpleName = null;
-        format.withHeader(simpleName);
-    }
-
-    @Test
-    public void testWithHeaderResultSetNull() throws SQLException {
-        final CSVFormat format = CSVFormat.DEFAULT;
-        final ResultSet resultSet = null;
-        format.withHeader(resultSet);
-    }
-
-    @Test
-    public void testWithIgnoreEmptyLines() {
-        assertFalse(CSVFormat.DEFAULT.withIgnoreEmptyLines(false).getIgnoreEmptyLines());
-        assertTrue(CSVFormat.DEFAULT.withIgnoreEmptyLines().getIgnoreEmptyLines());
-    }
-
-    @Test
-    public void testWithIgnoreSurround() {
-        assertFalse(CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(false).getIgnoreSurroundingSpaces());
-        assertTrue(CSVFormat.DEFAULT.withIgnoreSurroundingSpaces().getIgnoreSurroundingSpaces());
-    }
-
-    @Test
-    public void testWithNullString() {
-        final CSVFormat formatWithNullString = CSVFormat.DEFAULT.withNullString("null");
-        assertEquals("null", formatWithNullString.getNullString());
-    }
-
-    @Test
-    public void testWithQuoteChar() {
-        final CSVFormat formatWithQuoteChar = CSVFormat.DEFAULT.withQuote('"');
-        assertEquals(Character.valueOf('"'), formatWithQuoteChar.getQuoteCharacter());
-    }
-
-    @Test
-    public void testWithQuoteLFThrowsException() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote(LF));
-    }
-
-    @Test
-    public void testWithQuotePolicy() {
-        final CSVFormat formatWithQuotePolicy = CSVFormat.DEFAULT.withQuoteMode(QuoteMode.ALL);
-        assertEquals(QuoteMode.ALL, formatWithQuotePolicy.getQuoteMode());
-    }
-
-    @Test
-    public void testWithRecordSeparatorCR() {
-        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withRecordSeparator(CR);
-        assertEquals(String.valueOf(CR), formatWithRecordSeparator.getRecordSeparator());
-    }
-
-    @Test
-    public void testWithRecordSeparatorCRLF() {
-        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withRecordSeparator(CRLF);
-        assertEquals(CRLF, formatWithRecordSeparator.getRecordSeparator());
-    }
-
-    @Test
-    public void testWithRecordSeparatorLF() {
-        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withRecordSeparator(LF);
-        assertEquals(String.valueOf(LF), formatWithRecordSeparator.getRecordSeparator());
-    }
-
-    @Test
-    public void testWithSystemRecordSeparator() {
-        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withSystemRecordSeparator();
-        assertEquals(System.lineSeparator(), formatWithRecordSeparator.getRecordSeparator());
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.commons.csv;
+
+import static org.apache.commons.csv.CSVFormat.RFC4180;
+import static org.apache.commons.csv.Constants.CR;
+import static org.apache.commons.csv.Constants.CRLF;
+import static org.apache.commons.csv.Constants.LF;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Objects;
+
+import org.apache.commons.csv.CSVFormat.Builder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link CSVFormat}.
+ */
+public class CSVFormatTest {
+
+    public enum EmptyEnum {
+        // empty enum.
+    }
+
+    public enum Header {
+        Name, Email, Phone
+    }
+
+    private static void assertNotEquals(final Object right, final Object left) {
+        Assertions.assertNotEquals(right, left);
+        Assertions.assertNotEquals(left, right);
+    }
+
+    private static CSVFormat copy(final CSVFormat format) {
+        return format.builder().setDelimiter(format.getDelimiter()).get();
+    }
+
+    private void assertNotEquals(final String name, final String type, final Object left, final Object right) {
+        if (left.equals(right) || right.equals(left)) {
+            fail("Objects must not compare equal for " + name + "(" + type + ")");
+        }
+        if (left.hashCode() == right.hashCode()) {
+            fail("Hash code should not be equal for " + name + "(" + type + ")");
+        }
+    }
+
+    @Test
+    public void testBuildVsGet() {
+        final Builder builder = CSVFormat.DEFAULT.builder();
+        assertNotSame(builder.get(), builder.build());
+    }
+
+    @Test
+    public void testDelimiterEmptyStringThrowsException1() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter("").get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testDelimiterSameAsCommentStartThrowsException_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter('!').withCommentMarker('!'));
+    }
+
+    @Test
+    public void testDelimiterSameAsCommentStartThrowsException1() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter('!').setCommentMarker('!').get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testDelimiterSameAsEscapeThrowsException_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter('!').withEscape('!'));
+    }
+
+    @Test
+    public void testDelimiterSameAsEscapeThrowsException1() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter('!').setEscape('!').get());
+    }
+
+    @Test
+    public void testDelimiterSameAsRecordSeparatorThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat(CR));
+    }
+
+    @Test
+    public void testDuplicateHeaderElements() {
+        final String[] header = { "A", "A" };
+        final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader(header).get();
+        assertEquals(2, format.getHeader().length);
+        assertArrayEquals(header, format.getHeader());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testDuplicateHeaderElements_Deprecated() {
+        final String[] header = { "A", "A" };
+        final CSVFormat format = CSVFormat.DEFAULT.withHeader(header);
+        assertEquals(2, format.getHeader().length);
+        assertArrayEquals(header, format.getHeader());
+    }
+
+    @Test
+    public void testDuplicateHeaderElementsFalse() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setHeader("A", "A").get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testDuplicateHeaderElementsFalse_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(false).withHeader("A", "A"));
+    }
+
+    @Test
+    public void testDuplicateHeaderElementsTrue() {
+        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(true).setHeader("A", "A").get();
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testDuplicateHeaderElementsTrue_Deprecated() {
+        CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(true).withHeader("A", "A");
+    }
+
+    @Test
+    public void testDuplicateHeaderElementsTrueContainsEmpty1() {
+        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setHeader("A", "", "B", "").get();
+    }
+
+    @Test
+    public void testDuplicateHeaderElementsTrueContainsEmpty2() {
+        CSVFormat.DEFAULT.builder().setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).setHeader("A", "", "B", "").get();
+    }
+
+    @Test
+    public void testDuplicateHeaderElementsTrueContainsEmpty3() {
+        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setAllowMissingColumnNames(true).setHeader("A", "", "B", "").get();
+    }
+
+    @Test
+    public void testEquals() {
+        final CSVFormat right = CSVFormat.DEFAULT;
+        final CSVFormat left = copy(right);
+        Assertions.assertNotEquals(null, right);
+        Assertions.assertNotEquals("A String Instance", right);
+        assertEquals(right, right);
+        assertEquals(right, left);
+        assertEquals(left, right);
+        assertEquals(right.hashCode(), right.hashCode());
+        assertEquals(right.hashCode(), left.hashCode());
+    }
+
+    @Test
+    public void testEqualsCommentStart() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setCommentMarker('#').setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setCommentMarker('!').get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsCommentStart_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withCommentMarker('#').withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withCommentMarker('!');
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsDelimiter() {
+        final CSVFormat right = CSVFormat.newFormat('!');
+        final CSVFormat left = CSVFormat.newFormat('?');
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsEscape() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setCommentMarker('#').setEscape('+').setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setEscape('!').get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsEscape_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withCommentMarker('#').withEscape('+').withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withEscape('!');
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsHash() throws Exception {
+        final Method[] methods = CSVFormat.class.getDeclaredMethods();
+        for (final Method method : methods) {
+            if (Modifier.isPublic(method.getModifiers())) {
+                final String name = method.getName();
+                if (name.startsWith("with")) {
+                    for (final Class<?> cls : method.getParameterTypes()) {
+                        final String type = cls.getCanonicalName();
+                        switch (type) {
+                        case "boolean": {
+                            final Object defTrue = method.invoke(CSVFormat.DEFAULT, Boolean.TRUE);
+                            final Object defFalse = method.invoke(CSVFormat.DEFAULT, Boolean.FALSE);
+                            assertNotEquals(name, type, defTrue, defFalse);
+                            break;
+                        }
+                        case "char": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, 'a');
+                            final Object b = method.invoke(CSVFormat.DEFAULT, 'b');
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        case "java.lang.Character": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { null });
+                            final Object b = method.invoke(CSVFormat.DEFAULT, Character.valueOf('d'));
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        case "java.lang.String": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { null });
+                            final Object b = method.invoke(CSVFormat.DEFAULT, "e");
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        case "java.lang.String[]": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { new String[] { null, null } });
+                            final Object b = method.invoke(CSVFormat.DEFAULT, new Object[] { new String[] { "f", "g" } });
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        case "org.apache.commons.csv.QuoteMode": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, QuoteMode.MINIMAL);
+                            final Object b = method.invoke(CSVFormat.DEFAULT, QuoteMode.ALL);
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        case "org.apache.commons.csv.DuplicateHeaderMode": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, DuplicateHeaderMode.ALLOW_ALL);
+                            final Object b = method.invoke(CSVFormat.DEFAULT, DuplicateHeaderMode.DISALLOW);
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        case "java.lang.Object[]": {
+                            final Object a = method.invoke(CSVFormat.DEFAULT, new Object[] { new Object[] { null, null } });
+                            final Object b = method.invoke(CSVFormat.DEFAULT, new Object[] { new Object[] { new Object(), new Object() } });
+                            assertNotEquals(name, type, a, b);
+                            break;
+                        }
+                        default:
+                            if ("withHeader".equals(name)) { // covered above by String[]
+                                // ignored
+                            } else {
+                                fail("Unhandled method: " + name + "(" + type + ")");
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testEqualsHeader() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setHeader("One", "Two", "Three")
+                .setIgnoreEmptyLines(true).setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setHeader("Three", "Two", "One").get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsHeader_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withHeader("One", "Two", "Three")
+                .withIgnoreEmptyLines().withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withHeader("Three", "Two", "One");
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsIgnoreEmptyLines() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
+                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setIgnoreEmptyLines(false).get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsIgnoreEmptyLines_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withCommentMarker('#').withEscape('+').withIgnoreEmptyLines().withIgnoreSurroundingSpaces()
+                .withQuote('"').withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withIgnoreEmptyLines(false);
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsIgnoreSurroundingSpaces() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setCommentMarker('#').setEscape('+').setIgnoreSurroundingSpaces(true).setQuote('"')
+                .setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setIgnoreSurroundingSpaces(false).get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsIgnoreSurroundingSpaces_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withCommentMarker('#').withEscape('+').withIgnoreSurroundingSpaces().withQuote('"')
+                .withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withIgnoreSurroundingSpaces(false);
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsLeftNoQuoteRightQuote() {
+        final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).get();
+        final CSVFormat right = left.builder().setQuote('#').get();
+
+        assertNotEquals(left, right);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsLeftNoQuoteRightQuote_Deprecated() {
+        final CSVFormat left = CSVFormat.newFormat(',').withQuote(null);
+        final CSVFormat right = left.withQuote('#');
+
+        assertNotEquals(left, right);
+    }
+
+    @Test
+    public void testEqualsNoQuotes() {
+        final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).get();
+        final CSVFormat right = left.builder().setQuote(null).get();
+
+        assertEquals(left, right);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsNoQuotes_Deprecated() {
+        final CSVFormat left = CSVFormat.newFormat(',').withQuote(null);
+        final CSVFormat right = left.withQuote(null);
+
+        assertEquals(left, right);
+    }
+
+    @Test
+    public void testEqualsNullString() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
+                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).setNullString("null").get();
+        final CSVFormat left = right.builder().setNullString("---").get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsNullString_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withIgnoreEmptyLines()
+                .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL).withNullString("null");
+        final CSVFormat left = right.withNullString("---");
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsOne() {
+
+        final CSVFormat csvFormatOne = CSVFormat.INFORMIX_UNLOAD;
+        final CSVFormat csvFormatTwo = CSVFormat.MYSQL;
+
+        assertEquals('\\', (char) csvFormatOne.getEscapeCharacter());
+        assertEquals('\\', csvFormatOne.getEscapeChar());
+        assertNull(csvFormatOne.getQuoteMode());
+
+        assertTrue(csvFormatOne.getIgnoreEmptyLines());
+        assertFalse(csvFormatOne.getSkipHeaderRecord());
+
+        assertFalse(csvFormatOne.getIgnoreHeaderCase());
+        assertNull(csvFormatOne.getCommentMarker());
+
+        assertFalse(csvFormatOne.isCommentMarkerSet());
+        assertTrue(csvFormatOne.isQuoteCharacterSet());
+
+        assertEquals('|', csvFormatOne.getDelimiter());
+        assertFalse(csvFormatOne.getAllowMissingColumnNames());
+
+        assertTrue(csvFormatOne.isEscapeCharacterSet());
+        assertEquals("\n", csvFormatOne.getRecordSeparator());
+
+        assertEquals('\"', (char) csvFormatOne.getQuoteCharacter());
+        assertFalse(csvFormatOne.getTrailingDelimiter());
+
+        assertFalse(csvFormatOne.getTrim());
+        assertFalse(csvFormatOne.isNullStringSet());
+
+        assertNull(csvFormatOne.getNullString());
+        assertFalse(csvFormatOne.getIgnoreSurroundingSpaces());
+
+        assertTrue(csvFormatTwo.isEscapeCharacterSet());
+        assertNull(csvFormatTwo.getQuoteCharacter());
+
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+        assertEquals(QuoteMode.ALL_NON_NULL, csvFormatTwo.getQuoteMode());
+
+        assertEquals('\t', csvFormatTwo.getDelimiter());
+        assertArrayEquals(new char[] { '\t' }, csvFormatTwo.getDelimiterCharArray());
+        assertEquals("\t", csvFormatTwo.getDelimiterString());
+        assertEquals("\n", csvFormatTwo.getRecordSeparator());
+
+        assertFalse(csvFormatTwo.isQuoteCharacterSet());
+        assertTrue(csvFormatTwo.isNullStringSet());
+
+        assertEquals('\\', (char) csvFormatTwo.getEscapeCharacter());
+        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
+
+        assertFalse(csvFormatTwo.getTrim());
+        assertFalse(csvFormatTwo.getIgnoreEmptyLines());
+
+        assertEquals("\\N", csvFormatTwo.getNullString());
+        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+        assertFalse(csvFormatTwo.getSkipHeaderRecord());
+
+        assertNull(csvFormatTwo.getCommentMarker());
+        assertFalse(csvFormatTwo.isCommentMarkerSet());
+
+        assertNotSame(csvFormatTwo, csvFormatOne);
+        Assertions.assertNotEquals(csvFormatTwo, csvFormatOne);
+
+        assertEquals('\\', (char) csvFormatOne.getEscapeCharacter());
+        assertNull(csvFormatOne.getQuoteMode());
+
+        assertTrue(csvFormatOne.getIgnoreEmptyLines());
+        assertFalse(csvFormatOne.getSkipHeaderRecord());
+
+        assertFalse(csvFormatOne.getIgnoreHeaderCase());
+        assertNull(csvFormatOne.getCommentMarker());
+
+        assertFalse(csvFormatOne.isCommentMarkerSet());
+        assertTrue(csvFormatOne.isQuoteCharacterSet());
+
+        assertEquals('|', csvFormatOne.getDelimiter());
+        assertFalse(csvFormatOne.getAllowMissingColumnNames());
+
+        assertTrue(csvFormatOne.isEscapeCharacterSet());
+        assertEquals("\n", csvFormatOne.getRecordSeparator());
+
+        assertEquals('\"', (char) csvFormatOne.getQuoteCharacter());
+        assertFalse(csvFormatOne.getTrailingDelimiter());
+
+        assertFalse(csvFormatOne.getTrim());
+        assertFalse(csvFormatOne.isNullStringSet());
+
+        assertNull(csvFormatOne.getNullString());
+        assertFalse(csvFormatOne.getIgnoreSurroundingSpaces());
+
+        assertTrue(csvFormatTwo.isEscapeCharacterSet());
+        assertNull(csvFormatTwo.getQuoteCharacter());
+
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+        assertEquals(QuoteMode.ALL_NON_NULL, csvFormatTwo.getQuoteMode());
+
+        assertEquals('\t', csvFormatTwo.getDelimiter());
+        assertEquals("\n", csvFormatTwo.getRecordSeparator());
+
+        assertFalse(csvFormatTwo.isQuoteCharacterSet());
+        assertTrue(csvFormatTwo.isNullStringSet());
+
+        assertEquals('\\', (char) csvFormatTwo.getEscapeCharacter());
+        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
+
+        assertFalse(csvFormatTwo.getTrim());
+        assertFalse(csvFormatTwo.getIgnoreEmptyLines());
+
+        assertEquals("\\N", csvFormatTwo.getNullString());
+        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+        assertFalse(csvFormatTwo.getSkipHeaderRecord());
+
+        assertNull(csvFormatTwo.getCommentMarker());
+        assertFalse(csvFormatTwo.isCommentMarkerSet());
+
+        assertNotSame(csvFormatOne, csvFormatTwo);
+        assertNotSame(csvFormatTwo, csvFormatOne);
+
+        Assertions.assertNotEquals(csvFormatOne, csvFormatTwo);
+        Assertions.assertNotEquals(csvFormatTwo, csvFormatOne);
+
+        Assertions.assertNotEquals(csvFormatTwo, csvFormatOne);
+
+    }
+
+    @Test
+    public void testEqualsQuoteChar() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').get();
+        final CSVFormat left = right.builder().setQuote('!').get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsQuoteChar_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"');
+        final CSVFormat left = right.withQuote('!');
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsQuotePolicy() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setQuoteMode(QuoteMode.MINIMAL).get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsQuotePolicy_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withQuoteMode(QuoteMode.MINIMAL);
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsRecordSeparator() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
+                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get();
+        final CSVFormat left = right.builder().setRecordSeparator(LF).get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsRecordSeparator_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withIgnoreEmptyLines()
+                .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL);
+        final CSVFormat left = right.withRecordSeparator(LF);
+
+        assertNotEquals(right, left);
+    }
+
+    public void testEqualsSkipHeaderRecord() {
+        final CSVFormat right = CSVFormat.newFormat('\'').builder().setRecordSeparator(CR).setCommentMarker('#').setEscape('+').setIgnoreEmptyLines(true)
+                .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).setNullString("null").setSkipHeaderRecord(true).get();
+        final CSVFormat left = right.builder().setSkipHeaderRecord(false).get();
+
+        assertNotEquals(right, left);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEqualsSkipHeaderRecord_Deprecated() {
+        final CSVFormat right = CSVFormat.newFormat('\'').withRecordSeparator(CR).withCommentMarker('#').withEscape('+').withIgnoreEmptyLines()
+                .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL).withNullString("null").withSkipHeaderRecord();
+        final CSVFormat left = right.withSkipHeaderRecord(false);
+
+        assertNotEquals(right, left);
+    }
+
+    @Test
+    public void testEqualsWithNull() {
+
+        final CSVFormat csvFormat = CSVFormat.POSTGRESQL_TEXT;
+
+        assertEquals('\\', (char) csvFormat.getEscapeCharacter());
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormat.getTrailingDelimiter());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isQuoteCharacterSet());
+        assertEquals("\\N", csvFormat.getNullString());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertTrue(csvFormat.isEscapeCharacterSet());
+
+        assertFalse(csvFormat.isCommentMarkerSet());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertEquals(QuoteMode.ALL_NON_NULL, csvFormat.getQuoteMode());
+
+        assertEquals('\t', csvFormat.getDelimiter());
+        assertFalse(csvFormat.getSkipHeaderRecord());
+
+        assertEquals("\n", csvFormat.getRecordSeparator());
+        assertFalse(csvFormat.getIgnoreEmptyLines());
+
+        assertNull(csvFormat.getQuoteCharacter());
+        assertTrue(csvFormat.isNullStringSet());
+
+        assertEquals('\\', (char) csvFormat.getEscapeCharacter());
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormat.getTrailingDelimiter());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isQuoteCharacterSet());
+        assertEquals("\\N", csvFormat.getNullString());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertTrue(csvFormat.isEscapeCharacterSet());
+
+        assertFalse(csvFormat.isCommentMarkerSet());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertEquals(QuoteMode.ALL_NON_NULL, csvFormat.getQuoteMode());
+
+        assertEquals('\t', csvFormat.getDelimiter());
+        assertFalse(csvFormat.getSkipHeaderRecord());
+
+        assertEquals("\n", csvFormat.getRecordSeparator());
+        assertFalse(csvFormat.getIgnoreEmptyLines());
+
+        assertNull(csvFormat.getQuoteCharacter());
+        assertTrue(csvFormat.isNullStringSet());
+
+        Assertions.assertNotEquals(null, csvFormat);
+
+    }
+
+    @Test
+    public void testEscapeSameAsCommentStartThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setEscape('!').setCommentMarker('!').get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEscapeSameAsCommentStartThrowsException_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape('!').withCommentMarker('!'));
+    }
+
+    @Test
+    public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType() {
+        // Cannot assume that callers won't use different Character objects
+        assertThrows(IllegalArgumentException.class,
+                () -> CSVFormat.DEFAULT.builder().setEscape(Character.valueOf('!')).setCommentMarker(Character.valueOf('!')).get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType_Deprecated() {
+        // Cannot assume that callers won't use different Character objects
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape(Character.valueOf('!')).withCommentMarker(Character.valueOf('!')));
+    }
+
+    @Test
+    public void testFormat() {
+        final CSVFormat format = CSVFormat.DEFAULT;
+
+        assertEquals("", format.format());
+        assertEquals("a,b,c", format.format("a", "b", "c"));
+        assertEquals("\"x,y\",z", format.format("x,y", "z"));
+    }
+
+    @Test // I assume this to be a defect.
+    public void testFormatThrowsNullPointerException() {
+
+        final CSVFormat csvFormat = CSVFormat.MYSQL;
+
+        final NullPointerException e = assertThrows(NullPointerException.class, () -> csvFormat.format((Object[]) null));
+        assertEquals(Objects.class.getName(), e.getStackTrace()[0].getClassName());
+    }
+
+    @Test
+    public void testFormatToString() {
+        // @formatter:off
+        final CSVFormat format = CSVFormat.RFC4180
+                .withEscape('?')
+                .withDelimiter(',')
+                .withQuoteMode(QuoteMode.MINIMAL)
+                .withRecordSeparator(CRLF)
+                .withQuote('"')
+                .withNullString("")
+                .withIgnoreHeaderCase(true)
+                .withHeaderComments("This is HeaderComments")
+                .withHeader("col1", "col2", "col3");
+        // @formatter:on
+        assertEquals(
+                "Delimiter=<,> Escape=<?> QuoteChar=<\"> QuoteMode=<MINIMAL> NullString=<> RecordSeparator=<" + CRLF +
+                        "> IgnoreHeaderCase:ignored SkipHeaderRecord:false HeaderComments:[This is HeaderComments] Header:[col1, col2, col3]",
+                format.toString());
+    }
+
+    @Test
+    public void testGetAllowDuplicateHeaderNames() {
+        final Builder builder = CSVFormat.DEFAULT.builder();
+        assertTrue(builder.get().getAllowDuplicateHeaderNames());
+        assertTrue(builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL).get().getAllowDuplicateHeaderNames());
+        assertFalse(builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).get().getAllowDuplicateHeaderNames());
+        assertFalse(builder.setDuplicateHeaderMode(DuplicateHeaderMode.DISALLOW).get().getAllowDuplicateHeaderNames());
+    }
+
+    @Test
+    public void testGetDuplicateHeaderMode() {
+        final Builder builder = CSVFormat.DEFAULT.builder();
+
+        assertEquals(DuplicateHeaderMode.ALLOW_ALL, builder.get().getDuplicateHeaderMode());
+        assertEquals(DuplicateHeaderMode.ALLOW_ALL, builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL).get().getDuplicateHeaderMode());
+        assertEquals(DuplicateHeaderMode.ALLOW_EMPTY, builder.setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).get().getDuplicateHeaderMode());
+        assertEquals(DuplicateHeaderMode.DISALLOW, builder.setDuplicateHeaderMode(DuplicateHeaderMode.DISALLOW).get().getDuplicateHeaderMode());
+    }
+
+    @Test
+    public void testGetHeader() {
+        final String[] header = { "one", "two", "three" };
+        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(header);
+        // getHeader() makes a copy of the header array.
+        final String[] headerCopy = formatWithHeader.getHeader();
+        headerCopy[0] = "A";
+        headerCopy[1] = "B";
+        headerCopy[2] = "C";
+        assertFalse(Arrays.equals(formatWithHeader.getHeader(), headerCopy));
+        assertNotSame(formatWithHeader.getHeader(), headerCopy);
+    }
+
+    @Test
+    public void testHashCodeAndWithIgnoreHeaderCase() {
+
+        final CSVFormat csvFormat = CSVFormat.INFORMIX_UNLOAD_CSV;
+        final CSVFormat csvFormatTwo = csvFormat.withIgnoreHeaderCase();
+        csvFormatTwo.hashCode();
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertTrue(csvFormatTwo.getIgnoreHeaderCase()); // now different
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+
+        Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+
+        assertFalse(csvFormatTwo.getTrim());
+
+    }
+
+    @Test
+    public void testJiraCsv236() {
+        CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(true).setHeader("CC", "VV", "VV").get();
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testJiraCsv236__Deprecated() {
+        CSVFormat.DEFAULT.withAllowDuplicateHeaderNames().withHeader("CC", "VV", "VV");
+    }
+
+    @Test
+    public void testNewFormat() {
+
+        final CSVFormat csvFormat = CSVFormat.newFormat('X');
+
+        assertFalse(csvFormat.getSkipHeaderRecord());
+        assertFalse(csvFormat.isEscapeCharacterSet());
+
+        assertNull(csvFormat.getRecordSeparator());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertNull(csvFormat.getCommentMarker());
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertNull(csvFormat.getEscapeCharacter());
+
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+        assertFalse(csvFormat.getTrailingDelimiter());
+
+        assertEquals('X', csvFormat.getDelimiter());
+        assertNull(csvFormat.getNullString());
+
+        assertFalse(csvFormat.isQuoteCharacterSet());
+        assertFalse(csvFormat.isCommentMarkerSet());
+
+        assertNull(csvFormat.getQuoteCharacter());
+        assertFalse(csvFormat.getIgnoreEmptyLines());
+
+        assertFalse(csvFormat.getSkipHeaderRecord());
+        assertFalse(csvFormat.isEscapeCharacterSet());
+
+        assertNull(csvFormat.getRecordSeparator());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertNull(csvFormat.getCommentMarker());
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertNull(csvFormat.getEscapeCharacter());
+
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+        assertFalse(csvFormat.getTrailingDelimiter());
+
+        assertEquals('X', csvFormat.getDelimiter());
+        assertNull(csvFormat.getNullString());
+
+        assertFalse(csvFormat.isQuoteCharacterSet());
+        assertFalse(csvFormat.isCommentMarkerSet());
+
+        assertNull(csvFormat.getQuoteCharacter());
+        assertFalse(csvFormat.getIgnoreEmptyLines());
+
+    }
+
+    @Test
+    public void testNullRecordSeparatorCsv106() {
+        final CSVFormat format = CSVFormat.newFormat(';').builder().setSkipHeaderRecord(true).setHeader("H1", "H2").get();
+        final String formatStr = format.format("A", "B");
+        assertNotNull(formatStr);
+        assertFalse(formatStr.endsWith("null"));
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testNullRecordSeparatorCsv106__Deprecated() {
+        final CSVFormat format = CSVFormat.newFormat(';').withSkipHeaderRecord().withHeader("H1", "H2");
+        final String formatStr = format.format("A", "B");
+        assertNotNull(formatStr);
+        assertFalse(formatStr.endsWith("null"));
+    }
+
+    @Test
+    public void testPrintRecord() throws IOException {
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180;
+        format.printRecord(out, "a", "b", "c");
+        assertEquals("a,b,c" + format.getRecordSeparator(), out.toString());
+    }
+
+    @Test
+    public void testPrintRecordEmpty() throws IOException {
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180;
+        format.printRecord(out);
+        assertEquals(format.getRecordSeparator(), out.toString());
+    }
+
+    @Test
+    public void testPrintWithEscapesEndWithCRLF() throws IOException {
+        final Reader in = new StringReader("x,y,x\r\na,?b,c\r\n");
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180.withEscape('?').withDelimiter(',').withQuote(null).withRecordSeparator(CRLF);
+        format.print(in, out, true);
+        assertEquals("x?,y?,x?r?na?,??b?,c?r?n", out.toString());
+    }
+
+    @Test
+    public void testPrintWithEscapesEndWithoutCRLF() throws IOException {
+        final Reader in = new StringReader("x,y,x");
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180.withEscape('?').withDelimiter(',').withQuote(null).withRecordSeparator(CRLF);
+        format.print(in, out, true);
+        assertEquals("x?,y?,x", out.toString());
+    }
+
+    @Test
+    public void testPrintWithoutQuotes() throws IOException {
+        final Reader in = new StringReader("");
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180.withDelimiter(',').withQuote('"').withEscape('?').withQuoteMode(QuoteMode.NON_NUMERIC);
+        format.print(in, out, true);
+        assertEquals("\"\"", out.toString());
+    }
+
+    @Test
+    public void testPrintWithQuoteModeIsNONE() throws IOException {
+        final Reader in = new StringReader("a,b,c");
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180.withDelimiter(',').withQuote('"').withEscape('?').withQuoteMode(QuoteMode.NONE);
+        format.print(in, out, true);
+        assertEquals("a?,b?,c", out.toString());
+    }
+
+    @Test
+    public void testPrintWithQuotes() throws IOException {
+        final Reader in = new StringReader("\"a,b,c\r\nx,y,z");
+        final Appendable out = new StringBuilder();
+        final CSVFormat format = CSVFormat.RFC4180.withDelimiter(',').withQuote('"').withEscape('?').withQuoteMode(QuoteMode.NON_NUMERIC);
+        format.print(in, out, true);
+        assertEquals("\"\"\"a,b,c\r\nx,y,z\"", out.toString());
+    }
+
+    @Test
+    public void testQuoteCharSameAsCommentStartThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setCommentMarker('!').get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testQuoteCharSameAsCommentStartThrowsException_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withCommentMarker('!'));
+    }
+
+    @Test
+    public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType() {
+        // Cannot assume that callers won't use different Character objects
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote(Character.valueOf('!')).setCommentMarker('!').get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType_Deprecated() {
+        // Cannot assume that callers won't use different Character objects
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote(Character.valueOf('!')).withCommentMarker('!'));
+    }
+
+    @Test
+    public void testQuoteCharSameAsDelimiterThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setDelimiter('!').get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testQuoteCharSameAsDelimiterThrowsException_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withDelimiter('!'));
+    }
+
+    @Test
+    public void testQuoteModeNoneShouldReturnMeaningfulExceptionMessage() {
+        final Exception exception = assertThrows(IllegalArgumentException.class, () ->
+        // @formatter:off
+            CSVFormat.DEFAULT.builder()
+                    .setHeader("Col1", "Col2", "Col3", "Col4")
+                    .setQuoteMode(QuoteMode.NONE)
+                    .get()
+        // @formatter:on
+        );
+        final String actualMessage = exception.getMessage();
+        final String expectedMessage = "Quote mode set to NONE but no escape character is set";
+        assertEquals(expectedMessage, actualMessage);
+    }
+
+    @Test
+    public void testQuotePolicyNoneWithoutEscapeThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat('!').builder().setQuoteMode(QuoteMode.NONE).get());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testQuotePolicyNoneWithoutEscapeThrowsException_Deprecated() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat('!').withQuoteMode(QuoteMode.NONE));
+    }
+
+    @Test
+    public void testRFC4180() {
+        assertNull(RFC4180.getCommentMarker());
+        assertEquals(',', RFC4180.getDelimiter());
+        assertNull(RFC4180.getEscapeCharacter());
+        assertFalse(RFC4180.getIgnoreEmptyLines());
+        assertEquals(Character.valueOf('"'), RFC4180.getQuoteCharacter());
+        assertNull(RFC4180.getQuoteMode());
+        assertEquals("\r\n", RFC4180.getRecordSeparator());
+    }
+
+    @SuppressWarnings("boxing") // no need to worry about boxing here
+    @Test
+    public void testSerialization() throws Exception {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
+            oos.writeObject(CSVFormat.DEFAULT);
+            oos.flush();
+        }
+
+        final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
+        final CSVFormat format = (CSVFormat) in.readObject();
+
+        assertNotNull(format);
+        assertEquals(CSVFormat.DEFAULT.getDelimiter(), format.getDelimiter(), "delimiter");
+        assertEquals(CSVFormat.DEFAULT.getQuoteCharacter(), format.getQuoteCharacter(), "encapsulator");
+        assertEquals(CSVFormat.DEFAULT.getCommentMarker(), format.getCommentMarker(), "comment start");
+        assertEquals(CSVFormat.DEFAULT.getRecordSeparator(), format.getRecordSeparator(), "record separator");
+        assertEquals(CSVFormat.DEFAULT.getEscapeCharacter(), format.getEscapeCharacter(), "escape");
+        assertEquals(CSVFormat.DEFAULT.getIgnoreSurroundingSpaces(), format.getIgnoreSurroundingSpaces(), "trim");
+        assertEquals(CSVFormat.DEFAULT.getIgnoreEmptyLines(), format.getIgnoreEmptyLines(), "empty lines");
+    }
+
+    @Test
+    public void testToString() {
+
+        final String string = CSVFormat.INFORMIX_UNLOAD.toString();
+
+        assertEquals("Delimiter=<|> Escape=<\\> QuoteChar=<\"> RecordSeparator=<\n> EmptyLines:ignored SkipHeaderRecord:false", string);
+
+    }
+
+    @Test
+    public void testToStringAndWithCommentMarkerTakingCharacter() {
+
+        final CSVFormat.Predefined csvFormatPredefined = CSVFormat.Predefined.Default;
+        final CSVFormat csvFormat = csvFormatPredefined.getFormat();
+
+        assertNull(csvFormat.getEscapeCharacter());
+        assertTrue(csvFormat.isQuoteCharacterSet());
+
+        assertFalse(csvFormat.getTrim());
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormat.getTrailingDelimiter());
+        assertEquals(',', csvFormat.getDelimiter());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertEquals("\r\n", csvFormat.getRecordSeparator());
+
+        assertFalse(csvFormat.isCommentMarkerSet());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+
+        assertFalse(csvFormat.isEscapeCharacterSet());
+        assertFalse(csvFormat.getSkipHeaderRecord());
+
+        assertNull(csvFormat.getNullString());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertTrue(csvFormat.getIgnoreEmptyLines());
+        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
+
+        final Character character = Character.valueOf('n');
+
+        final CSVFormat csvFormatTwo = csvFormat.withCommentMarker(character);
+
+        assertNull(csvFormat.getEscapeCharacter());
+        assertTrue(csvFormat.isQuoteCharacterSet());
+
+        assertFalse(csvFormat.getTrim());
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormat.getTrailingDelimiter());
+        assertEquals(',', csvFormat.getDelimiter());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertEquals("\r\n", csvFormat.getRecordSeparator());
+
+        assertFalse(csvFormat.isCommentMarkerSet());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+
+        assertFalse(csvFormat.isEscapeCharacterSet());
+        assertFalse(csvFormat.getSkipHeaderRecord());
+
+        assertNull(csvFormat.getNullString());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertTrue(csvFormat.getIgnoreEmptyLines());
+        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
+
+        assertFalse(csvFormatTwo.isNullStringSet());
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+
+        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
+        assertNull(csvFormatTwo.getNullString());
+
+        assertEquals(',', csvFormatTwo.getDelimiter());
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+
+        assertTrue(csvFormatTwo.isCommentMarkerSet());
+        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
+
+        assertFalse(csvFormatTwo.getTrim());
+        assertNull(csvFormatTwo.getEscapeCharacter());
+
+        assertTrue(csvFormatTwo.isQuoteCharacterSet());
+        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
+
+        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
+        assertNull(csvFormatTwo.getQuoteMode());
+
+        assertEquals('n', (char) csvFormatTwo.getCommentMarker());
+        assertFalse(csvFormatTwo.getSkipHeaderRecord());
+
+        assertFalse(csvFormatTwo.isEscapeCharacterSet());
+        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
+
+        assertNotSame(csvFormat, csvFormatTwo);
+        assertNotSame(csvFormatTwo, csvFormat);
+
+        Assertions.assertNotEquals(csvFormatTwo, csvFormat);
+
+        assertNull(csvFormat.getEscapeCharacter());
+        assertTrue(csvFormat.isQuoteCharacterSet());
+
+        assertFalse(csvFormat.getTrim());
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+
+        assertFalse(csvFormat.getTrailingDelimiter());
+        assertEquals(',', csvFormat.getDelimiter());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertEquals("\r\n", csvFormat.getRecordSeparator());
+
+        assertFalse(csvFormat.isCommentMarkerSet());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+
+        assertFalse(csvFormat.isEscapeCharacterSet());
+        assertFalse(csvFormat.getSkipHeaderRecord());
+
+        assertNull(csvFormat.getNullString());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertTrue(csvFormat.getIgnoreEmptyLines());
+        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
+
+        assertFalse(csvFormatTwo.isNullStringSet());
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+
+        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
+        assertNull(csvFormatTwo.getNullString());
+
+        assertEquals(',', csvFormatTwo.getDelimiter());
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+
+        assertTrue(csvFormatTwo.isCommentMarkerSet());
+        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
+
+        assertFalse(csvFormatTwo.getTrim());
+        assertNull(csvFormatTwo.getEscapeCharacter());
+
+        assertTrue(csvFormatTwo.isQuoteCharacterSet());
+        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
+
+        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
+        assertNull(csvFormatTwo.getQuoteMode());
+
+        assertEquals('n', (char) csvFormatTwo.getCommentMarker());
+        assertFalse(csvFormatTwo.getSkipHeaderRecord());
+
+        assertFalse(csvFormatTwo.isEscapeCharacterSet());
+        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
+
+        assertNotSame(csvFormat, csvFormatTwo);
+        assertNotSame(csvFormatTwo, csvFormat);
+
+        Assertions.assertNotEquals(csvFormat, csvFormatTwo);
+
+        Assertions.assertNotEquals(csvFormatTwo, csvFormat);
+        assertEquals("Delimiter=<,> QuoteChar=<\"> CommentStart=<n> " + "RecordSeparator=<\r\n> EmptyLines:ignored SkipHeaderRecord:false",
+                csvFormatTwo.toString());
+
+    }
+
+    @Test
+    public void testTrim() throws IOException {
+        final CSVFormat formatWithTrim = CSVFormat.DEFAULT.withDelimiter(',').withTrim().withQuote(null).withRecordSeparator(CRLF);
+
+        CharSequence in = "a,b,c";
+        final StringBuilder out = new StringBuilder();
+        formatWithTrim.print(in, out, true);
+        assertEquals("a,b,c", out.toString());
+
+        in = new StringBuilder(" x,y,z");
+        out.setLength(0);
+        formatWithTrim.print(in, out, true);
+        assertEquals("x,y,z", out.toString());
+
+        in = new StringBuilder("");
+        out.setLength(0);
+        formatWithTrim.print(in, out, true);
+        assertEquals("", out.toString());
+
+        in = new StringBuilder("header\r\n");
+        out.setLength(0);
+        formatWithTrim.print(in, out, true);
+        assertEquals("header", out.toString());
+    }
+
+    @Test
+    public void testWithCommentStart() {
+        final CSVFormat formatWithCommentStart = CSVFormat.DEFAULT.withCommentMarker('#');
+        assertEquals(Character.valueOf('#'), formatWithCommentStart.getCommentMarker());
+    }
+
+    @Test
+    public void testWithCommentStartCRThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withCommentMarker(CR));
+    }
+
+    @Test
+    public void testWithDelimiter() {
+        final CSVFormat formatWithDelimiter = CSVFormat.DEFAULT.withDelimiter('!');
+        assertEquals('!', formatWithDelimiter.getDelimiter());
+    }
+
+    @Test
+    public void testWithDelimiterLFThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter(LF));
+    }
+
+    @Test
+    public void testWithEmptyDuplicates() {
+        final CSVFormat formatWithEmptyDuplicates = CSVFormat.DEFAULT.builder().setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_EMPTY).get();
+
+        assertEquals(DuplicateHeaderMode.ALLOW_EMPTY, formatWithEmptyDuplicates.getDuplicateHeaderMode());
+        assertFalse(formatWithEmptyDuplicates.getAllowDuplicateHeaderNames());
+    }
+
+    @Test
+    public void testWithEmptyEnum() {
+        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(EmptyEnum.class);
+        assertEquals(0, formatWithHeader.getHeader().length);
+    }
+
+    @Test
+    public void testWithEscape() {
+        final CSVFormat formatWithEscape = CSVFormat.DEFAULT.withEscape('&');
+        assertEquals(Character.valueOf('&'), formatWithEscape.getEscapeCharacter());
+    }
+
+    @Test
+    public void testWithEscapeCRThrowsExceptions() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape(CR));
+    }
+
+    @Test
+    public void testWithFirstRecordAsHeader() {
+        final CSVFormat formatWithFirstRecordAsHeader = CSVFormat.DEFAULT.withFirstRecordAsHeader();
+        assertTrue(formatWithFirstRecordAsHeader.getSkipHeaderRecord());
+        assertEquals(0, formatWithFirstRecordAsHeader.getHeader().length);
+    }
+
+    @Test
+    public void testWithHeader() {
+        final String[] header = { "one", "two", "three" };
+        // withHeader() makes a copy of the header array.
+        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(header);
+        assertArrayEquals(header, formatWithHeader.getHeader());
+        assertNotSame(header, formatWithHeader.getHeader());
+    }
+
+    @Test
+    public void testWithHeaderComments() {
+
+        final CSVFormat csvFormat = CSVFormat.DEFAULT;
+
+        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
+        assertFalse(csvFormat.isCommentMarkerSet());
+
+        assertFalse(csvFormat.isEscapeCharacterSet());
+        assertTrue(csvFormat.isQuoteCharacterSet());
+
+        assertFalse(csvFormat.getSkipHeaderRecord());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertEquals(',', csvFormat.getDelimiter());
+        assertTrue(csvFormat.getIgnoreEmptyLines());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertEquals("\r\n", csvFormat.getRecordSeparator());
+        assertFalse(csvFormat.getTrailingDelimiter());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertNull(csvFormat.getNullString());
+
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+        assertNull(csvFormat.getEscapeCharacter());
+
+        final Object[] objectArray = new Object[8];
+        final CSVFormat csvFormatTwo = csvFormat.withHeaderComments(objectArray);
+
+        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
+        assertFalse(csvFormat.isCommentMarkerSet());
+
+        assertFalse(csvFormat.isEscapeCharacterSet());
+        assertTrue(csvFormat.isQuoteCharacterSet());
+
+        assertFalse(csvFormat.getSkipHeaderRecord());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertEquals(',', csvFormat.getDelimiter());
+        assertTrue(csvFormat.getIgnoreEmptyLines());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertEquals("\r\n", csvFormat.getRecordSeparator());
+        assertFalse(csvFormat.getTrailingDelimiter());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertNull(csvFormat.getNullString());
+
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+        assertNull(csvFormat.getEscapeCharacter());
+
+        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
+        assertNull(csvFormatTwo.getQuoteMode());
+
+        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
+        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
+
+        assertNull(csvFormatTwo.getEscapeCharacter());
+        assertFalse(csvFormatTwo.getTrim());
+
+        assertFalse(csvFormatTwo.isEscapeCharacterSet());
+        assertTrue(csvFormatTwo.isQuoteCharacterSet());
+
+        assertFalse(csvFormatTwo.getSkipHeaderRecord());
+        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
+
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+        assertNull(csvFormatTwo.getNullString());
+
+        assertFalse(csvFormatTwo.isNullStringSet());
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+
+        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
+        assertEquals(',', csvFormatTwo.getDelimiter());
+
+        assertNull(csvFormatTwo.getCommentMarker());
+        assertFalse(csvFormatTwo.isCommentMarkerSet());
+
+        assertNotSame(csvFormat, csvFormatTwo);
+        assertNotSame(csvFormatTwo, csvFormat);
+
+        Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal
+
+        final String string = csvFormatTwo.format(objectArray);
+
+        assertEquals('\"', (char) csvFormat.getQuoteCharacter());
+        assertFalse(csvFormat.isCommentMarkerSet());
+
+        assertFalse(csvFormat.isEscapeCharacterSet());
+        assertTrue(csvFormat.isQuoteCharacterSet());
+
+        assertFalse(csvFormat.getSkipHeaderRecord());
+        assertNull(csvFormat.getQuoteMode());
+
+        assertEquals(',', csvFormat.getDelimiter());
+        assertTrue(csvFormat.getIgnoreEmptyLines());
+
+        assertFalse(csvFormat.getIgnoreHeaderCase());
+        assertNull(csvFormat.getCommentMarker());
+
+        assertEquals("\r\n", csvFormat.getRecordSeparator());
+        assertFalse(csvFormat.getTrailingDelimiter());
+
+        assertFalse(csvFormat.getAllowMissingColumnNames());
+        assertFalse(csvFormat.getTrim());
+
+        assertFalse(csvFormat.isNullStringSet());
+        assertNull(csvFormat.getNullString());
+
+        assertFalse(csvFormat.getIgnoreSurroundingSpaces());
+        assertNull(csvFormat.getEscapeCharacter());
+
+        assertFalse(csvFormatTwo.getIgnoreHeaderCase());
+        assertNull(csvFormatTwo.getQuoteMode());
+
+        assertTrue(csvFormatTwo.getIgnoreEmptyLines());
+        assertFalse(csvFormatTwo.getIgnoreSurroundingSpaces());
+
+        assertNull(csvFormatTwo.getEscapeCharacter());
+        assertFalse(csvFormatTwo.getTrim());
+
+        assertFalse(csvFormatTwo.isEscapeCharacterSet());
+        assertTrue(csvFormatTwo.isQuoteCharacterSet());
+
+        assertFalse(csvFormatTwo.getSkipHeaderRecord());
+        assertEquals('\"', (char) csvFormatTwo.getQuoteCharacter());
+
+        assertFalse(csvFormatTwo.getAllowMissingColumnNames());
+        assertNull(csvFormatTwo.getNullString());
+
+        assertFalse(csvFormatTwo.isNullStringSet());
+        assertFalse(csvFormatTwo.getTrailingDelimiter());
+
+        assertEquals("\r\n", csvFormatTwo.getRecordSeparator());
+        assertEquals(',', csvFormatTwo.getDelimiter());
+
+        assertNull(csvFormatTwo.getCommentMarker());
+        assertFalse(csvFormatTwo.isCommentMarkerSet());
+
+        assertNotSame(csvFormat, csvFormatTwo);
+        assertNotSame(csvFormatTwo, csvFormat);
+
+        assertNotNull(string);
+        Assertions.assertNotEquals(csvFormat, csvFormatTwo); // CSV-244 - should not be equal
+
+        Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal
+        assertEquals(",,,,,,,", string);
+
+    }
+
+    @Test
+    public void testWithHeaderEnum() {
+        final CSVFormat formatWithHeader = CSVFormat.DEFAULT.withHeader(Header.class);
+        assertArrayEquals(new String[] { "Name", "Email", "Phone" }, formatWithHeader.getHeader());
+    }
+
+    @Test
+    public void testWithHeaderEnumNull() {
+        final CSVFormat format = CSVFormat.DEFAULT;
+        final Class<Enum<?>> simpleName = null;
+        format.withHeader(simpleName);
+    }
+
+    @Test
+    public void testWithHeaderResultSetNull() throws SQLException {
+        final CSVFormat format = CSVFormat.DEFAULT;
+        final ResultSet resultSet = null;
+        format.withHeader(resultSet);
+    }
+
+    @Test
+    public void testWithIgnoreEmptyLines() {
+        assertFalse(CSVFormat.DEFAULT.withIgnoreEmptyLines(false).getIgnoreEmptyLines());
+        assertTrue(CSVFormat.DEFAULT.withIgnoreEmptyLines().getIgnoreEmptyLines());
+    }
+
+    @Test
+    public void testWithIgnoreSurround() {
+        assertFalse(CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(false).getIgnoreSurroundingSpaces());
+        assertTrue(CSVFormat.DEFAULT.withIgnoreSurroundingSpaces().getIgnoreSurroundingSpaces());
+    }
+
+    @Test
+    public void testWithNullString() {
+        final CSVFormat formatWithNullString = CSVFormat.DEFAULT.withNullString("null");
+        assertEquals("null", formatWithNullString.getNullString());
+    }
+
+    @Test
+    public void testWithQuoteChar() {
+        final CSVFormat formatWithQuoteChar = CSVFormat.DEFAULT.withQuote('"');
+        assertEquals(Character.valueOf('"'), formatWithQuoteChar.getQuoteCharacter());
+    }
+
+    @Test
+    public void testWithQuoteLFThrowsException() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote(LF));
+    }
+
+    @Test
+    public void testWithQuotePolicy() {
+        final CSVFormat formatWithQuotePolicy = CSVFormat.DEFAULT.withQuoteMode(QuoteMode.ALL);
+        assertEquals(QuoteMode.ALL, formatWithQuotePolicy.getQuoteMode());
+    }
+
+    @Test
+    public void testWithRecordSeparatorCR() {
+        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withRecordSeparator(CR);
+        assertEquals(String.valueOf(CR), formatWithRecordSeparator.getRecordSeparator());
+    }
+
+    @Test
+    public void testWithRecordSeparatorCRLF() {
+        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withRecordSeparator(CRLF);
+        assertEquals(CRLF, formatWithRecordSeparator.getRecordSeparator());
+    }
+
+    @Test
+    public void testWithRecordSeparatorLF() {
+        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withRecordSeparator(LF);
+        assertEquals(String.valueOf(LF), formatWithRecordSeparator.getRecordSeparator());
+    }
+
+    @Test
+    public void testWithSystemRecordSeparator() {
+        final CSVFormat formatWithRecordSeparator = CSVFormat.DEFAULT.withSystemRecordSeparator();
+        assertEquals(System.lineSeparator(), formatWithRecordSeparator.getRecordSeparator());
+    }
+}
diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java
index da49a78c..38d442e5 100644
--- a/src/test/java/org/apache/commons/csv/CSVParserTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java
@@ -1,1812 +1,1812 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.commons.csv;
-
-import static org.apache.commons.csv.Constants.CR;
-import static org.apache.commons.csv.Constants.CRLF;
-import static org.apache.commons.csv.Constants.LF;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PipedReader;
-import java.io.PipedWriter;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.UncheckedIOException;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.commons.io.input.BOMInputStream;
-import org.apache.commons.io.input.BrokenInputStream;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.EnumSource;
-
-/**
- * CSVParserTest
- *
- * The test are organized in three different sections: The 'setter/getter' section, the lexer section and finally the parser section. In case a test fails, you
- * should follow a top-down approach for fixing a potential bug (its likely that the parser itself fails if the lexer has problems...).
- */
-public class CSVParserTest {
-
-    private static final CSVFormat EXCEL_WITH_HEADER = CSVFormat.EXCEL.withHeader();
-
-    private static final Charset UTF_8 = StandardCharsets.UTF_8;
-
-    private static final String UTF_8_NAME = UTF_8.name();
-
-    private static final String CSV_INPUT = "a,b,c,d\n" + " a , b , 1 2 \n" + "\"foo baar\", b,\n" +
-            // + " \"foo\n,,\n\"\",,\n\\\"\",d,e\n";
-            "   \"foo\n,,\n\"\",,\n\"\"\",d,e\n"; // changed to use standard CSV escaping
-
-    private static final String CSV_INPUT_1 = "a,b,c,d";
-
-    private static final String CSV_INPUT_2 = "a,b,1 2";
-
-    private static final String[][] RESULT = { { "a", "b", "c", "d" }, { "a", "b", "1 2" }, { "foo baar", "b", "" }, { "foo\n,,\n\",,\n\"", "d", "e" } };
-
-    // CSV with no header comments
-    private static final String CSV_INPUT_NO_COMMENT = "A,B" + CRLF + "1,2" + CRLF;
-
-    // CSV with a header comment
-    private static final String CSV_INPUT_HEADER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF;
-
-    // CSV with a single line header and trailer comment
-    private static final String CSV_INPUT_HEADER_TRAILER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF + "# comment";
-
-    // CSV with a multi-line header and trailer comment
-    private static final String CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT = "# multi-line" + CRLF + "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF +
-            "# multi-line" + CRLF + "# comment";
-
-    // Format with auto-detected header
-    private static final CSVFormat FORMAT_AUTO_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().get();
-
-    // Format with explicit header
-    // @formatter:off
-    private static final CSVFormat FORMAT_EXPLICIT_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT)
-            .setSkipHeaderRecord(true)
-            .setCommentMarker('#')
-            .setHeader("A", "B")
-            .get();
-    // @formatter:on
-
-    // Format with explicit header that does not skip the header line
-    // @formatter:off
-    CSVFormat FORMAT_EXPLICIT_HEADER_NOSKIP = CSVFormat.Builder.create(CSVFormat.DEFAULT)
-            .setCommentMarker('#')
-            .setHeader("A", "B")
-            .get();
-    // @formatter:on
-
-    @SuppressWarnings("resource") // caller releases
-    private BOMInputStream createBOMInputStream(final String resource) throws IOException {
-        return new BOMInputStream(ClassLoader.getSystemClassLoader().getResource(resource).openStream());
-    }
-
-    CSVRecord parse(final CSVParser parser, final int failParseRecordNo) throws IOException {
-        if (parser.getRecordNumber() + 1 == failParseRecordNo) {
-            assertThrows(IOException.class, () -> parser.nextRecord());
-            return null;
-        }
-        return parser.nextRecord();
-    }
-
-    private void parseFully(final CSVParser parser) {
-        parser.forEach(Assertions::assertNotNull);
-    }
-
-    @Test
-    public void testBackslashEscaping() throws IOException {
-        // To avoid confusion over the need for escaping chars in java code,
-        // We will test with a forward slash as the escape char, and a single
-        // quote as the encapsulator.
-
-        // @formatter:off
-        final String code = "one,two,three\n" + // 0
-            "'',''\n" + // 1) empty encapsulators
-            "/',/'\n" + // 2) single encapsulators
-            "'/'','/''\n" + // 3) single encapsulators encapsulated via escape
-            "'''',''''\n" + // 4) single encapsulators encapsulated via doubling
-            "/,,/,\n" + // 5) separator escaped
-            "//,//\n" + // 6) escape escaped
-            "'//','//'\n" + // 7) escape escaped in encapsulation
-            "   8   ,   \"quoted \"\" /\" // string\"   \n" + // don't eat spaces
-            "9,   /\n   \n" + // escaped newline
-            "";
-        final String[][] res = {{"one", "two", "three"}, // 0
-            {"", ""}, // 1
-            {"'", "'"}, // 2
-            {"'", "'"}, // 3
-            {"'", "'"}, // 4
-            {",", ","}, // 5
-            {"/", "/"}, // 6
-            {"/", "/"}, // 7
-            {"   8   ", "   \"quoted \"\" /\" / string\"   "}, {"9", "   \n   "} };
-        // @formatter:on
-        final CSVFormat format = CSVFormat.newFormat(',').withQuote('\'').withRecordSeparator(CRLF).withEscape('/').withIgnoreEmptyLines();
-        try (CSVParser parser = CSVParser.parse(code, format)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertFalse(records.isEmpty());
-            Utils.compare("Records do not match expected result", res, records);
-        }
-    }
-
-    @Test
-    public void testBackslashEscaping2() throws IOException {
-        // To avoid confusion over the need for escaping chars in java code,
-        // We will test with a forward slash as the escape char, and a single
-        // quote as the encapsulator.
-        // @formatter:off
-        final String code = "" + " , , \n" + // 1)
-            " \t ,  , \n" + // 2)
-            " // , /, , /,\n" + // 3)
-            "";
-        final String[][] res = {{" ", " ", " "}, // 1
-            {" \t ", "  ", " "}, // 2
-            {" / ", " , ", " ,"}, // 3
-        };
-        // @formatter:on
-        final CSVFormat format = CSVFormat.newFormat(',').withRecordSeparator(CRLF).withEscape('/').withIgnoreEmptyLines();
-        try (CSVParser parser = CSVParser.parse(code, format)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertFalse(records.isEmpty());
-            Utils.compare("", res, records);
-        }
-    }
-
-    @Test
-    @Disabled
-    public void testBackslashEscapingOld() throws IOException {
-        final String code = "one,two,three\n" + "on\\\"e,two\n" + "on\"e,two\n" + "one,\"tw\\\"o\"\n" + "one,\"t\\,wo\"\n" + "one,two,\"th,ree\"\n" +
-                "\"a\\\\\"\n" + "a\\,b\n" + "\"a\\\\,b\"";
-        final String[][] res = { { "one", "two", "three" }, { "on\\\"e", "two" }, { "on\"e", "two" }, { "one", "tw\"o" }, { "one", "t\\,wo" }, // backslash in
-                                                                                                                                               // quotes only
-                                                                                                                                               // escapes a
-                                                                                                                                               // delimiter
-                                                                                                                                               // (",")
-                { "one", "two", "th,ree" }, { "a\\\\" }, // backslash in quotes only escapes a delimiter (",")
-                { "a\\", "b" }, // a backslash must be returned
-                { "a\\\\,b" } // backslash in quotes only escapes a delimiter (",")
-        };
-        try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(res.length, records.size());
-            assertFalse(records.isEmpty());
-            for (int i = 0; i < res.length; i++) {
-                assertArrayEquals(res[i], records.get(i).values());
-            }
-        }
-    }
-
-    @Test
-    @Disabled("CSV-107")
-    public void testBOM() throws IOException {
-        final URL url = ClassLoader.getSystemClassLoader().getResource("org/apache/commons/csv/CSVFileParser/bom.csv");
-        try (CSVParser parser = CSVParser.parse(url, StandardCharsets.UTF_8, EXCEL_WITH_HEADER)) {
-            parser.forEach(record -> assertNotNull(record.get("Date")));
-        }
-    }
-
-    @Test
-    public void testBOMInputStreamParserWithInputStream() throws IOException {
-        try (BOMInputStream inputStream = createBOMInputStream("org/apache/commons/csv/CSVFileParser/bom.csv");
-                CSVParser parser = CSVParser.parse(inputStream, UTF_8, EXCEL_WITH_HEADER)) {
-            parser.forEach(record -> assertNotNull(record.get("Date")));
-        }
-    }
-
-    @Test
-    public void testBOMInputStreamParserWithReader() throws IOException {
-        try (Reader reader = new InputStreamReader(createBOMInputStream("org/apache/commons/csv/CSVFileParser/bom.csv"), UTF_8_NAME);
-                CSVParser parser = CSVParser.builder()
-                        .setReader(reader)
-                        .setFormat(EXCEL_WITH_HEADER)
-                        .get()) {
-            parser.forEach(record -> assertNotNull(record.get("Date")));
-        }
-    }
-
-    @Test
-    public void testBOMInputStreamParseWithReader() throws IOException {
-        try (Reader reader = new InputStreamReader(createBOMInputStream("org/apache/commons/csv/CSVFileParser/bom.csv"), UTF_8_NAME);
-                CSVParser parser = CSVParser.builder()
-                        .setReader(reader)
-                        .setFormat(EXCEL_WITH_HEADER)
-                        .get()) {
-            parser.forEach(record -> assertNotNull(record.get("Date")));
-        }
-    }
-
-    @Test
-    public void testCarriageReturnEndings() throws IOException {
-        final String string = "foo\rbaar,\rhello,world\r,kanu";
-        try (CSVParser parser = CSVParser.builder().setCharSequence(string).get()) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(4, records.size());
-        }
-    }
-
-    @Test
-    public void testCarriageReturnLineFeedEndings() throws IOException {
-        final String string = "foo\r\nbaar,\r\nhello,world\r\n,kanu";
-        try (CSVParser parser = CSVParser.builder().setCharSequence(string).get()) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(4, records.size());
-        }
-    }
-
-    @Test
-    public void testClose() throws Exception {
-        final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z");
-        final Iterator<CSVRecord> records;
-        try (CSVParser parser = CSVFormat.DEFAULT.withCommentMarker('#').withHeader().parse(in)) {
-            records = parser.iterator();
-            assertTrue(records.hasNext());
-        }
-        assertFalse(records.hasNext());
-        assertThrows(NoSuchElementException.class, records::next);
-    }
-
-    @Test
-    public void testCSV141CSVFormat_DEFAULT() throws Exception {
-        testCSV141Failure(CSVFormat.DEFAULT, 3);
-    }
-
-    @Test
-    public void testCSV141CSVFormat_INFORMIX_UNLOAD() throws Exception {
-        testCSV141Failure(CSVFormat.INFORMIX_UNLOAD, 1);
-    }
-
-    @Test
-    public void testCSV141CSVFormat_INFORMIX_UNLOAD_CSV() throws Exception {
-        testCSV141Failure(CSVFormat.INFORMIX_UNLOAD_CSV, 3);
-    }
-
-    @Test
-    public void testCSV141CSVFormat_ORACLE() throws Exception {
-        testCSV141Failure(CSVFormat.ORACLE, 2);
-    }
-
-    @Test
-    public void testCSV141CSVFormat_POSTGRESQL_CSV() throws Exception {
-        testCSV141Failure(CSVFormat.POSTGRESQL_CSV, 3);
-    }
-
-    @Test
-    public void testCSV141Excel() throws Exception {
-        testCSV141Ok(CSVFormat.EXCEL);
-    }
-
-    private void testCSV141Failure(final CSVFormat format, final int failParseRecordNo) throws IOException {
-        final Path path = Paths.get("src/test/resources/org/apache/commons/csv/CSV-141/csv-141.csv");
-        try (CSVParser parser = CSVParser.parse(path, StandardCharsets.UTF_8, format)) {
-            // row 1
-            CSVRecord record = parse(parser, failParseRecordNo);
-            if (record == null) {
-                return; // expected failure
-            }
-            assertEquals("1414770317901", record.get(0));
-            assertEquals("android.widget.EditText", record.get(1));
-            assertEquals("pass sem1 _84*|*", record.get(2));
-            assertEquals("0", record.get(3));
-            assertEquals("pass sem1 _8", record.get(4));
-            assertEquals(5, record.size());
-            // row 2
-            record = parse(parser, failParseRecordNo);
-            if (record == null) {
-                return; // expected failure
-            }
-            assertEquals("1414770318470", record.get(0));
-            assertEquals("android.widget.EditText", record.get(1));
-            assertEquals("pass sem1 _84:|", record.get(2));
-            assertEquals("0", record.get(3));
-            assertEquals("pass sem1 _84:\\", record.get(4));
-            assertEquals(5, record.size());
-            // row 3: Fail for certain
-            assertThrows(IOException.class, () -> parser.nextRecord());
-        }
-    }
-
-    private void testCSV141Ok(final CSVFormat format) throws IOException {
-        final Path path = Paths.get("src/test/resources/org/apache/commons/csv/CSV-141/csv-141.csv");
-        try (CSVParser parser = CSVParser.parse(path, StandardCharsets.UTF_8, format)) {
-            // row 1
-            CSVRecord record = parser.nextRecord();
-            assertEquals("1414770317901", record.get(0));
-            assertEquals("android.widget.EditText", record.get(1));
-            assertEquals("pass sem1 _84*|*", record.get(2));
-            assertEquals("0", record.get(3));
-            assertEquals("pass sem1 _8", record.get(4));
-            assertEquals(5, record.size());
-            // row 2
-            record = parser.nextRecord();
-            assertEquals("1414770318470", record.get(0));
-            assertEquals("android.widget.EditText", record.get(1));
-            assertEquals("pass sem1 _84:|", record.get(2));
-            assertEquals("0", record.get(3));
-            assertEquals("pass sem1 _84:\\", record.get(4));
-            assertEquals(5, record.size());
-            // row 3
-            record = parser.nextRecord();
-            assertEquals("1414770318327", record.get(0));
-            assertEquals("android.widget.EditText", record.get(1));
-            assertEquals("pass sem1\n1414770318628\"", record.get(2));
-            assertEquals("android.widget.EditText", record.get(3));
-            assertEquals("pass sem1 _84*|*", record.get(4));
-            assertEquals("0", record.get(5));
-            assertEquals("pass sem1\n", record.get(6));
-            assertEquals(7, record.size());
-            // EOF
-            record = parser.nextRecord();
-            assertNull(record);
-        }
-    }
-
-    @Test
-    public void testCSV141RFC4180() throws Exception {
-        testCSV141Failure(CSVFormat.RFC4180, 3);
-    }
-
-    @Test
-    public void testCSV235() throws IOException {
-        final String dqString = "\"aaa\",\"b\"\"bb\",\"ccc\""; // "aaa","b""bb","ccc"
-        try (CSVParser parser = CSVFormat.RFC4180.parse(new StringReader(dqString))) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertFalse(records.hasNext());
-            assertEquals(3, record.size());
-            assertEquals("aaa", record.get(0));
-            assertEquals("b\"bb", record.get(1));
-            assertEquals("ccc", record.get(2));
-        }
-    }
-
-    @Test
-    public void testCSV57() throws Exception {
-        try (CSVParser parser = CSVParser.parse("", CSVFormat.DEFAULT)) {
-            final List<CSVRecord> list = parser.getRecords();
-            assertNotNull(list);
-            assertEquals(0, list.size());
-        }
-    }
-
-    @Test
-    public void testDefaultFormat() throws IOException {
-        // @formatter:off
-        final String code = "" + "a,b#\n" + // 1)
-            "\"\n\",\" \",#\n" +            // 2)
-            "#,\"\"\n" +                    // 3)
-            "# Final comment\n"             // 4)
-        ;
-        // @formatter:on
-        final String[][] res = { { "a", "b#" }, { "\n", " ", "#" }, { "#", "" }, { "# Final comment" } };
-        CSVFormat format = CSVFormat.DEFAULT;
-        assertFalse(format.isCommentMarkerSet());
-        final String[][] resComments = { { "a", "b#" }, { "\n", " ", "#" } };
-        try (CSVParser parser = CSVParser.parse(code, format)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertFalse(records.isEmpty());
-            Utils.compare("Failed to parse without comments", res, records);
-            format = CSVFormat.DEFAULT.withCommentMarker('#');
-        }
-        try (CSVParser parser = CSVParser.parse(code, format)) {
-            final List<CSVRecord> records = parser.getRecords();
-            Utils.compare("Failed to parse with comments", resComments, records);
-        }
-    }
-
-    @Test
-    public void testDuplicateHeadersAllowedByDefault() throws Exception {
-        try (CSVParser parser = CSVParser.parse("a,b,a\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader())) {
-            // noop
-        }
-    }
-
-    @Test
-    public void testDuplicateHeadersNotAllowed() {
-        assertThrows(IllegalArgumentException.class,
-                () -> CSVParser.parse("a,b,a\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader().withAllowDuplicateHeaderNames(false)));
-    }
-
-    @Test
-    public void testEmptyFile() throws Exception {
-        try (CSVParser parser = CSVParser.parse(Paths.get("src/test/resources/org/apache/commons/csv/empty.txt"), StandardCharsets.UTF_8,
-                CSVFormat.DEFAULT)) {
-            assertNull(parser.nextRecord());
-        }
-    }
-
-    @Test
-    public void testEmptyFileHeaderParsing() throws Exception {
-        try (CSVParser parser = CSVParser.parse("", CSVFormat.DEFAULT.withFirstRecordAsHeader())) {
-            assertNull(parser.nextRecord());
-            assertTrue(parser.getHeaderNames().isEmpty());
-        }
-    }
-
-    @Test
-    public void testEmptyLineBehaviorCSV() throws Exception {
-        final String[] codes = { "hello,\r\n\r\n\r\n", "hello,\n\n\n", "hello,\"\"\r\n\r\n\r\n", "hello,\"\"\n\n\n" };
-        final String[][] res = { { "hello", "" } // CSV format ignores empty lines
-        };
-        for (final String code : codes) {
-            try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
-                final List<CSVRecord> records = parser.getRecords();
-                assertEquals(res.length, records.size());
-                assertFalse(records.isEmpty());
-                for (int i = 0; i < res.length; i++) {
-                    assertArrayEquals(res[i], records.get(i).values());
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testEmptyLineBehaviorExcel() throws Exception {
-        final String[] codes = { "hello,\r\n\r\n\r\n", "hello,\n\n\n", "hello,\"\"\r\n\r\n\r\n", "hello,\"\"\n\n\n" };
-        final String[][] res = { { "hello", "" }, { "" }, // Excel format does not ignore empty lines
-                { "" } };
-        for (final String code : codes) {
-            try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
-                final List<CSVRecord> records = parser.getRecords();
-                assertEquals(res.length, records.size());
-                assertFalse(records.isEmpty());
-                for (int i = 0; i < res.length; i++) {
-                    assertArrayEquals(res[i], records.get(i).values());
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testEmptyString() throws Exception {
-        try (CSVParser parser = CSVParser.parse("", CSVFormat.DEFAULT)) {
-            assertNull(parser.nextRecord());
-        }
-    }
-
-    @Test
-    public void testEndOfFileBehaviorCSV() throws Exception {
-        final String[] codes = { "hello,\r\n\r\nworld,\r\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\r\n", "hello,\r\n\r\nworld,\"\"",
-                "hello,\r\n\r\nworld,\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\n", "hello,\r\n\r\nworld,\"\"" };
-        final String[][] res = { { "hello", "" }, // CSV format ignores empty lines
-                { "world", "" } };
-        for (final String code : codes) {
-            try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
-                final List<CSVRecord> records = parser.getRecords();
-                assertEquals(res.length, records.size());
-                assertFalse(records.isEmpty());
-                for (int i = 0; i < res.length; i++) {
-                    assertArrayEquals(res[i], records.get(i).values());
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testEndOfFileBehaviorExcel() throws Exception {
-        final String[] codes = { "hello,\r\n\r\nworld,\r\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\r\n", "hello,\r\n\r\nworld,\"\"",
-                "hello,\r\n\r\nworld,\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\n", "hello,\r\n\r\nworld,\"\"" };
-        final String[][] res = { { "hello", "" }, { "" }, // Excel format does not ignore empty lines
-                { "world", "" } };
-
-        for (final String code : codes) {
-            try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
-                final List<CSVRecord> records = parser.getRecords();
-                assertEquals(res.length, records.size());
-                assertFalse(records.isEmpty());
-                for (int i = 0; i < res.length; i++) {
-                    assertArrayEquals(res[i], records.get(i).values());
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testExcelFormat1() throws IOException {
-        final String code = "value1,value2,value3,value4\r\na,b,c,d\r\n  x,,," + "\r\n\r\n\"\"\"hello\"\"\",\"  \"\"world\"\"\",\"abc\ndef\",\r\n";
-        final String[][] res = { { "value1", "value2", "value3", "value4" }, { "a", "b", "c", "d" }, { "  x", "", "", "" }, { "" },
-                { "\"hello\"", "  \"world\"", "abc\ndef", "" } };
-        try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(res.length, records.size());
-            assertFalse(records.isEmpty());
-            for (int i = 0; i < res.length; i++) {
-                assertArrayEquals(res[i], records.get(i).values());
-            }
-        }
-    }
-
-    @Test
-    public void testExcelFormat2() throws Exception {
-        final String code = "foo,baar\r\n\r\nhello,\r\n\r\nworld,\r\n";
-        final String[][] res = { { "foo", "baar" }, { "" }, { "hello", "" }, { "" }, { "world", "" } };
-        try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(res.length, records.size());
-            assertFalse(records.isEmpty());
-            for (int i = 0; i < res.length; i++) {
-                assertArrayEquals(res[i], records.get(i).values());
-            }
-        }
-    }
-
-    /**
-     * Tests an exported Excel worksheet with a header row and rows that have more columns than the headers
-     */
-    @Test
-    public void testExcelHeaderCountLessThanData() throws Exception {
-        final String code = "A,B,C,,\r\na,b,c,d,e\r\n";
-        try (CSVParser parser = CSVParser.parse(code, EXCEL_WITH_HEADER)) {
-            parser.getRecords().forEach(record -> {
-                assertEquals("a", record.get("A"));
-                assertEquals("b", record.get("B"));
-                assertEquals("c", record.get("C"));
-            });
-        }
-    }
-
-    @Test
-    public void testFirstEndOfLineCr() throws IOException {
-        final String data = "foo\rbaar,\rhello,world\r,kanu";
-        try (CSVParser parser = CSVParser.parse(data, CSVFormat.DEFAULT)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(4, records.size());
-            assertEquals("\r", parser.getFirstEndOfLine());
-        }
-    }
-
-    @Test
-    public void testFirstEndOfLineCrLf() throws IOException {
-        final String data = "foo\r\nbaar,\r\nhello,world\r\n,kanu";
-        try (CSVParser parser = CSVParser.parse(data, CSVFormat.DEFAULT)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(4, records.size());
-            assertEquals("\r\n", parser.getFirstEndOfLine());
-        }
-    }
-
-    @Test
-    public void testFirstEndOfLineLf() throws IOException {
-        final String data = "foo\nbaar,\nhello,world\n,kanu";
-        try (CSVParser parser = CSVParser.parse(data, CSVFormat.DEFAULT)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(4, records.size());
-            assertEquals("\n", parser.getFirstEndOfLine());
-        }
-    }
-
-    @Test
-    public void testForEach() throws Exception {
-        try (Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-                CSVParser parser = CSVFormat.DEFAULT.parse(in)) {
-            final List<CSVRecord> records = new ArrayList<>();
-            for (final CSVRecord record : parser) {
-                records.add(record);
-            }
-            assertEquals(3, records.size());
-            assertArrayEquals(new String[] { "a", "b", "c" }, records.get(0).values());
-            assertArrayEquals(new String[] { "1", "2", "3" }, records.get(1).values());
-            assertArrayEquals(new String[] { "x", "y", "z" }, records.get(2).values());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_HeaderComment1() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) {
-            parser.getRecords();
-            // Expect a header comment
-            assertTrue(parser.hasHeaderComment());
-            assertEquals("header comment", parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_HeaderComment2() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
-            parser.getRecords();
-            // Expect a header comment
-            assertTrue(parser.hasHeaderComment());
-            assertEquals("header comment", parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_HeaderComment3() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
-            parser.getRecords();
-            // Expect no header comment - the text "comment" is attached to the first record
-            assertFalse(parser.hasHeaderComment());
-            assertNull(parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_HeaderTrailerComment() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
-            parser.getRecords();
-            // Expect a header comment
-            assertTrue(parser.hasHeaderComment());
-            assertEquals("multi-line" + LF + "header comment", parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_NoComment1() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_AUTO_HEADER)) {
-            parser.getRecords();
-            // Expect no header comment
-            assertFalse(parser.hasHeaderComment());
-            assertNull(parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_NoComment2() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER)) {
-            parser.getRecords();
-            // Expect no header comment
-            assertFalse(parser.hasHeaderComment());
-            assertNull(parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderComment_NoComment3() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
-            parser.getRecords();
-            // Expect no header comment
-            assertFalse(parser.hasHeaderComment());
-            assertNull(parser.getHeaderComment());
-        }
-    }
-
-    @Test
-    public void testGetHeaderMap() throws Exception {
-        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
-            final Map<String, Integer> headerMap = parser.getHeaderMap();
-            final Iterator<String> columnNames = headerMap.keySet().iterator();
-            // Headers are iterated in column order.
-            assertEquals("A", columnNames.next());
-            assertEquals("B", columnNames.next());
-            assertEquals("C", columnNames.next());
-            final Iterator<CSVRecord> records = parser.iterator();
-
-            // Parse to make sure getHeaderMap did not have a side-effect.
-            for (int i = 0; i < 3; i++) {
-                assertTrue(records.hasNext());
-                final CSVRecord record = records.next();
-                assertEquals(record.get(0), record.get("A"));
-                assertEquals(record.get(1), record.get("B"));
-                assertEquals(record.get(2), record.get("C"));
-            }
-
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    public void testGetHeaderNames() throws IOException {
-        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
-            final Map<String, Integer> nameIndexMap = parser.getHeaderMap();
-            final List<String> headerNames = parser.getHeaderNames();
-            assertNotNull(headerNames);
-            assertEquals(nameIndexMap.size(), headerNames.size());
-            for (int i = 0; i < headerNames.size(); i++) {
-                final String name = headerNames.get(i);
-                assertEquals(i, nameIndexMap.get(name).intValue());
-            }
-        }
-    }
-
-    @Test
-    public void testGetHeaderNamesReadOnly() throws IOException {
-        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
-            final List<String> headerNames = parser.getHeaderNames();
-            assertNotNull(headerNames);
-            assertThrows(UnsupportedOperationException.class, () -> headerNames.add("This is a read-only list."));
-        }
-    }
-
-    @Test
-    public void testGetLine() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT, CSVFormat.DEFAULT.withIgnoreSurroundingSpaces())) {
-            for (final String[] re : RESULT) {
-                assertArrayEquals(re, parser.nextRecord().values());
-            }
-
-            assertNull(parser.nextRecord());
-        }
-    }
-
-    @Test
-    public void testGetLineNumberWithCR() throws Exception {
-        validateLineNumbers(String.valueOf(CR));
-    }
-
-    @Test
-    public void testGetLineNumberWithCRLF() throws Exception {
-        validateLineNumbers(CRLF);
-    }
-
-    @Test
-    public void testGetLineNumberWithLF() throws Exception {
-        validateLineNumbers(String.valueOf(LF));
-    }
-
-    @Test
-    public void testGetOneLine() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_1, CSVFormat.DEFAULT)) {
-            final CSVRecord record = parser.getRecords().get(0);
-            assertArrayEquals(RESULT[0], record.values());
-        }
-    }
-
-    /**
-     * Tests reusing a parser to process new string records one at a time as they are being discovered. See [CSV-110].
-     *
-     * @throws IOException when an I/O error occurs.
-     */
-    @Test
-    public void testGetOneLineOneParser() throws IOException {
-        final CSVFormat format = CSVFormat.DEFAULT;
-        try (PipedWriter writer = new PipedWriter();
-                CSVParser parser = CSVParser.builder()
-                        .setReader(new PipedReader(writer))
-                        .setFormat(format)
-                        .get()) {
-            writer.append(CSV_INPUT_1);
-            writer.append(format.getRecordSeparator());
-            final CSVRecord record1 = parser.nextRecord();
-            assertArrayEquals(RESULT[0], record1.values());
-            writer.append(CSV_INPUT_2);
-            writer.append(format.getRecordSeparator());
-            final CSVRecord record2 = parser.nextRecord();
-            assertArrayEquals(RESULT[1], record2.values());
-        }
-    }
-
-    @Test
-    public void testGetRecordFourBytesRead() throws Exception {
-        final String code = "id,a,b,c\n" +
-            "1,😊,🤔,😂\n" +
-            "2,😊,🤔,😂\n" +
-            "3,😊,🤔,😂\n";
-        final CSVFormat format = CSVFormat.Builder.create()
-            .setDelimiter(',')
-            .setQuote('\'')
-            .get();
-        try (CSVParser parser = CSVParser.builder().setReader(new StringReader(code)).setFormat(format).setCharset(UTF_8).setTrackBytes(true).get()) {
-            CSVRecord record = new CSVRecord(parser, null, null, 1L, 0L, 0L);
-
-            assertEquals(0, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(1, record.getRecordNumber());
-            assertEquals(code.indexOf('i'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), record.getCharacterPosition());
-
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(2, record.getRecordNumber());
-            assertEquals(code.indexOf('1'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), record.getCharacterPosition());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(code.indexOf('2'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), 26);
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(4, record.getRecordNumber());
-            assertEquals(code.indexOf('3'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), 43);
-        }
-    }
-
-    @Test
-    public void testGetRecordNumberWithCR() throws Exception {
-        validateRecordNumbers(String.valueOf(CR));
-    }
-
-    @Test
-    public void testGetRecordNumberWithCRLF() throws Exception {
-        validateRecordNumbers(CRLF);
-    }
-
-    @Test
-    public void testGetRecordNumberWithLF() throws Exception {
-        validateRecordNumbers(String.valueOf(LF));
-    }
-
-    @Test
-    public void testGetRecordPositionWithCRLF() throws Exception {
-        validateRecordPosition(CRLF);
-    }
-
-    @Test
-    public void testGetRecordPositionWithLF() throws Exception {
-        validateRecordPosition(String.valueOf(LF));
-    }
-
-    @Test
-    public void testGetRecords() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT, CSVFormat.DEFAULT.withIgnoreSurroundingSpaces())) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(RESULT.length, records.size());
-            assertFalse(records.isEmpty());
-            for (int i = 0; i < RESULT.length; i++) {
-                assertArrayEquals(RESULT[i], records.get(i).values());
-            }
-        }
-    }
-
-    @Test
-    public void testGetRecordsFromBrokenInputStream() throws IOException {
-        @SuppressWarnings("resource") // We also get an exception on close, which is OK but can't assert in a try.
-        final CSVParser parser = CSVParser.parse(new BrokenInputStream(), UTF_8, CSVFormat.DEFAULT);
-        assertThrows(UncheckedIOException.class, parser::getRecords);
-
-    }
-
-    @Test
-    public void testGetRecordThreeBytesRead() throws Exception {
-        final String code = "id,date,val5,val4\n" +
-            "11111111111111,'4017-09-01',きちんと節分近くには咲いてる~,v4\n" +
-            "22222222222222,'4017-01-01',おはよう私の友人~,v4\n" +
-            "33333333333333,'4017-01-01',きる自然の力ってすごいな~,v4\n";
-        final CSVFormat format = CSVFormat.Builder.create()
-            .setDelimiter(',')
-            .setQuote('\'')
-            .get();
-        try (CSVParser parser = CSVParser.builder().setReader(new StringReader(code)).setFormat(format).setCharset(UTF_8).setTrackBytes(true).get()) {
-            CSVRecord record = new CSVRecord(parser, null, null, 1L, 0L, 0L);
-
-            assertEquals(0, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(1, record.getRecordNumber());
-            assertEquals(code.indexOf('i'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), record.getCharacterPosition());
-
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(2, record.getRecordNumber());
-            assertEquals(code.indexOf('1'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), record.getCharacterPosition());
-
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(code.indexOf('2'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), 95);
-
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(4, record.getRecordNumber());
-            assertEquals(code.indexOf('3'), record.getCharacterPosition());
-            assertEquals(record.getBytePosition(), 154);
-        }
-    }
-
-    @Test
-    public void testGetRecordWithMultiLineValues() throws Exception {
-        try (CSVParser parser = CSVParser.parse("\"a\r\n1\",\"a\r\n2\"" + CRLF + "\"b\r\n1\",\"b\r\n2\"" + CRLF + "\"c\r\n1\",\"c\r\n2\"",
-                CSVFormat.DEFAULT.withRecordSeparator(CRLF))) {
-            CSVRecord record;
-            assertEquals(0, parser.getRecordNumber());
-            assertEquals(0, parser.getCurrentLineNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(3, parser.getCurrentLineNumber());
-            assertEquals(1, record.getRecordNumber());
-            assertEquals(1, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(6, parser.getCurrentLineNumber());
-            assertEquals(2, record.getRecordNumber());
-            assertEquals(2, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(9, parser.getCurrentLineNumber());
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(3, parser.getRecordNumber());
-            assertNull(record = parser.nextRecord());
-            assertEquals(9, parser.getCurrentLineNumber());
-            assertEquals(3, parser.getRecordNumber());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_HeaderComment1() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) {
-            parser.getRecords();
-            assertFalse(parser.hasTrailerComment());
-            assertNull(parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_HeaderComment2() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
-            parser.getRecords();
-            assertFalse(parser.hasTrailerComment());
-            assertNull(parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_HeaderComment3() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
-            parser.getRecords();
-            assertFalse(parser.hasTrailerComment());
-            assertNull(parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_HeaderTrailerComment1() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
-            parser.getRecords();
-            assertTrue(parser.hasTrailerComment());
-            assertEquals("comment", parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_HeaderTrailerComment2() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
-            parser.getRecords();
-            assertTrue(parser.hasTrailerComment());
-            assertEquals("comment", parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_HeaderTrailerComment3() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
-            parser.getRecords();
-            assertTrue(parser.hasTrailerComment());
-            assertEquals("comment", parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testGetTrailerComment_MultilineComment() throws IOException {
-        try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
-            parser.getRecords();
-            assertTrue(parser.hasTrailerComment());
-            assertEquals("multi-line" + LF + "comment", parser.getTrailerComment());
-        }
-    }
-
-    @Test
-    public void testHeader() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-
-            for (int i = 0; i < 2; i++) {
-                assertTrue(records.hasNext());
-                final CSVRecord record = records.next();
-                assertEquals(record.get(0), record.get("a"));
-                assertEquals(record.get(1), record.get("b"));
-                assertEquals(record.get(2), record.get("c"));
-            }
-
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    public void testHeaderComment() throws Exception {
-        final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withCommentMarker('#').withHeader().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            for (int i = 0; i < 2; i++) {
-                assertTrue(records.hasNext());
-                final CSVRecord record = records.next();
-                assertEquals(record.get(0), record.get("a"));
-                assertEquals(record.get(1), record.get("b"));
-                assertEquals(record.get(2), record.get("c"));
-            }
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    public void testHeaderMissing() throws Exception {
-        final Reader in = new StringReader("a,,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().withAllowMissingColumnNames().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            for (int i = 0; i < 2; i++) {
-                assertTrue(records.hasNext());
-                final CSVRecord record = records.next();
-                assertEquals(record.get(0), record.get("a"));
-                assertEquals(record.get(2), record.get("c"));
-            }
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    public void testHeaderMissingWithNull() throws Exception {
-        final Reader in = new StringReader("a,,c,,e\n1,2,3,4,5\nv,w,x,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().withNullString("").withAllowMissingColumnNames().parse(in)) {
-            parser.iterator();
-        }
-    }
-
-    @Test
-    public void testHeadersMissing() throws Exception {
-        try (Reader in = new StringReader("a,,c,,e\n1,2,3,4,5\nv,w,x,y,z");
-                CSVParser parser = CSVFormat.DEFAULT.withHeader().withAllowMissingColumnNames().parse(in)) {
-            parser.iterator();
-        }
-    }
-
-    @Test
-    public void testHeadersMissingException() {
-        final Reader in = new StringReader("a,,c,,e\n1,2,3,4,5\nv,w,x,y,z");
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withHeader().parse(in).iterator());
-    }
-
-    @Test
-    public void testHeadersMissingOneColumnException() {
-        final Reader in = new StringReader("a,,c,d,e\n1,2,3,4,5\nv,w,x,y,z");
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withHeader().parse(in).iterator());
-    }
-
-    @Test
-    public void testHeadersWithNullColumnName() throws IOException {
-        final Reader in = new StringReader("header1,null,header3\n1,2,3\n4,5,6");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().withNullString("null").withAllowMissingColumnNames().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            // Expect the null header to be missing
-            @SuppressWarnings("resource")
-            final CSVParser recordParser = record.getParser();
-            assertEquals(Arrays.asList("header1", "header3"), recordParser.getHeaderNames());
-            assertEquals(2, recordParser.getHeaderMap().size());
-        }
-    }
-
-    @Test
-    public void testIgnoreCaseHeaderMapping() throws Exception {
-        final Reader reader = new StringReader("1,2,3");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("One", "TWO", "three").withIgnoreHeaderCase().parse(reader)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("one"));
-            assertEquals("2", record.get("two"));
-            assertEquals("3", record.get("THREE"));
-        }
-    }
-
-    @Test
-    public void testIgnoreEmptyLines() throws IOException {
-        final String code = "\nfoo,baar\n\r\n,\n\n,world\r\n\n";
-        // String code = "world\r\n\n";
-        // String code = "foo;baar\r\n\r\nhello;\r\n\r\nworld;\r\n";
-        try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(3, records.size());
-        }
-    }
-
-    @Test
-    public void testInvalidFormat() {
-        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter(CR));
-    }
-
-    @Test
-    public void testIterator() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.parse(in)) {
-            final Iterator<CSVRecord> iterator = parser.iterator();
-            assertTrue(iterator.hasNext());
-            assertThrows(UnsupportedOperationException.class, iterator::remove);
-            assertArrayEquals(new String[] { "a", "b", "c" }, iterator.next().values());
-            assertArrayEquals(new String[] { "1", "2", "3" }, iterator.next().values());
-            assertTrue(iterator.hasNext());
-            assertTrue(iterator.hasNext());
-            assertTrue(iterator.hasNext());
-            assertArrayEquals(new String[] { "x", "y", "z" }, iterator.next().values());
-            assertFalse(iterator.hasNext());
-            assertThrows(NoSuchElementException.class, iterator::next);
-        }
-    }
-
-    @Test
-    public void testIteratorSequenceBreaking() throws IOException {
-        final String fiveRows = "1\n2\n3\n4\n5\n";
-        // Iterator hasNext() shouldn't break sequence
-        try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(fiveRows))) {
-            final Iterator<CSVRecord> iter = parser.iterator();
-            int recordNumber = 0;
-            while (iter.hasNext()) {
-                final CSVRecord record = iter.next();
-                recordNumber++;
-                assertEquals(String.valueOf(recordNumber), record.get(0));
-                if (recordNumber >= 2) {
-                    break;
-                }
-            }
-            iter.hasNext();
-            while (iter.hasNext()) {
-                final CSVRecord record = iter.next();
-                recordNumber++;
-                assertEquals(String.valueOf(recordNumber), record.get(0));
-            }
-        }
-        // Consecutive enhanced for loops shouldn't break sequence
-        try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(fiveRows))) {
-            int recordNumber = 0;
-            for (final CSVRecord record : parser) {
-                recordNumber++;
-                assertEquals(String.valueOf(recordNumber), record.get(0));
-                if (recordNumber >= 2) {
-                    break;
-                }
-            }
-            for (final CSVRecord record : parser) {
-                recordNumber++;
-                assertEquals(String.valueOf(recordNumber), record.get(0));
-            }
-        }
-        // Consecutive enhanced for loops with hasNext() peeking shouldn't break sequence
-        try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(fiveRows))) {
-            int recordNumber = 0;
-            for (final CSVRecord record : parser) {
-                recordNumber++;
-                assertEquals(String.valueOf(recordNumber), record.get(0));
-                if (recordNumber >= 2) {
-                    break;
-                }
-            }
-            parser.iterator().hasNext();
-            for (final CSVRecord record : parser) {
-                recordNumber++;
-                assertEquals(String.valueOf(recordNumber), record.get(0));
-            }
-        }
-    }
-
-    @Test
-    public void testLineFeedEndings() throws IOException {
-        final String code = "foo\nbaar,\nhello,world\n,kanu";
-        try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
-            final List<CSVRecord> records = parser.getRecords();
-            assertEquals(4, records.size());
-        }
-    }
-
-    @Test
-    public void testMappedButNotSetAsOutlook2007ContactExport() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("A", "B", "C").withSkipHeaderRecord().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            CSVRecord record;
-            // 1st record
-            record = records.next();
-            assertTrue(record.isMapped("A"));
-            assertTrue(record.isMapped("B"));
-            assertTrue(record.isMapped("C"));
-            assertTrue(record.isSet("A"));
-            assertTrue(record.isSet("B"));
-            assertFalse(record.isSet("C"));
-            assertEquals("1", record.get("A"));
-            assertEquals("2", record.get("B"));
-            assertFalse(record.isConsistent());
-            // 2nd record
-            record = records.next();
-            assertTrue(record.isMapped("A"));
-            assertTrue(record.isMapped("B"));
-            assertTrue(record.isMapped("C"));
-            assertTrue(record.isSet("A"));
-            assertTrue(record.isSet("B"));
-            assertTrue(record.isSet("C"));
-            assertEquals("x", record.get("A"));
-            assertEquals("y", record.get("B"));
-            assertEquals("z", record.get("C"));
-            assertTrue(record.isConsistent());
-            // end
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    @Disabled
-    public void testMongoDbCsv() throws Exception {
-        try (CSVParser parser = CSVParser.parse("\"a a\",b,c" + LF + "d,e,f", CSVFormat.MONGODB_CSV)) {
-            final Iterator<CSVRecord> itr1 = parser.iterator();
-            final Iterator<CSVRecord> itr2 = parser.iterator();
-
-            final CSVRecord first = itr1.next();
-            assertEquals("a a", first.get(0));
-            assertEquals("b", first.get(1));
-            assertEquals("c", first.get(2));
-
-            final CSVRecord second = itr2.next();
-            assertEquals("d", second.get(0));
-            assertEquals("e", second.get(1));
-            assertEquals("f", second.get(2));
-        }
-    }
-
-    @Test
-    // TODO this may lead to strange behavior, throw an exception if iterator() has already been called?
-    public void testMultipleIterators() throws Exception {
-        try (CSVParser parser = CSVParser.parse("a,b,c" + CRLF + "d,e,f", CSVFormat.DEFAULT)) {
-            final Iterator<CSVRecord> itr1 = parser.iterator();
-
-            final CSVRecord first = itr1.next();
-            assertEquals("a", first.get(0));
-            assertEquals("b", first.get(1));
-            assertEquals("c", first.get(2));
-
-            final CSVRecord second = itr1.next();
-            assertEquals("d", second.get(0));
-            assertEquals("e", second.get(1));
-            assertEquals("f", second.get(2));
-        }
-    }
-
-    @Test
-    public void testNewCSVParserNullReaderFormat() {
-        assertThrows(NullPointerException.class, () -> new CSVParser(null, CSVFormat.DEFAULT));
-    }
-
-    @Test
-    public void testNewCSVParserReaderNullFormat() {
-        assertThrows(NullPointerException.class, () -> new CSVParser(new StringReader(""), null));
-    }
-
-    @Test
-    public void testNoHeaderMap() throws Exception {
-        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT)) {
-            assertNull(parser.getHeaderMap());
-        }
-    }
-
-    @Test
-    public void testNotValueCSV() throws IOException {
-        final String source = "#";
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.withCommentMarker('#');
-        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
-            final CSVRecord csvRecord = csvParser.nextRecord();
-            assertNull(csvRecord);
-        }
-    }
-
-    @Test
-    public void testParse() throws Exception {
-        final ClassLoader loader = ClassLoader.getSystemClassLoader();
-        final URL url = loader.getResource("org/apache/commons/csv/CSVFileParser/test.csv");
-        final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader("A", "B", "C", "D").get();
-        final Charset charset = StandardCharsets.UTF_8;
-        // Reader
-        try (CSVParser parser = CSVParser.parse(new InputStreamReader(url.openStream(), charset), format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setReader(new InputStreamReader(url.openStream(), charset)).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // String
-        final Path path = Paths.get(url.toURI());
-        final String string = new String(Files.readAllBytes(path), charset);
-        try (CSVParser parser = CSVParser.parse(string, format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setCharSequence(string).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // File
-        final File file = new File(url.toURI());
-        try (CSVParser parser = CSVParser.parse(file, charset, format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setFile(file).setCharset(charset).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // InputStream
-        try (CSVParser parser = CSVParser.parse(url.openStream(), charset, format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setInputStream(url.openStream()).setCharset(charset).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // Path
-        try (CSVParser parser = CSVParser.parse(path, charset, format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setPath(path).setCharset(charset).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // URL
-        try (CSVParser parser = CSVParser.parse(url, charset, format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setURI(url.toURI()).setCharset(charset).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // InputStreamReader
-        try (CSVParser parser = new CSVParser(new InputStreamReader(url.openStream(), charset), format)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setReader(new InputStreamReader(url.openStream(), charset)).setFormat(format).get()) {
-            parseFully(parser);
-        }
-        // InputStreamReader with longs
-        try (CSVParser parser = new CSVParser(new InputStreamReader(url.openStream(), charset), format, /* characterOffset= */0, /* recordNumber= */1)) {
-            parseFully(parser);
-        }
-        try (CSVParser parser = CSVParser.builder().setReader(new InputStreamReader(url.openStream(), charset)).setFormat(format).setCharacterOffset(0)
-                .setRecordNumber(0).get()) {
-            parseFully(parser);
-        }
-    }
-
-    @Test
-    public void testParseFileNullFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse(new File("CSVFileParser/test.csv"), Charset.defaultCharset(), null));
-    }
-
-    @Test
-    public void testParseNullFileFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse((File) null, Charset.defaultCharset(), CSVFormat.DEFAULT));
-    }
-
-    @Test
-    public void testParseNullPathFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse((Path) null, Charset.defaultCharset(), CSVFormat.DEFAULT));
-    }
-
-    @Test
-    public void testParseNullStringFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse((String) null, CSVFormat.DEFAULT));
-    }
-
-    @Test
-    public void testParseNullUrlCharsetFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse((URL) null, Charset.defaultCharset(), CSVFormat.DEFAULT));
-    }
-
-    @Test
-    public void testParserUrlNullCharsetFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse(new URL("https://commons.apache.org"), null, CSVFormat.DEFAULT));
-    }
-
-    @Test
-    public void testParseStringNullFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse("csv data", (CSVFormat) null));
-    }
-
-    @Test
-    public void testParseUrlCharsetNullFormat() {
-        assertThrows(NullPointerException.class, () -> CSVParser.parse(new URL("https://commons.apache.org"), Charset.defaultCharset(), null));
-    }
-
-    @Test
-    public void testParseWithDelimiterStringWithEscape() throws IOException {
-        final String source = "a![!|!]b![|]c[|]xyz\r\nabc[abc][|]xyz";
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setEscape('!').get();
-        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
-            CSVRecord csvRecord = csvParser.nextRecord();
-            assertEquals("a[|]b![|]c", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-            csvRecord = csvParser.nextRecord();
-            assertEquals("abc[abc]", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-        }
-    }
-
-    @Test
-    public void testParseWithDelimiterStringWithQuote() throws IOException {
-        final String source = "'a[|]b[|]c'[|]xyz\r\nabc[abc][|]xyz";
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setQuote('\'').get();
-        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
-            CSVRecord csvRecord = csvParser.nextRecord();
-            assertEquals("a[|]b[|]c", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-            csvRecord = csvParser.nextRecord();
-            assertEquals("abc[abc]", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-        }
-    }
-
-    @Test
-    public void testParseWithDelimiterWithEscape() throws IOException {
-        final String source = "a!,b!,c,xyz";
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.withEscape('!');
-        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
-            final CSVRecord csvRecord = csvParser.nextRecord();
-            assertEquals("a,b,c", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-        }
-    }
-
-    @Test
-    public void testParseWithDelimiterWithQuote() throws IOException {
-        final String source = "'a,b,c',xyz";
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('\'');
-        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
-            final CSVRecord csvRecord = csvParser.nextRecord();
-            assertEquals("a,b,c", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-        }
-    }
-
-    @Test
-    public void testParseWithQuoteThrowsException() {
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('\'');
-        assertThrows(IOException.class, () -> csvFormat.parse(new StringReader("'a,b,c','")).nextRecord());
-        assertThrows(IOException.class, () -> csvFormat.parse(new StringReader("'a,b,c'abc,xyz")).nextRecord());
-        assertThrows(IOException.class, () -> csvFormat.parse(new StringReader("'abc'a,b,c',xyz")).nextRecord());
-    }
-
-    @Test
-    public void testParseWithQuoteWithEscape() throws IOException {
-        final String source = "'a?,b?,c?d',xyz";
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('\'').withEscape('?');
-        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
-            final CSVRecord csvRecord = csvParser.nextRecord();
-            assertEquals("a,b,c?d", csvRecord.get(0));
-            assertEquals("xyz", csvRecord.get(1));
-        }
-    }
-
-    @ParameterizedTest
-    @EnumSource(CSVFormat.Predefined.class)
-    public void testParsingPrintedEmptyFirstColumn(final CSVFormat.Predefined format) throws Exception {
-        final String[][] lines = { { "a", "b" }, { "", "x" } };
-        final StringWriter buf = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(buf, format.getFormat())) {
-            printer.printRecords(Stream.of(lines));
-        }
-        try (CSVParser csvRecords = CSVParser.builder()
-                .setReader(new StringReader(buf.toString()))
-                .setFormat(format.getFormat())
-                .get()) {
-            for (final String[] line : lines) {
-                assertArrayEquals(line, csvRecords.nextRecord().values());
-            }
-            assertNull(csvRecords.nextRecord());
-        }
-    }
-
-    @Test
-    public void testProvidedHeader() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("A", "B", "C").parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-
-            for (int i = 0; i < 3; i++) {
-                assertTrue(records.hasNext());
-                final CSVRecord record = records.next();
-                assertTrue(record.isMapped("A"));
-                assertTrue(record.isMapped("B"));
-                assertTrue(record.isMapped("C"));
-                assertFalse(record.isMapped("NOT MAPPED"));
-                assertEquals(record.get(0), record.get("A"));
-                assertEquals(record.get(1), record.get("B"));
-                assertEquals(record.get(2), record.get("C"));
-            }
-
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    public void testProvidedHeaderAuto() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-
-            for (int i = 0; i < 2; i++) {
-                assertTrue(records.hasNext());
-                final CSVRecord record = records.next();
-                assertTrue(record.isMapped("a"));
-                assertTrue(record.isMapped("b"));
-                assertTrue(record.isMapped("c"));
-                assertFalse(record.isMapped("NOT MAPPED"));
-                assertEquals(record.get(0), record.get("a"));
-                assertEquals(record.get(1), record.get("b"));
-                assertEquals(record.get(2), record.get("c"));
-            }
-
-            assertFalse(records.hasNext());
-        }
-    }
-
-    @Test
-    public void testRepeatedHeadersAreReturnedInCSVRecordHeaderNames() throws IOException {
-        final Reader in = new StringReader("header1,header2,header1\n1,2,3\n4,5,6");
-        try (CSVParser parser = CSVFormat.DEFAULT.withFirstRecordAsHeader().withTrim().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            @SuppressWarnings("resource")
-            final CSVParser recordParser = record.getParser();
-            assertEquals(Arrays.asList("header1", "header2", "header1"), recordParser.getHeaderNames());
-        }
-    }
-
-    @Test
-    public void testRoundtrip() throws Exception {
-        final StringWriter out = new StringWriter();
-        final String data = "a,b,c\r\n1,2,3\r\nx,y,z\r\n";
-        try (CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT);
-                CSVParser parse = CSVParser.parse(data, CSVFormat.DEFAULT)) {
-            for (final CSVRecord record : parse) {
-                printer.printRecord(record);
-            }
-            assertEquals(data, out.toString());
-        }
-    }
-
-    @Test
-    public void testSkipAutoHeader() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("a"));
-            assertEquals("2", record.get("b"));
-            assertEquals("3", record.get("c"));
-        }
-    }
-
-    @Test
-    public void testSkipHeaderOverrideDuplicateHeaders() throws Exception {
-        final Reader in = new StringReader("a,a,a\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("X"));
-            assertEquals("2", record.get("Y"));
-            assertEquals("3", record.get("Z"));
-        }
-    }
-
-    @Test
-    public void testSkipSetAltHeaders() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("X"));
-            assertEquals("2", record.get("Y"));
-            assertEquals("3", record.get("Z"));
-        }
-    }
-
-    @Test
-    public void testSkipSetHeader() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("a", "b", "c").withSkipHeaderRecord().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("a"));
-            assertEquals("2", record.get("b"));
-            assertEquals("3", record.get("c"));
-        }
-    }
-
-    @Test
-    @Disabled
-    public void testStartWithEmptyLinesThenHeaders() throws Exception {
-        final String[] codes = { "\r\n\r\n\r\nhello,\r\n\r\n\r\n", "hello,\n\n\n", "hello,\"\"\r\n\r\n\r\n", "hello,\"\"\n\n\n" };
-        final String[][] res = { { "hello", "" }, { "" }, // Excel format does not ignore empty lines
-                { "" } };
-        for (final String code : codes) {
-            try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
-                final List<CSVRecord> records = parser.getRecords();
-                assertEquals(res.length, records.size());
-                assertFalse(records.isEmpty());
-                for (int i = 0; i < res.length; i++) {
-                    assertArrayEquals(res[i], records.get(i).values());
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testStream() throws Exception {
-        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.parse(in)) {
-            final List<CSVRecord> list = parser.stream().collect(Collectors.toList());
-            assertFalse(list.isEmpty());
-            assertArrayEquals(new String[] { "a", "b", "c" }, list.get(0).values());
-            assertArrayEquals(new String[] { "1", "2", "3" }, list.get(1).values());
-            assertArrayEquals(new String[] { "x", "y", "z" }, list.get(2).values());
-        }
-    }
-
-    @Test
-    public void testThrowExceptionWithLineAndPosition() throws IOException {
-        final String csvContent = "col1,col2,col3,col4,col5,col6,col7,col8,col9,col10\nrec1,rec2,rec3,rec4,rec5,rec6,rec7,rec8,\"\"rec9\"\",rec10";
-        final StringReader stringReader = new StringReader(csvContent);
-        // @formatter:off
-        final CSVFormat csvFormat = CSVFormat.DEFAULT.builder()
-                .setHeader()
-                .setSkipHeaderRecord(true)
-                .get();
-        // @formatter:on
-        try (CSVParser csvParser = csvFormat.parse(stringReader)) {
-            final UncheckedIOException exception = assertThrows(UncheckedIOException.class, csvParser::getRecords);
-            assertInstanceOf(CSVException.class, exception.getCause());
-            assertTrue(exception.getMessage().contains("Invalid character between encapsulated token and delimiter at line: 2, position: 94"),
-                    exception::getMessage);
-        }
-    }
-
-    @Test
-    public void testTrailingDelimiter() throws Exception {
-        final Reader in = new StringReader("a,a,a,\n\"1\",\"2\",\"3\",\nx,y,z,");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().withTrailingDelimiter().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("X"));
-            assertEquals("2", record.get("Y"));
-            assertEquals("3", record.get("Z"));
-            assertEquals(3, record.size());
-        }
-    }
-
-    @Test
-    public void testTrim() throws Exception {
-        final Reader in = new StringReader("a,a,a\n\" 1 \",\" 2 \",\" 3 \"\nx,y,z");
-        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().withTrim().parse(in)) {
-            final Iterator<CSVRecord> records = parser.iterator();
-            final CSVRecord record = records.next();
-            assertEquals("1", record.get("X"));
-            assertEquals("2", record.get("Y"));
-            assertEquals("3", record.get("Z"));
-            assertEquals(3, record.size());
-        }
-    }
-
-    private void validateLineNumbers(final String lineSeparator) throws IOException {
-        try (CSVParser parser = CSVParser.parse("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.DEFAULT.withRecordSeparator(lineSeparator))) {
-            assertEquals(0, parser.getCurrentLineNumber());
-            assertNotNull(parser.nextRecord());
-            assertEquals(1, parser.getCurrentLineNumber());
-            assertNotNull(parser.nextRecord());
-            assertEquals(2, parser.getCurrentLineNumber());
-            assertNotNull(parser.nextRecord());
-            // Read EOF without EOL should 3
-            assertEquals(3, parser.getCurrentLineNumber());
-            assertNull(parser.nextRecord());
-            // Read EOF without EOL should 3
-            assertEquals(3, parser.getCurrentLineNumber());
-        }
-    }
-
-    private void validateRecordNumbers(final String lineSeparator) throws IOException {
-        try (CSVParser parser = CSVParser.parse("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.DEFAULT.withRecordSeparator(lineSeparator))) {
-            CSVRecord record;
-            assertEquals(0, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(1, record.getRecordNumber());
-            assertEquals(1, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(2, record.getRecordNumber());
-            assertEquals(2, parser.getRecordNumber());
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(3, parser.getRecordNumber());
-            assertNull(record = parser.nextRecord());
-            assertEquals(3, parser.getRecordNumber());
-        }
-    }
-
-    private void validateRecordPosition(final String lineSeparator) throws IOException {
-        final String nl = lineSeparator; // used as linebreak in values for better distinction
-        final String code = "a,b,c" + lineSeparator + "1,2,3" + lineSeparator +
-                // to see if recordPosition correctly points to the enclosing quote
-                "'A" + nl + "A','B" + nl + "B',CC" + lineSeparator +
-                // unicode test... not very relevant while operating on strings instead of bytes, but for
-                // completeness...
-                "\u00c4,\u00d6,\u00dc" + lineSeparator + "EOF,EOF,EOF";
-        final CSVFormat format = CSVFormat.newFormat(',').withQuote('\'').withRecordSeparator(lineSeparator);
-        final long positionRecord3;
-        try (CSVParser parser = CSVParser.parse(code, format)) {
-            CSVRecord record;
-            assertEquals(0, parser.getRecordNumber());
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(1, record.getRecordNumber());
-            assertEquals(code.indexOf('a'), record.getCharacterPosition());
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(2, record.getRecordNumber());
-            assertEquals(code.indexOf('1'), record.getCharacterPosition());
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            positionRecord3 = record.getCharacterPosition();
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(code.indexOf("'A"), record.getCharacterPosition());
-            assertEquals("A" + lineSeparator + "A", record.get(0));
-            assertEquals("B" + lineSeparator + "B", record.get(1));
-            assertEquals("CC", record.get(2));
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(4, record.getRecordNumber());
-            assertEquals(code.indexOf('\u00c4'), record.getCharacterPosition());
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(5, record.getRecordNumber());
-            assertEquals(code.indexOf("EOF"), record.getCharacterPosition());
-        }
-        // now try to read starting at record 3
-        try (CSVParser parser = CSVParser.builder()
-                .setReader(new StringReader(code.substring((int) positionRecord3)))
-                .setFormat(format)
-                .setCharacterOffset(positionRecord3)
-                .setRecordNumber(3)
-                .get()) {
-            CSVRecord record;
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(code.indexOf("'A"), record.getCharacterPosition());
-            assertEquals("A" + lineSeparator + "A", record.get(0));
-            assertEquals("B" + lineSeparator + "B", record.get(1));
-            assertEquals("CC", record.get(2));
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(4, record.getRecordNumber());
-            assertEquals(code.indexOf('\u00c4'), record.getCharacterPosition());
-            assertEquals("\u00c4", record.get(0));
-        } // again with ctor
-        try (CSVParser parser = new CSVParser(new StringReader(code.substring((int) positionRecord3)), format, positionRecord3, 3)) {
-            CSVRecord record;
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(3, record.getRecordNumber());
-            assertEquals(code.indexOf("'A"), record.getCharacterPosition());
-            assertEquals("A" + lineSeparator + "A", record.get(0));
-            assertEquals("B" + lineSeparator + "B", record.get(1));
-            assertEquals("CC", record.get(2));
-            // nextRecord
-            assertNotNull(record = parser.nextRecord());
-            assertEquals(4, record.getRecordNumber());
-            assertEquals(code.indexOf('\u00c4'), record.getCharacterPosition());
-            assertEquals("\u00c4", record.get(0));
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.commons.csv;
+
+import static org.apache.commons.csv.Constants.CR;
+import static org.apache.commons.csv.Constants.CRLF;
+import static org.apache.commons.csv.Constants.LF;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PipedReader;
+import java.io.PipedWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.io.input.BOMInputStream;
+import org.apache.commons.io.input.BrokenInputStream;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ * CSVParserTest
+ *
+ * The test are organized in three different sections: The 'setter/getter' section, the lexer section and finally the parser section. In case a test fails, you
+ * should follow a top-down approach for fixing a potential bug (its likely that the parser itself fails if the lexer has problems...).
+ */
+public class CSVParserTest {
+
+    private static final CSVFormat EXCEL_WITH_HEADER = CSVFormat.EXCEL.withHeader();
+
+    private static final Charset UTF_8 = StandardCharsets.UTF_8;
+
+    private static final String UTF_8_NAME = UTF_8.name();
+
+    private static final String CSV_INPUT = "a,b,c,d\n" + " a , b , 1 2 \n" + "\"foo baar\", b,\n" +
+            // + " \"foo\n,,\n\"\",,\n\\\"\",d,e\n";
+            "   \"foo\n,,\n\"\",,\n\"\"\",d,e\n"; // changed to use standard CSV escaping
+
+    private static final String CSV_INPUT_1 = "a,b,c,d";
+
+    private static final String CSV_INPUT_2 = "a,b,1 2";
+
+    private static final String[][] RESULT = { { "a", "b", "c", "d" }, { "a", "b", "1 2" }, { "foo baar", "b", "" }, { "foo\n,,\n\",,\n\"", "d", "e" } };
+
+    // CSV with no header comments
+    private static final String CSV_INPUT_NO_COMMENT = "A,B" + CRLF + "1,2" + CRLF;
+
+    // CSV with a header comment
+    private static final String CSV_INPUT_HEADER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF;
+
+    // CSV with a single line header and trailer comment
+    private static final String CSV_INPUT_HEADER_TRAILER_COMMENT = "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF + "# comment";
+
+    // CSV with a multi-line header and trailer comment
+    private static final String CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT = "# multi-line" + CRLF + "# header comment" + CRLF + "A,B" + CRLF + "1,2" + CRLF +
+            "# multi-line" + CRLF + "# comment";
+
+    // Format with auto-detected header
+    private static final CSVFormat FORMAT_AUTO_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT).setCommentMarker('#').setHeader().get();
+
+    // Format with explicit header
+    // @formatter:off
+    private static final CSVFormat FORMAT_EXPLICIT_HEADER = CSVFormat.Builder.create(CSVFormat.DEFAULT)
+            .setSkipHeaderRecord(true)
+            .setCommentMarker('#')
+            .setHeader("A", "B")
+            .get();
+    // @formatter:on
+
+    // Format with explicit header that does not skip the header line
+    // @formatter:off
+    CSVFormat FORMAT_EXPLICIT_HEADER_NOSKIP = CSVFormat.Builder.create(CSVFormat.DEFAULT)
+            .setCommentMarker('#')
+            .setHeader("A", "B")
+            .get();
+    // @formatter:on
+
+    @SuppressWarnings("resource") // caller releases
+    private BOMInputStream createBOMInputStream(final String resource) throws IOException {
+        return new BOMInputStream(ClassLoader.getSystemClassLoader().getResource(resource).openStream());
+    }
+
+    CSVRecord parse(final CSVParser parser, final int failParseRecordNo) throws IOException {
+        if (parser.getRecordNumber() + 1 == failParseRecordNo) {
+            assertThrows(IOException.class, () -> parser.nextRecord());
+            return null;
+        }
+        return parser.nextRecord();
+    }
+
+    private void parseFully(final CSVParser parser) {
+        parser.forEach(Assertions::assertNotNull);
+    }
+
+    @Test
+    public void testBackslashEscaping() throws IOException {
+        // To avoid confusion over the need for escaping chars in java code,
+        // We will test with a forward slash as the escape char, and a single
+        // quote as the encapsulator.
+
+        // @formatter:off
+        final String code = "one,two,three\n" + // 0
+            "'',''\n" + // 1) empty encapsulators
+            "/',/'\n" + // 2) single encapsulators
+            "'/'','/''\n" + // 3) single encapsulators encapsulated via escape
+            "'''',''''\n" + // 4) single encapsulators encapsulated via doubling
+            "/,,/,\n" + // 5) separator escaped
+            "//,//\n" + // 6) escape escaped
+            "'//','//'\n" + // 7) escape escaped in encapsulation
+            "   8   ,   \"quoted \"\" /\" // string\"   \n" + // don't eat spaces
+            "9,   /\n   \n" + // escaped newline
+            "";
+        final String[][] res = {{"one", "two", "three"}, // 0
+            {"", ""}, // 1
+            {"'", "'"}, // 2
+            {"'", "'"}, // 3
+            {"'", "'"}, // 4
+            {",", ","}, // 5
+            {"/", "/"}, // 6
+            {"/", "/"}, // 7
+            {"   8   ", "   \"quoted \"\" /\" / string\"   "}, {"9", "   \n   "} };
+        // @formatter:on
+        final CSVFormat format = CSVFormat.newFormat(',').withQuote('\'').withRecordSeparator(CRLF).withEscape('/').withIgnoreEmptyLines();
+        try (CSVParser parser = CSVParser.parse(code, format)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertFalse(records.isEmpty());
+            Utils.compare("Records do not match expected result", res, records);
+        }
+    }
+
+    @Test
+    public void testBackslashEscaping2() throws IOException {
+        // To avoid confusion over the need for escaping chars in java code,
+        // We will test with a forward slash as the escape char, and a single
+        // quote as the encapsulator.
+        // @formatter:off
+        final String code = "" + " , , \n" + // 1)
+            " \t ,  , \n" + // 2)
+            " // , /, , /,\n" + // 3)
+            "";
+        final String[][] res = {{" ", " ", " "}, // 1
+            {" \t ", "  ", " "}, // 2
+            {" / ", " , ", " ,"}, // 3
+        };
+        // @formatter:on
+        final CSVFormat format = CSVFormat.newFormat(',').withRecordSeparator(CRLF).withEscape('/').withIgnoreEmptyLines();
+        try (CSVParser parser = CSVParser.parse(code, format)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertFalse(records.isEmpty());
+            Utils.compare("", res, records);
+        }
+    }
+
+    @Test
+    @Disabled
+    public void testBackslashEscapingOld() throws IOException {
+        final String code = "one,two,three\n" + "on\\\"e,two\n" + "on\"e,two\n" + "one,\"tw\\\"o\"\n" + "one,\"t\\,wo\"\n" + "one,two,\"th,ree\"\n" +
+                "\"a\\\\\"\n" + "a\\,b\n" + "\"a\\\\,b\"";
+        final String[][] res = { { "one", "two", "three" }, { "on\\\"e", "two" }, { "on\"e", "two" }, { "one", "tw\"o" }, { "one", "t\\,wo" }, // backslash in
+                                                                                                                                               // quotes only
+                                                                                                                                               // escapes a
+                                                                                                                                               // delimiter
+                                                                                                                                               // (",")
+                { "one", "two", "th,ree" }, { "a\\\\" }, // backslash in quotes only escapes a delimiter (",")
+                { "a\\", "b" }, // a backslash must be returned
+                { "a\\\\,b" } // backslash in quotes only escapes a delimiter (",")
+        };
+        try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(res.length, records.size());
+            assertFalse(records.isEmpty());
+            for (int i = 0; i < res.length; i++) {
+                assertArrayEquals(res[i], records.get(i).values());
+            }
+        }
+    }
+
+    @Test
+    @Disabled("CSV-107")
+    public void testBOM() throws IOException {
+        final URL url = ClassLoader.getSystemClassLoader().getResource("org/apache/commons/csv/CSVFileParser/bom.csv");
+        try (CSVParser parser = CSVParser.parse(url, StandardCharsets.UTF_8, EXCEL_WITH_HEADER)) {
+            parser.forEach(record -> assertNotNull(record.get("Date")));
+        }
+    }
+
+    @Test
+    public void testBOMInputStreamParserWithInputStream() throws IOException {
+        try (BOMInputStream inputStream = createBOMInputStream("org/apache/commons/csv/CSVFileParser/bom.csv");
+                CSVParser parser = CSVParser.parse(inputStream, UTF_8, EXCEL_WITH_HEADER)) {
+            parser.forEach(record -> assertNotNull(record.get("Date")));
+        }
+    }
+
+    @Test
+    public void testBOMInputStreamParserWithReader() throws IOException {
+        try (Reader reader = new InputStreamReader(createBOMInputStream("org/apache/commons/csv/CSVFileParser/bom.csv"), UTF_8_NAME);
+                CSVParser parser = CSVParser.builder()
+                        .setReader(reader)
+                        .setFormat(EXCEL_WITH_HEADER)
+                        .get()) {
+            parser.forEach(record -> assertNotNull(record.get("Date")));
+        }
+    }
+
+    @Test
+    public void testBOMInputStreamParseWithReader() throws IOException {
+        try (Reader reader = new InputStreamReader(createBOMInputStream("org/apache/commons/csv/CSVFileParser/bom.csv"), UTF_8_NAME);
+                CSVParser parser = CSVParser.builder()
+                        .setReader(reader)
+                        .setFormat(EXCEL_WITH_HEADER)
+                        .get()) {
+            parser.forEach(record -> assertNotNull(record.get("Date")));
+        }
+    }
+
+    @Test
+    public void testCarriageReturnEndings() throws IOException {
+        final String string = "foo\rbaar,\rhello,world\r,kanu";
+        try (CSVParser parser = CSVParser.builder().setCharSequence(string).get()) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(4, records.size());
+        }
+    }
+
+    @Test
+    public void testCarriageReturnLineFeedEndings() throws IOException {
+        final String string = "foo\r\nbaar,\r\nhello,world\r\n,kanu";
+        try (CSVParser parser = CSVParser.builder().setCharSequence(string).get()) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(4, records.size());
+        }
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z");
+        final Iterator<CSVRecord> records;
+        try (CSVParser parser = CSVFormat.DEFAULT.withCommentMarker('#').withHeader().parse(in)) {
+            records = parser.iterator();
+            assertTrue(records.hasNext());
+        }
+        assertFalse(records.hasNext());
+        assertThrows(NoSuchElementException.class, records::next);
+    }
+
+    @Test
+    public void testCSV141CSVFormat_DEFAULT() throws Exception {
+        testCSV141Failure(CSVFormat.DEFAULT, 3);
+    }
+
+    @Test
+    public void testCSV141CSVFormat_INFORMIX_UNLOAD() throws Exception {
+        testCSV141Failure(CSVFormat.INFORMIX_UNLOAD, 1);
+    }
+
+    @Test
+    public void testCSV141CSVFormat_INFORMIX_UNLOAD_CSV() throws Exception {
+        testCSV141Failure(CSVFormat.INFORMIX_UNLOAD_CSV, 3);
+    }
+
+    @Test
+    public void testCSV141CSVFormat_ORACLE() throws Exception {
+        testCSV141Failure(CSVFormat.ORACLE, 2);
+    }
+
+    @Test
+    public void testCSV141CSVFormat_POSTGRESQL_CSV() throws Exception {
+        testCSV141Failure(CSVFormat.POSTGRESQL_CSV, 3);
+    }
+
+    @Test
+    public void testCSV141Excel() throws Exception {
+        testCSV141Ok(CSVFormat.EXCEL);
+    }
+
+    private void testCSV141Failure(final CSVFormat format, final int failParseRecordNo) throws IOException {
+        final Path path = Paths.get("src/test/resources/org/apache/commons/csv/CSV-141/csv-141.csv");
+        try (CSVParser parser = CSVParser.parse(path, StandardCharsets.UTF_8, format)) {
+            // row 1
+            CSVRecord record = parse(parser, failParseRecordNo);
+            if (record == null) {
+                return; // expected failure
+            }
+            assertEquals("1414770317901", record.get(0));
+            assertEquals("android.widget.EditText", record.get(1));
+            assertEquals("pass sem1 _84*|*", record.get(2));
+            assertEquals("0", record.get(3));
+            assertEquals("pass sem1 _8", record.get(4));
+            assertEquals(5, record.size());
+            // row 2
+            record = parse(parser, failParseRecordNo);
+            if (record == null) {
+                return; // expected failure
+            }
+            assertEquals("1414770318470", record.get(0));
+            assertEquals("android.widget.EditText", record.get(1));
+            assertEquals("pass sem1 _84:|", record.get(2));
+            assertEquals("0", record.get(3));
+            assertEquals("pass sem1 _84:\\", record.get(4));
+            assertEquals(5, record.size());
+            // row 3: Fail for certain
+            assertThrows(IOException.class, () -> parser.nextRecord());
+        }
+    }
+
+    private void testCSV141Ok(final CSVFormat format) throws IOException {
+        final Path path = Paths.get("src/test/resources/org/apache/commons/csv/CSV-141/csv-141.csv");
+        try (CSVParser parser = CSVParser.parse(path, StandardCharsets.UTF_8, format)) {
+            // row 1
+            CSVRecord record = parser.nextRecord();
+            assertEquals("1414770317901", record.get(0));
+            assertEquals("android.widget.EditText", record.get(1));
+            assertEquals("pass sem1 _84*|*", record.get(2));
+            assertEquals("0", record.get(3));
+            assertEquals("pass sem1 _8", record.get(4));
+            assertEquals(5, record.size());
+            // row 2
+            record = parser.nextRecord();
+            assertEquals("1414770318470", record.get(0));
+            assertEquals("android.widget.EditText", record.get(1));
+            assertEquals("pass sem1 _84:|", record.get(2));
+            assertEquals("0", record.get(3));
+            assertEquals("pass sem1 _84:\\", record.get(4));
+            assertEquals(5, record.size());
+            // row 3
+            record = parser.nextRecord();
+            assertEquals("1414770318327", record.get(0));
+            assertEquals("android.widget.EditText", record.get(1));
+            assertEquals("pass sem1\n1414770318628\"", record.get(2));
+            assertEquals("android.widget.EditText", record.get(3));
+            assertEquals("pass sem1 _84*|*", record.get(4));
+            assertEquals("0", record.get(5));
+            assertEquals("pass sem1\n", record.get(6));
+            assertEquals(7, record.size());
+            // EOF
+            record = parser.nextRecord();
+            assertNull(record);
+        }
+    }
+
+    @Test
+    public void testCSV141RFC4180() throws Exception {
+        testCSV141Failure(CSVFormat.RFC4180, 3);
+    }
+
+    @Test
+    public void testCSV235() throws IOException {
+        final String dqString = "\"aaa\",\"b\"\"bb\",\"ccc\""; // "aaa","b""bb","ccc"
+        try (CSVParser parser = CSVFormat.RFC4180.parse(new StringReader(dqString))) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertFalse(records.hasNext());
+            assertEquals(3, record.size());
+            assertEquals("aaa", record.get(0));
+            assertEquals("b\"bb", record.get(1));
+            assertEquals("ccc", record.get(2));
+        }
+    }
+
+    @Test
+    public void testCSV57() throws Exception {
+        try (CSVParser parser = CSVParser.parse("", CSVFormat.DEFAULT)) {
+            final List<CSVRecord> list = parser.getRecords();
+            assertNotNull(list);
+            assertEquals(0, list.size());
+        }
+    }
+
+    @Test
+    public void testDefaultFormat() throws IOException {
+        // @formatter:off
+        final String code = "" + "a,b#\n" + // 1)
+            "\"\n\",\" \",#\n" +            // 2)
+            "#,\"\"\n" +                    // 3)
+            "# Final comment\n"             // 4)
+        ;
+        // @formatter:on
+        final String[][] res = { { "a", "b#" }, { "\n", " ", "#" }, { "#", "" }, { "# Final comment" } };
+        CSVFormat format = CSVFormat.DEFAULT;
+        assertFalse(format.isCommentMarkerSet());
+        final String[][] resComments = { { "a", "b#" }, { "\n", " ", "#" } };
+        try (CSVParser parser = CSVParser.parse(code, format)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertFalse(records.isEmpty());
+            Utils.compare("Failed to parse without comments", res, records);
+            format = CSVFormat.DEFAULT.withCommentMarker('#');
+        }
+        try (CSVParser parser = CSVParser.parse(code, format)) {
+            final List<CSVRecord> records = parser.getRecords();
+            Utils.compare("Failed to parse with comments", resComments, records);
+        }
+    }
+
+    @Test
+    public void testDuplicateHeadersAllowedByDefault() throws Exception {
+        try (CSVParser parser = CSVParser.parse("a,b,a\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader())) {
+            // noop
+        }
+    }
+
+    @Test
+    public void testDuplicateHeadersNotAllowed() {
+        assertThrows(IllegalArgumentException.class,
+                () -> CSVParser.parse("a,b,a\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader().withAllowDuplicateHeaderNames(false)));
+    }
+
+    @Test
+    public void testEmptyFile() throws Exception {
+        try (CSVParser parser = CSVParser.parse(Paths.get("src/test/resources/org/apache/commons/csv/empty.txt"), StandardCharsets.UTF_8,
+                CSVFormat.DEFAULT)) {
+            assertNull(parser.nextRecord());
+        }
+    }
+
+    @Test
+    public void testEmptyFileHeaderParsing() throws Exception {
+        try (CSVParser parser = CSVParser.parse("", CSVFormat.DEFAULT.withFirstRecordAsHeader())) {
+            assertNull(parser.nextRecord());
+            assertTrue(parser.getHeaderNames().isEmpty());
+        }
+    }
+
+    @Test
+    public void testEmptyLineBehaviorCSV() throws Exception {
+        final String[] codes = { "hello,\r\n\r\n\r\n", "hello,\n\n\n", "hello,\"\"\r\n\r\n\r\n", "hello,\"\"\n\n\n" };
+        final String[][] res = { { "hello", "" } // CSV format ignores empty lines
+        };
+        for (final String code : codes) {
+            try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
+                final List<CSVRecord> records = parser.getRecords();
+                assertEquals(res.length, records.size());
+                assertFalse(records.isEmpty());
+                for (int i = 0; i < res.length; i++) {
+                    assertArrayEquals(res[i], records.get(i).values());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testEmptyLineBehaviorExcel() throws Exception {
+        final String[] codes = { "hello,\r\n\r\n\r\n", "hello,\n\n\n", "hello,\"\"\r\n\r\n\r\n", "hello,\"\"\n\n\n" };
+        final String[][] res = { { "hello", "" }, { "" }, // Excel format does not ignore empty lines
+                { "" } };
+        for (final String code : codes) {
+            try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
+                final List<CSVRecord> records = parser.getRecords();
+                assertEquals(res.length, records.size());
+                assertFalse(records.isEmpty());
+                for (int i = 0; i < res.length; i++) {
+                    assertArrayEquals(res[i], records.get(i).values());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testEmptyString() throws Exception {
+        try (CSVParser parser = CSVParser.parse("", CSVFormat.DEFAULT)) {
+            assertNull(parser.nextRecord());
+        }
+    }
+
+    @Test
+    public void testEndOfFileBehaviorCSV() throws Exception {
+        final String[] codes = { "hello,\r\n\r\nworld,\r\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\r\n", "hello,\r\n\r\nworld,\"\"",
+                "hello,\r\n\r\nworld,\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\n", "hello,\r\n\r\nworld,\"\"" };
+        final String[][] res = { { "hello", "" }, // CSV format ignores empty lines
+                { "world", "" } };
+        for (final String code : codes) {
+            try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
+                final List<CSVRecord> records = parser.getRecords();
+                assertEquals(res.length, records.size());
+                assertFalse(records.isEmpty());
+                for (int i = 0; i < res.length; i++) {
+                    assertArrayEquals(res[i], records.get(i).values());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testEndOfFileBehaviorExcel() throws Exception {
+        final String[] codes = { "hello,\r\n\r\nworld,\r\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\r\n", "hello,\r\n\r\nworld,\"\"",
+                "hello,\r\n\r\nworld,\n", "hello,\r\n\r\nworld,", "hello,\r\n\r\nworld,\"\"\n", "hello,\r\n\r\nworld,\"\"" };
+        final String[][] res = { { "hello", "" }, { "" }, // Excel format does not ignore empty lines
+                { "world", "" } };
+
+        for (final String code : codes) {
+            try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
+                final List<CSVRecord> records = parser.getRecords();
+                assertEquals(res.length, records.size());
+                assertFalse(records.isEmpty());
+                for (int i = 0; i < res.length; i++) {
+                    assertArrayEquals(res[i], records.get(i).values());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testExcelFormat1() throws IOException {
+        final String code = "value1,value2,value3,value4\r\na,b,c,d\r\n  x,,," + "\r\n\r\n\"\"\"hello\"\"\",\"  \"\"world\"\"\",\"abc\ndef\",\r\n";
+        final String[][] res = { { "value1", "value2", "value3", "value4" }, { "a", "b", "c", "d" }, { "  x", "", "", "" }, { "" },
+                { "\"hello\"", "  \"world\"", "abc\ndef", "" } };
+        try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(res.length, records.size());
+            assertFalse(records.isEmpty());
+            for (int i = 0; i < res.length; i++) {
+                assertArrayEquals(res[i], records.get(i).values());
+            }
+        }
+    }
+
+    @Test
+    public void testExcelFormat2() throws Exception {
+        final String code = "foo,baar\r\n\r\nhello,\r\n\r\nworld,\r\n";
+        final String[][] res = { { "foo", "baar" }, { "" }, { "hello", "" }, { "" }, { "world", "" } };
+        try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(res.length, records.size());
+            assertFalse(records.isEmpty());
+            for (int i = 0; i < res.length; i++) {
+                assertArrayEquals(res[i], records.get(i).values());
+            }
+        }
+    }
+
+    /**
+     * Tests an exported Excel worksheet with a header row and rows that have more columns than the headers
+     */
+    @Test
+    public void testExcelHeaderCountLessThanData() throws Exception {
+        final String code = "A,B,C,,\r\na,b,c,d,e\r\n";
+        try (CSVParser parser = CSVParser.parse(code, EXCEL_WITH_HEADER)) {
+            parser.getRecords().forEach(record -> {
+                assertEquals("a", record.get("A"));
+                assertEquals("b", record.get("B"));
+                assertEquals("c", record.get("C"));
+            });
+        }
+    }
+
+    @Test
+    public void testFirstEndOfLineCr() throws IOException {
+        final String data = "foo\rbaar,\rhello,world\r,kanu";
+        try (CSVParser parser = CSVParser.parse(data, CSVFormat.DEFAULT)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(4, records.size());
+            assertEquals("\r", parser.getFirstEndOfLine());
+        }
+    }
+
+    @Test
+    public void testFirstEndOfLineCrLf() throws IOException {
+        final String data = "foo\r\nbaar,\r\nhello,world\r\n,kanu";
+        try (CSVParser parser = CSVParser.parse(data, CSVFormat.DEFAULT)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(4, records.size());
+            assertEquals("\r\n", parser.getFirstEndOfLine());
+        }
+    }
+
+    @Test
+    public void testFirstEndOfLineLf() throws IOException {
+        final String data = "foo\nbaar,\nhello,world\n,kanu";
+        try (CSVParser parser = CSVParser.parse(data, CSVFormat.DEFAULT)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(4, records.size());
+            assertEquals("\n", parser.getFirstEndOfLine());
+        }
+    }
+
+    @Test
+    public void testForEach() throws Exception {
+        try (Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+                CSVParser parser = CSVFormat.DEFAULT.parse(in)) {
+            final List<CSVRecord> records = new ArrayList<>();
+            for (final CSVRecord record : parser) {
+                records.add(record);
+            }
+            assertEquals(3, records.size());
+            assertArrayEquals(new String[] { "a", "b", "c" }, records.get(0).values());
+            assertArrayEquals(new String[] { "1", "2", "3" }, records.get(1).values());
+            assertArrayEquals(new String[] { "x", "y", "z" }, records.get(2).values());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_HeaderComment1() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) {
+            parser.getRecords();
+            // Expect a header comment
+            assertTrue(parser.hasHeaderComment());
+            assertEquals("header comment", parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_HeaderComment2() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
+            parser.getRecords();
+            // Expect a header comment
+            assertTrue(parser.hasHeaderComment());
+            assertEquals("header comment", parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_HeaderComment3() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
+            parser.getRecords();
+            // Expect no header comment - the text "comment" is attached to the first record
+            assertFalse(parser.hasHeaderComment());
+            assertNull(parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_HeaderTrailerComment() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
+            parser.getRecords();
+            // Expect a header comment
+            assertTrue(parser.hasHeaderComment());
+            assertEquals("multi-line" + LF + "header comment", parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_NoComment1() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_AUTO_HEADER)) {
+            parser.getRecords();
+            // Expect no header comment
+            assertFalse(parser.hasHeaderComment());
+            assertNull(parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_NoComment2() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER)) {
+            parser.getRecords();
+            // Expect no header comment
+            assertFalse(parser.hasHeaderComment());
+            assertNull(parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderComment_NoComment3() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_NO_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
+            parser.getRecords();
+            // Expect no header comment
+            assertFalse(parser.hasHeaderComment());
+            assertNull(parser.getHeaderComment());
+        }
+    }
+
+    @Test
+    public void testGetHeaderMap() throws Exception {
+        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
+            final Map<String, Integer> headerMap = parser.getHeaderMap();
+            final Iterator<String> columnNames = headerMap.keySet().iterator();
+            // Headers are iterated in column order.
+            assertEquals("A", columnNames.next());
+            assertEquals("B", columnNames.next());
+            assertEquals("C", columnNames.next());
+            final Iterator<CSVRecord> records = parser.iterator();
+
+            // Parse to make sure getHeaderMap did not have a side-effect.
+            for (int i = 0; i < 3; i++) {
+                assertTrue(records.hasNext());
+                final CSVRecord record = records.next();
+                assertEquals(record.get(0), record.get("A"));
+                assertEquals(record.get(1), record.get("B"));
+                assertEquals(record.get(2), record.get("C"));
+            }
+
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    public void testGetHeaderNames() throws IOException {
+        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
+            final Map<String, Integer> nameIndexMap = parser.getHeaderMap();
+            final List<String> headerNames = parser.getHeaderNames();
+            assertNotNull(headerNames);
+            assertEquals(nameIndexMap.size(), headerNames.size());
+            for (int i = 0; i < headerNames.size(); i++) {
+                final String name = headerNames.get(i);
+                assertEquals(i, nameIndexMap.get(name).intValue());
+            }
+        }
+    }
+
+    @Test
+    public void testGetHeaderNamesReadOnly() throws IOException {
+        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C"))) {
+            final List<String> headerNames = parser.getHeaderNames();
+            assertNotNull(headerNames);
+            assertThrows(UnsupportedOperationException.class, () -> headerNames.add("This is a read-only list."));
+        }
+    }
+
+    @Test
+    public void testGetLine() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT, CSVFormat.DEFAULT.withIgnoreSurroundingSpaces())) {
+            for (final String[] re : RESULT) {
+                assertArrayEquals(re, parser.nextRecord().values());
+            }
+
+            assertNull(parser.nextRecord());
+        }
+    }
+
+    @Test
+    public void testGetLineNumberWithCR() throws Exception {
+        validateLineNumbers(String.valueOf(CR));
+    }
+
+    @Test
+    public void testGetLineNumberWithCRLF() throws Exception {
+        validateLineNumbers(CRLF);
+    }
+
+    @Test
+    public void testGetLineNumberWithLF() throws Exception {
+        validateLineNumbers(String.valueOf(LF));
+    }
+
+    @Test
+    public void testGetOneLine() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_1, CSVFormat.DEFAULT)) {
+            final CSVRecord record = parser.getRecords().get(0);
+            assertArrayEquals(RESULT[0], record.values());
+        }
+    }
+
+    /**
+     * Tests reusing a parser to process new string records one at a time as they are being discovered. See [CSV-110].
+     *
+     * @throws IOException when an I/O error occurs.
+     */
+    @Test
+    public void testGetOneLineOneParser() throws IOException {
+        final CSVFormat format = CSVFormat.DEFAULT;
+        try (PipedWriter writer = new PipedWriter();
+                CSVParser parser = CSVParser.builder()
+                        .setReader(new PipedReader(writer))
+                        .setFormat(format)
+                        .get()) {
+            writer.append(CSV_INPUT_1);
+            writer.append(format.getRecordSeparator());
+            final CSVRecord record1 = parser.nextRecord();
+            assertArrayEquals(RESULT[0], record1.values());
+            writer.append(CSV_INPUT_2);
+            writer.append(format.getRecordSeparator());
+            final CSVRecord record2 = parser.nextRecord();
+            assertArrayEquals(RESULT[1], record2.values());
+        }
+    }
+
+    @Test
+    public void testGetRecordFourBytesRead() throws Exception {
+        final String code = "id,a,b,c\n" +
+            "1,😊,🤔,😂\n" +
+            "2,😊,🤔,😂\n" +
+            "3,😊,🤔,😂\n";
+        final CSVFormat format = CSVFormat.Builder.create()
+            .setDelimiter(',')
+            .setQuote('\'')
+            .get();
+        try (CSVParser parser = CSVParser.builder().setReader(new StringReader(code)).setFormat(format).setCharset(UTF_8).setTrackBytes(true).get()) {
+            CSVRecord record = new CSVRecord(parser, null, null, 1L, 0L, 0L);
+
+            assertEquals(0, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(1, record.getRecordNumber());
+            assertEquals(code.indexOf('i'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), record.getCharacterPosition());
+
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(2, record.getRecordNumber());
+            assertEquals(code.indexOf('1'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), record.getCharacterPosition());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(code.indexOf('2'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), 26);
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(4, record.getRecordNumber());
+            assertEquals(code.indexOf('3'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), 43);
+        }
+    }
+
+    @Test
+    public void testGetRecordNumberWithCR() throws Exception {
+        validateRecordNumbers(String.valueOf(CR));
+    }
+
+    @Test
+    public void testGetRecordNumberWithCRLF() throws Exception {
+        validateRecordNumbers(CRLF);
+    }
+
+    @Test
+    public void testGetRecordNumberWithLF() throws Exception {
+        validateRecordNumbers(String.valueOf(LF));
+    }
+
+    @Test
+    public void testGetRecordPositionWithCRLF() throws Exception {
+        validateRecordPosition(CRLF);
+    }
+
+    @Test
+    public void testGetRecordPositionWithLF() throws Exception {
+        validateRecordPosition(String.valueOf(LF));
+    }
+
+    @Test
+    public void testGetRecords() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT, CSVFormat.DEFAULT.withIgnoreSurroundingSpaces())) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(RESULT.length, records.size());
+            assertFalse(records.isEmpty());
+            for (int i = 0; i < RESULT.length; i++) {
+                assertArrayEquals(RESULT[i], records.get(i).values());
+            }
+        }
+    }
+
+    @Test
+    public void testGetRecordsFromBrokenInputStream() throws IOException {
+        @SuppressWarnings("resource") // We also get an exception on close, which is OK but can't assert in a try.
+        final CSVParser parser = CSVParser.parse(new BrokenInputStream(), UTF_8, CSVFormat.DEFAULT);
+        assertThrows(UncheckedIOException.class, parser::getRecords);
+
+    }
+
+    @Test
+    public void testGetRecordThreeBytesRead() throws Exception {
+        final String code = "id,date,val5,val4\n" +
+            "11111111111111,'4017-09-01',きちんと節分近くには咲いてる~,v4\n" +
+            "22222222222222,'4017-01-01',おはよう私の友人~,v4\n" +
+            "33333333333333,'4017-01-01',きる自然の力ってすごいな~,v4\n";
+        final CSVFormat format = CSVFormat.Builder.create()
+            .setDelimiter(',')
+            .setQuote('\'')
+            .get();
+        try (CSVParser parser = CSVParser.builder().setReader(new StringReader(code)).setFormat(format).setCharset(UTF_8).setTrackBytes(true).get()) {
+            CSVRecord record = new CSVRecord(parser, null, null, 1L, 0L, 0L);
+
+            assertEquals(0, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(1, record.getRecordNumber());
+            assertEquals(code.indexOf('i'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), record.getCharacterPosition());
+
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(2, record.getRecordNumber());
+            assertEquals(code.indexOf('1'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), record.getCharacterPosition());
+
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(code.indexOf('2'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), 95);
+
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(4, record.getRecordNumber());
+            assertEquals(code.indexOf('3'), record.getCharacterPosition());
+            assertEquals(record.getBytePosition(), 154);
+        }
+    }
+
+    @Test
+    public void testGetRecordWithMultiLineValues() throws Exception {
+        try (CSVParser parser = CSVParser.parse("\"a\r\n1\",\"a\r\n2\"" + CRLF + "\"b\r\n1\",\"b\r\n2\"" + CRLF + "\"c\r\n1\",\"c\r\n2\"",
+                CSVFormat.DEFAULT.withRecordSeparator(CRLF))) {
+            CSVRecord record;
+            assertEquals(0, parser.getRecordNumber());
+            assertEquals(0, parser.getCurrentLineNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(3, parser.getCurrentLineNumber());
+            assertEquals(1, record.getRecordNumber());
+            assertEquals(1, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(6, parser.getCurrentLineNumber());
+            assertEquals(2, record.getRecordNumber());
+            assertEquals(2, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(9, parser.getCurrentLineNumber());
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(3, parser.getRecordNumber());
+            assertNull(record = parser.nextRecord());
+            assertEquals(9, parser.getCurrentLineNumber());
+            assertEquals(3, parser.getRecordNumber());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_HeaderComment1() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) {
+            parser.getRecords();
+            assertFalse(parser.hasTrailerComment());
+            assertNull(parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_HeaderComment2() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
+            parser.getRecords();
+            assertFalse(parser.hasTrailerComment());
+            assertNull(parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_HeaderComment3() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
+            parser.getRecords();
+            assertFalse(parser.hasTrailerComment());
+            assertNull(parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_HeaderTrailerComment1() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
+            parser.getRecords();
+            assertTrue(parser.hasTrailerComment());
+            assertEquals("comment", parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_HeaderTrailerComment2() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER)) {
+            parser.getRecords();
+            assertTrue(parser.hasTrailerComment());
+            assertEquals("comment", parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_HeaderTrailerComment3() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_TRAILER_COMMENT, FORMAT_EXPLICIT_HEADER_NOSKIP)) {
+            parser.getRecords();
+            assertTrue(parser.hasTrailerComment());
+            assertEquals("comment", parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testGetTrailerComment_MultilineComment() throws IOException {
+        try (CSVParser parser = CSVParser.parse(CSV_INPUT_MULTILINE_HEADER_TRAILER_COMMENT, FORMAT_AUTO_HEADER)) {
+            parser.getRecords();
+            assertTrue(parser.hasTrailerComment());
+            assertEquals("multi-line" + LF + "comment", parser.getTrailerComment());
+        }
+    }
+
+    @Test
+    public void testHeader() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+
+            for (int i = 0; i < 2; i++) {
+                assertTrue(records.hasNext());
+                final CSVRecord record = records.next();
+                assertEquals(record.get(0), record.get("a"));
+                assertEquals(record.get(1), record.get("b"));
+                assertEquals(record.get(2), record.get("c"));
+            }
+
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    public void testHeaderComment() throws Exception {
+        final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withCommentMarker('#').withHeader().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            for (int i = 0; i < 2; i++) {
+                assertTrue(records.hasNext());
+                final CSVRecord record = records.next();
+                assertEquals(record.get(0), record.get("a"));
+                assertEquals(record.get(1), record.get("b"));
+                assertEquals(record.get(2), record.get("c"));
+            }
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    public void testHeaderMissing() throws Exception {
+        final Reader in = new StringReader("a,,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().withAllowMissingColumnNames().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            for (int i = 0; i < 2; i++) {
+                assertTrue(records.hasNext());
+                final CSVRecord record = records.next();
+                assertEquals(record.get(0), record.get("a"));
+                assertEquals(record.get(2), record.get("c"));
+            }
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    public void testHeaderMissingWithNull() throws Exception {
+        final Reader in = new StringReader("a,,c,,e\n1,2,3,4,5\nv,w,x,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().withNullString("").withAllowMissingColumnNames().parse(in)) {
+            parser.iterator();
+        }
+    }
+
+    @Test
+    public void testHeadersMissing() throws Exception {
+        try (Reader in = new StringReader("a,,c,,e\n1,2,3,4,5\nv,w,x,y,z");
+                CSVParser parser = CSVFormat.DEFAULT.withHeader().withAllowMissingColumnNames().parse(in)) {
+            parser.iterator();
+        }
+    }
+
+    @Test
+    public void testHeadersMissingException() {
+        final Reader in = new StringReader("a,,c,,e\n1,2,3,4,5\nv,w,x,y,z");
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withHeader().parse(in).iterator());
+    }
+
+    @Test
+    public void testHeadersMissingOneColumnException() {
+        final Reader in = new StringReader("a,,c,d,e\n1,2,3,4,5\nv,w,x,y,z");
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withHeader().parse(in).iterator());
+    }
+
+    @Test
+    public void testHeadersWithNullColumnName() throws IOException {
+        final Reader in = new StringReader("header1,null,header3\n1,2,3\n4,5,6");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().withNullString("null").withAllowMissingColumnNames().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            // Expect the null header to be missing
+            @SuppressWarnings("resource")
+            final CSVParser recordParser = record.getParser();
+            assertEquals(Arrays.asList("header1", "header3"), recordParser.getHeaderNames());
+            assertEquals(2, recordParser.getHeaderMap().size());
+        }
+    }
+
+    @Test
+    public void testIgnoreCaseHeaderMapping() throws Exception {
+        final Reader reader = new StringReader("1,2,3");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("One", "TWO", "three").withIgnoreHeaderCase().parse(reader)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("one"));
+            assertEquals("2", record.get("two"));
+            assertEquals("3", record.get("THREE"));
+        }
+    }
+
+    @Test
+    public void testIgnoreEmptyLines() throws IOException {
+        final String code = "\nfoo,baar\n\r\n,\n\n,world\r\n\n";
+        // String code = "world\r\n\n";
+        // String code = "foo;baar\r\n\r\nhello;\r\n\r\nworld;\r\n";
+        try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(3, records.size());
+        }
+    }
+
+    @Test
+    public void testInvalidFormat() {
+        assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter(CR));
+    }
+
+    @Test
+    public void testIterator() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.parse(in)) {
+            final Iterator<CSVRecord> iterator = parser.iterator();
+            assertTrue(iterator.hasNext());
+            assertThrows(UnsupportedOperationException.class, iterator::remove);
+            assertArrayEquals(new String[] { "a", "b", "c" }, iterator.next().values());
+            assertArrayEquals(new String[] { "1", "2", "3" }, iterator.next().values());
+            assertTrue(iterator.hasNext());
+            assertTrue(iterator.hasNext());
+            assertTrue(iterator.hasNext());
+            assertArrayEquals(new String[] { "x", "y", "z" }, iterator.next().values());
+            assertFalse(iterator.hasNext());
+            assertThrows(NoSuchElementException.class, iterator::next);
+        }
+    }
+
+    @Test
+    public void testIteratorSequenceBreaking() throws IOException {
+        final String fiveRows = "1\n2\n3\n4\n5\n";
+        // Iterator hasNext() shouldn't break sequence
+        try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(fiveRows))) {
+            final Iterator<CSVRecord> iter = parser.iterator();
+            int recordNumber = 0;
+            while (iter.hasNext()) {
+                final CSVRecord record = iter.next();
+                recordNumber++;
+                assertEquals(String.valueOf(recordNumber), record.get(0));
+                if (recordNumber >= 2) {
+                    break;
+                }
+            }
+            iter.hasNext();
+            while (iter.hasNext()) {
+                final CSVRecord record = iter.next();
+                recordNumber++;
+                assertEquals(String.valueOf(recordNumber), record.get(0));
+            }
+        }
+        // Consecutive enhanced for loops shouldn't break sequence
+        try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(fiveRows))) {
+            int recordNumber = 0;
+            for (final CSVRecord record : parser) {
+                recordNumber++;
+                assertEquals(String.valueOf(recordNumber), record.get(0));
+                if (recordNumber >= 2) {
+                    break;
+                }
+            }
+            for (final CSVRecord record : parser) {
+                recordNumber++;
+                assertEquals(String.valueOf(recordNumber), record.get(0));
+            }
+        }
+        // Consecutive enhanced for loops with hasNext() peeking shouldn't break sequence
+        try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(fiveRows))) {
+            int recordNumber = 0;
+            for (final CSVRecord record : parser) {
+                recordNumber++;
+                assertEquals(String.valueOf(recordNumber), record.get(0));
+                if (recordNumber >= 2) {
+                    break;
+                }
+            }
+            parser.iterator().hasNext();
+            for (final CSVRecord record : parser) {
+                recordNumber++;
+                assertEquals(String.valueOf(recordNumber), record.get(0));
+            }
+        }
+    }
+
+    @Test
+    public void testLineFeedEndings() throws IOException {
+        final String code = "foo\nbaar,\nhello,world\n,kanu";
+        try (CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT)) {
+            final List<CSVRecord> records = parser.getRecords();
+            assertEquals(4, records.size());
+        }
+    }
+
+    @Test
+    public void testMappedButNotSetAsOutlook2007ContactExport() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("A", "B", "C").withSkipHeaderRecord().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            CSVRecord record;
+            // 1st record
+            record = records.next();
+            assertTrue(record.isMapped("A"));
+            assertTrue(record.isMapped("B"));
+            assertTrue(record.isMapped("C"));
+            assertTrue(record.isSet("A"));
+            assertTrue(record.isSet("B"));
+            assertFalse(record.isSet("C"));
+            assertEquals("1", record.get("A"));
+            assertEquals("2", record.get("B"));
+            assertFalse(record.isConsistent());
+            // 2nd record
+            record = records.next();
+            assertTrue(record.isMapped("A"));
+            assertTrue(record.isMapped("B"));
+            assertTrue(record.isMapped("C"));
+            assertTrue(record.isSet("A"));
+            assertTrue(record.isSet("B"));
+            assertTrue(record.isSet("C"));
+            assertEquals("x", record.get("A"));
+            assertEquals("y", record.get("B"));
+            assertEquals("z", record.get("C"));
+            assertTrue(record.isConsistent());
+            // end
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    @Disabled
+    public void testMongoDbCsv() throws Exception {
+        try (CSVParser parser = CSVParser.parse("\"a a\",b,c" + LF + "d,e,f", CSVFormat.MONGODB_CSV)) {
+            final Iterator<CSVRecord> itr1 = parser.iterator();
+            final Iterator<CSVRecord> itr2 = parser.iterator();
+
+            final CSVRecord first = itr1.next();
+            assertEquals("a a", first.get(0));
+            assertEquals("b", first.get(1));
+            assertEquals("c", first.get(2));
+
+            final CSVRecord second = itr2.next();
+            assertEquals("d", second.get(0));
+            assertEquals("e", second.get(1));
+            assertEquals("f", second.get(2));
+        }
+    }
+
+    @Test
+    // TODO this may lead to strange behavior, throw an exception if iterator() has already been called?
+    public void testMultipleIterators() throws Exception {
+        try (CSVParser parser = CSVParser.parse("a,b,c" + CRLF + "d,e,f", CSVFormat.DEFAULT)) {
+            final Iterator<CSVRecord> itr1 = parser.iterator();
+
+            final CSVRecord first = itr1.next();
+            assertEquals("a", first.get(0));
+            assertEquals("b", first.get(1));
+            assertEquals("c", first.get(2));
+
+            final CSVRecord second = itr1.next();
+            assertEquals("d", second.get(0));
+            assertEquals("e", second.get(1));
+            assertEquals("f", second.get(2));
+        }
+    }
+
+    @Test
+    public void testNewCSVParserNullReaderFormat() {
+        assertThrows(NullPointerException.class, () -> new CSVParser(null, CSVFormat.DEFAULT));
+    }
+
+    @Test
+    public void testNewCSVParserReaderNullFormat() {
+        assertThrows(NullPointerException.class, () -> new CSVParser(new StringReader(""), null));
+    }
+
+    @Test
+    public void testNoHeaderMap() throws Exception {
+        try (CSVParser parser = CSVParser.parse("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT)) {
+            assertNull(parser.getHeaderMap());
+        }
+    }
+
+    @Test
+    public void testNotValueCSV() throws IOException {
+        final String source = "#";
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.withCommentMarker('#');
+        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
+            final CSVRecord csvRecord = csvParser.nextRecord();
+            assertNull(csvRecord);
+        }
+    }
+
+    @Test
+    public void testParse() throws Exception {
+        final ClassLoader loader = ClassLoader.getSystemClassLoader();
+        final URL url = loader.getResource("org/apache/commons/csv/CSVFileParser/test.csv");
+        final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader("A", "B", "C", "D").get();
+        final Charset charset = StandardCharsets.UTF_8;
+        // Reader
+        try (CSVParser parser = CSVParser.parse(new InputStreamReader(url.openStream(), charset), format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setReader(new InputStreamReader(url.openStream(), charset)).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // String
+        final Path path = Paths.get(url.toURI());
+        final String string = new String(Files.readAllBytes(path), charset);
+        try (CSVParser parser = CSVParser.parse(string, format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setCharSequence(string).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // File
+        final File file = new File(url.toURI());
+        try (CSVParser parser = CSVParser.parse(file, charset, format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setFile(file).setCharset(charset).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // InputStream
+        try (CSVParser parser = CSVParser.parse(url.openStream(), charset, format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setInputStream(url.openStream()).setCharset(charset).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // Path
+        try (CSVParser parser = CSVParser.parse(path, charset, format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setPath(path).setCharset(charset).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // URL
+        try (CSVParser parser = CSVParser.parse(url, charset, format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setURI(url.toURI()).setCharset(charset).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // InputStreamReader
+        try (CSVParser parser = new CSVParser(new InputStreamReader(url.openStream(), charset), format)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setReader(new InputStreamReader(url.openStream(), charset)).setFormat(format).get()) {
+            parseFully(parser);
+        }
+        // InputStreamReader with longs
+        try (CSVParser parser = new CSVParser(new InputStreamReader(url.openStream(), charset), format, /* characterOffset= */0, /* recordNumber= */1)) {
+            parseFully(parser);
+        }
+        try (CSVParser parser = CSVParser.builder().setReader(new InputStreamReader(url.openStream(), charset)).setFormat(format).setCharacterOffset(0)
+                .setRecordNumber(0).get()) {
+            parseFully(parser);
+        }
+    }
+
+    @Test
+    public void testParseFileNullFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse(new File("CSVFileParser/test.csv"), Charset.defaultCharset(), null));
+    }
+
+    @Test
+    public void testParseNullFileFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse((File) null, Charset.defaultCharset(), CSVFormat.DEFAULT));
+    }
+
+    @Test
+    public void testParseNullPathFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse((Path) null, Charset.defaultCharset(), CSVFormat.DEFAULT));
+    }
+
+    @Test
+    public void testParseNullStringFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse((String) null, CSVFormat.DEFAULT));
+    }
+
+    @Test
+    public void testParseNullUrlCharsetFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse((URL) null, Charset.defaultCharset(), CSVFormat.DEFAULT));
+    }
+
+    @Test
+    public void testParserUrlNullCharsetFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse(new URL("https://commons.apache.org"), null, CSVFormat.DEFAULT));
+    }
+
+    @Test
+    public void testParseStringNullFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse("csv data", (CSVFormat) null));
+    }
+
+    @Test
+    public void testParseUrlCharsetNullFormat() {
+        assertThrows(NullPointerException.class, () -> CSVParser.parse(new URL("https://commons.apache.org"), Charset.defaultCharset(), null));
+    }
+
+    @Test
+    public void testParseWithDelimiterStringWithEscape() throws IOException {
+        final String source = "a![!|!]b![|]c[|]xyz\r\nabc[abc][|]xyz";
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setEscape('!').get();
+        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
+            CSVRecord csvRecord = csvParser.nextRecord();
+            assertEquals("a[|]b![|]c", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+            csvRecord = csvParser.nextRecord();
+            assertEquals("abc[abc]", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+        }
+    }
+
+    @Test
+    public void testParseWithDelimiterStringWithQuote() throws IOException {
+        final String source = "'a[|]b[|]c'[|]xyz\r\nabc[abc][|]xyz";
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setQuote('\'').get();
+        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
+            CSVRecord csvRecord = csvParser.nextRecord();
+            assertEquals("a[|]b[|]c", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+            csvRecord = csvParser.nextRecord();
+            assertEquals("abc[abc]", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+        }
+    }
+
+    @Test
+    public void testParseWithDelimiterWithEscape() throws IOException {
+        final String source = "a!,b!,c,xyz";
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.withEscape('!');
+        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
+            final CSVRecord csvRecord = csvParser.nextRecord();
+            assertEquals("a,b,c", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+        }
+    }
+
+    @Test
+    public void testParseWithDelimiterWithQuote() throws IOException {
+        final String source = "'a,b,c',xyz";
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('\'');
+        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
+            final CSVRecord csvRecord = csvParser.nextRecord();
+            assertEquals("a,b,c", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+        }
+    }
+
+    @Test
+    public void testParseWithQuoteThrowsException() {
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('\'');
+        assertThrows(IOException.class, () -> csvFormat.parse(new StringReader("'a,b,c','")).nextRecord());
+        assertThrows(IOException.class, () -> csvFormat.parse(new StringReader("'a,b,c'abc,xyz")).nextRecord());
+        assertThrows(IOException.class, () -> csvFormat.parse(new StringReader("'abc'a,b,c',xyz")).nextRecord());
+    }
+
+    @Test
+    public void testParseWithQuoteWithEscape() throws IOException {
+        final String source = "'a?,b?,c?d',xyz";
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.withQuote('\'').withEscape('?');
+        try (CSVParser csvParser = csvFormat.parse(new StringReader(source))) {
+            final CSVRecord csvRecord = csvParser.nextRecord();
+            assertEquals("a,b,c?d", csvRecord.get(0));
+            assertEquals("xyz", csvRecord.get(1));
+        }
+    }
+
+    @ParameterizedTest
+    @EnumSource(CSVFormat.Predefined.class)
+    public void testParsingPrintedEmptyFirstColumn(final CSVFormat.Predefined format) throws Exception {
+        final String[][] lines = { { "a", "b" }, { "", "x" } };
+        final StringWriter buf = new StringWriter();
+        try (CSVPrinter printer = new CSVPrinter(buf, format.getFormat())) {
+            printer.printRecords(Stream.of(lines));
+        }
+        try (CSVParser csvRecords = CSVParser.builder()
+                .setReader(new StringReader(buf.toString()))
+                .setFormat(format.getFormat())
+                .get()) {
+            for (final String[] line : lines) {
+                assertArrayEquals(line, csvRecords.nextRecord().values());
+            }
+            assertNull(csvRecords.nextRecord());
+        }
+    }
+
+    @Test
+    public void testProvidedHeader() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("A", "B", "C").parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+
+            for (int i = 0; i < 3; i++) {
+                assertTrue(records.hasNext());
+                final CSVRecord record = records.next();
+                assertTrue(record.isMapped("A"));
+                assertTrue(record.isMapped("B"));
+                assertTrue(record.isMapped("C"));
+                assertFalse(record.isMapped("NOT MAPPED"));
+                assertEquals(record.get(0), record.get("A"));
+                assertEquals(record.get(1), record.get("B"));
+                assertEquals(record.get(2), record.get("C"));
+            }
+
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    public void testProvidedHeaderAuto() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+
+            for (int i = 0; i < 2; i++) {
+                assertTrue(records.hasNext());
+                final CSVRecord record = records.next();
+                assertTrue(record.isMapped("a"));
+                assertTrue(record.isMapped("b"));
+                assertTrue(record.isMapped("c"));
+                assertFalse(record.isMapped("NOT MAPPED"));
+                assertEquals(record.get(0), record.get("a"));
+                assertEquals(record.get(1), record.get("b"));
+                assertEquals(record.get(2), record.get("c"));
+            }
+
+            assertFalse(records.hasNext());
+        }
+    }
+
+    @Test
+    public void testRepeatedHeadersAreReturnedInCSVRecordHeaderNames() throws IOException {
+        final Reader in = new StringReader("header1,header2,header1\n1,2,3\n4,5,6");
+        try (CSVParser parser = CSVFormat.DEFAULT.withFirstRecordAsHeader().withTrim().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            @SuppressWarnings("resource")
+            final CSVParser recordParser = record.getParser();
+            assertEquals(Arrays.asList("header1", "header2", "header1"), recordParser.getHeaderNames());
+        }
+    }
+
+    @Test
+    public void testRoundtrip() throws Exception {
+        final StringWriter out = new StringWriter();
+        final String data = "a,b,c\r\n1,2,3\r\nx,y,z\r\n";
+        try (CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT);
+                CSVParser parse = CSVParser.parse(data, CSVFormat.DEFAULT)) {
+            for (final CSVRecord record : parse) {
+                printer.printRecord(record);
+            }
+            assertEquals(data, out.toString());
+        }
+    }
+
+    @Test
+    public void testSkipAutoHeader() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("a"));
+            assertEquals("2", record.get("b"));
+            assertEquals("3", record.get("c"));
+        }
+    }
+
+    @Test
+    public void testSkipHeaderOverrideDuplicateHeaders() throws Exception {
+        final Reader in = new StringReader("a,a,a\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("X"));
+            assertEquals("2", record.get("Y"));
+            assertEquals("3", record.get("Z"));
+        }
+    }
+
+    @Test
+    public void testSkipSetAltHeaders() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("X"));
+            assertEquals("2", record.get("Y"));
+            assertEquals("3", record.get("Z"));
+        }
+    }
+
+    @Test
+    public void testSkipSetHeader() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("a", "b", "c").withSkipHeaderRecord().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("a"));
+            assertEquals("2", record.get("b"));
+            assertEquals("3", record.get("c"));
+        }
+    }
+
+    @Test
+    @Disabled
+    public void testStartWithEmptyLinesThenHeaders() throws Exception {
+        final String[] codes = { "\r\n\r\n\r\nhello,\r\n\r\n\r\n", "hello,\n\n\n", "hello,\"\"\r\n\r\n\r\n", "hello,\"\"\n\n\n" };
+        final String[][] res = { { "hello", "" }, { "" }, // Excel format does not ignore empty lines
+                { "" } };
+        for (final String code : codes) {
+            try (CSVParser parser = CSVParser.parse(code, CSVFormat.EXCEL)) {
+                final List<CSVRecord> records = parser.getRecords();
+                assertEquals(res.length, records.size());
+                assertFalse(records.isEmpty());
+                for (int i = 0; i < res.length; i++) {
+                    assertArrayEquals(res[i], records.get(i).values());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testStream() throws Exception {
+        final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.parse(in)) {
+            final List<CSVRecord> list = parser.stream().collect(Collectors.toList());
+            assertFalse(list.isEmpty());
+            assertArrayEquals(new String[] { "a", "b", "c" }, list.get(0).values());
+            assertArrayEquals(new String[] { "1", "2", "3" }, list.get(1).values());
+            assertArrayEquals(new String[] { "x", "y", "z" }, list.get(2).values());
+        }
+    }
+
+    @Test
+    public void testThrowExceptionWithLineAndPosition() throws IOException {
+        final String csvContent = "col1,col2,col3,col4,col5,col6,col7,col8,col9,col10\nrec1,rec2,rec3,rec4,rec5,rec6,rec7,rec8,\"\"rec9\"\",rec10";
+        final StringReader stringReader = new StringReader(csvContent);
+        // @formatter:off
+        final CSVFormat csvFormat = CSVFormat.DEFAULT.builder()
+                .setHeader()
+                .setSkipHeaderRecord(true)
+                .get();
+        // @formatter:on
+        try (CSVParser csvParser = csvFormat.parse(stringReader)) {
+            final UncheckedIOException exception = assertThrows(UncheckedIOException.class, csvParser::getRecords);
+            assertInstanceOf(CSVException.class, exception.getCause());
+            assertTrue(exception.getMessage().contains("Invalid character between encapsulated token and delimiter at line: 2, position: 94"),
+                    exception::getMessage);
+        }
+    }
+
+    @Test
+    public void testTrailingDelimiter() throws Exception {
+        final Reader in = new StringReader("a,a,a,\n\"1\",\"2\",\"3\",\nx,y,z,");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().withTrailingDelimiter().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("X"));
+            assertEquals("2", record.get("Y"));
+            assertEquals("3", record.get("Z"));
+            assertEquals(3, record.size());
+        }
+    }
+
+    @Test
+    public void testTrim() throws Exception {
+        final Reader in = new StringReader("a,a,a\n\" 1 \",\" 2 \",\" 3 \"\nx,y,z");
+        try (CSVParser parser = CSVFormat.DEFAULT.withHeader("X", "Y", "Z").withSkipHeaderRecord().withTrim().parse(in)) {
+            final Iterator<CSVRecord> records = parser.iterator();
+            final CSVRecord record = records.next();
+            assertEquals("1", record.get("X"));
+            assertEquals("2", record.get("Y"));
+            assertEquals("3", record.get("Z"));
+            assertEquals(3, record.size());
+        }
+    }
+
+    private void validateLineNumbers(final String lineSeparator) throws IOException {
+        try (CSVParser parser = CSVParser.parse("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.DEFAULT.withRecordSeparator(lineSeparator))) {
+            assertEquals(0, parser.getCurrentLineNumber());
+            assertNotNull(parser.nextRecord());
+            assertEquals(1, parser.getCurrentLineNumber());
+            assertNotNull(parser.nextRecord());
+            assertEquals(2, parser.getCurrentLineNumber());
+            assertNotNull(parser.nextRecord());
+            // Read EOF without EOL should 3
+            assertEquals(3, parser.getCurrentLineNumber());
+            assertNull(parser.nextRecord());
+            // Read EOF without EOL should 3
+            assertEquals(3, parser.getCurrentLineNumber());
+        }
+    }
+
+    private void validateRecordNumbers(final String lineSeparator) throws IOException {
+        try (CSVParser parser = CSVParser.parse("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.DEFAULT.withRecordSeparator(lineSeparator))) {
+            CSVRecord record;
+            assertEquals(0, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(1, record.getRecordNumber());
+            assertEquals(1, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(2, record.getRecordNumber());
+            assertEquals(2, parser.getRecordNumber());
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(3, parser.getRecordNumber());
+            assertNull(record = parser.nextRecord());
+            assertEquals(3, parser.getRecordNumber());
+        }
+    }
+
+    private void validateRecordPosition(final String lineSeparator) throws IOException {
+        final String nl = lineSeparator; // used as linebreak in values for better distinction
+        final String code = "a,b,c" + lineSeparator + "1,2,3" + lineSeparator +
+                // to see if recordPosition correctly points to the enclosing quote
+                "'A" + nl + "A','B" + nl + "B',CC" + lineSeparator +
+                // unicode test... not very relevant while operating on strings instead of bytes, but for
+                // completeness...
+                "\u00c4,\u00d6,\u00dc" + lineSeparator + "EOF,EOF,EOF";
+        final CSVFormat format = CSVFormat.newFormat(',').withQuote('\'').withRecordSeparator(lineSeparator);
+        final long positionRecord3;
+        try (CSVParser parser = CSVParser.parse(code, format)) {
+            CSVRecord record;
+            assertEquals(0, parser.getRecordNumber());
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(1, record.getRecordNumber());
+            assertEquals(code.indexOf('a'), record.getCharacterPosition());
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(2, record.getRecordNumber());
+            assertEquals(code.indexOf('1'), record.getCharacterPosition());
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            positionRecord3 = record.getCharacterPosition();
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(code.indexOf("'A"), record.getCharacterPosition());
+            assertEquals("A" + lineSeparator + "A", record.get(0));
+            assertEquals("B" + lineSeparator + "B", record.get(1));
+            assertEquals("CC", record.get(2));
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(4, record.getRecordNumber());
+            assertEquals(code.indexOf('\u00c4'), record.getCharacterPosition());
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(5, record.getRecordNumber());
+            assertEquals(code.indexOf("EOF"), record.getCharacterPosition());
+        }
+        // now try to read starting at record 3
+        try (CSVParser parser = CSVParser.builder()
+                .setReader(new StringReader(code.substring((int) positionRecord3)))
+                .setFormat(format)
+                .setCharacterOffset(positionRecord3)
+                .setRecordNumber(3)
+                .get()) {
+            CSVRecord record;
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(code.indexOf("'A"), record.getCharacterPosition());
+            assertEquals("A" + lineSeparator + "A", record.get(0));
+            assertEquals("B" + lineSeparator + "B", record.get(1));
+            assertEquals("CC", record.get(2));
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(4, record.getRecordNumber());
+            assertEquals(code.indexOf('\u00c4'), record.getCharacterPosition());
+            assertEquals("\u00c4", record.get(0));
+        } // again with ctor
+        try (CSVParser parser = new CSVParser(new StringReader(code.substring((int) positionRecord3)), format, positionRecord3, 3)) {
+            CSVRecord record;
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(3, record.getRecordNumber());
+            assertEquals(code.indexOf("'A"), record.getCharacterPosition());
+            assertEquals("A" + lineSeparator + "A", record.get(0));
+            assertEquals("B" + lineSeparator + "B", record.get(1));
+            assertEquals("CC", record.get(2));
+            // nextRecord
+            assertNotNull(record = parser.nextRecord());
+            assertEquals(4, record.getRecordNumber());
+            assertEquals(code.indexOf('\u00c4'), record.getCharacterPosition());
+            assertEquals("\u00c4", record.get(0));
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
index b4b560a1..f457460c 100644
--- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
+++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java
@@ -1,1926 +1,1926 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.commons.csv;
-
-import static org.apache.commons.csv.Constants.BACKSLASH;
-import static org.apache.commons.csv.Constants.CR;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.sql.BatchUpdateException;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Vector;
-import java.util.stream.Stream;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.NullOutputStream;
-import org.apache.commons.lang3.StringUtils;
-import org.h2.tools.SimpleResultSet;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests {@link CSVPrinter}.
- */
-public class CSVPrinterTest {
-
-    private static final int TABLE_RECORD_COUNT = 2;
-    private static final char DQUOTE_CHAR = '"';
-    private static final char EURO_CH = '\u20AC';
-    private static final int ITERATIONS_FOR_RANDOM_TEST = 50000;
-    private static final char QUOTE_CH = '\'';
-
-    private static String printable(final String s) {
-        final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < s.length(); i++) {
-            final char ch = s.charAt(i);
-            if (ch <= ' ' || ch >= 128) {
-                sb.append("(").append((int) ch).append(")");
-            } else {
-                sb.append(ch);
-            }
-        }
-        return sb.toString();
-    }
-
-    private String longText2;
-
-    private final String recordSeparator = CSVFormat.DEFAULT.getRecordSeparator();
-
-    private void assertInitialState(final CSVPrinter printer) {
-        assertEquals(0, printer.getRecordCount());
-    }
-
-    private File createTempFile() throws IOException {
-        return createTempPath().toFile();
-    }
-
-    private Path createTempPath() throws IOException {
-        return Files.createTempFile(getClass().getName(), ".csv");
-    }
-
-    private void doOneRandom(final CSVFormat format) throws Exception {
-        final Random r = new Random();
-
-        final int nLines = r.nextInt(4) + 1;
-        final int nCol = r.nextInt(3) + 1;
-        // nLines=1;nCol=2;
-        final String[][] lines = generateLines(nLines, nCol);
-
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, format)) {
-
-            for (int i = 0; i < nLines; i++) {
-                // for (int j=0; j<lines[i].length; j++) System.out.println("### VALUE=:" + printable(lines[i][j]));
-                printer.printRecord((Object[]) lines[i]);
-            }
-
-            printer.flush();
-        }
-        final String result = sw.toString();
-        // System.out.println("### :" + printable(result));
-
-        try (CSVParser parser = CSVParser.parse(result, format)) {
-            final List<CSVRecord> parseResult = parser.getRecords();
-
-            final 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);
-        }
-    }
-
-    private void doRandom(final CSVFormat format, final int iter) throws Exception {
-        for (int i = 0; i < iter; i++) {
-            doOneRandom(format);
-        }
-    }
-
-    /**
-     * 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> T[] expectNulls(final T[] original, final CSVFormat csvFormat) {
-        final T[] fixed = original.clone();
-        for (int i = 0; i < fixed.length; i++) {
-            if (Objects.equals(csvFormat.getNullString(), fixed[i])) {
-                fixed[i] = null;
-            }
-        }
-        return fixed;
-    }
-
-    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 Connection getH2Connection() throws SQLException, ClassNotFoundException {
-        Class.forName("org.h2.Driver");
-        return DriverManager.getConnection("jdbc:h2:mem:my_test;", "sa", "");
-    }
-
-    private CSVPrinter printWithHeaderComments(final StringWriter sw, final Date now, final CSVFormat baseFormat) throws IOException {
-        // Use withHeaderComments first to test CSV-145
-        // @formatter:off
-        final CSVFormat format = baseFormat.builder()
-                .setHeaderComments((String[]) null) // don't blow up
-                .setHeaderComments((Object[]) null) // don't blow up
-                .setHeaderComments("Generated by Apache Commons CSV 1.1", now)
-                .setCommentMarker('#')
-                .setHeader("Col1", "Col2")
-                .get();
-        // @formatter:on
-        final CSVPrinter printer = format.print(sw);
-        printer.printRecord("A", "B");
-        printer.printRecord("C", "D");
-        printer.close();
-        return printer;
-    }
-
-    private String randStr() {
-        final Random r = new Random();
-        final int sz = r.nextInt(20);
-        // sz = r.nextInt(3);
-        final char[] buf = new char[sz];
-        for (int i = 0; i < sz; i++) {
-            // stick in special chars with greater frequency
-            final 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 = DQUOTE_CHAR;
-                break;
-            case 7:
-                ch = '\'';
-                break;
-            case 8:
-                ch = BACKSLASH;
-                break;
-            default:
-                ch = (char) r.nextInt(300);
-                break;
-            // default: ch = 'a'; break;
-            }
-            buf[i] = ch;
-        }
-        return new String(buf);
-    }
-
-    private void setUpTable(final Connection connection) throws SQLException {
-        try (Statement statement = connection.createStatement()) {
-            statement.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255), TEXT CLOB, BIN_DATA BLOB)");
-            statement.execute("insert into TEST values(1, 'r1', 'long text 1', 'binary data 1')");
-            longText2 = StringUtils.repeat('a', IOUtils.DEFAULT_BUFFER_SIZE - 4);
-            longText2 += "\"\r\n\"b\"";
-            longText2 += StringUtils.repeat('c', IOUtils.DEFAULT_BUFFER_SIZE - 1);
-            statement.execute("insert into TEST values(2, 'r2', '" + longText2 + "', 'binary data 2')");
-            longText2 = longText2.replace("\"", "\"\"");
-        }
-    }
-
-    @Test
-    public void testCloseBackwardCompatibility() throws IOException {
-        try (Writer writer = mock(Writer.class)) {
-            final CSVFormat csvFormat = CSVFormat.DEFAULT;
-            try (CSVPrinter printer = new CSVPrinter(writer, csvFormat)) {
-                assertInitialState(printer);
-            }
-            verify(writer, never()).flush();
-            verify(writer, times(1)).close();
-        }
-    }
-
-    @Test
-    public void testCloseWithCsvFormatAutoFlushOff() throws IOException {
-        try (Writer writer = mock(Writer.class)) {
-            final CSVFormat csvFormat = CSVFormat.DEFAULT.withAutoFlush(false);
-            try (CSVPrinter printer = new CSVPrinter(writer, csvFormat)) {
-                assertInitialState(printer);
-            }
-            verify(writer, never()).flush();
-            verify(writer, times(1)).close();
-        }
-    }
-
-    @Test
-    public void testCloseWithCsvFormatAutoFlushOn() throws IOException {
-        // System.out.println("start method");
-        try (Writer writer = mock(Writer.class)) {
-            final CSVFormat csvFormat = CSVFormat.DEFAULT.withAutoFlush(true);
-            try (CSVPrinter printer = new CSVPrinter(writer, csvFormat)) {
-                assertInitialState(printer);
-            }
-            verify(writer, times(1)).flush();
-            verify(writer, times(1)).close();
-        }
-    }
-
-    @Test
-    public void testCloseWithFlushOff() throws IOException {
-        try (Writer writer = mock(Writer.class)) {
-            final CSVFormat csvFormat = CSVFormat.DEFAULT;
-            @SuppressWarnings("resource")
-            final CSVPrinter printer = new CSVPrinter(writer, csvFormat);
-            assertInitialState(printer);
-            printer.close(false);
-            assertEquals(0, printer.getRecordCount());
-            verify(writer, never()).flush();
-            verify(writer, times(1)).close();
-        }
-    }
-
-    @Test
-    public void testCloseWithFlushOn() throws IOException {
-        try (Writer writer = mock(Writer.class)) {
-            @SuppressWarnings("resource")
-            final CSVPrinter printer = new CSVPrinter(writer, CSVFormat.DEFAULT);
-            assertInitialState(printer);
-            printer.close(true);
-            assertEquals(0, printer.getRecordCount());
-            verify(writer, times(1)).flush();
-        }
-    }
-
-    @Test
-    public void testCRComment() throws IOException {
-        final StringWriter sw = new StringWriter();
-        final Object value = "abc";
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentMarker('#'))) {
-            assertInitialState(printer);
-            printer.print(value);
-            assertEquals(0, printer.getRecordCount());
-            printer.printComment("This is a comment\r\non multiple lines\rthis is next comment\r");
-            assertEquals("abc" + recordSeparator + "# This is a comment" + recordSeparator + "# on multiple lines" + recordSeparator +
-                    "# this is next comment" + recordSeparator + "# " + recordSeparator, sw.toString());
-            assertEquals(0, printer.getRecordCount());
-        }
-    }
-
-    @Test
-    public void testCSV135() throws IOException {
-        final List<String> list = new LinkedList<>();
-        list.add("\"\""); // ""
-        list.add("\\\\"); // \\
-        list.add("\\\"\\"); // \"\
-        //
-        // "",\\,\"\ (unchanged)
-        tryFormat(list, null, null, "\"\",\\\\,\\\"\\");
-        //
-        // """""",\\,"\""\" (quoted, and embedded DQ doubled)
-        tryFormat(list, '"', null, "\"\"\"\"\"\",\\\\,\"\\\"\"\\\"");
-        //
-        // "",\\\\,\\"\\ (escapes escaped, not quoted)
-        tryFormat(list, null, '\\', "\"\",\\\\\\\\,\\\\\"\\\\");
-        //
-        // "\"\"","\\\\","\\\"\\" (quoted, and embedded DQ & escape escaped)
-        tryFormat(list, '"', '\\', "\"\\\"\\\"\",\"\\\\\\\\\",\"\\\\\\\"\\\\\"");
-        //
-        // """""",\\,"\""\" (quoted, embedded DQ escaped)
-        tryFormat(list, '"', '"', "\"\"\"\"\"\",\\\\,\"\\\"\"\\\"");
-    }
-
-    @Test
-    public void testCSV259() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (Reader reader = new FileReader("src/test/resources/org/apache/commons/csv/CSV-259/sample.txt");
-                CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape('!').withQuote(null))) {
-            assertInitialState(printer);
-            printer.print(reader);
-            assertEquals("x!,y!,z", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimeterQuoted() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote('\''))) {
-            assertInitialState(printer);
-            printer.print("a,b,c");
-            printer.print("xyz");
-            assertEquals("'a,b,c',xyz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimeterQuoteNone() throws IOException {
-        final StringWriter sw = new StringWriter();
-        final CSVFormat format = CSVFormat.DEFAULT.withEscape('!').withQuoteMode(QuoteMode.NONE);
-        try (CSVPrinter printer = new CSVPrinter(sw, format)) {
-            assertInitialState(printer);
-            printer.print("a,b,c");
-            printer.print("xyz");
-            assertEquals("a!,b!,c,xyz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimeterStringQuoted() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.builder().setDelimiter("[|]").setQuote('\'').get())) {
-            assertInitialState(printer);
-            printer.print("a[|]b[|]c");
-            printer.print("xyz");
-            assertEquals("'a[|]b[|]c'[|]xyz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimeterStringQuoteNone() throws IOException {
-        final StringWriter sw = new StringWriter();
-        final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setEscape('!').setQuoteMode(QuoteMode.NONE).get();
-        try (CSVPrinter printer = new CSVPrinter(sw, format)) {
-            assertInitialState(printer);
-            printer.print("a[|]b[|]c");
-            printer.print("xyz");
-            printer.print("a[xy]bc[]");
-            assertEquals("a![!|!]b![!|!]c[|]xyz[|]a[xy]bc[]", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimiterEscaped() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape('!').withQuote(null))) {
-            assertInitialState(printer);
-            printer.print("a,b,c");
-            printer.print("xyz");
-            assertEquals("a!,b!,c,xyz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimiterPlain() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) {
-            assertInitialState(printer);
-            printer.print("a,b,c");
-            printer.print("xyz");
-            assertEquals("a,b,c,xyz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDelimiterStringEscaped() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.builder().setDelimiter("|||").setEscape('!').setQuote(null).get())) {
-            assertInitialState(printer);
-            printer.print("a|||b|||c");
-            printer.print("xyz");
-            assertEquals("a!|!|!|b!|!|!|c|||xyz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testDisabledComment() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) {
-            assertInitialState(printer);
-            printer.printComment("This is a comment");
-            assertEquals("", sw.toString());
-            assertEquals(0, printer.getRecordCount());
-        }
-    }
-
-    @Test
-    public void testDontQuoteEuroFirstChar() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.RFC4180)) {
-            assertInitialState(printer);
-            printer.printRecord(EURO_CH, "Deux");
-            assertEquals(EURO_CH + ",Deux" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testEolEscaped() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withEscape('!'))) {
-            assertInitialState(printer);
-            printer.print("a\rb\nc");
-            printer.print("x\fy\bz");
-            assertEquals("a!rb!nc,x\fy\bz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testEolPlain() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) {
-            assertInitialState(printer);
-            printer.print("a\rb\nc");
-            printer.print("x\fy\bz");
-            assertEquals("a\rb\nc,x\fy\bz", sw.toString());
-        }
-    }
-
-    @Test
-    public void testEolQuoted() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote('\''))) {
-            assertInitialState(printer);
-            printer.print("a\rb\nc");
-            printer.print("x\by\fz");
-            assertEquals("'a\rb\nc',x\by\fz", sw.toString());
-        }
-    }
-
-    @SuppressWarnings("unlikely-arg-type")
-    @Test
-    public void testEquals() throws IOException {
-        // Don't use assertNotEquals here
-        assertFalse(CSVFormat.DEFAULT.equals(null));
-        // Don't use assertNotEquals here
-        assertFalse(CSVFormat.DEFAULT.equals(""));
-    }
-
-    @Test
-    public void testEscapeBackslash1() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
-            assertInitialState(printer);
-            printer.print("\\");
-        }
-        assertEquals("\\", sw.toString());
-    }
-
-    @Test
-    public void testEscapeBackslash2() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
-            assertInitialState(printer);
-            printer.print("\\\r");
-        }
-        assertEquals("'\\\r'", sw.toString());
-    }
-
-    @Test
-    public void testEscapeBackslash3() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
-            assertInitialState(printer);
-            printer.print("X\\\r");
-        }
-        assertEquals("'X\\\r'", sw.toString());
-    }
-
-    @Test
-    public void testEscapeBackslash4() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
-            assertInitialState(printer);
-            printer.print("\\\\");
-        }
-        assertEquals("\\\\", sw.toString());
-    }
-
-    @Test
-    public void testEscapeBackslash5() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(QUOTE_CH))) {
-            assertInitialState(printer);
-            printer.print("\\\\");
-        }
-        assertEquals("\\\\", sw.toString());
-    }
-
-    @Test
-    public void testEscapeNull1() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
-            assertInitialState(printer);
-            printer.print("\\");
-        }
-        assertEquals("\\", sw.toString());
-    }
-
-    @Test
-    public void testEscapeNull2() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
-            assertInitialState(printer);
-            printer.print("\\\r");
-        }
-        assertEquals("\"\\\r\"", sw.toString());
-    }
-
-    @Test
-    public void testEscapeNull3() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
-            assertInitialState(printer);
-            printer.print("X\\\r");
-        }
-        assertEquals("\"X\\\r\"", sw.toString());
-    }
-
-    @Test
-    public void testEscapeNull4() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
-            assertInitialState(printer);
-            printer.print("\\\\");
-        }
-        assertEquals("\\\\", sw.toString());
-    }
-
-    @Test
-    public void testEscapeNull5() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withEscape(null))) {
-            assertInitialState(printer);
-            printer.print("\\\\");
-        }
-        assertEquals("\\\\", sw.toString());
-    }
-
-    @Test
-    public void testExcelPrintAllArrayOfArrays() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords((Object[]) new String[][] { { "r1c1", "r1c2" }, { "r2c1", "r2c2" } });
-            assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllArrayOfArraysWithFirstEmptyValue2() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords((Object[]) new String[][] { { "" } });
-            assertEquals("\"\"" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllArrayOfArraysWithFirstSpaceValue1() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords((Object[]) new String[][] { { " ", "r1c2" } });
-            assertEquals("\" \",r1c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllArrayOfArraysWithFirstTabValue1() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords((Object[]) new String[][] { { "\t", "r1c2" } });
-            assertEquals("\"\t\",r1c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllArrayOfLists() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords((Object[]) new List[] { Arrays.asList("r1c1", "r1c2"), Arrays.asList("r2c1", "r2c2") });
-            assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllArrayOfListsWithFirstEmptyValue2() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords((Object[]) new List[] { Arrays.asList("") });
-            assertEquals("\"\"" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllIterableOfArrays() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords(Arrays.asList(new String[][] { { "r1c1", "r1c2" }, { "r2c1", "r2c2" } }));
-            assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllIterableOfArraysWithFirstEmptyValue2() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords(Arrays.asList(new String[][] { { "" } }));
-            assertEquals("\"\"" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllIterableOfLists() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords(Arrays.asList(Arrays.asList("r1c1", "r1c2"), Arrays.asList("r2c1", "r2c2")));
-            assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrintAllStreamOfArrays() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecords(Stream.of(new String[][] { { "r1c1", "r1c2" }, { "r2c1", "r2c2" } }));
-            assertEquals("r1c1,r1c2" + recordSeparator + "r2c1,r2c2" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrinter1() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecord("a", "b");
-            assertEquals("a,b" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testExcelPrinter2() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.EXCEL)) {
-            assertInitialState(printer);
-            printer.printRecord("a,b", "b");
-            assertEquals("\"a,b\",b" + recordSeparator, sw.toString());
-        }
-    }
-
-    @Test
-    public void testHeader() throws IOException {
-        final StringWriter sw = new StringWriter();
-        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3"))) {
-            assertEquals(1, printer.getRecordCount());
-            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());
-        }
-    }
-
-    @Test
-    public void testHeaderCommentExcel() throws IOException {
-        final StringWriter sw = new StringWriter();
-        final Date now = new Date();
-        final CSVFormat format = CSVFormat.EXCEL;
-        try (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());
-        }
-    }
-
-    @Test
... 4455 lines suppressed ...