diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..bec231c194 --- /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 d6f2c05dfd..1f42db3b1a 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 9aec002d71..71887b86d9 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 823014633b..f73d62f6df 100644 --- a/src/assembly/bin.xml +++ b/src/assembly/bin.xml @@ -1,56 +1,56 @@ - + - bin - - tar.gz - zip - - - - - LICENSE.txt - NOTICE.txt - RELEASE-NOTES.txt - - - - target - - - ${artifactId}-${version}.jar - - - - target/site/apidocs - apidocs - - **/* - - - - - target - - - ${artifactId}-${version}-sources.jar - - - - + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd"> + bin + + tar.gz + zip + + + + + LICENSE.txt + NOTICE.txt + RELEASE-NOTES.txt + + + + target + + + ${artifactId}-${version}.jar + + + + target/site/apidocs + apidocs + + **/* + + + + + target + + + ${artifactId}-${version}-sources.jar + + + + diff --git a/src/assembly/src.xml b/src/assembly/src.xml index 819553428b..9f33f58f20 100644 --- a/src/assembly/src.xml +++ b/src/assembly/src.xml @@ -1,45 +1,45 @@ - + - src - - tar.gz - zip - - ${artifactId}-${version}-src - - - - LICENSE* - NOTICE* - RELEASE-NOTES.txt - pom.xml - findbugs-exclude-filter.xml - checkstyle*.xml - - - - src - - **/*Benchmark.java - - - - - + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd"> + src + + tar.gz + zip + + ${artifactId}-${version}-src + + + + LICENSE* + NOTICE* + RELEASE-NOTES.txt + pom.xml + findbugs-exclude-filter.xml + checkstyle*.xml + + + + src + + **/*Benchmark.java + + + + + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b5359533de..a05e5d52e8 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -1,386 +1,386 @@ - - - - - - - - Apache Commons CSV Release Notes - - - - - Release history link changed from changes-report.html to changes.html #516. - - - Bump com.opencsv:opencsv from 5.9 to 5.10. - - - - Required OSGi Import-Package version numbers in MANIFEST.MF #504. - CSVParser.nextRecord() should throw CSVException (an IOException subclass) instead of IOException and IllegalStateException, no method signature changes needed. - - Add CSVPrinter.getRecordCount(). - Add and use CSVParser.Builder and builder() and deprecate CSVParser constructors. - CSVFormat.Builder implements Supplier<CSVFormat>. - Deprecate CSVFormat.Builder.build() for get(). - Track byte position #502. - - Bump org.apache.commons:commons-parent from 76 to 78 #486, #495. - Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #493. - Bump commons-io:commons-io from 2.17.0 to 2.18.0 #505. - Bump commons-codec:commons-codec from 1.17.1 to 1.17.2. - Bump org.apache.commons:commons-parent from 78 to 79. - - - - Add CSVException that extends IOException thrown on invalid input instead of IOException. - - Fix PMD issues for port to PMD 7.1.0. - Fix some Javadoc links #442. - Extract duplicated code into a method #444. - Migrate CSVFormat#print(File, Charset) to NIO #445. - Fix documentation for CSVFormat private constructor #466. - CSVFormat does not support explicit " as escape char. - Escaping is not disableable. - Fix Javadoc warnings on Java 23. - Improve parser performance by up to 20%, YMMV. - - Bump commons-codec:commons-codec from 1.16.1 to 1.17.1 #422, #449. - Bump org.apache.commons:commons-parent from 69 to 76 #435, #452, #465, #468, #475, #482. - Bump org.codehaus.mojo:taglist-maven-plugin from 3.0.0 to 3.1.0 #441. - Bump org.apache.commons:commons-lang3 from 3.14.0 to 3.17.0 #450, #459, #470. - Bump org.hamcrest:hamcrest from 2.2 to 3.0 #455. - Bump commons-io:commons-io from 2.16.1 to 2.17.0 #476. - - - - [Javadoc] Add example to CSVFormat#setHeaderComments() #344. - Add and use CSVFormat#setTrailingData(boolean) in CSVFormat.EXCEL for Excel compatibility #303. - Add and use CSVFormat#setLenientEof(boolean) in CSVFormat.EXCEL for Excel compatibility #303. - - Replace deprecated method in user guide, update external link #324, #325. - Document duplicate header behavior #309. - Add missing docs #328. - [StepSecurity] CI: Harden GitHub Actions #329, #330. - Better error message during faulty CSV record read #347. - Misleading error message when QuoteMode set to None #352. - OutOfMemory for very long rows despite using column value of type Reader. - Use try-with-resources to manage JDBC CLOB in CSVPrinter.printRecords(ResultSet). - JDBC Blob columns are now output as Base64 instead of Object#toString(), which usually is InputStream#toString(). - Support unusual Excel use cases: Add support for trailing data after the closing quote, and EOF without a final closing quote #303. - MongoDB CSV empty first column parsing fix #412. - - Bump commons-io:commons-io: from 2.11.0 to 2.16.1 #408, #413. - Bump commons-parent from 57 to 69 #410. - Bump h2 from 2.1.214 to 2.2.224 #333, #349, #359. - Bump commons-lang3 from 3.12.0 to 3.14.0. - Update exception message in CSVRecord#getNextRecord() #348. - Bump tests using com.opencsv:opencsv from 5.8 to 5.9 #373. - - - - Minor changes #172. - No Automatic-Module-Name prevents usage in JPMS projects without repacking the JAR. - Fix for multi-char delimiter not working as expected #218. - CSVRecord.get(Enum) should use Enum.name() instead of Enum.toString(). - Allow org.apache.commons.csv.IOUtils.copy(Reader, Appendable, CharBuffer) to compile on Java 11 and run on Java 8. - CSVRecord.toList() does not give write access to the new List. - CSVParser.getRecords() now throws UncheckedIOException instead of IOException. - Add comments to iterator() and stream() #270. - Fix wrong assumptions in PostgreSQL formats #265. - Validate input to setDelimiter(String) for empty string #266. - Bump CSVFormat#serialVersionUID from 1 to 2. - CSVParser: Identify duplicates in null, empty and blank header names #279. - - Serialization in CSVFormat is not supported from one version to the next. - - Make CSVRecord#values() public. - Add DuplicateHeaderMode for flexibility with header strictness. #114. - Support for parallelism in CSVPrinter. - Add CSVPrinter.printRecord[s](Stream). - Add accessors for header/trailer comments #257. - Add github/codeql-action. - - Bump actions/cache from 2.1.6 to 3.0.10 #196, #233, #243, #267, #271. - Bump actions/checkout from 2.3.4 to 3.1.0 #188, #195, #220, #272. - Bump actions/setup-java from 2 to 3.5.1. - Bump actions/upload-artifact from 3.1.0 to 3.1.1 #280. - Bump commons-parent from 52 to 57 #264, #288, #298, #323. - Bump checkstyle from 8.44 to 9.2.1 #180, #190, #194, #202, #207. - Bump junit-jupiter from 5.8.0-M1 to 5.9.1 #179, #186, #201, #244, #263. - Bump jmh-core from 1.32 to 1.36 #176, #208, #229, #285. - Bump jmh-generator-annprocess from 1.32 to 1.36 #175, #206, #226, #283. - Bump mockito-core from 3.11.2 to 4.11.0 #187, #197, #204, #212, #230, #237, #251, #259, #284, #292, #297. - Bump maven-pmd-plugin from 3.14.0 to 3.19.0 #184, #219, #238, #254, #258. - Bump pmd from 6.36.0 to 6.52.0 #173, #189, #193, #199, #227, #233, #214, #236, #240, #247, #255, #273. - Bump opencsv from 5.5.1 to 5.7.1 #182, #221, #260, #281. - Bump spotbugs-maven-plugin from 4.3.0 to 4.7.3.0 #192, #198, #203, #211, #225, #234, #242, #245, #261, #275, #282. - Bump com.github.spotbugs:spotbugs from 4.5.3 to 4.7.2. - Bump h2 from 1.4.200 to 2.1.214 #200, #205, #213, #239. - Bump maven-javadoc-plugin from 3.3.0 to 3.4.1. - Bump biz.aQute.bnd:biz.aQute.bndlib from 5.3.0 to 6.3.1. - Bump jacoco-maven-plugin from 0.8.7 to 0.8.8. - Bump japicmp-maven-plugin from 0.15.3 to 0.16.0. - Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #253. - - - - Replace FindBugs with SpotBugs #56. - Javadoc typo in CSVFormat let's -> lets #57. - CSVFormat.printWithEscapes throws StringIndexOutOfBoundsException when value is Reader #61. - Improve CSVFormat test coverage #63. - Fix CSVFileParserTest.java to allow for a null return value from record.getComment() #62. - Improve test coverage in CSVFormatTest #65. - Removed invalid Javadoc markup for CSVFormat EXCEL #64. - Improve CSVRecord and CSVPrinter code coverage #66. - Improve lexer and token coverage #67. - CSVFormat.format trims last delimiter if the delimiter is a white space #71. - Replace org.apache.commons.csv.Assertions.notNull() with Objects.requireNonNull(). - Line number is not proper at EOF. - Parser iterates over the last CSV Record twice. - Minor improvements #126, #127, #130. - Add possibility to use ResultSet header meta data as CSV header #11. - Add test cases for withIgnoreSurroundingSpaces() and withTrim() #70. - Update CSVParser.parse(File, Charset, CSVFormat) from IO to NIO. - Missing separator with print(object) followed by printRecord(Object[]) #157. - Fix EOL checking for read array in ExtendedBufferedReader #5. - Print from Reader with embedded quotes generates incorrect output #78. - Replace JUnit assert by simpler but equivalent calls. #159. - Update gitignore to ignore idea and vscode #160. - Update CSVBenchmark #165. - Remove Whitespace Check Determines Delimiter Twice #167. - Document and Automate CSV Benchmark Harness #166. - Optimize Lexer Delimiter Check for One Character Delimiter #163. - 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. - 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. - 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. - 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. - 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. - Formalize PerformanceTest #168. - Reuse Buffers in Lexer for Delimiter Detection #162. - Cleanup and Document Performance Test Harness #170. - Update buffer position when reading line comment #120. - - Make CSVRecord#toList() public. - Add CSVRecord#stream(). - Add CSVParser#stream(). - Make the method CSVRecord.putIn(Map) public. - Add test cases for CSVRecord with get(Enum) and toString. #54. - Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73. - Add support for String delimiters #76. - - Update org.junit.jupiter:junit-jupiter from 5.6.0 to 5.7.0, #84 #109 - Update tests from Apache Commons Lang 3.9 to 3.12.0. - Update tests from commons-io:commons-io 2.6 to 2.11.0, #108. - Bump actions/checkout from v1 to v2.3.4, #79, #92, #121. - Bump commons-parent from 50 to 51 #80. - Bump tests from opencsv from 3.1 to 5.5.1 #81, #137, #158. - Update tests from super-csv from 2.2.1 to 2.4.0 #86. - Bump build actions/setup-java from v1.4.0 to v2, #101, #113. - Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #122. - Bump tests from org.mockito:mockito-core 3.2.4 -> 3.11.2; #88, #107, #110, #123, #128, #129, #156. - Bump actions/cache from v2 to v2.1.6 #132, #153. - Bump maven-checkstyle-plugin from 3.0.0 to 3.1.2 #131. - Bump checkstyle from 8.29 to 8.44. - Bump junit-jupiter from 5.7.0 to 5.8.0-M1 #133, #149. - Bump commons.jacoco.version from 0.8.5 to 0.8.7 (Java 16). - Bump commons.spotbugs.version from 4.0.4 to 4.3.0 (Java 16). - Bump maven-javadoc-plugin from 3.2.0 to 3.3.0. - Bump jmh-generator-annprocess from 1.5.2 to 1.32 #151. - Bump PMD core from 6.29.0 to 6.36.0. - Bump biz.aQute.bnd:biz.aQute.bndlib from 5.1.2 to 5.3.0. - - - Add CSVRecord.isSet(int) method #52. - Char escape doesn't work properly with quoting. - Test case failures following CSVFormat#equals() update. - CSVFormat withTrim() and withIgnoreSurroundingSpaces() need better docs. - CSVFormat equals() and hashCode() don't use all fields. - CSVFormat#validate() does not account for allowDuplicateHeaderNames #43. - Post 1.7 release fixes. - Upgrade test framework to JUnit 5 Jupiter #49, #50. - A single empty header is allowed when not allowing empty column headers. #47. - CSVRecord is not Serializable. - Use test scope for supercsv #48. - Update tests from H2 1.4.199 to 1.4.200. - Update tests from Hamcrest 2.1 to 2.2. - Update tests from Mockito 3.1.0 to 3.2.4. - Fix typos in site and test #53. - Fix typo performance test #55. - - - Add predefined CSVFormats for printing MongoDB CSV and TSV. - Fix escape character for POSTGRESQL_TEXT and POSTGRESQL_CSV formats. - Site link "Source Repository" does not work. - Add support for java.sql.Clob. - Update to Java 8. - Escape quotes in CLOBs #39. - Cannot get headers in column order from CSVRecord. - Update tests from H2 1.4.198 to 1.4.199. - - - Add more documentation to CSVPrinter. - Add autoFlush option for CsvPrinter. PR #24. - The behavior of quote char using is not similar as Excel does when the first string contains CJK char(s). - Don't quote cells just because they have UTF-8 encoded characters. - Add API org.apache.commons.csv.CSVFormat.withSystemRecordSeparator(). - Inconsistency between Javadoc of CSVFormat DEFAULT EXCEL. - Create CSVFormat.ORACLE preset. - Some multi-iterator parsing peek sequences incorrectly consume elements. - Parse method should avoid creating a redundant BufferedReader. - Add predefined CSVFormats for printing MongoDB CSV and TSV. - - - withNullString value is printed without quotes when QuoteMode.ALL is specified; add QuoteMode.ALL_NON_NULL. PR #17. - Fix outdated comments about FileReader in CSVParser #13 - Fix incorrect method name 'withFirstRowAsHeader' in user guide. - Negative numeric values in the first column are always quoted in minimal mode. - Update platform requirement from Java 6 to 7. - Do not use RuntimeException in CSVParser.iterator().new Iterator() {...}.getNextRecord() - CSVParser: Add factory method accepting InputStream. - Add convenience API CSVFormat.print(File, Charset) - Add convenience API CSVFormat.print(Path, Charset) - Add convenience API CSVParser.parse(Path, Charset, CSVFormat) - Add convenience API CSVFormat#printer() to print to System.out - Provide a CSV Format for printing PostgreSQL CSV and Text formats. - Adding a placeholder in the Lexer and CSV parser to store the end-of-line string. - - - Make CSVPrinter.print(Object) GC-free. - Allow some printing operations directly from CSVFormat. - Drop ferc.gov tests. - - - Add shortcut method for using first record as header to CSVFormat - Add withHeader(Class<? extends Enum>) to CSVFormat - Comment line hides next record; update Javadoc to make behavior clear - CSVPrinter doesn't skip creation of header record if skipHeaderRecord is set to true - Add IgnoreCase option for accessing header names - The null string should be case-sensitive when reading records - CSVFormat.nullString should not be escaped - CSVFormat.MYSQL nullString should be "\N" - Fix Javadoc to say CSVFormat with() methods return a new CSVFormat - Support for ignoring trailing delimiter. - Support trimming leading and trailing blanks. - Create default formats for Informix UNLOAD and UNLOAD CSV. - - - CSVFormat.with* methods clear the header comments - Incorrect Javadoc on QuoteMode.NONE - Add enum CSVFormat.Predefined that contains the default CSVFormat values. - - - QuoteMode.NON_NUMERIC doesn't work with CSVPrinter.printRecords(ResultSet) - CSVFormat#withHeader doesn't work well with #printComment, add withHeaderComments(String...) - CSVFormat.EXCEL should ignore empty header names - Incorrect Javadoc referencing org.apache.commons.csv.CSVFormat withQuote() - Improve toString() implementation of CSVRecord - Unified parameter validation - Add CSVFormat#with 0-arg methods matching boolean arg methods - Save positions of records to enable random access - CSVPrinter.printRecord(ResultSet) with metadata - - - No longer works with Java 6 - NullPointerException when empty header string and null string of "" - Validate format parameters in constructor - IllegalArgumentException thrown when the header contains duplicate names when the column names are empty. - CSVFormat#withHeader doesn't work with CSVPrinter - CSVFormat is missing a print(...) method - CSVRecord.toMap() throws NPE on formats with no - headers. - Check whether ISE/IAE are being used appropriately - CSVFormat constructor should reject a header array with duplicate - entries - - HeaderMap is inconsistent when it is parsed from an input with - duplicate columns names - - CSVRecord.toMap() fails if row length shorter than header length - - CSVFormat.format allways append null - Add Map conversion API to CSVRecord - CSVParser: getHeaderMap throws NPE - Lots of possible changes - Use Character instead of char for char fields except delimiter - - Revert Builder implementation in CSVFormat - CSVRecord does not verify that the length of the header mapping - matches the number of values - - Allow the handling of NULL values - Use the Builder pattern for CSVFormat - Clarify comment handling - CSVParser.nextValue() seems pointless - Allow the String value for null to be customized for the CSV - printer - - Not possible to create a CSVFormat from scratch - Keep track of record number - Lexer should only use char fields - Need a way to extract parsed headers, e.g. for use in formatting - output - - Header support - Confusing semantic of the ignore leading/trailing spaces parameters - - Add convenience methods to CSVLexer - Is CharBuffer really needed, now that StringBuilder is available? - - Replace while(true)-loop in CSVParser.getRecord with do-while-loop - - CSVFormat describes itself as immutable, but it is not - in - particular it is not thread-safe - - Endless loops in CSV parser - NullPointerException in CSVPrinter.print()/println() - CSVPrinter overhaul - Excel strategy uses wrong separator - CSVStrategy has modifiable public static variables - - Predefined format for MYSQL - Reduce visibility of methods in internal classes - ExtendedBufferedReader does too much - Decide whether to keep the csv.writer subpackage - - - - + + + + + + + + Apache Commons CSV Release Notes + + + + + Release history link changed from changes-report.html to changes.html #516. + + + Bump com.opencsv:opencsv from 5.9 to 5.10. + + + + Required OSGi Import-Package version numbers in MANIFEST.MF #504. + CSVParser.nextRecord() should throw CSVException (an IOException subclass) instead of IOException and IllegalStateException, no method signature changes needed. + + Add CSVPrinter.getRecordCount(). + Add and use CSVParser.Builder and builder() and deprecate CSVParser constructors. + CSVFormat.Builder implements Supplier<CSVFormat>. + Deprecate CSVFormat.Builder.build() for get(). + Track byte position #502. + + Bump org.apache.commons:commons-parent from 76 to 78 #486, #495. + Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #493. + Bump commons-io:commons-io from 2.17.0 to 2.18.0 #505. + Bump commons-codec:commons-codec from 1.17.1 to 1.17.2. + Bump org.apache.commons:commons-parent from 78 to 79. + + + + Add CSVException that extends IOException thrown on invalid input instead of IOException. + + Fix PMD issues for port to PMD 7.1.0. + Fix some Javadoc links #442. + Extract duplicated code into a method #444. + Migrate CSVFormat#print(File, Charset) to NIO #445. + Fix documentation for CSVFormat private constructor #466. + CSVFormat does not support explicit " as escape char. + Escaping is not disableable. + Fix Javadoc warnings on Java 23. + Improve parser performance by up to 20%, YMMV. + + Bump commons-codec:commons-codec from 1.16.1 to 1.17.1 #422, #449. + Bump org.apache.commons:commons-parent from 69 to 76 #435, #452, #465, #468, #475, #482. + Bump org.codehaus.mojo:taglist-maven-plugin from 3.0.0 to 3.1.0 #441. + Bump org.apache.commons:commons-lang3 from 3.14.0 to 3.17.0 #450, #459, #470. + Bump org.hamcrest:hamcrest from 2.2 to 3.0 #455. + Bump commons-io:commons-io from 2.16.1 to 2.17.0 #476. + + + + [Javadoc] Add example to CSVFormat#setHeaderComments() #344. + Add and use CSVFormat#setTrailingData(boolean) in CSVFormat.EXCEL for Excel compatibility #303. + Add and use CSVFormat#setLenientEof(boolean) in CSVFormat.EXCEL for Excel compatibility #303. + + Replace deprecated method in user guide, update external link #324, #325. + Document duplicate header behavior #309. + Add missing docs #328. + [StepSecurity] CI: Harden GitHub Actions #329, #330. + Better error message during faulty CSV record read #347. + Misleading error message when QuoteMode set to None #352. + OutOfMemory for very long rows despite using column value of type Reader. + Use try-with-resources to manage JDBC CLOB in CSVPrinter.printRecords(ResultSet). + JDBC Blob columns are now output as Base64 instead of Object#toString(), which usually is InputStream#toString(). + Support unusual Excel use cases: Add support for trailing data after the closing quote, and EOF without a final closing quote #303. + MongoDB CSV empty first column parsing fix #412. + + Bump commons-io:commons-io: from 2.11.0 to 2.16.1 #408, #413. + Bump commons-parent from 57 to 69 #410. + Bump h2 from 2.1.214 to 2.2.224 #333, #349, #359. + Bump commons-lang3 from 3.12.0 to 3.14.0. + Update exception message in CSVRecord#getNextRecord() #348. + Bump tests using com.opencsv:opencsv from 5.8 to 5.9 #373. + + + + Minor changes #172. + No Automatic-Module-Name prevents usage in JPMS projects without repacking the JAR. + Fix for multi-char delimiter not working as expected #218. + CSVRecord.get(Enum) should use Enum.name() instead of Enum.toString(). + Allow org.apache.commons.csv.IOUtils.copy(Reader, Appendable, CharBuffer) to compile on Java 11 and run on Java 8. + CSVRecord.toList() does not give write access to the new List. + CSVParser.getRecords() now throws UncheckedIOException instead of IOException. + Add comments to iterator() and stream() #270. + Fix wrong assumptions in PostgreSQL formats #265. + Validate input to setDelimiter(String) for empty string #266. + Bump CSVFormat#serialVersionUID from 1 to 2. + CSVParser: Identify duplicates in null, empty and blank header names #279. + + Serialization in CSVFormat is not supported from one version to the next. + + Make CSVRecord#values() public. + Add DuplicateHeaderMode for flexibility with header strictness. #114. + Support for parallelism in CSVPrinter. + Add CSVPrinter.printRecord[s](Stream). + Add accessors for header/trailer comments #257. + Add github/codeql-action. + + Bump actions/cache from 2.1.6 to 3.0.10 #196, #233, #243, #267, #271. + Bump actions/checkout from 2.3.4 to 3.1.0 #188, #195, #220, #272. + Bump actions/setup-java from 2 to 3.5.1. + Bump actions/upload-artifact from 3.1.0 to 3.1.1 #280. + Bump commons-parent from 52 to 57 #264, #288, #298, #323. + Bump checkstyle from 8.44 to 9.2.1 #180, #190, #194, #202, #207. + Bump junit-jupiter from 5.8.0-M1 to 5.9.1 #179, #186, #201, #244, #263. + Bump jmh-core from 1.32 to 1.36 #176, #208, #229, #285. + Bump jmh-generator-annprocess from 1.32 to 1.36 #175, #206, #226, #283. + Bump mockito-core from 3.11.2 to 4.11.0 #187, #197, #204, #212, #230, #237, #251, #259, #284, #292, #297. + Bump maven-pmd-plugin from 3.14.0 to 3.19.0 #184, #219, #238, #254, #258. + Bump pmd from 6.36.0 to 6.52.0 #173, #189, #193, #199, #227, #233, #214, #236, #240, #247, #255, #273. + Bump opencsv from 5.5.1 to 5.7.1 #182, #221, #260, #281. + Bump spotbugs-maven-plugin from 4.3.0 to 4.7.3.0 #192, #198, #203, #211, #225, #234, #242, #245, #261, #275, #282. + Bump com.github.spotbugs:spotbugs from 4.5.3 to 4.7.2. + Bump h2 from 1.4.200 to 2.1.214 #200, #205, #213, #239. + Bump maven-javadoc-plugin from 3.3.0 to 3.4.1. + Bump biz.aQute.bnd:biz.aQute.bndlib from 5.3.0 to 6.3.1. + Bump jacoco-maven-plugin from 0.8.7 to 0.8.8. + Bump japicmp-maven-plugin from 0.15.3 to 0.16.0. + Bump maven-checkstyle-plugin from 3.1.2 to 3.2.0 #253. + + + + Replace FindBugs with SpotBugs #56. + Javadoc typo in CSVFormat let's -> lets #57. + CSVFormat.printWithEscapes throws StringIndexOutOfBoundsException when value is Reader #61. + Improve CSVFormat test coverage #63. + Fix CSVFileParserTest.java to allow for a null return value from record.getComment() #62. + Improve test coverage in CSVFormatTest #65. + Removed invalid Javadoc markup for CSVFormat EXCEL #64. + Improve CSVRecord and CSVPrinter code coverage #66. + Improve lexer and token coverage #67. + CSVFormat.format trims last delimiter if the delimiter is a white space #71. + Replace org.apache.commons.csv.Assertions.notNull() with Objects.requireNonNull(). + Line number is not proper at EOF. + Parser iterates over the last CSV Record twice. + Minor improvements #126, #127, #130. + Add possibility to use ResultSet header meta data as CSV header #11. + Add test cases for withIgnoreSurroundingSpaces() and withTrim() #70. + Update CSVParser.parse(File, Charset, CSVFormat) from IO to NIO. + Missing separator with print(object) followed by printRecord(Object[]) #157. + Fix EOL checking for read array in ExtendedBufferedReader #5. + Print from Reader with embedded quotes generates incorrect output #78. + Replace JUnit assert by simpler but equivalent calls. #159. + Update gitignore to ignore idea and vscode #160. + Update CSVBenchmark #165. + Remove Whitespace Check Determines Delimiter Twice #167. + Document and Automate CSV Benchmark Harness #166. + Optimize Lexer Delimiter Check for One Character Delimiter #163. + 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. + 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. + 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. + 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. + 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. + Formalize PerformanceTest #168. + Reuse Buffers in Lexer for Delimiter Detection #162. + Cleanup and Document Performance Test Harness #170. + Update buffer position when reading line comment #120. + + Make CSVRecord#toList() public. + Add CSVRecord#stream(). + Add CSVParser#stream(). + Make the method CSVRecord.putIn(Map) public. + Add test cases for CSVRecord with get(Enum) and toString. #54. + Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73. + Add support for String delimiters #76. + + Update org.junit.jupiter:junit-jupiter from 5.6.0 to 5.7.0, #84 #109 + Update tests from Apache Commons Lang 3.9 to 3.12.0. + Update tests from commons-io:commons-io 2.6 to 2.11.0, #108. + Bump actions/checkout from v1 to v2.3.4, #79, #92, #121. + Bump commons-parent from 50 to 51 #80. + Bump tests from opencsv from 3.1 to 5.5.1 #81, #137, #158. + Update tests from super-csv from 2.2.1 to 2.4.0 #86. + Bump build actions/setup-java from v1.4.0 to v2, #101, #113. + Bump maven-pmd-plugin from 3.13.0 to 3.14.0 #122. + Bump tests from org.mockito:mockito-core 3.2.4 -> 3.11.2; #88, #107, #110, #123, #128, #129, #156. + Bump actions/cache from v2 to v2.1.6 #132, #153. + Bump maven-checkstyle-plugin from 3.0.0 to 3.1.2 #131. + Bump checkstyle from 8.29 to 8.44. + Bump junit-jupiter from 5.7.0 to 5.8.0-M1 #133, #149. + Bump commons.jacoco.version from 0.8.5 to 0.8.7 (Java 16). + Bump commons.spotbugs.version from 4.0.4 to 4.3.0 (Java 16). + Bump maven-javadoc-plugin from 3.2.0 to 3.3.0. + Bump jmh-generator-annprocess from 1.5.2 to 1.32 #151. + Bump PMD core from 6.29.0 to 6.36.0. + Bump biz.aQute.bnd:biz.aQute.bndlib from 5.1.2 to 5.3.0. + + + Add CSVRecord.isSet(int) method #52. + Char escape doesn't work properly with quoting. + Test case failures following CSVFormat#equals() update. + CSVFormat withTrim() and withIgnoreSurroundingSpaces() need better docs. + CSVFormat equals() and hashCode() don't use all fields. + CSVFormat#validate() does not account for allowDuplicateHeaderNames #43. + Post 1.7 release fixes. + Upgrade test framework to JUnit 5 Jupiter #49, #50. + A single empty header is allowed when not allowing empty column headers. #47. + CSVRecord is not Serializable. + Use test scope for supercsv #48. + Update tests from H2 1.4.199 to 1.4.200. + Update tests from Hamcrest 2.1 to 2.2. + Update tests from Mockito 3.1.0 to 3.2.4. + Fix typos in site and test #53. + Fix typo performance test #55. + + + Add predefined CSVFormats for printing MongoDB CSV and TSV. + Fix escape character for POSTGRESQL_TEXT and POSTGRESQL_CSV formats. + Site link "Source Repository" does not work. + Add support for java.sql.Clob. + Update to Java 8. + Escape quotes in CLOBs #39. + Cannot get headers in column order from CSVRecord. + Update tests from H2 1.4.198 to 1.4.199. + + + Add more documentation to CSVPrinter. + Add autoFlush option for CsvPrinter. PR #24. + The behavior of quote char using is not similar as Excel does when the first string contains CJK char(s). + Don't quote cells just because they have UTF-8 encoded characters. + Add API org.apache.commons.csv.CSVFormat.withSystemRecordSeparator(). + Inconsistency between Javadoc of CSVFormat DEFAULT EXCEL. + Create CSVFormat.ORACLE preset. + Some multi-iterator parsing peek sequences incorrectly consume elements. + Parse method should avoid creating a redundant BufferedReader. + Add predefined CSVFormats for printing MongoDB CSV and TSV. + + + withNullString value is printed without quotes when QuoteMode.ALL is specified; add QuoteMode.ALL_NON_NULL. PR #17. + Fix outdated comments about FileReader in CSVParser #13 + Fix incorrect method name 'withFirstRowAsHeader' in user guide. + Negative numeric values in the first column are always quoted in minimal mode. + Update platform requirement from Java 6 to 7. + Do not use RuntimeException in CSVParser.iterator().new Iterator() {...}.getNextRecord() + CSVParser: Add factory method accepting InputStream. + Add convenience API CSVFormat.print(File, Charset) + Add convenience API CSVFormat.print(Path, Charset) + Add convenience API CSVParser.parse(Path, Charset, CSVFormat) + Add convenience API CSVFormat#printer() to print to System.out + Provide a CSV Format for printing PostgreSQL CSV and Text formats. + Adding a placeholder in the Lexer and CSV parser to store the end-of-line string. + + + Make CSVPrinter.print(Object) GC-free. + Allow some printing operations directly from CSVFormat. + Drop ferc.gov tests. + + + Add shortcut method for using first record as header to CSVFormat + Add withHeader(Class<? extends Enum>) to CSVFormat + Comment line hides next record; update Javadoc to make behavior clear + CSVPrinter doesn't skip creation of header record if skipHeaderRecord is set to true + Add IgnoreCase option for accessing header names + The null string should be case-sensitive when reading records + CSVFormat.nullString should not be escaped + CSVFormat.MYSQL nullString should be "\N" + Fix Javadoc to say CSVFormat with() methods return a new CSVFormat + Support for ignoring trailing delimiter. + Support trimming leading and trailing blanks. + Create default formats for Informix UNLOAD and UNLOAD CSV. + + + CSVFormat.with* methods clear the header comments + Incorrect Javadoc on QuoteMode.NONE + Add enum CSVFormat.Predefined that contains the default CSVFormat values. + + + QuoteMode.NON_NUMERIC doesn't work with CSVPrinter.printRecords(ResultSet) + CSVFormat#withHeader doesn't work well with #printComment, add withHeaderComments(String...) + CSVFormat.EXCEL should ignore empty header names + Incorrect Javadoc referencing org.apache.commons.csv.CSVFormat withQuote() + Improve toString() implementation of CSVRecord + Unified parameter validation + Add CSVFormat#with 0-arg methods matching boolean arg methods + Save positions of records to enable random access + CSVPrinter.printRecord(ResultSet) with metadata + + + No longer works with Java 6 + NullPointerException when empty header string and null string of "" + Validate format parameters in constructor + IllegalArgumentException thrown when the header contains duplicate names when the column names are empty. + CSVFormat#withHeader doesn't work with CSVPrinter + CSVFormat is missing a print(...) method + CSVRecord.toMap() throws NPE on formats with no + headers. + Check whether ISE/IAE are being used appropriately + CSVFormat constructor should reject a header array with duplicate + entries + + HeaderMap is inconsistent when it is parsed from an input with + duplicate columns names + + CSVRecord.toMap() fails if row length shorter than header length + + CSVFormat.format allways append null + Add Map conversion API to CSVRecord + CSVParser: getHeaderMap throws NPE + Lots of possible changes + Use Character instead of char for char fields except delimiter + + Revert Builder implementation in CSVFormat + CSVRecord does not verify that the length of the header mapping + matches the number of values + + Allow the handling of NULL values + Use the Builder pattern for CSVFormat + Clarify comment handling + CSVParser.nextValue() seems pointless + Allow the String value for null to be customized for the CSV + printer + + Not possible to create a CSVFormat from scratch + Keep track of record number + Lexer should only use char fields + Need a way to extract parsed headers, e.g. for use in formatting + output + + Header support + Confusing semantic of the ignore leading/trailing spaces parameters + + Add convenience methods to CSVLexer + Is CharBuffer really needed, now that StringBuilder is available? + + Replace while(true)-loop in CSVParser.getRecord with do-while-loop + + CSVFormat describes itself as immutable, but it is not - in + particular it is not thread-safe + + Endless loops in CSV parser + NullPointerException in CSVPrinter.print()/println() + CSVPrinter overhaul + Excel strategy uses wrong separator + CSVStrategy has modifiable public static variables + + Predefined format for MYSQL + Reduce visibility of methods in internal classes + ExtendedBufferedReader does too much + Decide whether to keep the csv.writer subpackage + + + + diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java index dce94692d7..67088c38a5 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}. - * - *

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. - *

- * - *

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. - *

- * - *

Example:

- * - *
- * 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();
- * }
- * 
- * - *

This code will write the following to csv.txt:

- *
- * id,userName,firstName,lastName,birthday
- * 1,john73,John,Doe,1973-09-15
- *
- * 2,mary,Mary,Meyer,1985-03-29
- * 
- */ -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. - *

- * 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. - *

- * - * @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. - * - *

- * 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. - *

- * - *

- * If comments are disabled in the current CSV format this method does nothing. - *

- * - *

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.

- * - * @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 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. - * - *

- * 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()}. - *

- * - * @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. - * - *

- * 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()}. - *

- * - * @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. - * - *

- * 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()}. - *

- * - * @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. - * - *

- * 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...)}. - *

- * - *

- * Given the following data structure: - *

- * - *
{@code
-     * List data = new ArrayList<>();
-     * data.add(new String[]{ "A", "B", "C" });
-     * data.add(new String[]{ "1", "2", "3" });
-     * data.add(new String[]{ "A1", "B2", "C3" });
-     * }
-     * 
- * - *

- * Calling this method will print: - *

- * - *
-     * {@code
-     * A, B, C
-     * 1, 2, 3
-     * A1, B2, C3
-     * }
-     * 
- * - * @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. - * - *

- * 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...)}. - *

- * - *

- * Given the following data structure: - *

- * - *
{@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" };
-     * }
-     * 
- * - *

- * Calling this method will print: - *

- * - *
{@code
-     * A, B, C
-     * 1, 2, 3
-     * A1, B2, C3
-     * }
-     * 
- * - * @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. - * - *

- * 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...)}. - *

- * - *

- * Given the following data structure: - *

- * - *
{@code
-     * List 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 stream = data.stream();
-     * }
-     * 
- * - *

- * Calling this method will print: - *

- * - *
-     * {@code
-     * A, B, C
-     * 1, 2, 3
-     * A1, B2, C3
-     * }
-     * 
- * - * @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}. + * + *

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. + *

+ * + *

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. + *

+ * + *

Example:

+ * + *
+ * 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();
+ * }
+ * 
+ * + *

This code will write the following to csv.txt:

+ *
+ * id,userName,firstName,lastName,birthday
+ * 1,john73,John,Doe,1973-09-15
+ *
+ * 2,mary,Mary,Meyer,1985-03-29
+ * 
+ */ +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. + *

+ * 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. + *

+ * + * @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. + * + *

+ * 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. + *

+ * + *

+ * If comments are disabled in the current CSV format this method does nothing. + *

+ * + *

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.

+ * + * @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 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. + * + *

+ * 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()}. + *

+ * + * @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. + * + *

+ * 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()}. + *

+ * + * @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. + * + *

+ * 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()}. + *

+ * + * @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. + * + *

+ * 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...)}. + *

+ * + *

+ * Given the following data structure: + *

+ * + *
{@code
+     * List data = new ArrayList<>();
+     * data.add(new String[]{ "A", "B", "C" });
+     * data.add(new String[]{ "1", "2", "3" });
+     * data.add(new String[]{ "A1", "B2", "C3" });
+     * }
+     * 
+ * + *

+ * Calling this method will print: + *

+ * + *
+     * {@code
+     * A, B, C
+     * 1, 2, 3
+     * A1, B2, C3
+     * }
+     * 
+ * + * @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. + * + *

+ * 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...)}. + *

+ * + *

+ * Given the following data structure: + *

+ * + *
{@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" };
+     * }
+     * 
+ * + *

+ * Calling this method will print: + *

+ * + *
{@code
+     * A, B, C
+     * 1, 2, 3
+     * A1, B2, C3
+     * }
+     * 
+ * + * @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. + * + *

+ * 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...)}. + *

+ * + *

+ * Given the following data structure: + *

+ * + *
{@code
+     * List 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 stream = data.stream();
+     * }
+     * 
+ * + *

+ * Calling this method will print: + *

+ * + *
+     * {@code
+     * A, B, C
+     * 1, 2, 3
+     * A1, B2, C3
+     * }
+     * 
+ * + * @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 5f8a5cf465..e85578467d 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 50dfd0f891..9677d8ecc2 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= 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= " + "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> 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= 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= " + "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> 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 da49a78cff..38d442e55b 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 headerMap = parser.getHeaderMap(); - final Iterator columnNames = headerMap.keySet().iterator(); - // Headers are iterated in column order. - assertEquals("A", columnNames.next()); - assertEquals("B", columnNames.next()); - assertEquals("C", columnNames.next()); - final Iterator 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 nameIndexMap = parser.getHeaderMap(); - final List 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 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 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 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 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 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 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 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 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 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 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 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 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 itr1 = parser.iterator(); - final Iterator 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 headerMap = parser.getHeaderMap(); + final Iterator columnNames = headerMap.keySet().iterator(); + // Headers are iterated in column order. + assertEquals("A", columnNames.next()); + assertEquals("B", columnNames.next()); + assertEquals("C", columnNames.next()); + final Iterator 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 nameIndexMap = parser.getHeaderMap(); + final List 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 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 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 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 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 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 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 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 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 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 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 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 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 itr1 = parser.iterator(); + final Iterator 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 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 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 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 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 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 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 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 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 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 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 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 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 b4b560a1d8..f457460c9b 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 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[] 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 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 - public void testHeaderCommentTdf() throws IOException { - final StringWriter sw = new StringWriter(); - final Date now = new Date(); - final CSVFormat format = CSVFormat.TDF; - try (CSVPrinter csvPrinter = printWithHeaderComments(sw, now, format)) { - assertEquals("# Generated by Apache Commons CSV 1.1\r\n# " + now + "\r\nCol1\tCol2\r\nA\tB\r\nC\tD\r\n", sw.toString()); - } - } - - @Test - public void testHeaderNotSet() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) { - assertInitialState(printer); - printer.printRecord("a", "b", "c"); - printer.printRecord("x", "y", "z"); - assertEquals("a,b,c\r\nx,y,z\r\n", sw.toString()); - } - } - - @Test - public void testInvalidFormat() { - assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter(CR)); - } - - @Test - public void testJdbcPrinter() throws IOException, ClassNotFoundException, SQLException { - final StringWriter sw = new StringWriter(); - final CSVFormat csvFormat = CSVFormat.DEFAULT; - try (Connection connection = getH2Connection()) { - setUpTable(connection); - try (Statement stmt = connection.createStatement(); - CSVPrinter printer = new CSVPrinter(sw, csvFormat); - ResultSet resultSet = stmt.executeQuery("select ID, NAME, TEXT, BIN_DATA from TEST")) { - assertInitialState(printer); - printer.printRecords(resultSet); - assertEquals(TABLE_RECORD_COUNT, printer.getRecordCount()); - } - } - final String csv = sw.toString(); - assertEquals("1,r1,\"long text 1\",\"YmluYXJ5IGRhdGEgMQ==\r\n\"" + recordSeparator + "2,r2,\"" + longText2 + "\",\"YmluYXJ5IGRhdGEgMg==\r\n\"" + - recordSeparator, csv); - // Round trip the data - try (StringReader reader = new StringReader(csv); - CSVParser csvParser = csvFormat.parse(reader)) { - // Row 1 - CSVRecord record = csvParser.nextRecord(); - assertEquals("1", record.get(0)); - assertEquals("r1", record.get(1)); - assertEquals("long text 1", record.get(2)); - assertEquals("YmluYXJ5IGRhdGEgMQ==\r\n", record.get(3)); - // Row 2 - record = csvParser.nextRecord(); - assertEquals("2", record.get(0)); - assertEquals("r2", record.get(1)); - assertEquals("YmluYXJ5IGRhdGEgMg==\r\n", record.get(3)); - } - } - - @Test - public void testJdbcPrinterWithFirstEmptyValue2() throws IOException, ClassNotFoundException, SQLException { - final StringWriter sw = new StringWriter(); - try (Connection connection = getH2Connection()) { - try (Statement stmt = connection.createStatement(); - ResultSet resultSet = stmt.executeQuery("select '' AS EMPTYVALUE from DUAL"); - CSVPrinter printer = CSVFormat.DEFAULT.withHeader(resultSet).print(sw)) { - printer.printRecords(resultSet); - } - } - assertEquals("EMPTYVALUE" + recordSeparator + "\"\"" + recordSeparator, sw.toString()); - } - - @Test - public void testJdbcPrinterWithResultSet() throws IOException, ClassNotFoundException, SQLException { - final StringWriter sw = new StringWriter(); - try (Connection connection = getH2Connection()) { - setUpTable(connection); - try (Statement stmt = connection.createStatement(); - ResultSet resultSet = stmt.executeQuery("select ID, NAME, TEXT from TEST"); - CSVPrinter printer = CSVFormat.DEFAULT.withHeader(resultSet).print(sw)) { - printer.printRecords(resultSet); - } - } - assertEquals("ID,NAME,TEXT" + recordSeparator + "1,r1,\"long text 1\"" + recordSeparator + "2,r2,\"" + longText2 + "\"" + recordSeparator, - sw.toString()); - } - - @Test - public void testJdbcPrinterWithResultSetHeader() throws IOException, ClassNotFoundException, SQLException { - final StringWriter sw = new StringWriter(); - try (Connection connection = getH2Connection()) { - setUpTable(connection); - try (Statement stmt = connection.createStatement(); - CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - try (ResultSet resultSet = stmt.executeQuery("select ID, NAME from TEST")) { - printer.printRecords(resultSet, true); - assertEquals(TABLE_RECORD_COUNT, printer.getRecordCount()); - assertEquals("ID,NAME" + recordSeparator + "1,r1" + recordSeparator + "2,r2" + recordSeparator, sw.toString()); - } - try (ResultSet resultSet = stmt.executeQuery("select ID, NAME from TEST")) { - printer.printRecords(resultSet, false); - assertEquals(TABLE_RECORD_COUNT * 2, printer.getRecordCount()); - assertNotEquals("ID,NAME" + recordSeparator + "1,r1" + recordSeparator + "2,r2" + recordSeparator, sw.toString()); - } - } - } - } - - @Test - public void testJdbcPrinterWithResultSetMetaData() throws IOException, ClassNotFoundException, SQLException { - final StringWriter sw = new StringWriter(); - try (Connection connection = getH2Connection()) { - setUpTable(connection); - try (Statement stmt = connection.createStatement(); - ResultSet resultSet = stmt.executeQuery("select ID, NAME, TEXT from TEST"); - CSVPrinter printer = CSVFormat.DEFAULT.withHeader(resultSet.getMetaData()).print(sw)) { - // The header is the first record. - assertEquals(1, printer.getRecordCount()); - printer.printRecords(resultSet); - assertEquals(3, printer.getRecordCount()); - assertEquals("ID,NAME,TEXT" + recordSeparator + "1,r1,\"long text 1\"" + recordSeparator + "2,r2,\"" + longText2 + "\"" + recordSeparator, - sw.toString()); - } - } - } - - @Test - public void testJira135_part1() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); - final StringWriter sw = new StringWriter(); - final List list = new LinkedList<>(); - try (CSVPrinter printer = new CSVPrinter(sw, format)) { - list.add("\""); - printer.printRecord(list); - } - final String expected = "\"\\\"\"" + format.getRecordSeparator(); - assertEquals(expected, sw.toString()); - final String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(list.toArray(), format), record0); - } - - @Test - @Disabled - public void testJira135_part2() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); - final StringWriter sw = new StringWriter(); - final List list = new LinkedList<>(); - try (CSVPrinter printer = new CSVPrinter(sw, format)) { - list.add("\n"); - printer.printRecord(list); - } - final String expected = "\"\\n\"" + format.getRecordSeparator(); - assertEquals(expected, sw.toString()); - final String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(list.toArray(), format), record0); - } - - @Test - public void testJira135_part3() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); - final StringWriter sw = new StringWriter(); - final List list = new LinkedList<>(); - try (CSVPrinter printer = new CSVPrinter(sw, format)) { - list.add("\\"); - printer.printRecord(list); - } - final String expected = "\"\\\\\"" + format.getRecordSeparator(); - assertEquals(expected, sw.toString()); - final String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(list.toArray(), format), record0); - } - - @Test - @Disabled - public void testJira135All() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); - final StringWriter sw = new StringWriter(); - final List list = new LinkedList<>(); - try (CSVPrinter printer = new CSVPrinter(sw, format)) { - list.add("\""); - list.add("\n"); - list.add("\\"); - printer.printRecord(list); - } - final String expected = "\"\\\"\",\"\\n\",\"\\\"" + format.getRecordSeparator(); - assertEquals(expected, sw.toString()); - final String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(list.toArray(), format), record0); - } - - @Test - public void testMongoDbCsvBasic() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { - printer.printRecord("a", "b"); - assertEquals("a,b" + recordSeparator, sw.toString()); - assertEquals(1, printer.getRecordCount()); - } - } - - @Test - public void testMongoDbCsvCommaInValue() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { - printer.printRecord("a,b", "c"); - assertEquals("\"a,b\",c" + recordSeparator, sw.toString()); - assertEquals(1, printer.getRecordCount()); - } - } - - @Test - public void testMongoDbCsvDoubleQuoteInValue() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { - printer.printRecord("a \"c\" b", "d"); - assertEquals("\"a \"\"c\"\" b\",d" + recordSeparator, sw.toString()); - assertEquals(1, printer.getRecordCount()); - } - } - - @Test - public void testMongoDbCsvTabInValue() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { - printer.printRecord("a\tb", "c"); - assertEquals("a\tb,c" + recordSeparator, sw.toString()); - assertEquals(1, printer.getRecordCount()); - } - } - - @Test - public void testMongoDbTsvBasic() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_TSV)) { - printer.printRecord("a", "b"); - assertEquals("a\tb" + recordSeparator, sw.toString()); - assertEquals(1, printer.getRecordCount()); - } - } - - @Test - public void testMongoDbTsvCommaInValue() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_TSV)) { - printer.printRecord("a,b", "c"); - assertEquals("a,b\tc" + recordSeparator, sw.toString()); - assertEquals(1, printer.getRecordCount()); - } - } - - @Test - public void testMongoDbTsvTabInValue() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_TSV)) { - printer.printRecord("a\tb", "c"); - assertEquals("\"a\tb\"\tc" + recordSeparator, sw.toString()); - } - } - - @Test - public void testMultiLineComment() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentMarker('#'))) { - printer.printComment("This is a comment\non multiple lines"); - assertEquals("# This is a comment" + recordSeparator + "# on multiple lines" + recordSeparator, sw.toString()); - assertEquals(0, printer.getRecordCount()); - } - } - - @Test - public void testMySqlNullOutput() throws IOException { - Object[] s = new String[] { "NULL", null }; - CSVFormat format = CSVFormat.MYSQL.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.NON_NUMERIC); - StringWriter writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - String expected = "\"NULL\"\tNULL\n"; - assertEquals(expected, writer.toString()); - String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(s, record0); - - s = new String[] { "\\N", null }; - format = CSVFormat.MYSQL.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\t\\N\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\N", "A" }; - format = CSVFormat.MYSQL.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\tA\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\n", "A" }; - format = CSVFormat.MYSQL.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\n\tA\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "", null }; - format = CSVFormat.MYSQL.withNullString("NULL"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\tNULL\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "", null }; - format = CSVFormat.MYSQL; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\t\\N\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\N", "", "\u000e,\\\r" }; - format = CSVFormat.MYSQL; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\t\t\u000e,\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "NULL", "\\\r" }; - format = CSVFormat.MYSQL; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "NULL\t\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\\r" }; - format = CSVFormat.MYSQL; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - } - - @Test - public void testMySqlNullStringDefault() { - assertEquals("\\N", CSVFormat.MYSQL.getNullString()); - } - - @Test - public void testNewCsvPrinterAppendableNullFormat() { - assertThrows(NullPointerException.class, () -> new CSVPrinter(new StringWriter(), null)); - } - - @Test - public void testNewCsvPrinterNullAppendableFormat() { - assertThrows(NullPointerException.class, () -> new CSVPrinter(null, CSVFormat.DEFAULT)); - } - - @Test - public void testNotFlushable() throws IOException { - final Appendable out = new StringBuilder(); - try (CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT)) { - printer.printRecord("a", "b", "c"); - assertEquals("a,b,c" + recordSeparator, out.toString()); - printer.flush(); - } - } - - @Test - public void testParseCustomNullValues() throws IOException { - final StringWriter sw = new StringWriter(); - final CSVFormat format = CSVFormat.DEFAULT.withNullString("NULL"); - try (CSVPrinter printer = new CSVPrinter(sw, format)) { - printer.printRecord("a", null, "b"); - } - final String csvString = sw.toString(); - assertEquals("a,NULL,b" + recordSeparator, csvString); - try (CSVParser iterable = format.parse(new StringReader(csvString))) { - final Iterator iterator = iterable.iterator(); - final CSVRecord record = iterator.next(); - assertEquals("a", record.get(0)); - assertNull(record.get(1)); - assertEquals("b", record.get(2)); - assertFalse(iterator.hasNext()); - } - } - - @Test - public void testPlainEscaped() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withEscape('!'))) { - printer.print("abc"); - printer.print("xyz"); - assertEquals("abc,xyz", sw.toString()); - } - } - - @Test - public void testPlainPlain() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) { - printer.print("abc"); - printer.print("xyz"); - assertEquals("abc,xyz", sw.toString()); - } - } - - @Test - public void testPlainQuoted() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote('\''))) { - printer.print("abc"); - assertEquals("abc", sw.toString()); - } - } - - @Test - @Disabled - public void testPostgreSqlCsvNullOutput() throws IOException { - Object[] s = new String[] { "NULL", null }; - CSVFormat format = CSVFormat.POSTGRESQL_CSV.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.ALL_NON_NULL); - StringWriter writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - String expected = "\"NULL\",NULL\n"; - assertEquals(expected, writer.toString()); - String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(new Object[2], record0); - - s = new String[] { "\\N", null }; - format = CSVFormat.POSTGRESQL_CSV.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\t\\N\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\N", "A" }; - format = CSVFormat.POSTGRESQL_CSV.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\tA\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\n", "A" }; - format = CSVFormat.POSTGRESQL_CSV.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\n\tA\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "", null }; - format = CSVFormat.POSTGRESQL_CSV.withNullString("NULL"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\tNULL\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "", null }; - format = CSVFormat.POSTGRESQL_CSV; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\t\\N\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\N", "", "\u000e,\\\r" }; - format = CSVFormat.POSTGRESQL_CSV; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\t\t\u000e,\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "NULL", "\\\r" }; - format = CSVFormat.POSTGRESQL_CSV; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "NULL\t\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\\r" }; - format = CSVFormat.POSTGRESQL_CSV; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - } - - @Test - @Disabled - public void testPostgreSqlCsvTextOutput() throws IOException { - Object[] s = new String[] { "NULL", null }; - CSVFormat format = CSVFormat.POSTGRESQL_TEXT.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.ALL_NON_NULL); - StringWriter writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - String expected = "\"NULL\"\tNULL\n"; - assertEquals(expected, writer.toString()); - String[] record0 = toFirstRecordValues(expected, format); - assertArrayEquals(new Object[2], record0); - - s = new String[] { "\\N", null }; - format = CSVFormat.POSTGRESQL_TEXT.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\t\\N\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\N", "A" }; - format = CSVFormat.POSTGRESQL_TEXT.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\tA\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\n", "A" }; - format = CSVFormat.POSTGRESQL_TEXT.withNullString("\\N"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\n\tA\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "", null }; - format = CSVFormat.POSTGRESQL_TEXT.withNullString("NULL"); - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\tNULL\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "", null }; - format = CSVFormat.POSTGRESQL_TEXT; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\t\\N\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\N", "", "\u000e,\\\r" }; - format = CSVFormat.POSTGRESQL_TEXT; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\N\t\t\u000e,\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "NULL", "\\\r" }; - format = CSVFormat.POSTGRESQL_TEXT; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "NULL\t\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - - s = new String[] { "\\\r" }; - format = CSVFormat.POSTGRESQL_TEXT; - writer = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(writer, format)) { - printer.printRecord(s); - } - expected = "\\\\\\r\n"; - assertEquals(expected, writer.toString()); - record0 = toFirstRecordValues(expected, format); - assertArrayEquals(expectNulls(s, format), record0); - } - - @Test - public void testPostgreSqlNullStringDefaultCsv() { - assertEquals("", CSVFormat.POSTGRESQL_CSV.getNullString()); - } - - @Test - public void testPostgreSqlNullStringDefaultText() { - assertEquals("\\N", CSVFormat.POSTGRESQL_TEXT.getNullString()); - } - - @Test - public void testPrint() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = CSVFormat.DEFAULT.print(sw)) { - assertInitialState(printer); - printer.printRecord("a", "b\\c"); - assertEquals("a,b\\c" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrintCSVParser() throws IOException { - // @formatter:off - final String code = "a1,b1\n" + // 1) - "a2,b2\n" + // 2) - "a3,b3\n" + // 3) - "a4,b4\n"; // 4) - // @formatter:on - final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; - final CSVFormat format = CSVFormat.DEFAULT; - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = format.print(sw); - CSVParser parser = CSVParser.parse(code, format)) { - assertInitialState(printer); - printer.printRecords(parser); - } - try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { - final List records = parser.getRecords(); - assertFalse(records.isEmpty()); - Utils.compare("Fail", res, records); - } - } - - @Test - public void testPrintCSVRecord() throws IOException { - // @formatter:off - final String code = "a1,b1\n" + // 1) - "a2,b2\n" + // 2) - "a3,b3\n" + // 3) - "a4,b4\n"; // 4) - // @formatter:on - final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; - final CSVFormat format = CSVFormat.DEFAULT; - final StringWriter sw = new StringWriter(); - int row = 0; - try (CSVPrinter printer = format.print(sw); - CSVParser parser = CSVParser.parse(code, format)) { - assertInitialState(printer); - for (final CSVRecord record : parser) { - printer.printRecord(record); - assertEquals(++row, printer.getRecordCount()); - } - assertEquals(row, printer.getRecordCount()); - } - try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { - final List records = parser.getRecords(); - assertFalse(records.isEmpty()); - Utils.compare("Fail", res, records); - } - } - - @Test - public void testPrintCSVRecords() throws IOException { - // @formatter:off - final String code = "a1,b1\n" + // 1) - "a2,b2\n" + // 2) - "a3,b3\n" + // 3) - "a4,b4\n"; // 4) - // @formatter:on - final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; - final CSVFormat format = CSVFormat.DEFAULT; - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = format.print(sw); - CSVParser parser = CSVParser.parse(code, format)) { - assertInitialState(printer); - printer.printRecords(parser.getRecords()); - } - try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { - final List records = parser.getRecords(); - assertFalse(records.isEmpty()); - Utils.compare("Fail", res, records); - } - } - - @Test - public void testPrintCustomNullValues() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withNullString("NULL"))) { - assertInitialState(printer); - printer.printRecord("a", null, "b"); - assertEquals("a,NULL,b" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter1() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a", "b"); - assertEquals(1, printer.getRecordCount()); - assertEquals("a,b" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter2() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a,b", "b"); - assertEquals("\"a,b\",b" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter3() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a, b", "b "); - assertEquals("\"a, b\",\"b \"" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter4() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a", "b\"c"); - assertEquals("a,\"b\"\"c\"" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter5() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a", "b\nc"); - assertEquals("a,\"b\nc\"" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter6() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a", "b\r\nc"); - assertEquals("a,\"b\r\nc\"" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrinter7() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a", "b\\c"); - assertEquals("a,b\\c" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrintNullValues() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { - assertInitialState(printer); - printer.printRecord("a", null, "b"); - assertEquals("a,,b" + recordSeparator, sw.toString()); - } - } - - @Test - public void testPrintOnePositiveInteger() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.MINIMAL))) { - assertInitialState(printer); - printer.print(Integer.MAX_VALUE); - assertEquals(String.valueOf(Integer.MAX_VALUE), sw.toString()); - } - } - - /** - * Test to target the use of {@link IOUtils#copy(java.io.Reader, Appendable)} which directly buffers the value from the Reader to the Appendable. - * - *

- * Requires the format to have no quote or escape character, value to be a {@link Reader Reader} and the output MUST NOT be a {@link Writer Writer} - * but some other Appendable. - *

- * - * @throws IOException Not expected to happen - */ - @Test - public void testPrintReaderWithoutQuoteToAppendable() throws IOException { - final StringBuilder sb = new StringBuilder(); - final String content = "testValue"; - try (CSVPrinter printer = new CSVPrinter(sb, CSVFormat.DEFAULT.withQuote(null))) { - assertInitialState(printer); - final StringReader value = new StringReader(content); - printer.print(value); - } - assertEquals(content, sb.toString()); - } - - /** - * Test to target the use of {@link IOUtils#copyLarge(java.io.Reader, Writer)} which directly buffers the value from the Reader to the Writer. - * - *

- * Requires the format to have no quote or escape character, value to be a {@link Reader Reader} and the output MUST be a {@link Writer Writer}. - *

- * - * @throws IOException Not expected to happen - */ - @Test - public void testPrintReaderWithoutQuoteToWriter() throws IOException { - final StringWriter sw = new StringWriter(); - final String content = "testValue"; - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) { - final StringReader value = new StringReader(content); - printer.print(value); - } - assertEquals(content, sw.toString()); - } - - @Test - public void testPrintRecordStream() throws IOException { - // @formatter:off - final String code = "a1,b1\n" + // 1) - "a2,b2\n" + // 2) - "a3,b3\n" + // 3) - "a4,b4\n"; // 4) - // @formatter:on - final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; - final CSVFormat format = CSVFormat.DEFAULT; - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = format.print(sw); - CSVParser parser = CSVParser.parse(code, format)) { - long count = 0; - for (final CSVRecord record : parser) { - printer.printRecord(record.stream()); - assertEquals(++count, printer.getRecordCount()); - } - } - try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { - final List records = parser.getRecords(); - assertFalse(records.isEmpty()); - Utils.compare("Fail", res, records); - } - } - - @Test - public void testPrintRecordsWithCSVRecord() throws IOException { - final String[] values = { "A", "B", "C" }; - final String rowData = StringUtils.join(values, ','); - final CharArrayWriter charArrayWriter = new CharArrayWriter(0); - try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(rowData)); - CSVPrinter printer = CSVFormat.INFORMIX_UNLOAD.print(charArrayWriter)) { - long count = 0; - for (final CSVRecord record : parser) { - printer.printRecord(record); - assertEquals(++count, printer.getRecordCount()); - } - } - assertEquals(6, charArrayWriter.size()); - assertEquals("A|B|C" + CSVFormat.INFORMIX_UNLOAD.getRecordSeparator(), charArrayWriter.toString()); - } - - @Test - public void testPrintRecordsWithEmptyVector() throws IOException { - final PrintStream out = System.out; - try { - System.setOut(new PrintStream(NullOutputStream.INSTANCE)); - try (CSVPrinter printer = CSVFormat.POSTGRESQL_TEXT.printer()) { - final Vector vector = new Vector<>(); - final int expectedCapacity = 23; - vector.setSize(expectedCapacity); - printer.printRecords(vector); - assertEquals(expectedCapacity, vector.capacity()); - assertEquals(expectedCapacity, printer.getRecordCount()); - } - } finally { - System.setOut(out); - } - } - - @Test - public void testPrintRecordsWithObjectArray() throws IOException { - final CharArrayWriter charArrayWriter = new CharArrayWriter(0); - final Object[] objectArray = new Object[6]; - try (CSVPrinter printer = CSVFormat.INFORMIX_UNLOAD.print(charArrayWriter)) { - final HashSet hashSet = new HashSet<>(); - objectArray[3] = hashSet; - printer.printRecords(objectArray); - assertEquals(objectArray.length, printer.getRecordCount()); - } - assertEquals(6, charArrayWriter.size()); - assertEquals("\n\n\n\n\n\n", charArrayWriter.toString()); - } - - @Test - public void testPrintRecordsWithResultSetOneRow() throws IOException, SQLException { - try (CSVPrinter printer = CSVFormat.MYSQL.printer()) { - try (ResultSet resultSet = new SimpleResultSet()) { - assertInitialState(printer); - printer.printRecords(resultSet); - assertInitialState(printer); - assertEquals(0, resultSet.getRow()); - } - } - } - - @Test - public void testPrintToFileWithCharsetUtf16Be() throws IOException { - final File file = createTempFile(); - try (CSVPrinter printer = CSVFormat.DEFAULT.print(file, StandardCharsets.UTF_16BE)) { - printer.printRecord("a", "b\\c"); - } - assertEquals("a,b\\c" + recordSeparator, FileUtils.readFileToString(file, StandardCharsets.UTF_16BE)); - } - - @Test - public void testPrintToFileWithDefaultCharset() throws IOException { - final File file = createTempFile(); - try (CSVPrinter printer = CSVFormat.DEFAULT.print(file, Charset.defaultCharset())) { - printer.printRecord("a", "b\\c"); - } - assertEquals("a,b\\c" + recordSeparator, FileUtils.readFileToString(file, Charset.defaultCharset())); - } - - @Test - public void testPrintToPathWithDefaultCharset() throws IOException { - final Path file = createTempPath(); - try (CSVPrinter printer = CSVFormat.DEFAULT.print(file, Charset.defaultCharset())) { - printer.printRecord("a", "b\\c"); - } - assertEquals("a,b\\c" + recordSeparator, new String(Files.readAllBytes(file), Charset.defaultCharset())); - } - - @Test - public void testQuoteAll() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.ALL))) { - printer.printRecord("a", "b\nc", "d"); - assertEquals("\"a\",\"b\nc\",\"d\"" + recordSeparator, sw.toString()); - } - } - - @Test - public void testQuoteCommaFirstChar() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.RFC4180)) { - printer.printRecord(","); - assertEquals("\",\"" + recordSeparator, sw.toString()); - } - } - - @Test - public void testQuoteNonNumeric() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.NON_NUMERIC))) { - printer.printRecord("a", "b\nc", Integer.valueOf(1)); - assertEquals("\"a\",\"b\nc\",1" + recordSeparator, sw.toString()); - } - } - - @Test - public void testRandomDefault() throws Exception { - doRandom(CSVFormat.DEFAULT, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - public void testRandomExcel() throws Exception { - doRandom(CSVFormat.EXCEL, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - @Disabled - public void testRandomMongoDbCsv() throws Exception { - doRandom(CSVFormat.MONGODB_CSV, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - public void testRandomMySql() throws Exception { - doRandom(CSVFormat.MYSQL, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - @Disabled - public void testRandomOracle() throws Exception { - doRandom(CSVFormat.ORACLE, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - @Disabled - public void testRandomPostgreSqlCsv() throws Exception { - doRandom(CSVFormat.POSTGRESQL_CSV, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - public void testRandomPostgreSqlText() throws Exception { - doRandom(CSVFormat.POSTGRESQL_TEXT, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - public void testRandomRfc4180() throws Exception { - doRandom(CSVFormat.RFC4180, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - public void testRandomTdf() throws Exception { - doRandom(CSVFormat.TDF, ITERATIONS_FOR_RANDOM_TEST); - } - - @Test - public void testSingleLineComment() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentMarker('#'))) { - printer.printComment("This is a comment"); - assertEquals("# This is a comment" + recordSeparator, sw.toString()); - assertEquals(0, printer.getRecordCount()); - } - } - - @Test - public void testSingleQuoteQuoted() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote('\''))) { - printer.print("a'b'c"); - printer.print("xyz"); - assertEquals("'a''b''c',xyz", sw.toString()); - } - } - - @Test - public void testSkipHeaderRecordFalse() throws IOException { - // functionally identical to testHeader, used to test CSV-153 - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3").withSkipHeaderRecord(false))) { - printer.printRecord("a", "b", "c"); - printer.printRecord("x", "y", "z"); - assertEquals("C1,C2,C3\r\na,b,c\r\nx,y,z\r\n", sw.toString()); - } - } - - @Test - public void testSkipHeaderRecordTrue() throws IOException { - // functionally identical to testHeaderNotSet, used to test CSV-153 - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3").withSkipHeaderRecord(true))) { - printer.printRecord("a", "b", "c"); - printer.printRecord("x", "y", "z"); - assertEquals("a,b,c\r\nx,y,z\r\n", sw.toString()); - } - } - - @Test - public void testTrailingDelimiterOnTwoColumns() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrailingDelimiter())) { - printer.printRecord("A", "B"); - assertEquals("A,B,\r\n", sw.toString()); - } - } - - @Test - public void testTrimOffOneColumn() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrim(false))) { - printer.print(" A "); - assertEquals("\" A \"", sw.toString()); - } - } - - @Test - public void testTrimOnOneColumn() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrim())) { - printer.print(" A "); - assertEquals("A", sw.toString()); - } - } - - @Test - public void testTrimOnTwoColumns() throws IOException { - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrim())) { - printer.print(" A "); - printer.print(" B "); - assertEquals("A,B", sw.toString()); - } - } - - private String[] toFirstRecordValues(final String expected, final CSVFormat format) throws IOException { - try (CSVParser parser = CSVParser.parse(expected, format)) { - return parser.getRecords().get(0).values(); - } - } - - private void tryFormat(final List list, final Character quote, final Character escape, final String expected) throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.withQuote(quote).withEscape(escape).withRecordSeparator(null); - final Appendable out = new StringBuilder(); - try (CSVPrinter printer = new CSVPrinter(out, format)) { - printer.printRecord(list); - } - assertEquals(expected, out.toString()); - } - -} +/* + * 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 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[] 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 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 + public void testHeaderCommentTdf() throws IOException { + final StringWriter sw = new StringWriter(); + final Date now = new Date(); + final CSVFormat format = CSVFormat.TDF; + try (CSVPrinter csvPrinter = printWithHeaderComments(sw, now, format)) { + assertEquals("# Generated by Apache Commons CSV 1.1\r\n# " + now + "\r\nCol1\tCol2\r\nA\tB\r\nC\tD\r\n", sw.toString()); + } + } + + @Test + public void testHeaderNotSet() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) { + assertInitialState(printer); + printer.printRecord("a", "b", "c"); + printer.printRecord("x", "y", "z"); + assertEquals("a,b,c\r\nx,y,z\r\n", sw.toString()); + } + } + + @Test + public void testInvalidFormat() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter(CR)); + } + + @Test + public void testJdbcPrinter() throws IOException, ClassNotFoundException, SQLException { + final StringWriter sw = new StringWriter(); + final CSVFormat csvFormat = CSVFormat.DEFAULT; + try (Connection connection = getH2Connection()) { + setUpTable(connection); + try (Statement stmt = connection.createStatement(); + CSVPrinter printer = new CSVPrinter(sw, csvFormat); + ResultSet resultSet = stmt.executeQuery("select ID, NAME, TEXT, BIN_DATA from TEST")) { + assertInitialState(printer); + printer.printRecords(resultSet); + assertEquals(TABLE_RECORD_COUNT, printer.getRecordCount()); + } + } + final String csv = sw.toString(); + assertEquals("1,r1,\"long text 1\",\"YmluYXJ5IGRhdGEgMQ==\r\n\"" + recordSeparator + "2,r2,\"" + longText2 + "\",\"YmluYXJ5IGRhdGEgMg==\r\n\"" + + recordSeparator, csv); + // Round trip the data + try (StringReader reader = new StringReader(csv); + CSVParser csvParser = csvFormat.parse(reader)) { + // Row 1 + CSVRecord record = csvParser.nextRecord(); + assertEquals("1", record.get(0)); + assertEquals("r1", record.get(1)); + assertEquals("long text 1", record.get(2)); + assertEquals("YmluYXJ5IGRhdGEgMQ==\r\n", record.get(3)); + // Row 2 + record = csvParser.nextRecord(); + assertEquals("2", record.get(0)); + assertEquals("r2", record.get(1)); + assertEquals("YmluYXJ5IGRhdGEgMg==\r\n", record.get(3)); + } + } + + @Test + public void testJdbcPrinterWithFirstEmptyValue2() throws IOException, ClassNotFoundException, SQLException { + final StringWriter sw = new StringWriter(); + try (Connection connection = getH2Connection()) { + try (Statement stmt = connection.createStatement(); + ResultSet resultSet = stmt.executeQuery("select '' AS EMPTYVALUE from DUAL"); + CSVPrinter printer = CSVFormat.DEFAULT.withHeader(resultSet).print(sw)) { + printer.printRecords(resultSet); + } + } + assertEquals("EMPTYVALUE" + recordSeparator + "\"\"" + recordSeparator, sw.toString()); + } + + @Test + public void testJdbcPrinterWithResultSet() throws IOException, ClassNotFoundException, SQLException { + final StringWriter sw = new StringWriter(); + try (Connection connection = getH2Connection()) { + setUpTable(connection); + try (Statement stmt = connection.createStatement(); + ResultSet resultSet = stmt.executeQuery("select ID, NAME, TEXT from TEST"); + CSVPrinter printer = CSVFormat.DEFAULT.withHeader(resultSet).print(sw)) { + printer.printRecords(resultSet); + } + } + assertEquals("ID,NAME,TEXT" + recordSeparator + "1,r1,\"long text 1\"" + recordSeparator + "2,r2,\"" + longText2 + "\"" + recordSeparator, + sw.toString()); + } + + @Test + public void testJdbcPrinterWithResultSetHeader() throws IOException, ClassNotFoundException, SQLException { + final StringWriter sw = new StringWriter(); + try (Connection connection = getH2Connection()) { + setUpTable(connection); + try (Statement stmt = connection.createStatement(); + CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + try (ResultSet resultSet = stmt.executeQuery("select ID, NAME from TEST")) { + printer.printRecords(resultSet, true); + assertEquals(TABLE_RECORD_COUNT, printer.getRecordCount()); + assertEquals("ID,NAME" + recordSeparator + "1,r1" + recordSeparator + "2,r2" + recordSeparator, sw.toString()); + } + try (ResultSet resultSet = stmt.executeQuery("select ID, NAME from TEST")) { + printer.printRecords(resultSet, false); + assertEquals(TABLE_RECORD_COUNT * 2, printer.getRecordCount()); + assertNotEquals("ID,NAME" + recordSeparator + "1,r1" + recordSeparator + "2,r2" + recordSeparator, sw.toString()); + } + } + } + } + + @Test + public void testJdbcPrinterWithResultSetMetaData() throws IOException, ClassNotFoundException, SQLException { + final StringWriter sw = new StringWriter(); + try (Connection connection = getH2Connection()) { + setUpTable(connection); + try (Statement stmt = connection.createStatement(); + ResultSet resultSet = stmt.executeQuery("select ID, NAME, TEXT from TEST"); + CSVPrinter printer = CSVFormat.DEFAULT.withHeader(resultSet.getMetaData()).print(sw)) { + // The header is the first record. + assertEquals(1, printer.getRecordCount()); + printer.printRecords(resultSet); + assertEquals(3, printer.getRecordCount()); + assertEquals("ID,NAME,TEXT" + recordSeparator + "1,r1,\"long text 1\"" + recordSeparator + "2,r2,\"" + longText2 + "\"" + recordSeparator, + sw.toString()); + } + } + } + + @Test + public void testJira135_part1() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); + final StringWriter sw = new StringWriter(); + final List list = new LinkedList<>(); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + list.add("\""); + printer.printRecord(list); + } + final String expected = "\"\\\"\"" + format.getRecordSeparator(); + assertEquals(expected, sw.toString()); + final String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(list.toArray(), format), record0); + } + + @Test + @Disabled + public void testJira135_part2() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); + final StringWriter sw = new StringWriter(); + final List list = new LinkedList<>(); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + list.add("\n"); + printer.printRecord(list); + } + final String expected = "\"\\n\"" + format.getRecordSeparator(); + assertEquals(expected, sw.toString()); + final String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(list.toArray(), format), record0); + } + + @Test + public void testJira135_part3() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); + final StringWriter sw = new StringWriter(); + final List list = new LinkedList<>(); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + list.add("\\"); + printer.printRecord(list); + } + final String expected = "\"\\\\\"" + format.getRecordSeparator(); + assertEquals(expected, sw.toString()); + final String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(list.toArray(), format), record0); + } + + @Test + @Disabled + public void testJira135All() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.withRecordSeparator('\n').withQuote(DQUOTE_CHAR).withEscape(BACKSLASH); + final StringWriter sw = new StringWriter(); + final List list = new LinkedList<>(); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + list.add("\""); + list.add("\n"); + list.add("\\"); + printer.printRecord(list); + } + final String expected = "\"\\\"\",\"\\n\",\"\\\"" + format.getRecordSeparator(); + assertEquals(expected, sw.toString()); + final String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(list.toArray(), format), record0); + } + + @Test + public void testMongoDbCsvBasic() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { + printer.printRecord("a", "b"); + assertEquals("a,b" + recordSeparator, sw.toString()); + assertEquals(1, printer.getRecordCount()); + } + } + + @Test + public void testMongoDbCsvCommaInValue() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { + printer.printRecord("a,b", "c"); + assertEquals("\"a,b\",c" + recordSeparator, sw.toString()); + assertEquals(1, printer.getRecordCount()); + } + } + + @Test + public void testMongoDbCsvDoubleQuoteInValue() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { + printer.printRecord("a \"c\" b", "d"); + assertEquals("\"a \"\"c\"\" b\",d" + recordSeparator, sw.toString()); + assertEquals(1, printer.getRecordCount()); + } + } + + @Test + public void testMongoDbCsvTabInValue() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_CSV)) { + printer.printRecord("a\tb", "c"); + assertEquals("a\tb,c" + recordSeparator, sw.toString()); + assertEquals(1, printer.getRecordCount()); + } + } + + @Test + public void testMongoDbTsvBasic() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_TSV)) { + printer.printRecord("a", "b"); + assertEquals("a\tb" + recordSeparator, sw.toString()); + assertEquals(1, printer.getRecordCount()); + } + } + + @Test + public void testMongoDbTsvCommaInValue() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_TSV)) { + printer.printRecord("a,b", "c"); + assertEquals("a,b\tc" + recordSeparator, sw.toString()); + assertEquals(1, printer.getRecordCount()); + } + } + + @Test + public void testMongoDbTsvTabInValue() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.MONGODB_TSV)) { + printer.printRecord("a\tb", "c"); + assertEquals("\"a\tb\"\tc" + recordSeparator, sw.toString()); + } + } + + @Test + public void testMultiLineComment() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentMarker('#'))) { + printer.printComment("This is a comment\non multiple lines"); + assertEquals("# This is a comment" + recordSeparator + "# on multiple lines" + recordSeparator, sw.toString()); + assertEquals(0, printer.getRecordCount()); + } + } + + @Test + public void testMySqlNullOutput() throws IOException { + Object[] s = new String[] { "NULL", null }; + CSVFormat format = CSVFormat.MYSQL.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.NON_NUMERIC); + StringWriter writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + String expected = "\"NULL\"\tNULL\n"; + assertEquals(expected, writer.toString()); + String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(s, record0); + + s = new String[] { "\\N", null }; + format = CSVFormat.MYSQL.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\t\\N\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\N", "A" }; + format = CSVFormat.MYSQL.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\tA\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\n", "A" }; + format = CSVFormat.MYSQL.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\n\tA\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "", null }; + format = CSVFormat.MYSQL.withNullString("NULL"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\tNULL\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "", null }; + format = CSVFormat.MYSQL; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\t\\N\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\N", "", "\u000e,\\\r" }; + format = CSVFormat.MYSQL; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\t\t\u000e,\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "NULL", "\\\r" }; + format = CSVFormat.MYSQL; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "NULL\t\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\\r" }; + format = CSVFormat.MYSQL; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + } + + @Test + public void testMySqlNullStringDefault() { + assertEquals("\\N", CSVFormat.MYSQL.getNullString()); + } + + @Test + public void testNewCsvPrinterAppendableNullFormat() { + assertThrows(NullPointerException.class, () -> new CSVPrinter(new StringWriter(), null)); + } + + @Test + public void testNewCsvPrinterNullAppendableFormat() { + assertThrows(NullPointerException.class, () -> new CSVPrinter(null, CSVFormat.DEFAULT)); + } + + @Test + public void testNotFlushable() throws IOException { + final Appendable out = new StringBuilder(); + try (CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT)) { + printer.printRecord("a", "b", "c"); + assertEquals("a,b,c" + recordSeparator, out.toString()); + printer.flush(); + } + } + + @Test + public void testParseCustomNullValues() throws IOException { + final StringWriter sw = new StringWriter(); + final CSVFormat format = CSVFormat.DEFAULT.withNullString("NULL"); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecord("a", null, "b"); + } + final String csvString = sw.toString(); + assertEquals("a,NULL,b" + recordSeparator, csvString); + try (CSVParser iterable = format.parse(new StringReader(csvString))) { + final Iterator iterator = iterable.iterator(); + final CSVRecord record = iterator.next(); + assertEquals("a", record.get(0)); + assertNull(record.get(1)); + assertEquals("b", record.get(2)); + assertFalse(iterator.hasNext()); + } + } + + @Test + public void testPlainEscaped() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withEscape('!'))) { + printer.print("abc"); + printer.print("xyz"); + assertEquals("abc,xyz", sw.toString()); + } + } + + @Test + public void testPlainPlain() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) { + printer.print("abc"); + printer.print("xyz"); + assertEquals("abc,xyz", sw.toString()); + } + } + + @Test + public void testPlainQuoted() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote('\''))) { + printer.print("abc"); + assertEquals("abc", sw.toString()); + } + } + + @Test + @Disabled + public void testPostgreSqlCsvNullOutput() throws IOException { + Object[] s = new String[] { "NULL", null }; + CSVFormat format = CSVFormat.POSTGRESQL_CSV.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.ALL_NON_NULL); + StringWriter writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + String expected = "\"NULL\",NULL\n"; + assertEquals(expected, writer.toString()); + String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(new Object[2], record0); + + s = new String[] { "\\N", null }; + format = CSVFormat.POSTGRESQL_CSV.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\t\\N\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\N", "A" }; + format = CSVFormat.POSTGRESQL_CSV.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\tA\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\n", "A" }; + format = CSVFormat.POSTGRESQL_CSV.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\n\tA\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "", null }; + format = CSVFormat.POSTGRESQL_CSV.withNullString("NULL"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\tNULL\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "", null }; + format = CSVFormat.POSTGRESQL_CSV; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\t\\N\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\N", "", "\u000e,\\\r" }; + format = CSVFormat.POSTGRESQL_CSV; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\t\t\u000e,\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "NULL", "\\\r" }; + format = CSVFormat.POSTGRESQL_CSV; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "NULL\t\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\\r" }; + format = CSVFormat.POSTGRESQL_CSV; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + } + + @Test + @Disabled + public void testPostgreSqlCsvTextOutput() throws IOException { + Object[] s = new String[] { "NULL", null }; + CSVFormat format = CSVFormat.POSTGRESQL_TEXT.withQuote(DQUOTE_CHAR).withNullString("NULL").withQuoteMode(QuoteMode.ALL_NON_NULL); + StringWriter writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + String expected = "\"NULL\"\tNULL\n"; + assertEquals(expected, writer.toString()); + String[] record0 = toFirstRecordValues(expected, format); + assertArrayEquals(new Object[2], record0); + + s = new String[] { "\\N", null }; + format = CSVFormat.POSTGRESQL_TEXT.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\t\\N\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\N", "A" }; + format = CSVFormat.POSTGRESQL_TEXT.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\tA\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\n", "A" }; + format = CSVFormat.POSTGRESQL_TEXT.withNullString("\\N"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\n\tA\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "", null }; + format = CSVFormat.POSTGRESQL_TEXT.withNullString("NULL"); + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\tNULL\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "", null }; + format = CSVFormat.POSTGRESQL_TEXT; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\t\\N\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\N", "", "\u000e,\\\r" }; + format = CSVFormat.POSTGRESQL_TEXT; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\N\t\t\u000e,\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "NULL", "\\\r" }; + format = CSVFormat.POSTGRESQL_TEXT; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "NULL\t\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + + s = new String[] { "\\\r" }; + format = CSVFormat.POSTGRESQL_TEXT; + writer = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(writer, format)) { + printer.printRecord(s); + } + expected = "\\\\\\r\n"; + assertEquals(expected, writer.toString()); + record0 = toFirstRecordValues(expected, format); + assertArrayEquals(expectNulls(s, format), record0); + } + + @Test + public void testPostgreSqlNullStringDefaultCsv() { + assertEquals("", CSVFormat.POSTGRESQL_CSV.getNullString()); + } + + @Test + public void testPostgreSqlNullStringDefaultText() { + assertEquals("\\N", CSVFormat.POSTGRESQL_TEXT.getNullString()); + } + + @Test + public void testPrint() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = CSVFormat.DEFAULT.print(sw)) { + assertInitialState(printer); + printer.printRecord("a", "b\\c"); + assertEquals("a,b\\c" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrintCSVParser() throws IOException { + // @formatter:off + final String code = "a1,b1\n" + // 1) + "a2,b2\n" + // 2) + "a3,b3\n" + // 3) + "a4,b4\n"; // 4) + // @formatter:on + final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; + final CSVFormat format = CSVFormat.DEFAULT; + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = format.print(sw); + CSVParser parser = CSVParser.parse(code, format)) { + assertInitialState(printer); + printer.printRecords(parser); + } + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertFalse(records.isEmpty()); + Utils.compare("Fail", res, records); + } + } + + @Test + public void testPrintCSVRecord() throws IOException { + // @formatter:off + final String code = "a1,b1\n" + // 1) + "a2,b2\n" + // 2) + "a3,b3\n" + // 3) + "a4,b4\n"; // 4) + // @formatter:on + final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; + final CSVFormat format = CSVFormat.DEFAULT; + final StringWriter sw = new StringWriter(); + int row = 0; + try (CSVPrinter printer = format.print(sw); + CSVParser parser = CSVParser.parse(code, format)) { + assertInitialState(printer); + for (final CSVRecord record : parser) { + printer.printRecord(record); + assertEquals(++row, printer.getRecordCount()); + } + assertEquals(row, printer.getRecordCount()); + } + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertFalse(records.isEmpty()); + Utils.compare("Fail", res, records); + } + } + + @Test + public void testPrintCSVRecords() throws IOException { + // @formatter:off + final String code = "a1,b1\n" + // 1) + "a2,b2\n" + // 2) + "a3,b3\n" + // 3) + "a4,b4\n"; // 4) + // @formatter:on + final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; + final CSVFormat format = CSVFormat.DEFAULT; + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = format.print(sw); + CSVParser parser = CSVParser.parse(code, format)) { + assertInitialState(printer); + printer.printRecords(parser.getRecords()); + } + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertFalse(records.isEmpty()); + Utils.compare("Fail", res, records); + } + } + + @Test + public void testPrintCustomNullValues() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withNullString("NULL"))) { + assertInitialState(printer); + printer.printRecord("a", null, "b"); + assertEquals("a,NULL,b" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter1() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a", "b"); + assertEquals(1, printer.getRecordCount()); + assertEquals("a,b" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter2() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a,b", "b"); + assertEquals("\"a,b\",b" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter3() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a, b", "b "); + assertEquals("\"a, b\",\"b \"" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter4() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a", "b\"c"); + assertEquals("a,\"b\"\"c\"" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter5() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a", "b\nc"); + assertEquals("a,\"b\nc\"" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter6() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a", "b\r\nc"); + assertEquals("a,\"b\r\nc\"" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrinter7() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a", "b\\c"); + assertEquals("a,b\\c" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrintNullValues() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT)) { + assertInitialState(printer); + printer.printRecord("a", null, "b"); + assertEquals("a,,b" + recordSeparator, sw.toString()); + } + } + + @Test + public void testPrintOnePositiveInteger() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.MINIMAL))) { + assertInitialState(printer); + printer.print(Integer.MAX_VALUE); + assertEquals(String.valueOf(Integer.MAX_VALUE), sw.toString()); + } + } + + /** + * Test to target the use of {@link IOUtils#copy(java.io.Reader, Appendable)} which directly buffers the value from the Reader to the Appendable. + * + *

+ * Requires the format to have no quote or escape character, value to be a {@link Reader Reader} and the output MUST NOT be a {@link Writer Writer} + * but some other Appendable. + *

+ * + * @throws IOException Not expected to happen + */ + @Test + public void testPrintReaderWithoutQuoteToAppendable() throws IOException { + final StringBuilder sb = new StringBuilder(); + final String content = "testValue"; + try (CSVPrinter printer = new CSVPrinter(sb, CSVFormat.DEFAULT.withQuote(null))) { + assertInitialState(printer); + final StringReader value = new StringReader(content); + printer.print(value); + } + assertEquals(content, sb.toString()); + } + + /** + * Test to target the use of {@link IOUtils#copyLarge(java.io.Reader, Writer)} which directly buffers the value from the Reader to the Writer. + * + *

+ * Requires the format to have no quote or escape character, value to be a {@link Reader Reader} and the output MUST be a {@link Writer Writer}. + *

+ * + * @throws IOException Not expected to happen + */ + @Test + public void testPrintReaderWithoutQuoteToWriter() throws IOException { + final StringWriter sw = new StringWriter(); + final String content = "testValue"; + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null))) { + final StringReader value = new StringReader(content); + printer.print(value); + } + assertEquals(content, sw.toString()); + } + + @Test + public void testPrintRecordStream() throws IOException { + // @formatter:off + final String code = "a1,b1\n" + // 1) + "a2,b2\n" + // 2) + "a3,b3\n" + // 3) + "a4,b4\n"; // 4) + // @formatter:on + final String[][] res = { { "a1", "b1" }, { "a2", "b2" }, { "a3", "b3" }, { "a4", "b4" } }; + final CSVFormat format = CSVFormat.DEFAULT; + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = format.print(sw); + CSVParser parser = CSVParser.parse(code, format)) { + long count = 0; + for (final CSVRecord record : parser) { + printer.printRecord(record.stream()); + assertEquals(++count, printer.getRecordCount()); + } + } + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertFalse(records.isEmpty()); + Utils.compare("Fail", res, records); + } + } + + @Test + public void testPrintRecordsWithCSVRecord() throws IOException { + final String[] values = { "A", "B", "C" }; + final String rowData = StringUtils.join(values, ','); + final CharArrayWriter charArrayWriter = new CharArrayWriter(0); + try (CSVParser parser = CSVFormat.DEFAULT.parse(new StringReader(rowData)); + CSVPrinter printer = CSVFormat.INFORMIX_UNLOAD.print(charArrayWriter)) { + long count = 0; + for (final CSVRecord record : parser) { + printer.printRecord(record); + assertEquals(++count, printer.getRecordCount()); + } + } + assertEquals(6, charArrayWriter.size()); + assertEquals("A|B|C" + CSVFormat.INFORMIX_UNLOAD.getRecordSeparator(), charArrayWriter.toString()); + } + + @Test + public void testPrintRecordsWithEmptyVector() throws IOException { + final PrintStream out = System.out; + try { + System.setOut(new PrintStream(NullOutputStream.INSTANCE)); + try (CSVPrinter printer = CSVFormat.POSTGRESQL_TEXT.printer()) { + final Vector vector = new Vector<>(); + final int expectedCapacity = 23; + vector.setSize(expectedCapacity); + printer.printRecords(vector); + assertEquals(expectedCapacity, vector.capacity()); + assertEquals(expectedCapacity, printer.getRecordCount()); + } + } finally { + System.setOut(out); + } + } + + @Test + public void testPrintRecordsWithObjectArray() throws IOException { + final CharArrayWriter charArrayWriter = new CharArrayWriter(0); + final Object[] objectArray = new Object[6]; + try (CSVPrinter printer = CSVFormat.INFORMIX_UNLOAD.print(charArrayWriter)) { + final HashSet hashSet = new HashSet<>(); + objectArray[3] = hashSet; + printer.printRecords(objectArray); + assertEquals(objectArray.length, printer.getRecordCount()); + } + assertEquals(6, charArrayWriter.size()); + assertEquals("\n\n\n\n\n\n", charArrayWriter.toString()); + } + + @Test + public void testPrintRecordsWithResultSetOneRow() throws IOException, SQLException { + try (CSVPrinter printer = CSVFormat.MYSQL.printer()) { + try (ResultSet resultSet = new SimpleResultSet()) { + assertInitialState(printer); + printer.printRecords(resultSet); + assertInitialState(printer); + assertEquals(0, resultSet.getRow()); + } + } + } + + @Test + public void testPrintToFileWithCharsetUtf16Be() throws IOException { + final File file = createTempFile(); + try (CSVPrinter printer = CSVFormat.DEFAULT.print(file, StandardCharsets.UTF_16BE)) { + printer.printRecord("a", "b\\c"); + } + assertEquals("a,b\\c" + recordSeparator, FileUtils.readFileToString(file, StandardCharsets.UTF_16BE)); + } + + @Test + public void testPrintToFileWithDefaultCharset() throws IOException { + final File file = createTempFile(); + try (CSVPrinter printer = CSVFormat.DEFAULT.print(file, Charset.defaultCharset())) { + printer.printRecord("a", "b\\c"); + } + assertEquals("a,b\\c" + recordSeparator, FileUtils.readFileToString(file, Charset.defaultCharset())); + } + + @Test + public void testPrintToPathWithDefaultCharset() throws IOException { + final Path file = createTempPath(); + try (CSVPrinter printer = CSVFormat.DEFAULT.print(file, Charset.defaultCharset())) { + printer.printRecord("a", "b\\c"); + } + assertEquals("a,b\\c" + recordSeparator, new String(Files.readAllBytes(file), Charset.defaultCharset())); + } + + @Test + public void testQuoteAll() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.ALL))) { + printer.printRecord("a", "b\nc", "d"); + assertEquals("\"a\",\"b\nc\",\"d\"" + recordSeparator, sw.toString()); + } + } + + @Test + public void testQuoteCommaFirstChar() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.RFC4180)) { + printer.printRecord(","); + assertEquals("\",\"" + recordSeparator, sw.toString()); + } + } + + @Test + public void testQuoteNonNumeric() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.NON_NUMERIC))) { + printer.printRecord("a", "b\nc", Integer.valueOf(1)); + assertEquals("\"a\",\"b\nc\",1" + recordSeparator, sw.toString()); + } + } + + @Test + public void testRandomDefault() throws Exception { + doRandom(CSVFormat.DEFAULT, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + public void testRandomExcel() throws Exception { + doRandom(CSVFormat.EXCEL, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + @Disabled + public void testRandomMongoDbCsv() throws Exception { + doRandom(CSVFormat.MONGODB_CSV, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + public void testRandomMySql() throws Exception { + doRandom(CSVFormat.MYSQL, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + @Disabled + public void testRandomOracle() throws Exception { + doRandom(CSVFormat.ORACLE, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + @Disabled + public void testRandomPostgreSqlCsv() throws Exception { + doRandom(CSVFormat.POSTGRESQL_CSV, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + public void testRandomPostgreSqlText() throws Exception { + doRandom(CSVFormat.POSTGRESQL_TEXT, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + public void testRandomRfc4180() throws Exception { + doRandom(CSVFormat.RFC4180, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + public void testRandomTdf() throws Exception { + doRandom(CSVFormat.TDF, ITERATIONS_FOR_RANDOM_TEST); + } + + @Test + public void testSingleLineComment() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentMarker('#'))) { + printer.printComment("This is a comment"); + assertEquals("# This is a comment" + recordSeparator, sw.toString()); + assertEquals(0, printer.getRecordCount()); + } + } + + @Test + public void testSingleQuoteQuoted() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote('\''))) { + printer.print("a'b'c"); + printer.print("xyz"); + assertEquals("'a''b''c',xyz", sw.toString()); + } + } + + @Test + public void testSkipHeaderRecordFalse() throws IOException { + // functionally identical to testHeader, used to test CSV-153 + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3").withSkipHeaderRecord(false))) { + printer.printRecord("a", "b", "c"); + printer.printRecord("x", "y", "z"); + assertEquals("C1,C2,C3\r\na,b,c\r\nx,y,z\r\n", sw.toString()); + } + } + + @Test + public void testSkipHeaderRecordTrue() throws IOException { + // functionally identical to testHeaderNotSet, used to test CSV-153 + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuote(null).withHeader("C1", "C2", "C3").withSkipHeaderRecord(true))) { + printer.printRecord("a", "b", "c"); + printer.printRecord("x", "y", "z"); + assertEquals("a,b,c\r\nx,y,z\r\n", sw.toString()); + } + } + + @Test + public void testTrailingDelimiterOnTwoColumns() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrailingDelimiter())) { + printer.printRecord("A", "B"); + assertEquals("A,B,\r\n", sw.toString()); + } + } + + @Test + public void testTrimOffOneColumn() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrim(false))) { + printer.print(" A "); + assertEquals("\" A \"", sw.toString()); + } + } + + @Test + public void testTrimOnOneColumn() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrim())) { + printer.print(" A "); + assertEquals("A", sw.toString()); + } + } + + @Test + public void testTrimOnTwoColumns() throws IOException { + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withTrim())) { + printer.print(" A "); + printer.print(" B "); + assertEquals("A,B", sw.toString()); + } + } + + private String[] toFirstRecordValues(final String expected, final CSVFormat format) throws IOException { + try (CSVParser parser = CSVParser.parse(expected, format)) { + return parser.getRecords().get(0).values(); + } + } + + private void tryFormat(final List list, final Character quote, final Character escape, final String expected) throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.withQuote(quote).withEscape(escape).withRecordSeparator(null); + final Appendable out = new StringBuilder(); + try (CSVPrinter printer = new CSVPrinter(out, format)) { + printer.printRecord(list); + } + assertEquals(expected, out.toString()); + } + +} diff --git a/src/test/java/org/apache/commons/csv/PerformanceTest.java b/src/test/java/org/apache/commons/csv/PerformanceTest.java index f692ae8e8d..bf0d483897 100644 --- a/src/test/java/org/apache/commons/csv/PerformanceTest.java +++ b/src/test/java/org/apache/commons/csv/PerformanceTest.java @@ -1,345 +1,345 @@ -/* - * 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.io.IOUtils.EOF; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.zip.GZIPInputStream; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; - -/** - * Basic test harness. - */ -@SuppressWarnings("boxing") -public class PerformanceTest { - - @FunctionalInterface - private interface CSVParserFactory { - CSVParser createParser() throws IOException; - } - - // Container for basic statistics - private static final class Stats { - final int count; - final int fields; - - Stats(final int c, final int f) { - count = c; - fields = f; - } - } - - private static final String[] PROPERTY_NAMES = { "java.version", // Java Runtime Environment version - "java.vendor", // Java Runtime Environment vendor -// "java.vm.specification.version", // Java Virtual Machine specification version -// "java.vm.specification.vendor", // Java Virtual Machine specification vendor -// "java.vm.specification.name", // Java Virtual Machine specification name - "java.vm.version", // Java Virtual Machine implementation version -// "java.vm.vendor", // Java Virtual Machine implementation vendor - "java.vm.name", // Java Virtual Machine implementation name -// "java.specification.version", // Java Runtime Environment specification version -// "java.specification.vendor", // Java Runtime Environment specification vendor -// "java.specification.name", // Java Runtime Environment specification name - - "os.name", // Operating system name - "os.arch", // Operating system architecture - "os.version", // Operating system version - }; - private static int max = 11; // skip first test - - private static int num; // number of elapsed times recorded - - private static final long[] ELAPSED_TIMES = new long[max]; - private static final CSVFormat format = CSVFormat.EXCEL; - - private static final String TEST_RESRC = "org/apache/commons/csv/perf/worldcitiespop.txt.gz"; - - private static final File BIG_FILE = new File(FileUtils.getTempDirectoryPath(), "worldcitiespop.txt"); - - private static Reader createReader() throws IOException { - return new InputStreamReader(new FileInputStream(BIG_FILE), StandardCharsets.ISO_8859_1); - } - - private static Lexer createTestCSVLexer(final String test, final ExtendedBufferedReader input) - throws InstantiationException, IllegalAccessException, InvocationTargetException, Exception { - return test.startsWith("CSVLexer") ? getLexerCtor(test).newInstance(format, input) : new Lexer(format, input); - } - - private static Constructor getLexerCtor(final String clazz) throws Exception { - @SuppressWarnings("unchecked") - final Class lexer = (Class) Class.forName("org.apache.commons.csv." + clazz); - return lexer.getConstructor(CSVFormat.class, ExtendedBufferedReader.class); - } - - private static Stats iterate(final Iterable iterable) { - int count = 0; - int fields = 0; - for (final CSVRecord record : iterable) { - count++; - fields += record.size(); - } - return new Stats(count, fields); - } - - public static void main(final String[] args) throws Exception { - if (BIG_FILE.exists()) { - System.out.printf("Found test fixture %s: %,d bytes.%n", BIG_FILE, BIG_FILE.length()); - } else { - System.out.println("Decompressing test fixture to: " + BIG_FILE + "..."); - try (InputStream input = new GZIPInputStream(PerformanceTest.class.getClassLoader().getResourceAsStream(TEST_RESRC)); - OutputStream output = new FileOutputStream(BIG_FILE)) { - IOUtils.copy(input, output); - System.out.println(String.format("Decompressed test fixture %s: %,d bytes.", BIG_FILE, BIG_FILE.length())); - } - } - final int argc = args.length; - if (argc > 0) { - max = Integer.parseInt(args[0]); - } - - final String[] tests; - if (argc > 1) { - tests = new String[argc - 1]; - System.arraycopy(args, 1, tests, 0, argc - 1); - } else { - tests = new String[] { "file", "split", "extb", "exts", "csv", "csv-path", "csv-path-db", "csv-url", "lexreset", "lexnew" }; - } - for (final String p : PROPERTY_NAMES) { - System.out.printf("%s=%s%n", p, System.getProperty(p)); - } - System.out.printf("Max count: %d%n%n", max); - - for (final String test : tests) { - switch (test) { - case "file": - testReadBigFile(false); - break; - case "split": - testReadBigFile(true); - break; - case "csv": - testParseCommonsCSV(); - break; - case "csv-path": - testParsePath(); - break; - case "csv-path-db": - testParsePathDoubleBuffering(); - break; - case "csv-url": - testParseURL(); - break; - case "lexreset": - testCSVLexer(false, test); - break; - case "lexnew": - testCSVLexer(true, test); - break; - default: - if (test.startsWith("CSVLexer")) { - testCSVLexer(false, test); - } else if ("extb".equals(test)) { - testExtendedBuffer(false); - } else if ("exts".equals(test)) { - testExtendedBuffer(true); - } else { - System.out.printf("Invalid test name: %s%n", test); - } - break; - } - } - } - - private static Stats readAll(final BufferedReader in, final boolean split) throws IOException { - int count = 0; - int fields = 0; - String record; - while ((record = in.readLine()) != null) { - count++; - fields += split ? record.split(",").length : 1; - } - return new Stats(count, fields); - } - - // calculate and show average - private static void show() { - if (num > 1) { - long tot = 0; - for (int i = 1; i < num; i++) { // skip first test - tot += ELAPSED_TIMES[i]; - } - System.out.printf("%-20s: %5dms%n%n", "Average(not first)", tot / (num - 1)); - } - num = 0; // ready for next set - } - - // Display end stats; store elapsed for average - private static void show(final String msg, final Stats s, final long start) { - final long elapsed = System.currentTimeMillis() - start; - System.out.printf("%-20s: %5dms %d lines %d fields%n", msg, elapsed, s.count, s.fields); - ELAPSED_TIMES[num] = elapsed; - num++; - } - - private static void testCSVLexer(final boolean newToken, final String test) throws Exception { - Token token = new Token(); - String dynamic = ""; - for (int i = 0; i < max; i++) { - final String simpleName; - final Stats stats; - final long startMillis; - try (ExtendedBufferedReader input = new ExtendedBufferedReader(createReader()); - Lexer lexer = createTestCSVLexer(test, input)) { - if (test.startsWith("CSVLexer")) { - dynamic = "!"; - } - simpleName = lexer.getClass().getSimpleName(); - int count = 0; - int fields = 0; - startMillis = System.currentTimeMillis(); - do { - if (newToken) { - token = new Token(); - } else { - token.reset(); - } - lexer.nextToken(token); - switch (token.type) { - case EOF: - break; - case EORECORD: - fields++; - count++; - break; - case INVALID: - throw new IOException("invalid parse sequence <" + token.content.toString() + ">"); - case TOKEN: - fields++; - break; - case COMMENT: // not really expecting these - break; - default: - throw new IllegalStateException("Unexpected Token type: " + token.type); - } - } while (!token.type.equals(Token.Type.EOF)); - stats = new Stats(count, fields); - } - show(simpleName + dynamic + " " + (newToken ? "new" : "reset"), stats, startMillis); - } - show(); - } - - private static void testExtendedBuffer(final boolean makeString) throws Exception { - for (int i = 0; i < max; i++) { - int fields = 0; - int lines = 0; - final long startMillis; - try (ExtendedBufferedReader in = new ExtendedBufferedReader(createReader())) { - startMillis = System.currentTimeMillis(); - int read; - if (makeString) { - StringBuilder sb = new StringBuilder(); - while ((read = in.read()) != EOF) { - sb.append((char) read); - if (read == ',') { // count delimiters - sb.toString(); - sb = new StringBuilder(); - fields++; - } else if (read == '\n') { - sb.toString(); - sb = new StringBuilder(); - lines++; - } - } - } else { - while ((read = in.read()) != EOF) { - if (read == ',') { // count delimiters - fields++; - } else if (read == '\n') { - lines++; - } - } - } - fields += lines; // EOL is a delimiter too - } - show("Extended" + (makeString ? " toString" : ""), new Stats(lines, fields), startMillis); - } - show(); - } - - private static void testParseCommonsCSV() throws Exception { - testParser("CSV", () -> CSVParser.builder().setReader(createReader()).setFormat(format).get()); - } - - private static void testParsePath() throws Exception { - testParser("CSV-PATH", () -> CSVParser.parse(Files.newInputStream(Paths.get(BIG_FILE.toURI())), StandardCharsets.ISO_8859_1, format)); - } - - private static void testParsePathDoubleBuffering() throws Exception { - testParser("CSV-PATH-DB", () -> CSVParser.parse(Files.newBufferedReader(Paths.get(BIG_FILE.toURI()), StandardCharsets.ISO_8859_1), format)); - } - - private static void testParser(final String msg, final CSVParserFactory fac) throws Exception { - for (int i = 0; i < max; i++) { - final long startMillis; - final Stats stats; - try (CSVParser parser = fac.createParser()) { - startMillis = System.currentTimeMillis(); - stats = iterate(parser); - } - show(msg, stats, startMillis); - } - show(); - } - - private static void testParseURL() throws Exception { - testParser("CSV-URL", () -> CSVParser.parse(BIG_FILE.toURI().toURL(), StandardCharsets.ISO_8859_1, format)); - } - - private static void testReadBigFile(final boolean split) throws Exception { - for (int i = 0; i < max; i++) { - final long startMillis; - final Stats stats; - try (BufferedReader in = new BufferedReader(createReader())) { - startMillis = System.currentTimeMillis(); - stats = readAll(in, split); - } - show(split ? "file+split" : "file", stats, startMillis); - } - show(); - } -} - +/* + * 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.io.IOUtils.EOF; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.zip.GZIPInputStream; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +/** + * Basic test harness. + */ +@SuppressWarnings("boxing") +public class PerformanceTest { + + @FunctionalInterface + private interface CSVParserFactory { + CSVParser createParser() throws IOException; + } + + // Container for basic statistics + private static final class Stats { + final int count; + final int fields; + + Stats(final int c, final int f) { + count = c; + fields = f; + } + } + + private static final String[] PROPERTY_NAMES = { "java.version", // Java Runtime Environment version + "java.vendor", // Java Runtime Environment vendor +// "java.vm.specification.version", // Java Virtual Machine specification version +// "java.vm.specification.vendor", // Java Virtual Machine specification vendor +// "java.vm.specification.name", // Java Virtual Machine specification name + "java.vm.version", // Java Virtual Machine implementation version +// "java.vm.vendor", // Java Virtual Machine implementation vendor + "java.vm.name", // Java Virtual Machine implementation name +// "java.specification.version", // Java Runtime Environment specification version +// "java.specification.vendor", // Java Runtime Environment specification vendor +// "java.specification.name", // Java Runtime Environment specification name + + "os.name", // Operating system name + "os.arch", // Operating system architecture + "os.version", // Operating system version + }; + private static int max = 11; // skip first test + + private static int num; // number of elapsed times recorded + + private static final long[] ELAPSED_TIMES = new long[max]; + private static final CSVFormat format = CSVFormat.EXCEL; + + private static final String TEST_RESRC = "org/apache/commons/csv/perf/worldcitiespop.txt.gz"; + + private static final File BIG_FILE = new File(FileUtils.getTempDirectoryPath(), "worldcitiespop.txt"); + + private static Reader createReader() throws IOException { + return new InputStreamReader(new FileInputStream(BIG_FILE), StandardCharsets.ISO_8859_1); + } + + private static Lexer createTestCSVLexer(final String test, final ExtendedBufferedReader input) + throws InstantiationException, IllegalAccessException, InvocationTargetException, Exception { + return test.startsWith("CSVLexer") ? getLexerCtor(test).newInstance(format, input) : new Lexer(format, input); + } + + private static Constructor getLexerCtor(final String clazz) throws Exception { + @SuppressWarnings("unchecked") + final Class lexer = (Class) Class.forName("org.apache.commons.csv." + clazz); + return lexer.getConstructor(CSVFormat.class, ExtendedBufferedReader.class); + } + + private static Stats iterate(final Iterable iterable) { + int count = 0; + int fields = 0; + for (final CSVRecord record : iterable) { + count++; + fields += record.size(); + } + return new Stats(count, fields); + } + + public static void main(final String[] args) throws Exception { + if (BIG_FILE.exists()) { + System.out.printf("Found test fixture %s: %,d bytes.%n", BIG_FILE, BIG_FILE.length()); + } else { + System.out.println("Decompressing test fixture to: " + BIG_FILE + "..."); + try (InputStream input = new GZIPInputStream(PerformanceTest.class.getClassLoader().getResourceAsStream(TEST_RESRC)); + OutputStream output = new FileOutputStream(BIG_FILE)) { + IOUtils.copy(input, output); + System.out.println(String.format("Decompressed test fixture %s: %,d bytes.", BIG_FILE, BIG_FILE.length())); + } + } + final int argc = args.length; + if (argc > 0) { + max = Integer.parseInt(args[0]); + } + + final String[] tests; + if (argc > 1) { + tests = new String[argc - 1]; + System.arraycopy(args, 1, tests, 0, argc - 1); + } else { + tests = new String[] { "file", "split", "extb", "exts", "csv", "csv-path", "csv-path-db", "csv-url", "lexreset", "lexnew" }; + } + for (final String p : PROPERTY_NAMES) { + System.out.printf("%s=%s%n", p, System.getProperty(p)); + } + System.out.printf("Max count: %d%n%n", max); + + for (final String test : tests) { + switch (test) { + case "file": + testReadBigFile(false); + break; + case "split": + testReadBigFile(true); + break; + case "csv": + testParseCommonsCSV(); + break; + case "csv-path": + testParsePath(); + break; + case "csv-path-db": + testParsePathDoubleBuffering(); + break; + case "csv-url": + testParseURL(); + break; + case "lexreset": + testCSVLexer(false, test); + break; + case "lexnew": + testCSVLexer(true, test); + break; + default: + if (test.startsWith("CSVLexer")) { + testCSVLexer(false, test); + } else if ("extb".equals(test)) { + testExtendedBuffer(false); + } else if ("exts".equals(test)) { + testExtendedBuffer(true); + } else { + System.out.printf("Invalid test name: %s%n", test); + } + break; + } + } + } + + private static Stats readAll(final BufferedReader in, final boolean split) throws IOException { + int count = 0; + int fields = 0; + String record; + while ((record = in.readLine()) != null) { + count++; + fields += split ? record.split(",").length : 1; + } + return new Stats(count, fields); + } + + // calculate and show average + private static void show() { + if (num > 1) { + long tot = 0; + for (int i = 1; i < num; i++) { // skip first test + tot += ELAPSED_TIMES[i]; + } + System.out.printf("%-20s: %5dms%n%n", "Average(not first)", tot / (num - 1)); + } + num = 0; // ready for next set + } + + // Display end stats; store elapsed for average + private static void show(final String msg, final Stats s, final long start) { + final long elapsed = System.currentTimeMillis() - start; + System.out.printf("%-20s: %5dms %d lines %d fields%n", msg, elapsed, s.count, s.fields); + ELAPSED_TIMES[num] = elapsed; + num++; + } + + private static void testCSVLexer(final boolean newToken, final String test) throws Exception { + Token token = new Token(); + String dynamic = ""; + for (int i = 0; i < max; i++) { + final String simpleName; + final Stats stats; + final long startMillis; + try (ExtendedBufferedReader input = new ExtendedBufferedReader(createReader()); + Lexer lexer = createTestCSVLexer(test, input)) { + if (test.startsWith("CSVLexer")) { + dynamic = "!"; + } + simpleName = lexer.getClass().getSimpleName(); + int count = 0; + int fields = 0; + startMillis = System.currentTimeMillis(); + do { + if (newToken) { + token = new Token(); + } else { + token.reset(); + } + lexer.nextToken(token); + switch (token.type) { + case EOF: + break; + case EORECORD: + fields++; + count++; + break; + case INVALID: + throw new IOException("invalid parse sequence <" + token.content.toString() + ">"); + case TOKEN: + fields++; + break; + case COMMENT: // not really expecting these + break; + default: + throw new IllegalStateException("Unexpected Token type: " + token.type); + } + } while (!token.type.equals(Token.Type.EOF)); + stats = new Stats(count, fields); + } + show(simpleName + dynamic + " " + (newToken ? "new" : "reset"), stats, startMillis); + } + show(); + } + + private static void testExtendedBuffer(final boolean makeString) throws Exception { + for (int i = 0; i < max; i++) { + int fields = 0; + int lines = 0; + final long startMillis; + try (ExtendedBufferedReader in = new ExtendedBufferedReader(createReader())) { + startMillis = System.currentTimeMillis(); + int read; + if (makeString) { + StringBuilder sb = new StringBuilder(); + while ((read = in.read()) != EOF) { + sb.append((char) read); + if (read == ',') { // count delimiters + sb.toString(); + sb = new StringBuilder(); + fields++; + } else if (read == '\n') { + sb.toString(); + sb = new StringBuilder(); + lines++; + } + } + } else { + while ((read = in.read()) != EOF) { + if (read == ',') { // count delimiters + fields++; + } else if (read == '\n') { + lines++; + } + } + } + fields += lines; // EOL is a delimiter too + } + show("Extended" + (makeString ? " toString" : ""), new Stats(lines, fields), startMillis); + } + show(); + } + + private static void testParseCommonsCSV() throws Exception { + testParser("CSV", () -> CSVParser.builder().setReader(createReader()).setFormat(format).get()); + } + + private static void testParsePath() throws Exception { + testParser("CSV-PATH", () -> CSVParser.parse(Files.newInputStream(Paths.get(BIG_FILE.toURI())), StandardCharsets.ISO_8859_1, format)); + } + + private static void testParsePathDoubleBuffering() throws Exception { + testParser("CSV-PATH-DB", () -> CSVParser.parse(Files.newBufferedReader(Paths.get(BIG_FILE.toURI()), StandardCharsets.ISO_8859_1), format)); + } + + private static void testParser(final String msg, final CSVParserFactory fac) throws Exception { + for (int i = 0; i < max; i++) { + final long startMillis; + final Stats stats; + try (CSVParser parser = fac.createParser()) { + startMillis = System.currentTimeMillis(); + stats = iterate(parser); + } + show(msg, stats, startMillis); + } + show(); + } + + private static void testParseURL() throws Exception { + testParser("CSV-URL", () -> CSVParser.parse(BIG_FILE.toURI().toURL(), StandardCharsets.ISO_8859_1, format)); + } + + private static void testReadBigFile(final boolean split) throws Exception { + for (int i = 0; i < max; i++) { + final long startMillis; + final Stats stats; + try (BufferedReader in = new BufferedReader(createReader())) { + startMillis = System.currentTimeMillis(); + stats = readAll(in, split); + } + show(split ? "file+split" : "file", stats, startMillis); + } + show(); + } +} + diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv198Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv198Test.java index cee88f15e0..641797fe88 100644 --- a/src/test/java/org/apache/commons/csv/issues/JiraCsv198Test.java +++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv198Test.java @@ -1,54 +1,54 @@ -/* - * 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.issues; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.junit.jupiter.api.Test; - -public class JiraCsv198Test { - - // @formatter:off - private static final CSVFormat CSV_FORMAT = CSVFormat.EXCEL.builder() - .setDelimiter('^') - .setHeader() - .setSkipHeaderRecord(true) - .get(); - // @formatter:on - - @Test - public void test() throws UnsupportedEncodingException, IOException { - final InputStream pointsOfReference = getClass().getResourceAsStream("/org/apache/commons/csv/CSV-198/optd_por_public.csv"); - assertNotNull(pointsOfReference); - try (@SuppressWarnings("resource") - CSVParser parser = CSV_FORMAT.parse(new InputStreamReader(pointsOfReference, StandardCharsets.UTF_8))) { - parser.forEach(record -> assertNotNull(record.get("location_type"))); - } - } - -} +/* + * 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.issues; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.junit.jupiter.api.Test; + +public class JiraCsv198Test { + + // @formatter:off + private static final CSVFormat CSV_FORMAT = CSVFormat.EXCEL.builder() + .setDelimiter('^') + .setHeader() + .setSkipHeaderRecord(true) + .get(); + // @formatter:on + + @Test + public void test() throws UnsupportedEncodingException, IOException { + final InputStream pointsOfReference = getClass().getResourceAsStream("/org/apache/commons/csv/CSV-198/optd_por_public.csv"); + assertNotNull(pointsOfReference); + try (@SuppressWarnings("resource") + CSVParser parser = CSV_FORMAT.parse(new InputStreamReader(pointsOfReference, StandardCharsets.UTF_8))) { + parser.forEach(record -> assertNotNull(record.get("location_type"))); + } + } + +} diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv211Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv211Test.java index 9da17465cb..a4e3960c96 100644 --- a/src/test/java/org/apache/commons/csv/issues/JiraCsv211Test.java +++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv211Test.java @@ -1,54 +1,54 @@ -/* - * 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.issues; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.io.StringReader; - -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.junit.jupiter.api.Test; - -public class JiraCsv211Test { - - @Test - public void testJiraCsv211Format() throws IOException { - // @formatter:off - final CSVFormat printFormat = CSVFormat.DEFAULT.builder() - .setDelimiter('\t') - .setHeader("ID", "Name", "Country", "Age") - .get(); - // @formatter:on - final String formatted = printFormat.format("1", "Jane Doe", "USA", ""); - assertEquals("ID\tName\tCountry\tAge\r\n1\tJane Doe\tUSA\t", formatted); - - final CSVFormat parseFormat = CSVFormat.DEFAULT.builder().setDelimiter('\t').setHeader().setSkipHeaderRecord(true).get(); - try (CSVParser parser = parseFormat.parse(new StringReader(formatted))) { - parser.forEach(record -> { - assertEquals("1", record.get(0)); - assertEquals("Jane Doe", record.get(1)); - assertEquals("USA", record.get(2)); - assertEquals("", record.get(3)); - }); - } - } -} +/* + * 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.issues; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.StringReader; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.junit.jupiter.api.Test; + +public class JiraCsv211Test { + + @Test + public void testJiraCsv211Format() throws IOException { + // @formatter:off + final CSVFormat printFormat = CSVFormat.DEFAULT.builder() + .setDelimiter('\t') + .setHeader("ID", "Name", "Country", "Age") + .get(); + // @formatter:on + final String formatted = printFormat.format("1", "Jane Doe", "USA", ""); + assertEquals("ID\tName\tCountry\tAge\r\n1\tJane Doe\tUSA\t", formatted); + + final CSVFormat parseFormat = CSVFormat.DEFAULT.builder().setDelimiter('\t').setHeader().setSkipHeaderRecord(true).get(); + try (CSVParser parser = parseFormat.parse(new StringReader(formatted))) { + parser.forEach(record -> { + assertEquals("1", record.get(0)); + assertEquals("Jane Doe", record.get(1)); + assertEquals("USA", record.get(2)); + assertEquals("", record.get(3)); + }); + } + } +} diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv288Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv288Test.java index 4e614816a1..0be6a52f81 100644 --- a/src/test/java/org/apache/commons/csv/issues/JiraCsv288Test.java +++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv288Test.java @@ -1,216 +1,216 @@ -/* - * 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.issues; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVPrinter; -import org.apache.commons.csv.CSVRecord; -import org.junit.jupiter.api.Test; - -public class JiraCsv288Test { - - private void print(final CSVRecord csvRecord, final CSVPrinter csvPrinter) throws IOException { - for (final String value : csvRecord) { - csvPrinter.print(value); - } - } - - @Test - // Before fix: - // expected: but was: - public void testParseWithABADelimiter() throws Exception { - final Reader in = new StringReader("a|~|b|~|c|~|d|~||~|f"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser parser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("|~|").get())) { - for (final CSVRecord csvRecord : parser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f", stringBuilder.toString()); - } - } - } - - @Test - // Before fix: - // expected: but was: - public void testParseWithDoublePipeDelimiter() throws Exception { - final Reader in = new StringReader("a||b||c||d||||f"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f", stringBuilder.toString()); - } - } - } - - @Test - // Regression, already passed before fix - - public void testParseWithDoublePipeDelimiterDoubleCharValue() throws Exception { - final Reader in = new StringReader("a||bb||cc||dd||f"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,bb,cc,dd,f", stringBuilder.toString()); - } - } - } - - @Test - // Before fix: - // expected: but was: - public void testParseWithDoublePipeDelimiterEndsWithDelimiter() throws Exception { - final Reader in = new StringReader("a||b||c||d||||f||"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f,", stringBuilder.toString()); - } - } - } - - @Test - // Before fix: - // expected: but was: - public void testParseWithDoublePipeDelimiterQuoted() throws Exception { - final Reader in = new StringReader("a||\"b||c\"||d||||f"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b||c,d,,f", stringBuilder.toString()); - } - } - } - - @Test - // Regression, already passed before fix - public void testParseWithSinglePipeDelimiterEndsWithDelimiter() throws Exception { - final Reader in = new StringReader("a|b|c|d||f|"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("|").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f,", stringBuilder.toString()); - } - } - } - - @Test - // Before fix: - // expected: but was: - public void testParseWithTriplePipeDelimiter() throws Exception { - final Reader in = new StringReader("a|||b|||c|||d||||||f"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("|||").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f", stringBuilder.toString()); - } - } - } - - @Test - // Regression, already passed before fix - public void testParseWithTwoCharDelimiter1() throws Exception { - final Reader in = new StringReader("a~|b~|c~|d~|~|f"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f", stringBuilder.toString()); - } - } - } - - @Test - // Regression, already passed before fix - public void testParseWithTwoCharDelimiter2() throws Exception { - final Reader in = new StringReader("a~|b~|c~|d~|~|f~"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f~", stringBuilder.toString()); - } - } - } - - @Test - // Regression, already passed before fix - public void testParseWithTwoCharDelimiter3() throws Exception { - final Reader in = new StringReader("a~|b~|c~|d~|~|f|"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f|", stringBuilder.toString()); - } - } - } - - @Test - // Regression, already passed before fix - public void testParseWithTwoCharDelimiter4() throws Exception { - final Reader in = new StringReader("a~|b~|c~|d~|~|f~~||g"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f~,|g", stringBuilder.toString()); - } - } - } - - @Test - // Before fix: - // expected: but was: - public void testParseWithTwoCharDelimiterEndsWithDelimiter() throws Exception { - final Reader in = new StringReader("a~|b~|c~|d~|~|f~|"); - final StringBuilder stringBuilder = new StringBuilder(); - try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); - CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { - for (final CSVRecord csvRecord : csvParser) { - print(csvRecord, csvPrinter); - assertEquals("a,b,c,d,,f,", stringBuilder.toString()); - } - } - } -} +/* + * 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.issues; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; +import org.junit.jupiter.api.Test; + +public class JiraCsv288Test { + + private void print(final CSVRecord csvRecord, final CSVPrinter csvPrinter) throws IOException { + for (final String value : csvRecord) { + csvPrinter.print(value); + } + } + + @Test + // Before fix: + // expected: but was: + public void testParseWithABADelimiter() throws Exception { + final Reader in = new StringReader("a|~|b|~|c|~|d|~||~|f"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser parser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("|~|").get())) { + for (final CSVRecord csvRecord : parser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f", stringBuilder.toString()); + } + } + } + + @Test + // Before fix: + // expected: but was: + public void testParseWithDoublePipeDelimiter() throws Exception { + final Reader in = new StringReader("a||b||c||d||||f"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f", stringBuilder.toString()); + } + } + } + + @Test + // Regression, already passed before fix + + public void testParseWithDoublePipeDelimiterDoubleCharValue() throws Exception { + final Reader in = new StringReader("a||bb||cc||dd||f"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,bb,cc,dd,f", stringBuilder.toString()); + } + } + } + + @Test + // Before fix: + // expected: but was: + public void testParseWithDoublePipeDelimiterEndsWithDelimiter() throws Exception { + final Reader in = new StringReader("a||b||c||d||||f||"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f,", stringBuilder.toString()); + } + } + } + + @Test + // Before fix: + // expected: but was: + public void testParseWithDoublePipeDelimiterQuoted() throws Exception { + final Reader in = new StringReader("a||\"b||c\"||d||||f"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("||").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b||c,d,,f", stringBuilder.toString()); + } + } + } + + @Test + // Regression, already passed before fix + public void testParseWithSinglePipeDelimiterEndsWithDelimiter() throws Exception { + final Reader in = new StringReader("a|b|c|d||f|"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("|").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f,", stringBuilder.toString()); + } + } + } + + @Test + // Before fix: + // expected: but was: + public void testParseWithTriplePipeDelimiter() throws Exception { + final Reader in = new StringReader("a|||b|||c|||d||||||f"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("|||").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f", stringBuilder.toString()); + } + } + } + + @Test + // Regression, already passed before fix + public void testParseWithTwoCharDelimiter1() throws Exception { + final Reader in = new StringReader("a~|b~|c~|d~|~|f"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f", stringBuilder.toString()); + } + } + } + + @Test + // Regression, already passed before fix + public void testParseWithTwoCharDelimiter2() throws Exception { + final Reader in = new StringReader("a~|b~|c~|d~|~|f~"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f~", stringBuilder.toString()); + } + } + } + + @Test + // Regression, already passed before fix + public void testParseWithTwoCharDelimiter3() throws Exception { + final Reader in = new StringReader("a~|b~|c~|d~|~|f|"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f|", stringBuilder.toString()); + } + } + } + + @Test + // Regression, already passed before fix + public void testParseWithTwoCharDelimiter4() throws Exception { + final Reader in = new StringReader("a~|b~|c~|d~|~|f~~||g"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f~,|g", stringBuilder.toString()); + } + } + } + + @Test + // Before fix: + // expected: but was: + public void testParseWithTwoCharDelimiterEndsWithDelimiter() throws Exception { + final Reader in = new StringReader("a~|b~|c~|d~|~|f~|"); + final StringBuilder stringBuilder = new StringBuilder(); + try (CSVPrinter csvPrinter = new CSVPrinter(stringBuilder, CSVFormat.EXCEL); + CSVParser csvParser = CSVParser.parse(in, CSVFormat.Builder.create().setDelimiter("~|").get())) { + for (final CSVRecord csvRecord : csvParser) { + print(csvRecord, csvPrinter); + assertEquals("a,b,c,d,,f,", stringBuilder.toString()); + } + } + } +}