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<CSVFormat>.</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<? extends Enum>) 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<CSVFormat>.</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<? extends Enum>) 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 ...