From 9d258736b749270d74f6f0460b8c1006c1124e67 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 26 Feb 2026 07:24:08 -0500 Subject: [PATCH 01/98] Add JiraCsv227Test See https://issues.apache.org/jira/browse/CSV-227 --- .../commons/csv/issues/JiraCsv227Test.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/test/java/org/apache/commons/csv/issues/JiraCsv227Test.java diff --git a/src/test/java/org/apache/commons/csv/issues/JiraCsv227Test.java b/src/test/java/org/apache/commons/csv/issues/JiraCsv227Test.java new file mode 100644 index 000000000..2b9e335a8 --- /dev/null +++ b/src/test/java/org/apache/commons/csv/issues/JiraCsv227Test.java @@ -0,0 +1,49 @@ +/* + * 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 org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.QuoteMode; +import org.junit.jupiter.api.Test; + +/** + * Tests https://issues.apache.org/jira/browse/CSV-227 + */ +class JiraCsv227Test { + + @Test + public void test() throws IOException { + final StringBuilder out = new StringBuilder(); + try (CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT.withQuoteMode(QuoteMode.MINIMAL))) { + printer.printRecord("ㅁㅎㄷㄹ", "ㅁㅎㄷㄹ", "", "test2"); + printer.printRecord("한글3", "hello3", "3한글3", "test3"); + printer.printRecord("", "hello4", "", "test4"); + } + // ㅁㅎㄷㄹ,ㅁㅎㄷㄹ,,test2 + // 한글3,hello3,3한글3,test3 + // "",hello4,,test4 + assertEquals("ㅁㅎㄷㄹ,ㅁㅎㄷㄹ,,test2\r\n한글3,hello3,3한글3,test3\r\n\"\",hello4,,test4\r\n", out.toString()); + } +} From c5d2bc1decefdad0a01de3c06dee53c21107a2eb Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 26 Feb 2026 07:34:00 -0500 Subject: [PATCH 02/98] Remove meaningless comments. --- src/site/xdoc/index.xml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml index af49476f9..f24175e8c 100644 --- a/src/site/xdoc/index.xml +++ b/src/site/xdoc/index.xml @@ -24,12 +24,10 @@ limitations under the License. -

Commons CSV reads and writes files in variations of the Comma Separated Value (CSV) format.

Read the documentation starting with the Javadoc Overview.

-

An overview of the functionality is provided in the @@ -48,7 +46,6 @@ The git repository can be browsed.

-
-

The commons mailing lists act as the main support forum. @@ -101,8 +96,6 @@ For previous releases, see the

Commons CSV was started to unify a common and simple interface for reading and writing CSV files under an ASL license. It has been bootstrapped by a code donation from Netcetera in Switzerland. There are three pre-existing BSD compatible CSV parsers which this component will hopefully make redundant (authors willing):

- - From 533bcca147ffae99cbf967e50a1dcf97f27b0488 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 26 Feb 2026 07:42:42 -0500 Subject: [PATCH 03/98] Document Android compatibility Like PR 597 with better text and a table with links --- src/site/xdoc/index.xml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml index f24175e8c..ac5b8cfa9 100644 --- a/src/site/xdoc/index.xml +++ b/src/site/xdoc/index.xml @@ -66,12 +66,32 @@ For previous releases, see the Dependency Information

-

The latest code can be checked out from our git repository at https://gitbox.apache.org/repos/asf/commons-csv.git. You can build the component using Apache Maven using mvn clean package.

+
+

+ Apache Commons CSV requires Java 8 or above. +

+ + + + + + + + + + + + + + + +
Commons CSVJavaAndroid
1.10.0+8Android 7.0 (API level 24)
+

The commons developer mailing list is the main channel of communication for contributors. Please remember that the lists are shared between all commons components, so prefix your email by [csv].

From 4edb2cf3021a925a732ebd99a8dd33f1a15536bb Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 26 Feb 2026 07:49:29 -0500 Subject: [PATCH 04/98] Add an "Android Compatibility" section to the web site. --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2e24024c8..0a702eaec 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -46,6 +46,7 @@ Remove broken website link #577. Fix Apache RAT plugin console warnings. + Add an "Android Compatibility" section to the web site. Bump org.apache.commons:commons-parent from 85 to 96 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. From 4f3ff11dad5ab573dcab371bb4cc7e4b45b84772 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 27 Feb 2026 20:48:34 -0500 Subject: [PATCH 05/98] Bump org.apache.commons:commons-parent from 96 to 97. --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0ade7e0d4..07f030ae3 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.apache.commons commons-parent - 96 + 97 commons-csv 1.14.2-SNAPSHOT diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0a702eaec..9e4bce664 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -48,7 +48,7 @@ Add an "Android Compatibility" section to the web site. - Bump org.apache.commons:commons-parent from 85 to 96 #573, #595. + Bump org.apache.commons:commons-parent from 85 to 97 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.21.0. From 15602b65a9aea4f2df8156198a7915b51f480f9a Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 2 Mar 2026 08:34:01 -0500 Subject: [PATCH 06/98] Bump github/codeql-action from 4.32.4 to 4.32.5 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6c4aa3f57..e2ee451b4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/init@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/autobuild@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/analyze@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index f01ca3a11..569f386ee 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 with: sarif_file: results.sarif From dce781b48086d705eb3690ed5b566d06466a5ae4 Mon Sep 17 00:00:00 2001 From: Makarand Milind Hinge Date: Mon, 2 Mar 2026 22:03:35 +0530 Subject: [PATCH 07/98] docs: clarify behavior of deprecated withFirstRecordAsHeader() --- src/main/java/org/apache/commons/csv/CSVFormat.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 7251f83dc..62d2cd785 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -2788,6 +2788,9 @@ public CSVFormat withEscape(final Character escape) { * .get(); * * + *

Note: Any previously set headers are reset to empty. + * The resulting format will have {@code skipHeaderRecord = true}.

+ * * @return A new CSVFormat that is equal to this but using the first record as header. * @see Builder#setSkipHeaderRecord(boolean) * @see Builder#setHeader(String...) From 7ef443c93d6fd34432f4d04d19a34a815a1ae97d Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 2 Mar 2026 13:41:10 -0500 Subject: [PATCH 08/98] Javadoc --- src/main/java/org/apache/commons/csv/CSVFormat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 62d2cd785..fff1f055b 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -2788,7 +2788,7 @@ public CSVFormat withEscape(final Character escape) { * .get(); * * - *

Note: Any previously set headers are reset to empty. + *

Any previously set headers are reset to empty. * The resulting format will have {@code skipHeaderRecord = true}.

* * @return A new CSVFormat that is equal to this but using the first record as header. From a529f36b64fc1016db5eedce73bd396781980773 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 2 Mar 2026 13:44:29 -0500 Subject: [PATCH 09/98] [Javadoc] Clarify behavior of deprecated CSVFormat#withFirstRecordAsHeader() #2413. --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 9e4bce664..cadb261cf 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -45,6 +45,7 @@ Remove Spotbugs dependency and use exclude-filter instead #564. Remove broken website link #577. Fix Apache RAT plugin console warnings. + [Javadoc] Clarify behavior of deprecated CSVFormat#withFirstRecordAsHeader() #2413. Add an "Android Compatibility" section to the web site. From 3ae9bc442ec3220620fc11c31efab5e160412bc3 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 2 Mar 2026 13:48:00 -0500 Subject: [PATCH 10/98] Use new Apache Commons CSV Oak leaf logo. --- src/media/commons-logo-component-100.xcf | Bin 0 -> 25347 bytes src/media/commons-logo-component.xcf | Bin 0 -> 150900 bytes src/media/logo-large.xcf | Bin 143556 -> 0 bytes src/media/logo.png | Bin 10083 -> 9881 bytes src/media/logo.xcf | Bin 22088 -> 0 bytes src/site/resources/images/logo.png | Bin 10083 -> 9881 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/media/commons-logo-component-100.xcf create mode 100644 src/media/commons-logo-component.xcf delete mode 100644 src/media/logo-large.xcf delete mode 100644 src/media/logo.xcf diff --git a/src/media/commons-logo-component-100.xcf b/src/media/commons-logo-component-100.xcf new file mode 100644 index 0000000000000000000000000000000000000000..77d92f27797075858c5b4ac2ae554bb01f9ac763 GIT binary patch literal 25347 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

IF&1inKEbkX10uLPXxqr;x<@Z}-q6zP7}t)+zrm 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%0DT>P4EBLLaZ78HkT1s& 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 literal 0 HcmV?d00001 diff --git a/src/media/logo-large.xcf b/src/media/logo-large.xcf deleted file mode 100644 index 7bb07af3eb06c4b961e2b27ee4cbdf9e0ba50828..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143556 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 diff --git a/src/media/logo.png b/src/media/logo.png index 582fba9806c9b77d605e1145ed2d246e18e91d78..93bb6c0148a810ef06a616afa5871e286f4961e6 100644 GIT binary patch delta 9845 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 diff --git a/src/media/logo.xcf b/src/media/logo.xcf deleted file mode 100644 index ac6376f71f1406c121ce0c160dfda147103f2019..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22088 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 diff --git a/src/site/resources/images/logo.png b/src/site/resources/images/logo.png index 582fba9806c9b77d605e1145ed2d246e18e91d78..93bb6c0148a810ef06a616afa5871e286f4961e6 100644 GIT binary patch delta 9845 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 From f40411f6b463b05b595098bb97fda9144a731e37 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 6 Mar 2026 07:35:54 -0500 Subject: [PATCH 11/98] Bump github/codeql-action from 4.32.5 to 4.32.6 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e2ee451b4..4c52d9d27 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 + uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 + uses: github/codeql-action/autobuild@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 + uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 569f386ee..d41ee17be 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: sarif_file: results.sarif From 66a838202d64a9b05be4e74b846619688b26cb10 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 9 Mar 2026 08:43:40 -0400 Subject: [PATCH 12/98] Bump actions/upload-artifact from 6.0.0 to 7.0.0 --- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index d41ee17be..c170b0e14 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -57,7 +57,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # 6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: SARIF file path: results.sarif From f6e75453ed102741dcc7df6e8fce13ef91bc7900 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 19 Mar 2026 23:46:36 +0000 Subject: [PATCH 13/98] Add a reference to safe deserlialization --- src/site/xdoc/security.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/site/xdoc/security.xml b/src/site/xdoc/security.xml index ab0056049..47edf5d11 100644 --- a/src/site/xdoc/security.xml +++ b/src/site/xdoc/security.xml @@ -47,5 +47,10 @@

None.

+
+

+ For information about safe deserialization, please see Safe Deserialization. +

+
\ No newline at end of file From b639f788aa3650a556630918502a463ac3f20241 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 20 Mar 2026 17:32:10 -0700 Subject: [PATCH 14/98] Bump github/codeql-action from 4.32.6 to 4.34.0 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4c52d9d27..e072e3dd7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/init@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/autobuild@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/analyze@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index c170b0e14..b7c1f9fb3 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/upload-sarif@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 with: sarif_file: results.sarif From 1df69a88832fe75d8816aa9112ce5c2373209ecb Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sun, 22 Mar 2026 22:29:26 -0700 Subject: [PATCH 15/98] Javadoc --- src/main/java/org/apache/commons/csv/CSVFormat.java | 2 +- src/main/java/org/apache/commons/csv/DuplicateHeaderMode.java | 2 +- src/main/java/org/apache/commons/csv/QuoteMode.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index fff1f055b..09e76e3a6 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -900,7 +900,7 @@ public Builder setTrim(final boolean trim) { } /** - * Predefines formats. + * Enumerates predefines formats. * * @since 1.2 */ diff --git a/src/main/java/org/apache/commons/csv/DuplicateHeaderMode.java b/src/main/java/org/apache/commons/csv/DuplicateHeaderMode.java index 01989d664..8087f16ee 100644 --- a/src/main/java/org/apache/commons/csv/DuplicateHeaderMode.java +++ b/src/main/java/org/apache/commons/csv/DuplicateHeaderMode.java @@ -20,7 +20,7 @@ package org.apache.commons.csv; /** - * Determines how duplicate header fields should be handled + * Enumerates how duplicate header fields should be handled * if {@link CSVFormat.Builder#setHeader(Class)} is not null. * * @since 1.10.0 diff --git a/src/main/java/org/apache/commons/csv/QuoteMode.java b/src/main/java/org/apache/commons/csv/QuoteMode.java index 79bb1b34e..ae64ab486 100644 --- a/src/main/java/org/apache/commons/csv/QuoteMode.java +++ b/src/main/java/org/apache/commons/csv/QuoteMode.java @@ -19,7 +19,7 @@ package org.apache.commons.csv; /** - * Defines quoting behavior. + * Enumerates quoting behavior. * * @see CSVFormat.Builder#setQuoteMode(QuoteMode) */ From 4f51fe73c48604b945ac3e40ffa3f65e4343d8e2 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 27 Mar 2026 14:48:55 -0400 Subject: [PATCH 16/98] Bump github/codeql-action from 4.34.0 to 4.34.1 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e072e3dd7..1dd2d623c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 + uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 + uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 + uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index b7c1f9fb3..3156ec8b5 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4.34.0 + uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: sarif_file: results.sarif From 09af4ba49b06fd3af5cc810f0dc178f085f35cfc Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 30 Mar 2026 07:16:03 -0400 Subject: [PATCH 17/98] Bump Bump actions/cache from 5.0.3 to 5.0.4 --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/maven.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1dd2d623c..c51994f92 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 #v5.0.3 + - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 518219a1d..e4cfb933b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 #v5.0.3 + - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From e44608974671b49f464c3b3a01606593f618a4c7 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 31 Mar 2026 20:41:06 -0400 Subject: [PATCH 18/98] Bump actions/dependency-review-action from 4.8.2 to 4.9.0 --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index a04da5090..f0d8ca94e 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -28,4 +28,4 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 'Dependency Review PR' - uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 From f85eb5bac48e5d5206227ddeb4427ddba4b2ba8f Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 31 Mar 2026 21:44:18 -0400 Subject: [PATCH 19/98] Bump github/codeql-action from 4.34.1 to 4.35.1 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c51994f92..7d3074419 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 + uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 3156ec8b5..85a584736 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 + uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: sarif_file: results.sarif From 89c05999855f6884b4e297399b6b29f600a49e4b Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 1 Apr 2026 06:20:29 -0400 Subject: [PATCH 20/98] Bump actions/checkout from 6.0.1 to 6.0.2 --- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 85a584736..a0e5b9faf 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2 with: persist-credentials: false From 87349e514b2a5496405f53fc4cd973b29c01fc56 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 3 Apr 2026 06:52:55 -0400 Subject: [PATCH 21/98] Run builds only on push to master and pull requests --- .github/workflows/maven.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e4cfb933b..ebe4bc3b9 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -15,7 +15,11 @@ name: Java CI -on: [push, pull_request] +on: + push: + branches: + - 'master' + pull_request: {} permissions: contents: read From 2efa47f7bde77001c1910243c4b3b8f08ca3c9d9 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sun, 5 Apr 2026 09:31:39 -0400 Subject: [PATCH 22/98] Use static imports only for JUnit Rename internal test method --- .../commons/csv/CSVDuplicateHeaderTest.java | 17 +-- .../org/apache/commons/csv/CSVFormatTest.java | 100 +++++++++--------- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVDuplicateHeaderTest.java b/src/test/java/org/apache/commons/csv/CSVDuplicateHeaderTest.java index cc47b999a..2f518a120 100644 --- a/src/test/java/org/apache/commons/csv/CSVDuplicateHeaderTest.java +++ b/src/test/java/org/apache/commons/csv/CSVDuplicateHeaderTest.java @@ -19,13 +19,16 @@ package org.apache.commons.csv; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -285,11 +288,11 @@ void testCSVFormat(final DuplicateHeaderMode duplicateHeaderMode, .setHeader(headers); if (valid) { final CSVFormat format = builder.get(); - Assertions.assertEquals(duplicateHeaderMode, format.getDuplicateHeaderMode(), "DuplicateHeaderMode"); - Assertions.assertEquals(allowMissingColumnNames, format.getAllowMissingColumnNames(), "AllowMissingColumnNames"); - Assertions.assertArrayEquals(headers, format.getHeader(), "Header"); + assertEquals(duplicateHeaderMode, format.getDuplicateHeaderMode(), "DuplicateHeaderMode"); + assertEquals(allowMissingColumnNames, format.getAllowMissingColumnNames(), "AllowMissingColumnNames"); + assertArrayEquals(headers, format.getHeader(), "Header"); } else { - Assertions.assertThrows(IllegalArgumentException.class, builder::get); + assertThrows(IllegalArgumentException.class, builder::get); } } @@ -327,10 +330,10 @@ void testCSVParser(final DuplicateHeaderMode duplicateHeaderMode, try (CSVParser parser = CSVParser.parse(input, format)) { // Parser ignores null headers final List expected = Arrays.stream(headers).filter(s -> s != null).collect(Collectors.toList()); - Assertions.assertEquals(expected, parser.getHeaderNames(), "HeaderNames"); + assertEquals(expected, parser.getHeaderNames(), "HeaderNames"); } } else { - Assertions.assertThrows(IllegalArgumentException.class, () -> CSVParser.parse(input, format)); + assertThrows(IllegalArgumentException.class, () -> CSVParser.parse(input, format)); } } } diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index d1d19f755..4d428b465 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -26,6 +26,7 @@ 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -48,7 +49,6 @@ import java.util.Objects; import org.apache.commons.csv.CSVFormat.Builder; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** @@ -64,16 +64,16 @@ 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 void assertNotEqualsFlip(final Object right, final Object left) { + assertNotEquals(right, left); + 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) { + private void assertNotEqualsHash(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 + ")"); } @@ -198,8 +198,8 @@ void testDuplicateHeaderElementsTrueContainsEmpty3() { void testEquals() { final CSVFormat right = CSVFormat.DEFAULT; final CSVFormat left = copy(right); - Assertions.assertNotEquals(null, right); - Assertions.assertNotEquals("A String Instance", right); + assertNotEquals(null, right); + assertNotEquals("A String Instance", right); assertEquals(right, right); assertEquals(right, left); assertEquals(left, right); @@ -212,7 +212,7 @@ void testEqualsCommentStart() { final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').setCommentMarker('#').setQuoteMode(QuoteMode.ALL).get(); final CSVFormat left = right.builder().setCommentMarker('!').get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -221,7 +221,7 @@ void testEqualsCommentStart_Deprecated() { final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withCommentMarker('#').withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withCommentMarker('!'); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -229,7 +229,7 @@ void testEqualsDelimiter() { final CSVFormat right = CSVFormat.newFormat('!'); final CSVFormat left = CSVFormat.newFormat('?'); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -237,7 +237,7 @@ 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); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -246,7 +246,7 @@ void testEqualsEscape_Deprecated() { final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withCommentMarker('#').withEscape('+').withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withEscape('!'); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -262,49 +262,49 @@ void testEqualsHash() throws Exception { 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); + assertNotEqualsHash(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); + assertNotEqualsHash(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); + assertNotEqualsHash(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); + assertNotEqualsHash(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); + assertNotEqualsHash(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); + assertNotEqualsHash(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); + assertNotEqualsHash(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); + assertNotEqualsHash(name, type, a, b); break; } default: @@ -327,7 +327,7 @@ void testEqualsHeader() { .setIgnoreEmptyLines(true).setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get(); final CSVFormat left = right.builder().setHeader("Three", "Two", "One").get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -337,7 +337,7 @@ void testEqualsHeader_Deprecated() { .withIgnoreEmptyLines().withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withHeader("Three", "Two", "One"); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -346,7 +346,7 @@ void testEqualsIgnoreEmptyLines() { .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get(); final CSVFormat left = right.builder().setIgnoreEmptyLines(false).get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -356,7 +356,7 @@ void testEqualsIgnoreEmptyLines_Deprecated() { .withQuote('"').withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withIgnoreEmptyLines(false); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -365,7 +365,7 @@ void testEqualsIgnoreSurroundingSpaces() { .setQuoteMode(QuoteMode.ALL).get(); final CSVFormat left = right.builder().setIgnoreSurroundingSpaces(false).get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -375,7 +375,7 @@ void testEqualsIgnoreSurroundingSpaces_Deprecated() { .withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withIgnoreSurroundingSpaces(false); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -383,7 +383,7 @@ void testEqualsLeftNoQuoteRightQuote() { final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).get(); final CSVFormat right = left.builder().setQuote('#').get(); - assertNotEquals(left, right); + assertNotEqualsFlip(left, right); } @SuppressWarnings("deprecation") @@ -392,7 +392,7 @@ void testEqualsLeftNoQuoteRightQuote_Deprecated() { final CSVFormat left = CSVFormat.newFormat(',').withQuote(null); final CSVFormat right = left.withQuote('#'); - assertNotEquals(left, right); + assertNotEqualsFlip(left, right); } @Test @@ -418,7 +418,7 @@ void testEqualsNullString() { .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).setNullString("null").get(); final CSVFormat left = right.builder().setNullString("---").get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -428,7 +428,7 @@ void testEqualsNullString_Deprecated() { .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL).withNullString("null"); final CSVFormat left = right.withNullString("---"); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -495,7 +495,7 @@ void testEqualsOne() { assertFalse(csvFormatTwo.isCommentMarkerSet()); assertNotSame(csvFormatTwo, csvFormatOne); - Assertions.assertNotEquals(csvFormatTwo, csvFormatOne); + assertNotEquals(csvFormatTwo, csvFormatOne); assertEquals('\\', (char) csvFormatOne.getEscapeCharacter()); assertNull(csvFormatOne.getQuoteMode()); @@ -554,10 +554,10 @@ void testEqualsOne() { assertNotSame(csvFormatOne, csvFormatTwo); assertNotSame(csvFormatTwo, csvFormatOne); - Assertions.assertNotEquals(csvFormatOne, csvFormatTwo); - Assertions.assertNotEquals(csvFormatTwo, csvFormatOne); + assertNotEquals(csvFormatOne, csvFormatTwo); + assertNotEquals(csvFormatTwo, csvFormatOne); - Assertions.assertNotEquals(csvFormatTwo, csvFormatOne); + assertNotEquals(csvFormatTwo, csvFormatOne); } @@ -566,7 +566,7 @@ void testEqualsQuoteChar() { final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').get(); final CSVFormat left = right.builder().setQuote('!').get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -575,7 +575,7 @@ void testEqualsQuoteChar_Deprecated() { final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"'); final CSVFormat left = right.withQuote('!'); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -583,7 +583,7 @@ 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); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -592,7 +592,7 @@ void testEqualsQuotePolicy_Deprecated() { final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"').withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withQuoteMode(QuoteMode.MINIMAL); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -601,7 +601,7 @@ void testEqualsRecordSeparator() { .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).get(); final CSVFormat left = right.builder().setRecordSeparator(LF).get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -611,7 +611,7 @@ void testEqualsRecordSeparator_Deprecated() { .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL); final CSVFormat left = right.withRecordSeparator(LF); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } void testEqualsSkipHeaderRecord() { @@ -619,7 +619,7 @@ void testEqualsSkipHeaderRecord() { .setIgnoreSurroundingSpaces(true).setQuote('"').setQuoteMode(QuoteMode.ALL).setNullString("null").setSkipHeaderRecord(true).get(); final CSVFormat left = right.builder().setSkipHeaderRecord(false).get(); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @SuppressWarnings("deprecation") @@ -629,7 +629,7 @@ void testEqualsSkipHeaderRecord_Deprecated() { .withIgnoreSurroundingSpaces().withQuote('"').withQuoteMode(QuoteMode.ALL).withNullString("null").withSkipHeaderRecord(); final CSVFormat left = right.withSkipHeaderRecord(false); - assertNotEquals(right, left); + assertNotEqualsFlip(right, left); } @Test @@ -691,7 +691,7 @@ void testEqualsWithNull() { assertNull(csvFormat.getQuoteCharacter()); assertTrue(csvFormat.isNullStringSet()); - Assertions.assertNotEquals(null, csvFormat); + assertNotEquals(null, csvFormat); } @@ -801,7 +801,7 @@ void testHashCodeAndWithIgnoreHeaderCase() { assertTrue(csvFormatTwo.getIgnoreHeaderCase()); // now different assertFalse(csvFormatTwo.getTrailingDelimiter()); - Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal + assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal assertFalse(csvFormatTwo.getAllowMissingColumnNames()); assertFalse(csvFormatTwo.getTrim()); @@ -1156,7 +1156,7 @@ void testToStringAndWithCommentMarkerTakingCharacter() { assertNotSame(csvFormat, csvFormatTwo); assertNotSame(csvFormatTwo, csvFormat); - Assertions.assertNotEquals(csvFormatTwo, csvFormat); + assertNotEquals(csvFormatTwo, csvFormat); assertNull(csvFormat.getEscapeCharacter()); assertTrue(csvFormat.isQuoteCharacterSet()); @@ -1215,9 +1215,9 @@ void testToStringAndWithCommentMarkerTakingCharacter() { assertNotSame(csvFormat, csvFormatTwo); assertNotSame(csvFormatTwo, csvFormat); - Assertions.assertNotEquals(csvFormat, csvFormatTwo); + assertNotEquals(csvFormat, csvFormatTwo); - Assertions.assertNotEquals(csvFormatTwo, csvFormat); + assertNotEquals(csvFormatTwo, csvFormat); assertEquals("Delimiter=<,> QuoteChar=<\"> CommentStart= RecordSeparator=<\r\n> EmptyLines:ignored SkipHeaderRecord:false", csvFormatTwo.toString()); @@ -1403,7 +1403,7 @@ void testWithHeaderComments() { assertNotSame(csvFormat, csvFormatTwo); assertNotSame(csvFormatTwo, csvFormat); - Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal + assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal final String string = csvFormatTwo.format(objectArray); @@ -1465,9 +1465,9 @@ void testWithHeaderComments() { assertNotSame(csvFormatTwo, csvFormat); assertNotNull(string); - Assertions.assertNotEquals(csvFormat, csvFormatTwo); // CSV-244 - should not be equal + assertNotEquals(csvFormat, csvFormatTwo); // CSV-244 - should not be equal - Assertions.assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal + assertNotEquals(csvFormatTwo, csvFormat); // CSV-244 - should not be equal assertEquals(",,,,,,,", string); } From 3ac39bbbe690adff4ddbabc59a0d3a76ae658028 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 7 Apr 2026 12:39:35 -0400 Subject: [PATCH 23/98] Bump org.apache.commons:commons-parent from 97 to 98 --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 07f030ae3..1c0fd5b29 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.apache.commons commons-parent - 97 + 98 commons-csv 1.14.2-SNAPSHOT diff --git a/src/changes/changes.xml b/src/changes/changes.xml index cadb261cf..56b8a796e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -49,7 +49,7 @@ Add an "Android Compatibility" section to the web site. - Bump org.apache.commons:commons-parent from 85 to 97 #573, #595. + Bump org.apache.commons:commons-parent from 85 to 98 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.21.0. From 5093022c32c8158b4c40d5280ed959b788b0667c Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 14 Apr 2026 07:48:43 -0400 Subject: [PATCH 24/98] Bump actions/cache from 5.0.4 to 5.0.5 --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/maven.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7d3074419..61abbca77 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index ebe4bc3b9..10c18b607 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -46,7 +46,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 #v5.0.4 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From d92359b69e035f1fb3fbbc0cfff9fd6942c19dc6 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 14 Apr 2026 07:54:51 -0400 Subject: [PATCH 25/98] Bump actions/upload-artifact from 7.0.0 to 7.0.1 --- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index a0e5b9faf..4ad7175c8 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -57,7 +57,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file path: results.sarif From 636a5036db6c4f2d6347e8e6141800930d24049e Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 16 Apr 2026 07:10:20 -0400 Subject: [PATCH 26/98] Bump github/codeql-action from 4.35.1 to 4.35.2 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 61abbca77..b7b7fdf05 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 4ad7175c8..16e37f605 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: sarif_file: results.sarif From f0f184853b765f377a8c6c9685c5b1bb5fbee351 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 23 Apr 2026 06:47:55 -0400 Subject: [PATCH 27/98] Bump commons-io:commons-io from 2.21.0 to 2.22.0. --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1c0fd5b29..0485c80c3 100644 --- a/pom.xml +++ b/pom.xml @@ -109,7 +109,7 @@ true 2025-07-30T14:51:35Z 1.21.0 - 2.21.0 + 2.22.0 org.apache.commons.codec.binary;version="${commons.codec.version}", diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 56b8a796e..de03faab0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -53,7 +53,7 @@ [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.21.0. - Bump commons-io:commons-io from 2.20.0 to 2.21.0 #594. + Bump commons-io:commons-io from 2.20.0 to 2.22.0 #594. From 4571bc0bc4bbc83bfb6f8b7594a9bd36b1ce5a93 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 24 Apr 2026 20:40:41 -0400 Subject: [PATCH 28/98] Bump commons-codec:commons-codec from 1.21.0 to 1.22.0. --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0485c80c3..6d9a4ef20 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ false true 2025-07-30T14:51:35Z - 1.21.0 + 1.22.0 2.22.0 diff --git a/src/changes/changes.xml b/src/changes/changes.xml index de03faab0..f8229eb75 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -52,7 +52,7 @@ Bump org.apache.commons:commons-parent from 85 to 98 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. - Bump commons-codec:commons-codec from 1.19.0 to 1.21.0. + Bump commons-codec:commons-codec from 1.19.0 to 1.22.0. Bump commons-io:commons-io from 2.20.0 to 2.22.0 #594. From b6ab627e062ed80e65bcebe299f779717fca2e8a Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sun, 26 Apr 2026 07:36:22 -0400 Subject: [PATCH 29/98] Bump org.apache.commons:commons-parent from 98 to 99 --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6d9a4ef20..7e796568e 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.apache.commons commons-parent - 98 + 99 commons-csv 1.14.2-SNAPSHOT diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f8229eb75..aacd40586 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -49,7 +49,7 @@ Add an "Android Compatibility" section to the web site. - Bump org.apache.commons:commons-parent from 85 to 98 #573, #595. + Bump org.apache.commons:commons-parent from 85 to 99 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.22.0. From e1ffa42ebef4907d02929b210852f466aa16d204 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 5 May 2026 11:23:19 -0400 Subject: [PATCH 30/98] Bump github/codeql-action from 4.35.2 to 4.35.3 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b7b7fdf05..3571ff3f4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/autobuild@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 16e37f605..95dbcd027 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: sarif_file: results.sarif From 6f93c7edfa0f758f757227b1d30588411fdbf669 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 12 May 2026 18:49:17 +0000 Subject: [PATCH 31/98] Bump github/codeql-action from 4.35.3 to 4.35.4 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3571ff3f4..e597a6dbb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 + uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 + uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 + uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 95dbcd027..2eed26f73 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: results.sarif From 8b041dba39eba4975ddd884ea813b6334d39c8de Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 06:43:45 -0400 Subject: [PATCH 32/98] Bump github/codeql-action from 4.35.4 to 4.35.5 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e597a6dbb..f95d030d8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 2eed26f73..79c088cf3 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 with: sarif_file: results.sarif From 5e2d0590ca74424d5a51defef489e374abfb921a Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 07:53:21 -0400 Subject: [PATCH 33/98] [CSV-321] CSVFormat.equals()/hashCode() ignores maxRows --- .../java/org/apache/commons/csv/CSVFormat.java | 15 ++++++++------- .../org/apache/commons/csv/CSVFormatTest.java | 8 ++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 09e76e3a6..123fa2635 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -1690,11 +1690,11 @@ public boolean equals(final Object obj) { duplicateHeaderMode == other.duplicateHeaderMode && Objects.equals(escapeCharacter, other.escapeCharacter) && Arrays.equals(headerComments, other.headerComments) && Arrays.equals(headers, other.headers) && ignoreEmptyLines == other.ignoreEmptyLines && ignoreHeaderCase == other.ignoreHeaderCase && - ignoreSurroundingSpaces == other.ignoreSurroundingSpaces && lenientEof == other.lenientEof && - Objects.equals(nullString, other.nullString) && Objects.equals(quoteCharacter, other.quoteCharacter) && - quoteMode == other.quoteMode && Objects.equals(quotedNullString, other.quotedNullString) && - Objects.equals(recordSeparator, other.recordSeparator) && skipHeaderRecord == other.skipHeaderRecord && - trailingData == other.trailingData && trailingDelimiter == other.trailingDelimiter && trim == other.trim; + ignoreSurroundingSpaces == other.ignoreSurroundingSpaces && lenientEof == other.lenientEof && maxRows == other.maxRows && + Objects.equals(nullString, other.nullString) && Objects.equals(quoteCharacter, other.quoteCharacter) && quoteMode == other.quoteMode && + Objects.equals(quotedNullString, other.quotedNullString) && Objects.equals(recordSeparator, other.recordSeparator) && + skipHeaderRecord == other.skipHeaderRecord && trailingData == other.trailingData && trailingDelimiter == other.trailingDelimiter && + trim == other.trim; } private void escape(final char c, final Appendable appendable) throws IOException { @@ -2029,9 +2029,10 @@ public int hashCode() { int result = 1; result = prime * result + Arrays.hashCode(headerComments); result = prime * result + Arrays.hashCode(headers); - return prime * result + Objects.hash(allowMissingColumnNames, autoFlush, commentMarker, delimiter, duplicateHeaderMode, escapeCharacter, - ignoreEmptyLines, ignoreHeaderCase, ignoreSurroundingSpaces, lenientEof, nullString, quoteCharacter, quoteMode, quotedNullString, + result = prime * result + Objects.hash(allowMissingColumnNames, autoFlush, commentMarker, delimiter, duplicateHeaderMode, escapeCharacter, + ignoreEmptyLines, ignoreHeaderCase, ignoreSurroundingSpaces, lenientEof, maxRows, nullString, quoteCharacter, quoteMode, quotedNullString, recordSeparator, skipHeaderRecord, trailingData, trailingDelimiter, trim); + return result; } /** diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index 4d428b465..23bc9c8dc 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -395,6 +395,14 @@ void testEqualsLeftNoQuoteRightQuote_Deprecated() { assertNotEqualsFlip(left, right); } + @Test + void testEqualsMaxRows() { + final CSVFormat right = CSVFormat.DEFAULT.builder().setMaxRows(10).get(); + final CSVFormat left = CSVFormat.DEFAULT.builder().setMaxRows(1000).get(); + assertNotEqualsFlip(right, left); + assertNotEquals(right.hashCode(), left.hashCode()); + } + @Test void testEqualsNoQuotes() { final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).get(); From 69248ba0bed03c71454b71ec0c79805f48f5c447 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 07:58:48 -0400 Subject: [PATCH 34/98] Bump org.apache.commons:commons-parent from 99 to 100 --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7e796568e..f0578c820 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.apache.commons commons-parent - 99 + 100 commons-csv 1.14.2-SNAPSHOT diff --git a/src/changes/changes.xml b/src/changes/changes.xml index aacd40586..34e8f8aa2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -49,7 +49,7 @@ Add an "Android Compatibility" section to the web site. - Bump org.apache.commons:commons-parent from 85 to 99 #573, #595. + Bump org.apache.commons:commons-parent from 85 to 100 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.22.0. From add38c3e06d1b90ebcb87ad4390a000b09850df1 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 08:05:51 -0400 Subject: [PATCH 35/98] [CSV-321] CSVFormat.equals()/hashCode() ignores maxRows --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 34e8f8aa2..2bbb0946d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -46,6 +46,7 @@ Remove broken website link #577. Fix Apache RAT plugin console warnings. [Javadoc] Clarify behavior of deprecated CSVFormat#withFirstRecordAsHeader() #2413. + CSVFormat.equals()/hashCode() ignores maxRows (#600). Add an "Android Compatibility" section to the web site. From 23eb602d25a9cf0c989085e9a618dd2b3bff5b45 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 10:42:03 -0400 Subject: [PATCH 36/98] [CSV-323] ExtendedBufferedReader byte tracking leads to an incorrect CSVRecord.getBytePosition() --- .../commons/csv/ExtendedBufferedReader.java | 76 ++++++++++--------- .../org/apache/commons/csv/CSVParserTest.java | 18 +++++ 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java index 8dcda6517..889b58edc 100644 --- a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java +++ b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java @@ -37,26 +37,30 @@ /** * A special buffered reader which supports sophisticated read access. *

- * In particular the reader supports a look-ahead option, which allows you to see the next char returned by - * {@link #read()}. This reader also tracks how many characters have been read with {@link #getPosition()}. + * In particular the reader supports a look-ahead option, which allows you to see the next char returned by {@link #read()}. This reader also tracks how many + * characters have been read with {@link #getPosition()}. *

*/ final class ExtendedBufferedReader extends UnsynchronizedBufferedReader { /** The last char returned */ private int lastChar = UNDEFINED; + private int lastCharMark = UNDEFINED; /** The count of EOLs (CR/LF/CRLF) seen so far */ private long lineNumber; + private long lineNumberMark; /** The position, which is the number of characters read so far */ private long position; + private long positionMark; /** The number of bytes read so far. */ private long bytesRead; + private long bytesReadMark; /** Encoder for calculating the number of bytes for each character read. */ @@ -70,12 +74,11 @@ final class ExtendedBufferedReader extends UnsynchronizedBufferedReader { } /** - * Constructs a new instance with the specified reader, character set, - * and byte tracking option. Initializes an encoder if byte tracking is enabled - * and a character set is provided. + * Constructs a new instance with the specified reader, character set, and byte tracking option. Initializes an encoder if byte tracking is enabled and a + * character set is provided. * - * @param reader the reader supports a look-ahead option. - * @param charset the character set for encoding, or {@code null} if not applicable. + * @param reader the reader supports a look-ahead option. + * @param charset the character set for encoding, or {@code null} if not applicable. * @param trackBytes {@code true} to enable byte tracking; {@code false} to disable it. */ ExtendedBufferedReader(final Reader reader, final Charset charset, final boolean trackBytes) { @@ -86,8 +89,7 @@ final class ExtendedBufferedReader extends UnsynchronizedBufferedReader { /** * Closes the stream. * - * @throws IOException - * If an I/O error occurs + * @throws IOException If an I/O error occurs */ @Override public void close() throws IOException { @@ -105,26 +107,33 @@ long getBytesRead() { return this.bytesRead; } + private long getEncodedCharLength(final char[] buf, final int offset, final int length) throws CharacterCodingException { + int len = 0; + for (int i = offset; i < length; i++) { + len += getEncodedCharLength(buf[i]); + } + return len; + } + /** - * Gets the byte length of the given character based on the original Unicode - * specification, which defined characters as fixed-width 16-bit entities. + * Gets the byte length of the given character based on the original Unicode specification, which defined characters as fixed-width 16-bit entities. *

* The Unicode characters are divided into two main ranges: *

    - *
  • U+0000 to U+FFFF (Basic Multilingual Plane, BMP): - *
      - *
    • Represented using a single 16-bit {@code char}.
    • - *
    • Includes UTF-8 encodings of 1-byte, 2-byte, and some 3-byte characters.
    • - *
    - *
  • - *
  • U+10000 to U+10FFFF (Supplementary Characters): - *
      - *
    • Represented as a pair of {@code char}s:
    • - *
    • The first {@code char} is from the high-surrogates range (\uD800-\uDBFF).
    • - *
    • The second {@code char} is from the low-surrogates range (\uDC00-\uDFFF).
    • - *
    • Includes UTF-8 encodings of some 3-byte characters and all 4-byte characters.
    • - *
    - *
  • + *
  • U+0000 to U+FFFF (Basic Multilingual Plane, BMP): + *
      + *
    • Represented using a single 16-bit {@code char}.
    • + *
    • Includes UTF-8 encodings of 1-byte, 2-byte, and some 3-byte characters.
    • + *
    + *
  • + *
  • U+10000 to U+10FFFF (Supplementary Characters): + *
      + *
    • Represented as a pair of {@code char}s:
    • + *
    • The first {@code char} is from the high-surrogates range (\uD800-\uDBFF).
    • + *
    • The second {@code char} is from the low-surrogates range (\uDC00-\uDFFF).
    • + *
    • Includes UTF-8 encodings of some 3-byte characters and all 4-byte characters.
    • + *
    + *
  • *
* * @param current the current character to process. @@ -148,10 +157,9 @@ private int getEncodedCharLength(final int current) throws CharacterCodingExcept } /** - * Returns the last character that was read as an integer (0 to 65535). This will be the last character returned by - * any of the read methods. This will not include a character read using the {@link #peek()} method. If no - * character has been read then this will return {@link Constants#UNDEFINED}. If the end of the stream was reached - * on the last read then this will return {@link IOUtils#EOF}. + * Returns the last character that was read as an integer (0 to 65535). This will be the last character returned by any of the read methods. This will not + * include a character read using the {@link #peek()} method. If no character has been read then this will return {@link Constants#UNDEFINED}. If the end of + * the stream was reached on the last read then this will return {@link IOUtils#EOF}. * * @return the last character that was read */ @@ -193,8 +201,7 @@ public void mark(final int readAheadLimit) throws IOException { @Override public int read() throws IOException { final int current = super.read(); - if (current == CR || current == LF && lastChar != CR || - current == EOF && lastChar != CR && lastChar != LF && lastChar != EOF) { + if (current == CR || current == LF && lastChar != CR || current == EOF && lastChar != CR && lastChar != LF && lastChar != EOF) { lineNumber++; } if (encoder != null) { @@ -226,13 +233,15 @@ public int read(final char[] buf, final int offset, final int length) throws IOE } else if (len == EOF) { lastChar = EOF; } + if (encoder != null) { + this.bytesRead += getEncodedCharLength(buf, offset, len); + } position += len; return len; } /** - * Gets the next line, dropping the line terminator(s). This method should only be called when processing a - * comment, otherwise, information can be lost. + * Gets the next line, dropping the line terminator(s). This method should only be called when processing a comment, otherwise, information can be lost. *

* Increments {@link #lineNumber} and updates {@link #position}. *

@@ -272,5 +281,4 @@ public void reset() throws IOException { bytesRead = bytesReadMark; super.reset(); } - } diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index d9dd4e545..e18eee026 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -648,6 +648,24 @@ void testForEach() throws Exception { } } + @Test + void testGetBytePositionMultiCharacterDelimiter() throws IOException { + final String code = "aa[|]bb\ncc[|]dd\n"; + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + try (CSVParser parser = CSVParser.builder() + .setReader(new StringReader(code)) + .setFormat(format) + .setCharset(StandardCharsets.UTF_8) + .setTrackBytes(true) + .get()) { + final Iterator it = parser.iterator(); + final CSVRecord first = it.next(); + final CSVRecord second = it.next(); + assertEquals(0, first.getBytePosition()); + assertEquals(8, second.getBytePosition()); + } + } + @Test void testGetHeaderComment_HeaderComment1() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) { From 7cf896b0f3da94d77ba7c4d9e76309ef0ef1273b Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 11:49:48 -0400 Subject: [PATCH 37/98] [CSV-323] ExtendedBufferedReader byte tracking leads to an incorrect CSVRecord.getBytePosition() --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2bbb0946d..be2d55598 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -47,6 +47,7 @@ Fix Apache RAT plugin console warnings. [Javadoc] Clarify behavior of deprecated CSVFormat#withFirstRecordAsHeader() #2413. CSVFormat.equals()/hashCode() ignores maxRows (#600). + ExtendedBufferedReader byte tracking leads to an incorrect CSVRecord.getBytePosition() (#601). Add an "Android Compatibility" section to the web site. From 4a28ba8254ce1af6985e3dcd95c753ef1c7165ea Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 12:05:44 -0400 Subject: [PATCH 38/98] [CSV-322] CSVFormat.Builder.setQuote() does not refresh quotedNullString --- .../org/apache/commons/csv/CSVFormat.java | 2 ++ .../org/apache/commons/csv/CSVFormatTest.java | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 123fa2635..46d9d0931 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -806,6 +806,8 @@ public Builder setQuote(final Character quoteCharacter) { throw new IllegalArgumentException("The quoteCharacter cannot be a line break"); } this.quoteCharacter = quoteCharacter; + final Character quote = quoteCharacter != null ? quoteCharacter : Constants.DOUBLE_QUOTE_CHAR; + this.quotedNullString = quote + nullString + quote; return this; } diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index 23bc9c8dc..ca18754f7 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -1001,6 +1001,30 @@ void testQuoteCharSameAsDelimiterThrowsException_Deprecated() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withDelimiter('!')); } + @Test + void testQuotedNullStringTracksQuoteCharacter() throws IOException { + final StringBuilder out = new StringBuilder(); + // @formatter:off + final Builder builder = CSVFormat.DEFAULT.builder(); + final CSVFormat format = builder + .setQuoteMode(QuoteMode.ALL) + .setNullString("NULL") + .get(); + // @formatter:on + format.print(null, out, true); + assertEquals("\"NULL\"", out.toString()); + // set + out.setLength(0); + builder.setQuote('\''); + builder.get().print(null, out, true); + assertEquals("'NULL'", out.toString()); + // reset + out.setLength(0); + builder.setQuote((Character) null); + builder.get().print(null, out, true); + assertEquals("\"NULL\"", out.toString()); + } + @Test void testQuoteModeNoneShouldReturnMeaningfulExceptionMessage() { final Exception exception = assertThrows(IllegalArgumentException.class, () -> From 4c67ed89df41d4a1eb2795c1cdd4fda9ed6edb9a Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 14:34:32 -0400 Subject: [PATCH 39/98] [CSV-322] CSVFormat.Builder.setQuote() does not refresh quotedNullString #2447 --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index be2d55598..a86ffcf88 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -48,6 +48,7 @@ [Javadoc] Clarify behavior of deprecated CSVFormat#withFirstRecordAsHeader() #2413. CSVFormat.equals()/hashCode() ignores maxRows (#600). ExtendedBufferedReader byte tracking leads to an incorrect CSVRecord.getBytePosition() (#601). + [CSV-322] CSVFormat.Builder.setQuote() does not refresh quotedNullString (#2447). Add an "Android Compatibility" section to the web site. From f25612fff9fc55e40d2e9a1c1ab37ee69dd0588e Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 20 May 2026 16:23:26 -0400 Subject: [PATCH 40/98] Internal parser ctor refactoring . --- .../org/apache/commons/csv/CSVParser.java | 53 +++++++------------ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index bce62ea54..5351f8726 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -165,10 +165,9 @@ protected Builder() { // empty } - @SuppressWarnings("resource") @Override public CSVParser get() throws IOException { - return new CSVParser(getReader(), format != null ? format : CSVFormat.DEFAULT, characterOffset, recordNumber, getCharset(), trackBytes); + return new CSVParser(this); } /** @@ -524,46 +523,30 @@ public CSVParser(final Reader reader, final CSVFormat format) throws IOException */ @Deprecated public CSVParser(final Reader reader, final CSVFormat format, final long characterOffset, final long recordNumber) throws IOException { - this(reader, format, characterOffset, recordNumber, null, false); + // @formatter:off + this(builder() + .setReader(reader) + .setFormat(Objects.requireNonNull(format, "format")) // requireNonNull for full compatibility + .setCharacterOffset(characterOffset) + .setRecordNumber(recordNumber) + .setCharset((Charset) null).setTrackBytes(false)); + // @formatter:off } /** - * Constructs a new instance using the given {@link CSVFormat}. - * - *

- * If you do not read all records from the given {@code reader}, you should call {@link #close()} on the parser, - * unless you close the {@code reader}. - *

+ * Constructs a new instance from a builder. * - * @param reader - * a Reader containing CSV-formatted input. Must not be null. - * @param format - * the CSVFormat used for CSV parsing. Must not be null. - * @param characterOffset - * Lexer offset when the parser does not start parsing at the beginning of the source. - * @param recordNumber - * The next record number to assign. - * @param charset - * The character encoding to be used for the reader when enableByteTracking is true. - * @param trackBytes - * {@code true} to enable byte tracking for the parser; {@code false} to disable it. - * @throws IllegalArgumentException - * If the parameters of the format are inconsistent or if either the reader or format is null. - * @throws IOException - * If there is a problem reading the header or skipping the first record. - * @throws CSVException Thrown on invalid CSV input data. + * @param builder The source builder. + * @throws IOException if an I/O error occurs. */ - @SuppressWarnings("resource") // reader is managed by lexer. - private CSVParser(final Reader reader, final CSVFormat format, final long characterOffset, final long recordNumber, final Charset charset, - final boolean trackBytes) throws IOException { - Objects.requireNonNull(reader, "reader"); - Objects.requireNonNull(format, "format"); - this.format = format.copy(); - this.lexer = new Lexer(format, new ExtendedBufferedReader(reader, charset, trackBytes)); + @SuppressWarnings("resource") // Lexer manages ExtendedBufferedReader. + private CSVParser(final Builder builder) throws IOException { + this.format = (builder.format != null ? builder.format : CSVFormat.DEFAULT).copy(); + this.lexer = new Lexer(format, new ExtendedBufferedReader(builder.getReader(), builder.getCharset(), builder.trackBytes)); this.csvRecordIterator = new CSVRecordIterator(); this.headers = createHeaders(); - this.characterOffset = characterOffset; - this.recordNumber = recordNumber - 1; + this.characterOffset = builder.characterOffset; + this.recordNumber = builder.recordNumber - 1; } private void addRecordValue(final boolean lastRecord) { From c7c54e12ef5b0aa5a3bfef85498081eff9e899a5 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 21 May 2026 17:11:43 -0400 Subject: [PATCH 41/98] Javadoc --- .../java/org/apache/commons/csv/Lexer.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index 3d00fe0bf..e83f5465f 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -68,8 +68,8 @@ final class Lexer implements Closeable { /** * Appends the next escaped character to the token's content. * - * @param token the current token - * @throws IOException on stream access error + * @param token the current token. + * @throws IOException on stream access error. * @throws CSVException Thrown on invalid input. */ private void appendNextEscapedCharacterToToken(final Token token) throws IOException { @@ -89,7 +89,7 @@ private void appendNextEscapedCharacterToToken(final Token token) throws IOExcep * Closes resources. * * @throws IOException - * If an I/O error occurs + * If an I/O error occurs. */ @Override public void close() throws IOException { @@ -97,27 +97,27 @@ public void close() throws IOException { } /** - * Gets the number of bytes read + * Gets the number of bytes read. * - * @return the number of bytes read + * @return the number of bytes read. */ long getBytesRead() { return reader.getBytesRead(); } /** - * Returns the current character position + * Gets the current character position. * - * @return the current character position + * @return the current character position. */ long getCharacterPosition() { return reader.getPosition(); } /** - * Returns the current line number + * Gets the current line number. * - * @return the current line number + * @return the current line number. */ long getCurrentLineNumber() { return reader.getLineNumber(); @@ -136,7 +136,7 @@ boolean isCommentStart(final int ch) { } /** - * Determine whether the next characters constitute a delimiter through {@link ExtendedBufferedReader#peek(char[])}. + * Tests whether the next characters constitute a delimiter through {@link ExtendedBufferedReader#peek(char[])}. * * @param ch * the current character. @@ -214,7 +214,7 @@ boolean isQuoteChar(final int ch) { /** * Tests if the current character represents the start of a line: a CR, LF, or is at the start of the file. * - * @param ch the character to check + * @param ch the character to check. * @return true if the character is at the start of a line. */ boolean isStartOfLine(final int ch) { @@ -400,10 +400,10 @@ private Token parseEncapsulatedToken(final Token token) throws IOException { *
  • An unescaped delimiter has been reached (TOKEN)
  • * * - * @param token the current token - * @param ch the current character - * @return the filled token - * @throws IOException on stream access error + * @param token the current token. + * @param ch the current character. + * @return the filled token. + * @throws IOException on stream access error. * @throws CSVException Thrown on invalid input. */ private Token parseSimpleToken(final Token token, final int ch) throws IOException { @@ -442,7 +442,7 @@ private Token parseSimpleToken(final Token token, final int ch) throws IOExcepti /** * Greedily accepts \n, \r and \r\n This checker consumes silently the second control-character... * - * @return true if the given or next character is a line-terminator + * @return true if the given or next character is a line-terminator. */ boolean readEndOfLine(final int ch) throws IOException { // check if we have \r\n... From b29fb40acd3ecc09bb4f43d20f88ad5ee9fa8324 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 21 May 2026 17:35:06 -0400 Subject: [PATCH 42/98] Lexer.isDelimiter() accepts a partial multi-character delimiter at EOF. --- src/main/java/org/apache/commons/csv/Lexer.java | 2 ++ .../java/org/apache/commons/csv/CSVParserTest.java | 14 ++++++++++++++ .../java/org/apache/commons/csv/LexerTest.java | 12 ++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index 3d00fe0bf..e0b96b079 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -23,6 +23,7 @@ import java.io.Closeable; import java.io.IOException; +import java.util.Arrays; import org.apache.commons.io.IOUtils; @@ -272,6 +273,7 @@ Token nextToken(final Token token) throws IOException { token.type = Token.Type.COMMENT; return token; } + Arrays.fill(delimiterBuf, '\0'); // Important: make sure a new char gets consumed in each iteration while (token.type == Token.Type.INVALID) { // ignore whitespaces at beginning of a token diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index e18eee026..d7bc07e99 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1621,6 +1621,20 @@ void testParsingPrintedEmptyFirstColumn(final CSVFormat.Predefined format) throw } } + /** + * Tests CSV-324. + */ + @Test + void testPartialMultiCharacterDelimiterAtEOF() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + try (CSVParser parser = format.parse(new StringReader("a[|]b[|"))) { + final CSVRecord record = parser.nextRecord(); + assertEquals("a", record.get(0)); + assertEquals("b[|", record.get(1)); + assertEquals(2, record.size()); + } + } + @Test void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index e54e93365..511876a28 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -409,6 +409,18 @@ void testNextToken6() throws IOException { } } + /** + * Tests CSV-324. + */ + @Test + void testPartialMultiCharacterDelimiterAtEOF() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + try (Lexer lexer = createLexer("a[|]b[|", format)) { + assertNextToken(TOKEN, "a", lexer); + assertNextToken(EOF, "b[|", lexer); + } + } + @Test void testReadEscapeBackspace() throws IOException { try (Lexer lexer = createLexer("b", CSVFormat.DEFAULT.withEscape('\b'))) { From f11278e603d7641fe46c81b5dc88bd797b725114 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 21 May 2026 17:56:23 -0400 Subject: [PATCH 43/98] [CSV-324] Lexer.isDelimiter() accepts a partial multi-character delimiter at EOF. #603 --- src/changes/changes.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index a86ffcf88..f72472eeb 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -48,7 +48,8 @@ [Javadoc] Clarify behavior of deprecated CSVFormat#withFirstRecordAsHeader() #2413. CSVFormat.equals()/hashCode() ignores maxRows (#600). ExtendedBufferedReader byte tracking leads to an incorrect CSVRecord.getBytePosition() (#601). - [CSV-322] CSVFormat.Builder.setQuote() does not refresh quotedNullString (#2447). + CSVFormat.Builder.setQuote() does not refresh quotedNullString (#2447). + Lexer.isDelimiter() accepts a partial multi-character delimiter at EOF (#603). Add an "Android Compatibility" section to the web site. From 64ea660f8131d3257c2c312c8396fc5b9044a628 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 21 May 2026 20:45:12 -0400 Subject: [PATCH 44/98] Sort members --- .../org/apache/commons/csv/CSVParser.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 5351f8726..97c65a9b3 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -473,6 +473,22 @@ public static CSVParser parse(final URL url, final Charset charset, final CSVFor private final Token reusableToken = new Token(); + /** + * Constructs a new instance from a builder. + * + * @param builder The source builder. + * @throws IOException if an I/O error occurs. + */ + @SuppressWarnings("resource") // Lexer manages ExtendedBufferedReader. + private CSVParser(final Builder builder) throws IOException { + this.format = (builder.format != null ? builder.format : CSVFormat.DEFAULT).copy(); + this.lexer = new Lexer(format, new ExtendedBufferedReader(builder.getReader(), builder.getCharset(), builder.trackBytes)); + this.csvRecordIterator = new CSVRecordIterator(); + this.headers = createHeaders(); + this.characterOffset = builder.characterOffset; + this.recordNumber = builder.recordNumber - 1; + } + /** * Constructs a new instance using the given {@link CSVFormat}. * @@ -533,22 +549,6 @@ public CSVParser(final Reader reader, final CSVFormat format, final long charact // @formatter:off } - /** - * Constructs a new instance from a builder. - * - * @param builder The source builder. - * @throws IOException if an I/O error occurs. - */ - @SuppressWarnings("resource") // Lexer manages ExtendedBufferedReader. - private CSVParser(final Builder builder) throws IOException { - this.format = (builder.format != null ? builder.format : CSVFormat.DEFAULT).copy(); - this.lexer = new Lexer(format, new ExtendedBufferedReader(builder.getReader(), builder.getCharset(), builder.trackBytes)); - this.csvRecordIterator = new CSVRecordIterator(); - this.headers = createHeaders(); - this.characterOffset = builder.characterOffset; - this.recordNumber = builder.recordNumber - 1; - } - private void addRecordValue(final boolean lastRecord) { final String input = format.trim(reusableToken.content.toString()); if (lastRecord && input.isEmpty() && format.getTrailingDelimiter()) { From 513aac23983337ddd2dddb266e43a5eff145957e Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 22 May 2026 07:24:53 -0400 Subject: [PATCH 45/98] [CSV-325] CSVParser applies characterOffset to bytePosition, which breaks getBytePosition() for multi-byte prefixes Add CSVParser.Builder.setByteOffset(long) --- .../org/apache/commons/csv/CSVParser.java | 31 +++++++++++++++++-- .../org/apache/commons/csv/CSVParserTest.java | 30 ++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 97c65a9b3..208f5a0da 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -154,6 +154,7 @@ public final class CSVParser implements Iterable, Closeable { public static class Builder extends AbstractStreamBuilder { private CSVFormat format; + private long byteOffset = -1; private long characterOffset; private long recordNumber = 1; private boolean trackBytes; @@ -171,10 +172,27 @@ public CSVParser get() throws IOException { } /** - * Sets the lexer offset when the parser does not start parsing at the beginning of the source. + * Sets the lexer byte offset when the parser does not start parsing at the beginning of the source. + *

    + * By default, the value is {@code -1}, which reuses the character offset for the byte offset. + *

    * - * @param characterOffset the lexer offset. + * @param byteOffset the lexer byte offset. * @return {@code this} instance. + * @see #setCharacterOffset(long) + * @since 1.15.0 + */ + public Builder setByteOffset(final long byteOffset) { + this.byteOffset = byteOffset; + return asThis(); + } + + /** + * Sets the lexer character offset when the parser does not start parsing at the beginning of the source. + * + * @param characterOffset the lexer character offset. + * @return {@code this} instance. + * @see #setByteOffset(long) */ public Builder setCharacterOffset(final long characterOffset) { this.characterOffset = characterOffset; @@ -465,6 +483,12 @@ public static CSVParser parse(final URL url, final Charset charset, final CSVFor */ private long recordNumber; + /** + * Lexer offset when the parser does not start parsing at the beginning of the source. Usually used in combination + * with {@link #recordNumber}. + */ + private final long byteOffset; + /** * Lexer offset when the parser does not start parsing at the beginning of the source. Usually used in combination * with {@link #recordNumber}. @@ -485,6 +509,7 @@ private CSVParser(final Builder builder) throws IOException { this.lexer = new Lexer(format, new ExtendedBufferedReader(builder.getReader(), builder.getCharset(), builder.trackBytes)); this.csvRecordIterator = new CSVRecordIterator(); this.headers = createHeaders(); + this.byteOffset = builder.byteOffset != -1 ? builder.byteOffset : builder.characterOffset; this.characterOffset = builder.characterOffset; this.recordNumber = builder.recordNumber - 1; } @@ -870,7 +895,7 @@ CSVRecord nextRecord() throws IOException { recordList.clear(); StringBuilder sb = null; final long startCharPosition = lexer.getCharacterPosition() + characterOffset; - final long startBytePosition = lexer.getBytesRead() + characterOffset; + final long startBytePosition = lexer.getBytesRead() + byteOffset; do { reusableToken.reset(); lexer.nextToken(reusableToken); diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index d7bc07e99..8b1527c42 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -666,6 +666,36 @@ void testGetBytePositionMultiCharacterDelimiter() throws IOException { } } + @Test + void testGetBytePositionWithCharacterOffsetAndMultiBytePrefix() throws Exception { + final String row0 = "é,x\n"; + final Charset charset = UTF_8; + // row0 char count is 4 + assertEquals(4, row0.length()); + // row0 byte count is 5 + final int record1ByteOffset = row0.getBytes(charset).length; + assertEquals(5, record1ByteOffset); + final String row1 = "b,c\n"; + final String rows = row0 + row1; + final long record1CharOffset = row0.length(); + final long expectedByteOffset = row0.getBytes(charset).length; + try (CSVParser parser = CSVParser.builder() + .setReader(new StringReader(row1)) + .setFormat(CSVFormat.DEFAULT) + .setCharset(charset) + .setTrackBytes(true) + .setByteOffset(record1ByteOffset) + .setCharacterOffset(record1CharOffset) + .setRecordNumber(2) // not relevant but a better use case example. + .get()) { + final CSVRecord record = parser.nextRecord(); + assertNotNull(record); + assertEquals(4, record.getCharacterPosition()); + assertEquals(record1CharOffset, record.getCharacterPosition()); + assertEquals(expectedByteOffset, record.getBytePosition()); + } + } + @Test void testGetHeaderComment_HeaderComment1() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) { From 971b629a7ad78fa053b23a45127d44dc7701e73b Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 22 May 2026 07:58:11 -0400 Subject: [PATCH 46/98] [CSV-325] CSVParser applies characterOffset to bytePosition, breaking getBytePosition() for multi-byte prefixes. Add CSVParser.Builder.setByteOffset(long) (#604). --- src/changes/changes.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f72472eeb..e32ba1776 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -50,8 +50,10 @@ ExtendedBufferedReader byte tracking leads to an incorrect CSVRecord.getBytePosition() (#601). CSVFormat.Builder.setQuote() does not refresh quotedNullString (#2447). Lexer.isDelimiter() accepts a partial multi-character delimiter at EOF (#603). + CSVParser applies characterOffset to bytePosition (#604). Add an "Android Compatibility" section to the web site. + Add CSVParser.Builder.setByteOffset(long) (#604). Bump org.apache.commons:commons-parent from 85 to 100 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. From df91b6edfcf1d30e4601bb6f6ec128533ec2dbff Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 22 May 2026 07:59:58 -0400 Subject: [PATCH 47/98] The next version will be 1.15.0 --- pom.xml | 6 +++--- src/changes/changes.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f0578c820..f362f8130 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 100 commons-csv - 1.14.2-SNAPSHOT + 1.15.0-SNAPSHOT Apache Commons CSV https://commons.apache.org/proper/commons-csv/ 2005 @@ -89,12 +89,12 @@ - 1.14.2 + 1.15.0 (Java 8 or above) RC1 1.14.1 - 1.14.3 + 1.15.1 csv org.apache.commons.csv CSV diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e32ba1776..664c922ed 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -40,7 +40,7 @@ Apache Commons CSV Release Notes - + Remove Spotbugs dependency and use exclude-filter instead #564. Remove broken website link #577. From 019456e09af622978bb5349168c5512dc4daac70 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 22 May 2026 08:39:20 -0400 Subject: [PATCH 48/98] Add comment --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index f362f8130..6d814251b 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,7 @@ com.h2database h2 + 2.2.224 test From ae6949807e894b62c57547236b8e805646a2a6b9 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 27 May 2026 16:51:47 -0400 Subject: [PATCH 49/98] Bump github/codeql-action from 4.35.5 to 4.36.0 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f95d030d8..a8afcbfb7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 79c088cf3..fd682a9a1 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 + uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: sarif_file: results.sarif From bbf07cbfb4c6f723105f8e82c4c805e15f96e605 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 4 Jun 2026 10:20:17 -0400 Subject: [PATCH 50/98] Bump org.apache.commons:commons-parent from 100 to 101 --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6d814251b..57bca27b2 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.apache.commons commons-parent - 100 + 101 commons-csv 1.15.0-SNAPSHOT diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 664c922ed..633de96bd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -55,7 +55,7 @@ Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). - Bump org.apache.commons:commons-parent from 85 to 100 #573, #595. + Bump org.apache.commons:commons-parent from 85 to 101 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.22.0. From 9f8c596a0cf3cd864960421f3222e73d586cadb9 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 4 Jun 2026 10:21:56 -0400 Subject: [PATCH 51/98] Add GH CI Java 26 Bump GH CI from Java 26-ea to 27-ea --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 10c18b607..a5ff694fc 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -34,12 +34,12 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - java: [ 8, 11, 17, 21, 25 ] + java: [ 8, 11, 17, 21, 25, 36 ] experimental: [false] # Keep the same parameter order as the matrix above include: - os: ubuntu-latest - java: 26-ea + java: 27-ea experimental: true steps: From 6183c6ec5f78cd726b72c24c06e1bc0afe84df45 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 4 Jun 2026 10:22:08 -0400 Subject: [PATCH 52/98] Add GH CI Java 26 Bump GH CI from Java 26-ea to 27-ea --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index a5ff694fc..f1bfba770 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -34,7 +34,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - java: [ 8, 11, 17, 21, 25, 36 ] + java: [ 8, 11, 17, 21, 25, 26 ] experimental: [false] # Keep the same parameter order as the matrix above include: From ca64eb8439b26404e2c5baa383d42ffa3436733d Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 5 Jun 2026 07:27:06 -0400 Subject: [PATCH 53/98] Better inline comments --- .../org/apache/commons/csv/CSVFormat.java | 20 +++++++++---------- .../org/apache/commons/csv/CSVParser.java | 2 +- .../org/apache/commons/csv/CSVPrinter.java | 4 ++-- .../org/apache/commons/csv/CSVRecord.java | 10 ++++------ .../org/apache/commons/csv/Constants.java | 2 +- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 46d9d0931..f6b2c5ae0 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -1479,7 +1479,7 @@ private static boolean isLineBreak(final char c) { * @return true if {@code c} is a line break character (and not null). */ private static boolean isLineBreak(final Character c) { - return c != null && isLineBreak(c.charValue()); // Explicit (un)boxing is intentional + return c != null && isLineBreak(c.charValue()); // Explicit unboxing is intentional } /** Same test as in as {@link String#trim()}. */ @@ -1700,7 +1700,7 @@ public boolean equals(final Object obj) { } private void escape(final char c, final Appendable appendable) throws IOException { - append(escapeCharacter.charValue(), appendable); // Explicit (un)boxing is intentional + append(escapeCharacter.charValue(), appendable); // Explicit unboxing is intentional append(c, appendable); } @@ -1838,7 +1838,7 @@ public DuplicateHeaderMode getDuplicateHeaderMode() { * @return the escape character, may be {@code 0} */ char getEscapeChar() { - return escapeCharacter != null ? escapeCharacter.charValue() : 0; // Explicit (un)boxing is intentional + return escapeCharacter != null ? escapeCharacter.charValue() : 0; // Explicit unboxing is intentional } /** @@ -2161,7 +2161,7 @@ private void print(final InputStream inputStream, final Appendable out, final bo } final boolean quoteCharacterSet = isQuoteCharacterSet(); if (quoteCharacterSet) { - append(getQuoteCharacter().charValue(), out); // Explicit (un)boxing is intentional + append(getQuoteCharacter().charValue(), out); // Explicit unboxing is intentional } // Stream the input to the output without reading or holding the whole value in memory. // AppendableOutputStream cannot "close" an Appendable. @@ -2169,7 +2169,7 @@ private void print(final InputStream inputStream, final Appendable out, final bo IOUtils.copy(inputStream, outputStream); } if (quoteCharacterSet) { - append(getQuoteCharacter().charValue(), out); // Explicit (un)boxing is intentional + append(getQuoteCharacter().charValue(), out); // Explicit unboxing is intentional } } @@ -2418,7 +2418,7 @@ private void printWithQuotes(final Object object, final CharSequence charSeq, fi final int len = charSeq.length(); final char[] delim = getDelimiterCharArray(); final int delimLength = delim.length; - final char quoteChar = getQuoteCharacter().charValue(); // Explicit (un)boxing is intentional + final char quoteChar = getQuoteCharacter().charValue(); // Explicit unboxing is intentional // If escape char not specified, default to the quote char // This avoids having to keep checking whether there is an escape character // at the cost of checking against quote twice @@ -2521,7 +2521,7 @@ private void printWithQuotes(final Reader reader, final Appendable appendable) t printWithEscapes(reader, appendable); return; } - final char quote = getQuoteCharacter().charValue(); // Explicit (un)boxing is intentional + final char quote = getQuoteCharacter().charValue(); // Explicit unboxing is intentional // (1) Append opening quote append(quote, appendable); // (2) Append Reader contents, doubling quotes @@ -2607,13 +2607,13 @@ boolean useRow(final long rowNum) { * @throws IllegalArgumentException Throw when any attribute is invalid or inconsistent with other attributes. */ private void validate() throws IllegalArgumentException { - if (quoteCharacter != null && contains(delimiter, quoteCharacter.charValue())) { // Explicit (un)boxing is intentional + if (quoteCharacter != null && contains(delimiter, quoteCharacter.charValue())) { // Explicit unboxing is intentional throw new IllegalArgumentException("The quoteChar character and the delimiter cannot be the same ('" + quoteCharacter + "')"); } - if (escapeCharacter != null && contains(delimiter, escapeCharacter.charValue())) { // Explicit (un)boxing is intentional + if (escapeCharacter != null && contains(delimiter, escapeCharacter.charValue())) { // Explicit unboxing is intentional throw new IllegalArgumentException("The escape character and the delimiter cannot be the same ('" + escapeCharacter + "')"); } - if (commentMarker != null && contains(delimiter, commentMarker.charValue())) { // Explicit (un)boxing is intentional + if (commentMarker != null && contains(delimiter, commentMarker.charValue())) { // Explicit unboxing is intentional throw new IllegalArgumentException("The comment start character and the delimiter cannot be the same ('" + commentMarker + "')"); } if (quoteCharacter != null && quoteCharacter.equals(commentMarker)) { diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 208f5a0da..c9b2dc44f 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -650,7 +650,7 @@ private Headers createHeaders() throws IOException { } observedMissing |= blankHeader; if (header != null) { - headerMap.put(header, Integer.valueOf(i)); // Explicit (un)boxing is intentional + headerMap.put(header, Integer.valueOf(i)); // Explicit boxing is intentional if (headerNames == null) { headerNames = new ArrayList<>(headerRecord.length); } diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java index 087129ec5..a7048fd62 100644 --- a/src/main/java/org/apache/commons/csv/CSVPrinter.java +++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java @@ -235,7 +235,7 @@ public void printComment(final String comment) throws IOException { if (!newRecord) { println(); } - appendable.append(format.getCommentMarker().charValue()); // Explicit (un)boxing is intentional + appendable.append(format.getCommentMarker().charValue()); // Explicit unboxing is intentional appendable.append(SP); for (int i = 0; i < comment.length(); i++) { final char c = comment.charAt(i); @@ -247,7 +247,7 @@ public void printComment(final String comment) throws IOException { // falls-through: break intentionally excluded. case LF: println(); - appendable.append(format.getCommentMarker().charValue()); // Explicit (un)boxing is intentional + appendable.append(format.getCommentMarker().charValue()); // Explicit unboxing is intentional appendable.append(SP); break; default: diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index f619717d0..502bf318a 100644 --- a/src/main/java/org/apache/commons/csv/CSVRecord.java +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java @@ -132,13 +132,11 @@ public String get(final String name) { throw new IllegalArgumentException(String.format("Mapping for %s not found, expected one of %s", name, headerMap.keySet())); } try { - return values[index.intValue()]; // Explicit (un)boxing is intentional + return values[index.intValue()]; // Explicit unboxing is intentional } catch (final ArrayIndexOutOfBoundsException e) { + // Explicit boxing is intentional throw new IllegalArgumentException( - String.format("Index for header '%s' is %d but CSVRecord only has %d values!", name, index, Integer.valueOf(values.length))); // Explicit - // (un)boxing - // is - // intentional + String.format("Index for header '%s' is %d but CSVRecord only has %d values!", name, index, Integer.valueOf(values.length))); } } @@ -267,7 +265,7 @@ public boolean isSet(final int index) { * @return whether a given column is mapped and has a value. */ public boolean isSet(final String name) { - return isMapped(name) && getHeaderMapRaw().get(name).intValue() < values.length; // Explicit (un)boxing is intentional + return isMapped(name) && getHeaderMapRaw().get(name).intValue() < values.length; // Explicit unboxing is intentional } /** diff --git a/src/main/java/org/apache/commons/csv/Constants.java b/src/main/java/org/apache/commons/csv/Constants.java index 0b9476e1c..9dd276ecc 100644 --- a/src/main/java/org/apache/commons/csv/Constants.java +++ b/src/main/java/org/apache/commons/csv/Constants.java @@ -40,7 +40,7 @@ final class Constants { /** RFC 4180 defines line breaks as CRLF. */ static final String CRLF = "\r\n"; - static final Character DOUBLE_QUOTE_CHAR = Character.valueOf('"'); // Explicit (un)boxing is intentional. + static final Character DOUBLE_QUOTE_CHAR = Character.valueOf('"'); // Explicit boxing is intentional. static final String EMPTY = ""; From 8192d9d196a554d67d7d65b0a131001b9d1eb412 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 5 Jun 2026 07:28:04 -0400 Subject: [PATCH 54/98] Bump github/codeql-action from 4.36.0 to 4.36.2 --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a8afcbfb7..4e69d1942 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -58,7 +58,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -69,7 +69,7 @@ jobs: # 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@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -83,4 +83,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index fd682a9a1..bf246c140 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -64,6 +64,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: sarif_file: results.sarif From 297ae10f6a2d753862485b7c77fab746a1ae885d Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sun, 7 Jun 2026 10:09:01 -0400 Subject: [PATCH 55/98] Bump GH CI actions/checkout from 6.0.2 to 6.0.3 --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/maven.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4e69d1942..57f55cae0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index f0d8ca94e..7bf60ad9e 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -26,6 +26,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: 'Dependency Review PR' uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index f1bfba770..3ee3dec2b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -43,7 +43,7 @@ jobs: experimental: true steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 From 361056d668fb2c682fb26e848c58fc3f666a607d Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sun, 7 Jun 2026 10:12:54 -0400 Subject: [PATCH 56/98] Bump GH CI actions/dependency-review-action from 4.9.0 to 5.0.0 --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 7bf60ad9e..114f3d8a2 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -28,4 +28,4 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: 'Dependency Review PR' - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 From 68b4b0910be1b9c0beb52a39b906851f0c41c75c Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Tue, 9 Jun 2026 21:15:13 +1000 Subject: [PATCH 57/98] [CSV-326] Escape Reader values with quote and escape --- src/changes/changes.xml | 1 + .../java/org/apache/commons/csv/CSVFormat.java | 7 ++++--- .../org/apache/commons/csv/CSVFormatTest.java | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 633de96bd..a3e03e372 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -51,6 +51,7 @@ CSVFormat.Builder.setQuote() does not refresh quotedNullString (#2447). Lexer.isDelimiter() accepts a partial multi-character delimiter at EOF (#603). CSVParser applies characterOffset to bytePosition (#604). + CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index f6b2c5ae0..852a3956c 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -2522,14 +2522,15 @@ private void printWithQuotes(final Reader reader, final Appendable appendable) t return; } final char quote = getQuoteCharacter().charValue(); // Explicit unboxing is intentional + final char escape = isEscapeCharacterSet() ? getEscapeChar() : quote; // (1) Append opening quote append(quote, appendable); - // (2) Append Reader contents, doubling quotes + // (2) Append Reader contents, doubling quotes and escape characters int c; while (EOF != (c = reader.read())) { append((char) c, appendable); - if (c == quote) { - append(quote, appendable); + if (c == quote || c == escape) { + append((char) c, appendable); } } // (3) Append closing quote diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index ca18754f7..c3fdeeb77 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -966,6 +966,23 @@ void testPrintWithQuotes() throws IOException { assertEquals("\"\"\"a,b,c\r\nx,y,z\"", out.toString()); } + /** + * Tests CSV-326. + */ + @Test + void testPrintWithQuotesEscapeBeforeQuote() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder() + .setEscape('\\') + .setQuote('"') + .get(); + final String value = "\\\""; + final Appendable out = new StringBuilder(); + format.print(new StringReader(value), out, true); + try (CSVParser parser = CSVParser.parse(out.toString(), format)) { + assertEquals(value, parser.getRecords().get(0).get(0)); + } + } + @Test void testQuoteCharSameAsCommentStartThrowsException() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setCommentMarker('!').get()); From 966b38519e45c1fd85c76fbbdc47cb5bb1905238 Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Tue, 9 Jun 2026 21:25:28 +1000 Subject: [PATCH 58/98] [CSV-327] Limit parser maxRows by produced records --- src/changes/changes.xml | 1 + .../java/org/apache/commons/csv/CSVParser.java | 6 +++++- .../org/apache/commons/csv/CSVParserTest.java | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 633de96bd..44f1a6be0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -51,6 +51,7 @@ CSVFormat.Builder.setQuote() does not refresh quotedNullString (#2447). Lexer.isDelimiter() accepts a partial multi-character delimiter at EOF (#603). CSVParser applies characterOffset to bytePosition (#604). + CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index c9b2dc44f..83b60170e 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -237,6 +237,7 @@ public Builder setTrackBytes(final boolean trackBytes) { final class CSVRecordIterator implements Iterator { private CSVRecord current; + private long recordCount; /** * Gets the next record or null at the end of stream or max rows read. @@ -247,8 +248,11 @@ final class CSVRecordIterator implements Iterator { */ private CSVRecord getNextRecord() { CSVRecord record = null; - if (format.useRow(recordNumber + 1)) { + if (format.useRow(recordCount + 1)) { record = Uncheck.get(CSVParser.this::nextRecord); + if (record != null) { + recordCount++; + } } return record; } diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 8b1527c42..816c1c853 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -965,6 +965,23 @@ void testGetRecordsMaxRows(final long maxRows) throws IOException { } } + /** + * Tests CSV-327. + */ + @Test + void testGetRecordsMaxRowsWithRecordNumberOffset() throws IOException { + try (CSVParser parser = CSVParser.builder() + .setReader(new StringReader("a,b\nc,d\n")) + .setFormat(CSVFormat.DEFAULT.builder().setMaxRows(1).get()) + .setRecordNumber(2) + .get()) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertEquals(2, records.get(0).getRecordNumber()); + assertValuesEquals(new String[] { "a", "b" }, records.get(0)); + } + } + @Test void testGetRecordThreeBytesRead() throws Exception { final String code = "id,date,val5,val4\n" + From 1e3de1274636959d3cf70acbba14ae10128369a5 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Tue, 9 Jun 2026 17:37:44 +0530 Subject: [PATCH 59/98] clear escape delimiter buffer before peek in isEscapeDelimiter --- src/main/java/org/apache/commons/csv/Lexer.java | 1 + .../java/org/apache/commons/csv/CSVParserTest.java | 14 ++++++++++++++ .../java/org/apache/commons/csv/LexerTest.java | 12 ++++++++++++ 3 files changed, 27 insertions(+) diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index de97868e4..238e64cee 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -191,6 +191,7 @@ boolean isEscape(final int ch) { * @throws IOException If an I/O error occurs. */ boolean isEscapeDelimiter() throws IOException { + Arrays.fill(escapeDelimiterBuf, '\0'); reader.peek(escapeDelimiterBuf); if (escapeDelimiterBuf[0] != delimiter[0]) { return false; diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 8b1527c42..5443c5e84 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1665,6 +1665,20 @@ void testPartialMultiCharacterDelimiterAtEOF() throws IOException { } } + /** + * A truncated escaped multi-character delimiter at EOF must stay literal data and not be completed from a stale + * escape delimiter look-ahead. + */ + @Test + void testPartialEscapedMultiCharacterDelimiterAtEOF() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setEscape('!').get(); + try (CSVParser parser = format.parse(new StringReader("x![!|!]y![!|"))) { + final CSVRecord record = parser.nextRecord(); + assertEquals("x[|]y![!|", record.get(0)); + assertEquals(1, record.size()); + } + } + @Test void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index 511876a28..da60df07e 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -421,6 +421,18 @@ void testPartialMultiCharacterDelimiterAtEOF() throws IOException { } } + /** + * A truncated escaped multi-character delimiter at EOF must not be accepted by reusing the previous escape delimiter + * look-ahead in {@link Lexer#isEscapeDelimiter()}. + */ + @Test + void testPartialEscapedMultiCharacterDelimiterAtEOF() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").setEscape('!').get(); + try (Lexer lexer = createLexer("x![!|!]y![!|", format)) { + assertNextToken(EOF, "x[|]y![!|", lexer); + } + } + @Test void testReadEscapeBackspace() throws IOException { try (Lexer lexer = createLexer("b", CSVFormat.DEFAULT.withEscape('\b'))) { From 4f9a4037a2c1890154e1f077d66306ec54afbf60 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 11 Jun 2026 09:40:09 -0400 Subject: [PATCH 60/98] Bump org.apache.commons:commons-parent from 101 to 102. --- pom.xml | 2 +- src/changes/changes.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 57bca27b2..8cb13ed7c 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.apache.commons commons-parent - 101 + 102 commons-csv 1.15.0-SNAPSHOT diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 633de96bd..87d68ea2a 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -55,7 +55,7 @@ Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). - Bump org.apache.commons:commons-parent from 85 to 101 #573, #595. + Bump org.apache.commons:commons-parent from 85 to 102 #573, #595. [test] Bump com.opencsv:opencsv from 5.11.2 to 5.12.0 #558. Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0. Bump commons-codec:commons-codec from 1.19.0 to 1.22.0. From 27126657d2117afd40e8972b8a34659abc753a65 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 11 Jun 2026 14:23:37 -0400 Subject: [PATCH 61/98] Update legacy GitHub links in CONTRIBUTING.md --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb15f2518..3423e18ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,13 +48,13 @@ Getting Started --------------- + Make sure you have a [JIRA account](https://issues.apache.org/jira/). -+ Make sure you have a [GitHub account](https://github.com/signup/free). This is not essential, but makes providing patches much easier. ++ Make sure you have a [GitHub account](https://github.com/signup). This is not essential, but makes providing patches much easier. + If you're planning to implement a new feature it makes sense to discuss your changes on the [dev list](https://commons.apache.org/mail-lists.html) first. This way you can make sure you're not wasting your time on something that isn't considered to be in Apache Commons CSV's scope. + Submit a [Jira Ticket][jira] for your issue, assuming one does not already exist. + Clearly describe the issue including steps to reproduce when it is a bug. + Make sure you fill in the earliest version that you know has the issue. + Find the corresponding [repository on GitHub](https://github.com/apache/?query=commons-), -[fork](https://help.github.com/articles/fork-a-repo/) and check out your forked repository. If you don't have a GitHub account, you can still clone the Commons repository. +[fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) and check out your forked repository. If you don't have a GitHub account, you can still clone the Commons repository. Making Changes -------------- @@ -108,8 +108,8 @@ Additional Resources + [Contributing patches](https://commons.apache.org/patches.html) + [Apache Commons CSV JIRA project page][jira] + [Contributor License Agreement][cla] -+ [General GitHub documentation](https://help.github.com/) -+ [GitHub pull request documentation](https://help.github.com/articles/creating-a-pull-request/) ++ [General GitHub documentation](https://docs.github.com/) ++ [GitHub pull request documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) + [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) [cla]:https://www.apache.org/licenses/#clas From 6887303cbca84216a3324e103504d9dd91660ea8 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 11 Jun 2026 15:33:31 -0400 Subject: [PATCH 62/98] [CSV-326] Escape Reader values with quote and escape (#606). --- src/changes/changes.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8ea12d983..2475f2b9b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -53,6 +53,8 @@ CSVParser applies characterOffset to bytePosition (#604). CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. + Escape Reader values with quote and escape (#606). +. Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From 037e2e0cd161c9d4f485aae8e49879d6cf2048ab Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 11 Jun 2026 15:59:34 -0400 Subject: [PATCH 63/98] Fix typo. --- src/changes/changes.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2475f2b9b..cb4a17848 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -53,8 +53,7 @@ CSVParser applies characterOffset to bytePosition (#604). CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. - Escape Reader values with quote and escape (#606). -. + Escape Reader values with quote and escape (#606). Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From 19b29139dfdb58426bf1330567e6d6c750abe81c Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 11 Jun 2026 16:00:26 -0400 Subject: [PATCH 64/98] Clear escape delimiter buffer before peek in isEscapeDelimiter (#608). --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index cb4a17848..0786cc365 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -54,6 +54,7 @@ CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. Escape Reader values with quote and escape (#606). + Clear escape delimiter buffer before peek in isEscapeDelimiter (#608). Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From b5940a642eef0de550733887fc5b78451d4a8eed Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Sat, 13 Jun 2026 12:30:53 +0530 Subject: [PATCH 65/98] escape quote char in printWithEscapes when QuoteMode is NONE --- .../java/org/apache/commons/csv/CSVFormat.java | 8 ++++++-- .../org/apache/commons/csv/CSVPrinterTest.java | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 852a3956c..03211e689 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -2324,12 +2324,14 @@ private void printWithEscapes(final CharSequence charSeq, final Appendable appen final char[] delimArray = getDelimiterCharArray(); final int delimLength = delimArray.length; final char escape = getEscapeChar(); + final boolean quoteSet = isQuoteCharacterSet(); + final char quote = quoteSet ? getQuoteCharacter().charValue() : 0; while (pos < end) { char c = charSeq.charAt(pos); final boolean isDelimiterStart = isDelimiter(c, charSeq, pos, delimArray, delimLength); final boolean isCr = c == Constants.CR; final boolean isLf = c == Constants.LF; - if (isCr || isLf || c == escape || isDelimiterStart) { + if (isCr || isLf || c == escape || quoteSet && c == quote || isDelimiterStart) { // write out segment up until this char if (pos > start) { appendable.append(charSeq, start, pos); @@ -2368,6 +2370,8 @@ private void printWithEscapes(final Reader reader, final Appendable appendable) final char[] delimArray = getDelimiterCharArray(); final int delimLength = delimArray.length; final char escape = getEscapeChar(); + final boolean quoteSet = isQuoteCharacterSet(); + final char quote = quoteSet ? getQuoteCharacter().charValue() : 0; final StringBuilder builder = new StringBuilder(IOUtils.DEFAULT_BUFFER_SIZE); int c; final char[] lookAheadBuffer = new char[delimLength - 1]; @@ -2379,7 +2383,7 @@ private void printWithEscapes(final Reader reader, final Appendable appendable) final boolean isDelimiterStart = isDelimiter((char) c, test, pos, delimArray, delimLength); final boolean isCr = c == Constants.CR; final boolean isLf = c == Constants.LF; - if (isCr || isLf || c == escape || isDelimiterStart) { + if (isCr || isLf || c == escape || quoteSet && c == quote || isDelimiterStart) { // write out segment up until this char if (pos > start) { append(builder.substring(start, pos), appendable); diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index 1ff791010..7d1993e01 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -423,6 +423,23 @@ void testDelimeterStringQuoteNone() throws IOException { } } + @Test + void testQuoteCharEscapedWithQuoteModeNone() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setQuote('"').setEscape('?').setQuoteMode(QuoteMode.NONE).get(); + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecord("\"abc", "x\"y"); + } + assertEquals("?\"abc,x?\"y\r\n", sw.toString()); + // The emitted record must read back as the original values. + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertEquals("\"abc", records.get(0).get(0)); + assertEquals("x\"y", records.get(0).get(1)); + } + } + @Test void testDelimiterEscaped() throws IOException { final StringWriter sw = new StringWriter(); From 27a439ae0aba41221d296bca7bb5e00379bc25a8 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 13 Jun 2026 08:33:49 -0400 Subject: [PATCH 66/98] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/org/apache/commons/csv/CSVPrinterTest.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index 7d1993e01..79ce987bd 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -429,14 +429,17 @@ void testQuoteCharEscapedWithQuoteModeNone() throws IOException { final StringWriter sw = new StringWriter(); try (CSVPrinter printer = new CSVPrinter(sw, format)) { printer.printRecord("\"abc", "x\"y"); + printer.printRecord(new StringReader("\"abc"), new StringReader("x\"y")); } - assertEquals("?\"abc,x?\"y\r\n", sw.toString()); - // The emitted record must read back as the original values. + assertEquals("?\"abc,x?\"y" + RECORD_SEPARATOR + "?\"abc,x?\"y" + RECORD_SEPARATOR, sw.toString()); + // The emitted records must read back as the original values. try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { final List records = parser.getRecords(); - assertEquals(1, records.size()); - assertEquals("\"abc", records.get(0).get(0)); - assertEquals("x\"y", records.get(0).get(1)); + assertEquals(2, records.size()); + for (final CSVRecord record : records) { + assertEquals("\"abc", record.get(0)); + assertEquals("x\"y", record.get(1)); + } } } From d729b442e4bbdf6c603e3e64955d27352744cc29 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 13 Jun 2026 12:37:26 +0000 Subject: [PATCH 67/98] Sort members --- .../org/apache/commons/csv/CSVParserTest.java | 28 ++++++------- .../apache/commons/csv/CSVPrinterTest.java | 40 +++++++++---------- .../org/apache/commons/csv/LexerTest.java | 24 +++++------ 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index cccbef4e6..dca37fc5a 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1668,20 +1668,6 @@ void testParsingPrintedEmptyFirstColumn(final CSVFormat.Predefined format) throw } } - /** - * Tests CSV-324. - */ - @Test - void testPartialMultiCharacterDelimiterAtEOF() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); - try (CSVParser parser = format.parse(new StringReader("a[|]b[|"))) { - final CSVRecord record = parser.nextRecord(); - assertEquals("a", record.get(0)); - assertEquals("b[|", record.get(1)); - assertEquals(2, record.size()); - } - } - /** * A truncated escaped multi-character delimiter at EOF must stay literal data and not be completed from a stale * escape delimiter look-ahead. @@ -1696,6 +1682,20 @@ void testPartialEscapedMultiCharacterDelimiterAtEOF() throws IOException { } } + /** + * Tests CSV-324. + */ + @Test + void testPartialMultiCharacterDelimiterAtEOF() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + try (CSVParser parser = format.parse(new StringReader("a[|]b[|"))) { + final CSVRecord record = parser.nextRecord(); + assertEquals("a", record.get(0)); + assertEquals("b[|", record.get(1)); + assertEquals(2, record.size()); + } + } + @Test void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index 79ce987bd..b58782210 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -423,26 +423,6 @@ void testDelimeterStringQuoteNone() throws IOException { } } - @Test - void testQuoteCharEscapedWithQuoteModeNone() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.builder().setQuote('"').setEscape('?').setQuoteMode(QuoteMode.NONE).get(); - final StringWriter sw = new StringWriter(); - try (CSVPrinter printer = new CSVPrinter(sw, format)) { - printer.printRecord("\"abc", "x\"y"); - printer.printRecord(new StringReader("\"abc"), new StringReader("x\"y")); - } - assertEquals("?\"abc,x?\"y" + RECORD_SEPARATOR + "?\"abc,x?\"y" + RECORD_SEPARATOR, sw.toString()); - // The emitted records must read back as the original values. - try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { - final List records = parser.getRecords(); - assertEquals(2, records.size()); - for (final CSVRecord record : records) { - assertEquals("\"abc", record.get(0)); - assertEquals("x\"y", record.get(1)); - } - } - } - @Test void testDelimiterEscaped() throws IOException { final StringWriter sw = new StringWriter(); @@ -1818,6 +1798,26 @@ void testQuoteAll() throws IOException { } } + @Test + void testQuoteCharEscapedWithQuoteModeNone() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setQuote('"').setEscape('?').setQuoteMode(QuoteMode.NONE).get(); + final StringWriter sw = new StringWriter(); + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecord("\"abc", "x\"y"); + printer.printRecord(new StringReader("\"abc"), new StringReader("x\"y")); + } + assertEquals("?\"abc,x?\"y" + RECORD_SEPARATOR + "?\"abc,x?\"y" + RECORD_SEPARATOR, sw.toString()); + // The emitted records must read back as the original values. + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertEquals(2, records.size()); + for (final CSVRecord record : records) { + assertEquals("\"abc", record.get(0)); + assertEquals("x\"y", record.get(1)); + } + } + } + @Test void testQuoteCommaFirstChar() throws IOException { final StringWriter sw = new StringWriter(); diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index da60df07e..244079df6 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -409,18 +409,6 @@ void testNextToken6() throws IOException { } } - /** - * Tests CSV-324. - */ - @Test - void testPartialMultiCharacterDelimiterAtEOF() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); - try (Lexer lexer = createLexer("a[|]b[|", format)) { - assertNextToken(TOKEN, "a", lexer); - assertNextToken(EOF, "b[|", lexer); - } - } - /** * A truncated escaped multi-character delimiter at EOF must not be accepted by reusing the previous escape delimiter * look-ahead in {@link Lexer#isEscapeDelimiter()}. @@ -433,6 +421,18 @@ void testPartialEscapedMultiCharacterDelimiterAtEOF() throws IOException { } } + /** + * Tests CSV-324. + */ + @Test + void testPartialMultiCharacterDelimiterAtEOF() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + try (Lexer lexer = createLexer("a[|]b[|", format)) { + assertNextToken(TOKEN, "a", lexer); + assertNextToken(EOF, "b[|", lexer); + } + } + @Test void testReadEscapeBackspace() throws IOException { try (Lexer lexer = createLexer("b", CSVFormat.DEFAULT.withEscape('\b'))) { From d4d4154454b43c46aff65ed75c721605eafb142b Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 13 Jun 2026 12:43:32 +0000 Subject: [PATCH 68/98] Refactor some magic strings in CSVPrinterTest.testQuoteCharEscapedWithQuoteModeNone() --- .../java/org/apache/commons/csv/CSVPrinterTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index b58782210..16901c4e2 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -1802,9 +1802,11 @@ void testQuoteAll() throws IOException { void testQuoteCharEscapedWithQuoteModeNone() throws IOException { final CSVFormat format = CSVFormat.DEFAULT.builder().setQuote('"').setEscape('?').setQuoteMode(QuoteMode.NONE).get(); final StringWriter sw = new StringWriter(); + final String col1 = "\"abc"; + final String col2 = "x\"y"; try (CSVPrinter printer = new CSVPrinter(sw, format)) { - printer.printRecord("\"abc", "x\"y"); - printer.printRecord(new StringReader("\"abc"), new StringReader("x\"y")); + printer.printRecord(col1, col2); + printer.printRecord(new StringReader(col1), new StringReader(col2)); } assertEquals("?\"abc,x?\"y" + RECORD_SEPARATOR + "?\"abc,x?\"y" + RECORD_SEPARATOR, sw.toString()); // The emitted records must read back as the original values. @@ -1812,8 +1814,8 @@ void testQuoteCharEscapedWithQuoteModeNone() throws IOException { final List records = parser.getRecords(); assertEquals(2, records.size()); for (final CSVRecord record : records) { - assertEquals("\"abc", record.get(0)); - assertEquals("x\"y", record.get(1)); + assertEquals(col1, record.get(0)); + assertEquals(col2, record.get(1)); } } } From 411d3a37c923361f2ccca1c26862e6e4d7fd4742 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 13 Jun 2026 12:45:52 +0000 Subject: [PATCH 69/98] Escape quote char in printWithEscapes when QuoteMode is NONE (#609). --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0786cc365..41b1a038c 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -55,6 +55,7 @@ CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. Escape Reader values with quote and escape (#606). Clear escape delimiter buffer before peek in isEscapeDelimiter (#608). + Escape quote char in printWithEscapes when QuoteMode is NONE (#609). Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From a99f2609299a72b08a3e43c2968822555528da4e Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Mon, 15 Jun 2026 21:31:34 +0530 Subject: [PATCH 70/98] quote value starting with comment marker in minimal quote mode --- .../java/org/apache/commons/csv/CSVFormat.java | 5 +++-- .../org/apache/commons/csv/CSVPrinterTest.java | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 03211e689..4f60eff93 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -2454,10 +2454,11 @@ private void printWithQuotes(final Object object, final CharSequence charSeq, fi } } else { char c = charSeq.charAt(pos); - if (c <= Constants.COMMENT) { + if (c <= Constants.COMMENT || isCommentMarkerSet() && c == commentMarker.charValue()) { // Some other chars at the start of a value caused the parser to fail, so for now // encapsulate if we start in anything less than '#'. We are being conservative - // by including the default comment char too. + // by including the default comment char and any configured comment marker too, + // which the parser would otherwise read back as a comment line. quote = true; } else { while (pos < len) { diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index 16901c4e2..e00accfb0 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -1829,6 +1829,24 @@ void testQuoteCommaFirstChar() throws IOException { } } + @Test + void testQuoteCommentMarkerFirstChar() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setCommentMarker(';').get(); + final StringWriter sw = new StringWriter(); + final String col1 = ";comment-like"; + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecord(col1, "b"); + } + assertEquals("\";comment-like\",b" + RECORD_SEPARATOR, sw.toString()); + // A value starting with the comment marker must read back as data, not a dropped comment line. + try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertEquals(col1, records.get(0).get(0)); + assertEquals("b", records.get(0).get(1)); + } + } + @Test void testQuoteNonNumeric() throws IOException { final StringWriter sw = new StringWriter(); From 3c2291cf5e40c5b9a19f3a6b3165fb27c8de5321 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Tue, 16 Jun 2026 14:35:39 +0530 Subject: [PATCH 71/98] expand comment marker test to contrast printed comment with quoted value --- .../org/apache/commons/csv/CSVPrinterTest.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index e00accfb0..f4f3c85b1 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -1835,15 +1835,24 @@ void testQuoteCommentMarkerFirstChar() throws IOException { final StringWriter sw = new StringWriter(); final String col1 = ";comment-like"; try (CSVPrinter printer = new CSVPrinter(sw, format)) { + // A real comment is written with the marker, unquoted. + printer.printComment("a real comment"); + // A value starting with the marker is quoted, so it does not read back as a comment. printer.printRecord(col1, "b"); + // The marker past the first character does not start a comment, so only the leading-marker value is quoted. + printer.printRecord("a;b", ";c"); } - assertEquals("\";comment-like\",b" + RECORD_SEPARATOR, sw.toString()); - // A value starting with the comment marker must read back as data, not a dropped comment line. + assertEquals("; a real comment" + RECORD_SEPARATOR + + "\";comment-like\",b" + RECORD_SEPARATOR + + "a;b,\";c\"" + RECORD_SEPARATOR, sw.toString()); + // The comment is dropped on read; both data records survive intact. try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { final List records = parser.getRecords(); - assertEquals(1, records.size()); + assertEquals(2, records.size()); assertEquals(col1, records.get(0).get(0)); assertEquals("b", records.get(0).get(1)); + assertEquals("a;b", records.get(1).get(0)); + assertEquals(";c", records.get(1).get(1)); } } From e21d66e410cdafca2e822361de5eb6b2596291f2 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 16 Jun 2026 21:20:03 +0000 Subject: [PATCH 72/98] Quote value starting with comment marker in minimal quote mode (#610). Extract to local variable. --- src/changes/changes.xml | 1 + src/test/java/org/apache/commons/csv/CSVPrinterTest.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 41b1a038c..431da6b5f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -56,6 +56,7 @@ Escape Reader values with quote and escape (#606). Clear escape delimiter buffer before peek in isEscapeDelimiter (#608). Escape quote char in printWithEscapes when QuoteMode is NONE (#609). + Quote value starting with comment marker in minimal quote mode (#610).. Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index f4f3c85b1..e68d4c243 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -1842,11 +1842,12 @@ void testQuoteCommentMarkerFirstChar() throws IOException { // The marker past the first character does not start a comment, so only the leading-marker value is quoted. printer.printRecord("a;b", ";c"); } + final String string = sw.toString(); assertEquals("; a real comment" + RECORD_SEPARATOR + "\";comment-like\",b" + RECORD_SEPARATOR + - "a;b,\";c\"" + RECORD_SEPARATOR, sw.toString()); + "a;b,\";c\"" + RECORD_SEPARATOR, string); // The comment is dropped on read; both data records survive intact. - try (CSVParser parser = CSVParser.parse(sw.toString(), format)) { + try (CSVParser parser = CSVParser.parse(string, format)) { final List records = parser.getRecords(); assertEquals(2, records.size()); assertEquals(col1, records.get(0).get(0)); From 110e830616e44844a0a57256401093a151ae0e66 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Wed, 17 Jun 2026 16:38:10 +0530 Subject: [PATCH 73/98] clear delimiter buffer before each peek in isDelimiter --- src/main/java/org/apache/commons/csv/Lexer.java | 2 +- src/test/java/org/apache/commons/csv/LexerTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index 238e64cee..93a584663 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -153,6 +153,7 @@ boolean isDelimiter(final int ch) throws IOException { isLastTokenDelimiter = true; return true; } + Arrays.fill(delimiterBuf, '\0'); reader.peek(delimiterBuf); for (int i = 0; i < delimiterBuf.length; i++) { if (delimiterBuf[i] != delimiter[i + 1]) { @@ -274,7 +275,6 @@ Token nextToken(final Token token) throws IOException { token.type = Token.Type.COMMENT; return token; } - Arrays.fill(delimiterBuf, '\0'); // Important: make sure a new char gets consumed in each iteration while (token.type == Token.Type.INVALID) { // ignore whitespaces at beginning of a token diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index 244079df6..e5f831fb2 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -433,6 +433,19 @@ void testPartialMultiCharacterDelimiterAtEOF() throws IOException { } } + /** + * A truncated multi-character delimiter at EOF must not be accepted by reusing the look-ahead buffer left dirty by an + * earlier non-matching peek in the same token (CSV-324 only cleared the buffer once per token). + */ + @Test + void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + // The "[a]" peek leaves ']' in the look-ahead buffer; the trailing "[|" must not match "[|]". + try (Lexer lexer = createLexer("x[a][|", format)) { + assertNextToken(EOF, "x[a][|", lexer); + } + } + @Test void testReadEscapeBackspace() throws IOException { try (Lexer lexer = createLexer("b", CSVFormat.DEFAULT.withEscape('\b'))) { From 61f521350b34c5605bfd68760cdce120a5da4ed7 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Wed, 17 Jun 2026 17:07:51 +0530 Subject: [PATCH 74/98] add public-api parser test for partial delimiter false-match at eof --- .../org/apache/commons/csv/CSVParserTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index dca37fc5a..c1ca3d7a4 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1696,6 +1696,21 @@ void testPartialMultiCharacterDelimiterAtEOF() throws IOException { } } + /** + * A truncated multi-character delimiter at EOF must not be completed from the look-ahead buffer left dirty by an + * earlier non-matching peek in the same token. + */ + @Test + void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); + // The "[a]" peek leaves ']' in the look-ahead buffer; the trailing "[|" must not match "[|]". + try (CSVParser parser = format.parse(new StringReader("x[a][|"))) { + final CSVRecord record = parser.nextRecord(); + assertEquals("x[a][|", record.get(0)); + assertEquals(1, record.size()); + } + } + @Test void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); From ed8dbf25ad73856cfa10cba4f5e9855fdcae0d88 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 17 Jun 2026 12:08:58 +0000 Subject: [PATCH 75/98] Clear escape delimiter buffer before peek in Lexer.isEscapeDelimiter() (#608, #611). Refactor magic strings in tests --- src/changes/changes.xml | 2 +- src/test/java/org/apache/commons/csv/CSVParserTest.java | 5 +++-- src/test/java/org/apache/commons/csv/LexerTest.java | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 431da6b5f..66073c9dd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -54,7 +54,7 @@ CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. Escape Reader values with quote and escape (#606). - Clear escape delimiter buffer before peek in isEscapeDelimiter (#608). + Clear escape delimiter buffer before peek in Lexer.isEscapeDelimiter() (#608, #611). Escape quote char in printWithEscapes when QuoteMode is NONE (#609). Quote value starting with comment marker in minimal quote mode (#610).. diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index c1ca3d7a4..5bece571f 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1704,9 +1704,10 @@ void testPartialMultiCharacterDelimiterAtEOF() throws IOException { void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); // The "[a]" peek leaves ']' in the look-ahead buffer; the trailing "[|" must not match "[|]". - try (CSVParser parser = format.parse(new StringReader("x[a][|"))) { + final String recordString = "x[a][|"; + try (CSVParser parser = format.parse(new StringReader(recordString))) { final CSVRecord record = parser.nextRecord(); - assertEquals("x[a][|", record.get(0)); + assertEquals(recordString, record.get(0)); assertEquals(1, record.size()); } } diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index e5f831fb2..db1ab3a6d 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -441,8 +441,9 @@ void testPartialMultiCharacterDelimiterAtEOF() throws IOException { void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter("[|]").get(); // The "[a]" peek leaves ']' in the look-ahead buffer; the trailing "[|" must not match "[|]". - try (Lexer lexer = createLexer("x[a][|", format)) { - assertNextToken(EOF, "x[a][|", lexer); + final String recordString = "x[a][|"; + try (Lexer lexer = createLexer(recordString, format)) { + assertNextToken(EOF, recordString, lexer); } } From a6ee67ecf0d4b9208ffc640f433d8b40c258e1f3 Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Fri, 19 Jun 2026 01:32:16 +1000 Subject: [PATCH 76/98] [CSV-328] Fix quoted null string after disabling quote setNullString(String) rebuilt quotedNullString by concatenating the nullable quoteCharacter field directly, so calling setQuote(null) before setNullString(...) produced a literal "nullNULLnull". Extract a shared setQuotedNullString() helper that applies the default-quote fallback, so both builder orders produce the same state. Reviewed-by: OpenAI Codex Reviewed-by: Anthropic Claude Code --- src/changes/changes.xml | 1 + src/main/java/org/apache/commons/csv/CSVFormat.java | 7 +++++-- src/test/java/org/apache/commons/csv/CSVFormatTest.java | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 66073c9dd..64f936554 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -53,6 +53,7 @@ CSVParser applies characterOffset to bytePosition (#604). CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. + CSVFormat.Builder.setNullString(String) can build an invalid quoted null string after setQuote(null). Escape Reader values with quote and escape (#606). Clear escape delimiter buffer before peek in Lexer.isEscapeDelimiter() (#608, #611). Escape quote char in printWithEscapes when QuoteMode is NONE (#609). diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 4f60eff93..9c403d9e1 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -780,8 +780,7 @@ public Builder setMaxRows(final long maxRows) { */ public Builder setNullString(final String nullString) { this.nullString = nullString; - this.quotedNullString = quoteCharacter + nullString + quoteCharacter; - return this; + return setQuotedNullString(); } /** @@ -806,6 +805,10 @@ public Builder setQuote(final Character quoteCharacter) { throw new IllegalArgumentException("The quoteCharacter cannot be a line break"); } this.quoteCharacter = quoteCharacter; + return setQuotedNullString(); + } + + private Builder setQuotedNullString() { final Character quote = quoteCharacter != null ? quoteCharacter : Constants.DOUBLE_QUOTE_CHAR; this.quotedNullString = quote + nullString + quote; return this; diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index c3fdeeb77..ed20898de 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -1040,6 +1040,11 @@ void testQuotedNullStringTracksQuoteCharacter() throws IOException { builder.setQuote((Character) null); builder.get().print(null, out, true); assertEquals("\"NULL\"", out.toString()); + // reset, reverse setter order + out.setLength(0); + builder.setNullString(null).setQuote((Character) null).setNullString("NULL"); + builder.get().print(null, out, true); + assertEquals("\"NULL\"", out.toString()); } @Test From 1d89cd5f0aa454ef3853dfc7528242399ef26b74 Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Fri, 19 Jun 2026 02:05:55 +1000 Subject: [PATCH 77/98] [CSV-329] Fix byte tracking for supplementary delimiters ExtendedBufferedReader.read(char[], int, int) updated lastChar before computing the encoded byte length, so a surrogate pair in the delimiter lookahead buffer was paired against the post-update lastChar and threw CharacterCodingException. Count bytes before updating lastChar, and pair each char against the preceding char in the buffer seeded from lastChar so pairs split across reads still count. Add parser and ExtendedBufferedReader regression tests. Reviewed-by: OpenAI Codex Reviewed-by: Anthropic Claude Code --- src/changes/changes.xml | 1 + .../commons/csv/ExtendedBufferedReader.java | 20 +++++++++------ .../org/apache/commons/csv/CSVParserTest.java | 25 +++++++++++++++++++ .../csv/ExtendedBufferedReaderTest.java | 14 +++++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 66073c9dd..f6a474dbf 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -53,6 +53,7 @@ CSVParser applies characterOffset to bytePosition (#604). CSVPrinter Reader printing with quote and escape can emit CSV that its parser cannot read back. CSVParser applies maxRows to record numbers instead of rows produced when setRecordNumber(...) is used. + CSVParser with trackBytes enabled throws on multi-character delimiters containing supplementary Unicode characters. Escape Reader values with quote and escape (#606). Clear escape delimiter buffer before peek in Lexer.isEscapeDelimiter() (#608, #611). Escape quote char in printWithEscapes when QuoteMode is NONE (#609). diff --git a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java index 889b58edc..5b519a08c 100644 --- a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java +++ b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java @@ -108,9 +108,11 @@ long getBytesRead() { } private long getEncodedCharLength(final char[] buf, final int offset, final int length) throws CharacterCodingException { - int len = 0; - for (int i = offset; i < length; i++) { - len += getEncodedCharLength(buf[i]); + long len = 0; + int previous = lastChar; + for (int i = offset; i < offset + length; i++) { + len += getEncodedCharLength(previous, buf[i]); + previous = buf[i]; } return len; } @@ -141,8 +143,12 @@ private long getEncodedCharLength(final char[] buf, final int offset, final int * @throws CharacterCodingException if the character cannot be encoded. */ private int getEncodedCharLength(final int current) throws CharacterCodingException { + return getEncodedCharLength(lastChar, current); + } + + private int getEncodedCharLength(final int previous, final int current) throws CharacterCodingException { final char cChar = (char) current; - final char lChar = (char) lastChar; + final char lChar = (char) previous; if (!Character.isSurrogate(cChar)) { return encoder.encode(CharBuffer.wrap(new char[] { cChar })).limit(); } @@ -218,6 +224,9 @@ public int read(final char[] buf, final int offset, final int length) throws IOE return 0; } final int len = super.read(buf, offset, length); + if (encoder != null && len > 0) { + this.bytesRead += getEncodedCharLength(buf, offset, len); + } if (len > 0) { for (int i = offset; i < offset + len; i++) { final char ch = buf[i]; @@ -233,9 +242,6 @@ public int read(final char[] buf, final int offset, final int length) throws IOE } else if (len == EOF) { lastChar = EOF; } - if (encoder != null) { - this.bytesRead += getEncodedCharLength(buf, offset, len); - } position += len; return len; } diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 5bece571f..29ca0cf1f 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -666,6 +666,31 @@ void testGetBytePositionMultiCharacterDelimiter() throws IOException { } } + /** + * Tests CSV-329. + */ + @Test + void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() throws IOException { + final String delimiter = "x😀"; + final String code = "ax😀b\ncx😀d\n"; + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(delimiter).get(); + try (CSVParser parser = CSVParser.builder() + .setReader(new StringReader(code)) + .setFormat(format) + .setCharset(UTF_8) + .setTrackBytes(true) + .get()) { + final CSVRecord first = parser.nextRecord(); + final CSVRecord second = parser.nextRecord(); + assertNotNull(first); + assertNotNull(second); + assertValuesEquals(new String[] { "a", "b" }, first); + assertValuesEquals(new String[] { "c", "d" }, second); + assertEquals(0, first.getBytePosition()); + assertEquals("ax😀b\n".getBytes(UTF_8).length, second.getBytePosition()); + } + } + @Test void testGetBytePositionWithCharacterOffsetAndMultiBytePrefix() throws Exception { final String row0 = "é,x\n"; diff --git a/src/test/java/org/apache/commons/csv/ExtendedBufferedReaderTest.java b/src/test/java/org/apache/commons/csv/ExtendedBufferedReaderTest.java index 056b8a9c9..b8d9b9f19 100644 --- a/src/test/java/org/apache/commons/csv/ExtendedBufferedReaderTest.java +++ b/src/test/java/org/apache/commons/csv/ExtendedBufferedReaderTest.java @@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import java.io.StringReader; +import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; @@ -104,6 +105,19 @@ void testReadingInDifferentBuffer() throws Exception { } } + @Test + void testReadingSupplementaryCharacterTracksBytes() throws Exception { + final String input = "😀"; + final char[] buffer = new char[input.length()]; + try (ExtendedBufferedReader reader = new ExtendedBufferedReader(new StringReader(input), StandardCharsets.UTF_8, true)) { + assertEquals(input.length(), reader.read(buffer, 0, buffer.length)); + assertArrayEquals(input.toCharArray(), buffer); + assertEquals(input.getBytes(StandardCharsets.UTF_8).length, reader.getBytesRead()); + assertEquals(input.length(), reader.getPosition()); + assertEquals(input.charAt(input.length() - 1), reader.getLastChar()); + } + } + @Test void testReadLine() throws Exception { try (ExtendedBufferedReader br = createBufferedReader("")) { From d8e12423b47109c76196bbae454726a114cf7c07 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 19 Jun 2026 07:28:18 -0400 Subject: [PATCH 78/98] Bump actions/checkout from 6.0.3 to 7.0.0. --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/maven.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 57f55cae0..08c673ee0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 114f3d8a2..7bc02bdd2 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -26,6 +26,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Dependency Review PR' uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 3ee3dec2b..3cb743cbf 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -43,7 +43,7 @@ jobs: experimental: true steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 From a1cf4f2a73065281ca7c22841c2f3ec0c00098c1 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 19 Jun 2026 11:44:21 +0000 Subject: [PATCH 79/98] Refactor delimiter in test Rename local variable --- src/test/java/org/apache/commons/csv/CSVParserTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 29ca0cf1f..aa4a639dd 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -672,10 +672,10 @@ void testGetBytePositionMultiCharacterDelimiter() throws IOException { @Test void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() throws IOException { final String delimiter = "x😀"; - final String code = "ax😀b\ncx😀d\n"; + final String data = "a" + delimiter + "b\nc" + delimiter + "d\n"; final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(delimiter).get(); try (CSVParser parser = CSVParser.builder() - .setReader(new StringReader(code)) + .setReader(new StringReader(data)) .setFormat(format) .setCharset(UTF_8) .setTrackBytes(true) @@ -687,7 +687,7 @@ void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() thro assertValuesEquals(new String[] { "a", "b" }, first); assertValuesEquals(new String[] { "c", "d" }, second); assertEquals(0, first.getBytePosition()); - assertEquals("ax😀b\n".getBytes(UTF_8).length, second.getBytePosition()); + assertEquals("a" + delimiter + "b\n".getBytes(UTF_8).length, second.getBytePosition()); } } From caa1c8d0ed1a05f4a75f9a4fc6e5e2dc6fa5bf51 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 19 Jun 2026 18:51:44 +0000 Subject: [PATCH 80/98] Revert "Refactor delimiter in test" This reverts commit a1cf4f2a73065281ca7c22841c2f3ec0c00098c1. --- src/test/java/org/apache/commons/csv/CSVParserTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index aa4a639dd..29ca0cf1f 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -672,10 +672,10 @@ void testGetBytePositionMultiCharacterDelimiter() throws IOException { @Test void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() throws IOException { final String delimiter = "x😀"; - final String data = "a" + delimiter + "b\nc" + delimiter + "d\n"; + final String code = "ax😀b\ncx😀d\n"; final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(delimiter).get(); try (CSVParser parser = CSVParser.builder() - .setReader(new StringReader(data)) + .setReader(new StringReader(code)) .setFormat(format) .setCharset(UTF_8) .setTrackBytes(true) @@ -687,7 +687,7 @@ void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() thro assertValuesEquals(new String[] { "a", "b" }, first); assertValuesEquals(new String[] { "c", "d" }, second); assertEquals(0, first.getBytePosition()); - assertEquals("a" + delimiter + "b\n".getBytes(UTF_8).length, second.getBytePosition()); + assertEquals("ax😀b\n".getBytes(UTF_8).length, second.getBytePosition()); } } From 61aa0555d60c68120914b4952232f8c6bfc72ed3 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Fri, 19 Jun 2026 23:39:51 +0530 Subject: [PATCH 81/98] escape leading comment marker in printWithEscapes --- .../org/apache/commons/csv/CSVFormat.java | 14 ++++- .../apache/commons/csv/CSVPrinterTest.java | 51 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 9c403d9e1..eaa8c8ffe 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -2329,12 +2329,16 @@ private void printWithEscapes(final CharSequence charSeq, final Appendable appen final char escape = getEscapeChar(); final boolean quoteSet = isQuoteCharacterSet(); final char quote = quoteSet ? getQuoteCharacter().charValue() : 0; + final boolean commentMarkerSet = isCommentMarkerSet(); + final char commentChar = commentMarkerSet ? commentMarker.charValue() : 0; // Explicit unboxing is intentional while (pos < end) { char c = charSeq.charAt(pos); final boolean isDelimiterStart = isDelimiter(c, charSeq, pos, delimArray, delimLength); final boolean isCr = c == Constants.CR; final boolean isLf = c == Constants.LF; - if (isCr || isLf || c == escape || quoteSet && c == quote || isDelimiterStart) { + // A leading comment marker would be read back as a comment, so escape it. + final boolean isComment = commentMarkerSet && pos == 0 && c == commentChar; + if (isCr || isLf || c == escape || quoteSet && c == quote || isDelimiterStart || isComment) { // write out segment up until this char if (pos > start) { appendable.append(charSeq, start, pos); @@ -2375,8 +2379,11 @@ private void printWithEscapes(final Reader reader, final Appendable appendable) final char escape = getEscapeChar(); final boolean quoteSet = isQuoteCharacterSet(); final char quote = quoteSet ? getQuoteCharacter().charValue() : 0; + final boolean commentMarkerSet = isCommentMarkerSet(); + final char commentChar = commentMarkerSet ? commentMarker.charValue() : 0; // Explicit unboxing is intentional final StringBuilder builder = new StringBuilder(IOUtils.DEFAULT_BUFFER_SIZE); int c; + boolean firstChar = true; final char[] lookAheadBuffer = new char[delimLength - 1]; while (EOF != (c = bufferedReader.read())) { builder.append((char) c); @@ -2386,7 +2393,10 @@ private void printWithEscapes(final Reader reader, final Appendable appendable) final boolean isDelimiterStart = isDelimiter((char) c, test, pos, delimArray, delimLength); final boolean isCr = c == Constants.CR; final boolean isLf = c == Constants.LF; - if (isCr || isLf || c == escape || quoteSet && c == quote || isDelimiterStart) { + // A leading comment marker would be read back as a comment, so escape it. + final boolean isComment = commentMarkerSet && firstChar && c == commentChar; + firstChar = false; + if (isCr || isLf || c == escape || quoteSet && c == quote || isDelimiterStart || isComment) { // write out segment up until this char if (pos > start) { append(builder.substring(start, pos), appendable); diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index e68d4c243..9ae80c1e5 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -569,6 +569,57 @@ void testEscapeBackslash5() throws IOException { assertEquals("\\\\", sw.toString()); } + @Test + void testEscapeCommentMarkerFirstChar() throws IOException { + // No quoting available in escape mode, so a leading comment marker must be escaped or the + // record reads back as a comment and is dropped. Mirrors the quoting fix for QuoteMode.MINIMAL. + final CSVFormat format = CSVFormat.DEFAULT.builder().setQuote(null).setEscape('\\').setCommentMarker(';').get(); + final StringWriter sw = new StringWriter(); + final String col1 = ";comment-like"; + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecord(col1, "b"); + printer.printRecord(new StringReader(col1), new StringReader("b")); + // The marker past the first character does not start a comment and is left alone. + printer.printRecord("a;b", ";c"); + } + final String string = sw.toString(); + assertEquals("\\;comment-like,b" + RECORD_SEPARATOR + + "\\;comment-like,b" + RECORD_SEPARATOR + + "a;b,\\;c" + RECORD_SEPARATOR, string); + // The emitted records must read back as the original values, none parsed as a comment. + try (CSVParser parser = CSVParser.parse(string, format)) { + final List records = parser.getRecords(); + assertEquals(3, records.size()); + assertEquals(col1, records.get(0).get(0)); + assertEquals("b", records.get(0).get(1)); + assertEquals(col1, records.get(1).get(0)); + assertEquals("b", records.get(1).get(1)); + assertEquals("a;b", records.get(2).get(0)); + assertEquals(";c", records.get(2).get(1)); + } + } + + @Test + void testEscapeCommentMarkerFirstCharWithQuoteModeNone() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setEscape('\\').setQuoteMode(QuoteMode.NONE).setCommentMarker(';').get(); + final StringWriter sw = new StringWriter(); + final String col1 = ";bar"; + try (CSVPrinter printer = new CSVPrinter(sw, format)) { + printer.printRecord(col1, "b"); + printer.printRecord(new StringReader(col1), new StringReader("b")); + } + final String string = sw.toString(); + assertEquals("\\;bar,b" + RECORD_SEPARATOR + "\\;bar,b" + RECORD_SEPARATOR, string); + try (CSVParser parser = CSVParser.parse(string, format)) { + final List records = parser.getRecords(); + assertEquals(2, records.size()); + for (final CSVRecord record : records) { + assertEquals(col1, record.get(0)); + assertEquals("b", record.get(1)); + } + } + } + @Test void testEscapeNull1() throws IOException { final StringWriter sw = new StringWriter(); From b112daacc74664c925b28d413b660dc47faddcef Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Fri, 19 Jun 2026 21:14:05 +0000 Subject: [PATCH 82/98] Escape leading comment marker in printWithEscapes (#614). --- src/changes/changes.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 006de7711..f172a96fe 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -58,7 +58,8 @@ Escape Reader values with quote and escape (#606). Clear escape delimiter buffer before peek in Lexer.isEscapeDelimiter() (#608, #611). Escape quote char in printWithEscapes when QuoteMode is NONE (#609). - Quote value starting with comment marker in minimal quote mode (#610).. + Quote value starting with comment marker in minimal quote mode (#610). + Escape leading comment marker in printWithEscapes (#614). Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From 274b4ceba4e418726a5c9e7043bf9d460b0429c5 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Sat, 20 Jun 2026 20:33:44 +0530 Subject: [PATCH 83/98] skip byte counting at EOF in ExtendedBufferedReader.read --- .../commons/csv/ExtendedBufferedReader.java | 2 +- .../org/apache/commons/csv/CSVParserTest.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java index 5b519a08c..20c1ef544 100644 --- a/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java +++ b/src/main/java/org/apache/commons/csv/ExtendedBufferedReader.java @@ -210,7 +210,7 @@ public int read() throws IOException { if (current == CR || current == LF && lastChar != CR || current == EOF && lastChar != CR && lastChar != LF && lastChar != EOF) { lineNumber++; } - if (encoder != null) { + if (encoder != null && current != EOF) { this.bytesRead += getEncodedCharLength(current); } lastChar = current; diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 29ca0cf1f..1332fa582 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -691,6 +691,27 @@ void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() thro } } + @Test + void testGetBytePositionWithSingleByteCharset() throws IOException { + // A single-byte charset cannot encode U+FFFF, the char value of the EOF sentinel. + // Byte counting must skip the EOF read so a valid file parses without throwing. + final String code = "a,b\nc,d\n"; + try (CSVParser parser = CSVParser.builder() + .setReader(new StringReader(code)) + .setFormat(CSVFormat.DEFAULT) + .setCharset(StandardCharsets.ISO_8859_1) + .setTrackBytes(true) + .get()) { + final CSVRecord first = parser.nextRecord(); + final CSVRecord second = parser.nextRecord(); + assertNotNull(first); + assertNotNull(second); + assertNull(parser.nextRecord()); + assertEquals(0, first.getBytePosition()); + assertEquals(4, second.getBytePosition()); + } + } + @Test void testGetBytePositionWithCharacterOffsetAndMultiBytePrefix() throws Exception { final String row0 = "é,x\n"; From 0633c989c9ff892d99bacd3289a3ff8d4cb0fbd6 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Sat, 20 Jun 2026 15:11:11 +0000 Subject: [PATCH 84/98] Skip byte counting at EOF in ExtendedBufferedReader.read (#615). Sort members. --- src/changes/changes.xml | 1 + .../org/apache/commons/csv/CSVParserTest.java | 42 +++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f172a96fe..867a20507 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -60,6 +60,7 @@ Escape quote char in printWithEscapes when QuoteMode is NONE (#609). Quote value starting with comment marker in minimal quote mode (#610). Escape leading comment marker in printWithEscapes (#614). + Skip byte counting at EOF in ExtendedBufferedReader.read (#615). Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 1332fa582..309a073cf 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -691,27 +691,6 @@ void testGetBytePositionMultiCharacterDelimiterWithSupplementaryCharacter() thro } } - @Test - void testGetBytePositionWithSingleByteCharset() throws IOException { - // A single-byte charset cannot encode U+FFFF, the char value of the EOF sentinel. - // Byte counting must skip the EOF read so a valid file parses without throwing. - final String code = "a,b\nc,d\n"; - try (CSVParser parser = CSVParser.builder() - .setReader(new StringReader(code)) - .setFormat(CSVFormat.DEFAULT) - .setCharset(StandardCharsets.ISO_8859_1) - .setTrackBytes(true) - .get()) { - final CSVRecord first = parser.nextRecord(); - final CSVRecord second = parser.nextRecord(); - assertNotNull(first); - assertNotNull(second); - assertNull(parser.nextRecord()); - assertEquals(0, first.getBytePosition()); - assertEquals(4, second.getBytePosition()); - } - } - @Test void testGetBytePositionWithCharacterOffsetAndMultiBytePrefix() throws Exception { final String row0 = "é,x\n"; @@ -742,6 +721,27 @@ void testGetBytePositionWithCharacterOffsetAndMultiBytePrefix() throws Exception } } + @Test + void testGetBytePositionWithSingleByteCharset() throws IOException { + // A single-byte charset cannot encode U+FFFF, the char value of the EOF sentinel. + // Byte counting must skip the EOF read so a valid file parses without throwing. + final String code = "a,b\nc,d\n"; + try (CSVParser parser = CSVParser.builder() + .setReader(new StringReader(code)) + .setFormat(CSVFormat.DEFAULT) + .setCharset(StandardCharsets.ISO_8859_1) + .setTrackBytes(true) + .get()) { + final CSVRecord first = parser.nextRecord(); + final CSVRecord second = parser.nextRecord(); + assertNotNull(first); + assertNotNull(second); + assertNull(parser.nextRecord()); + assertEquals(0, first.getBytePosition()); + assertEquals(4, second.getBytePosition()); + } + } + @Test void testGetHeaderComment_HeaderComment1() throws IOException { try (CSVParser parser = CSVParser.parse(CSV_INPUT_HEADER_COMMENT, FORMAT_AUTO_HEADER)) { From 609a228e35adf9d73275a6b88e065de091c94be6 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Mon, 22 Jun 2026 02:01:19 +0530 Subject: [PATCH 85/98] keep quoted empty trailing field with trailingDelimiter --- .../java/org/apache/commons/csv/CSVParser.java | 4 +++- .../org/apache/commons/csv/CSVParserTest.java | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 83b60170e..141eba732 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -580,7 +580,9 @@ public CSVParser(final Reader reader, final CSVFormat format, final long charact private void addRecordValue(final boolean lastRecord) { final String input = format.trim(reusableToken.content.toString()); - if (lastRecord && input.isEmpty() && format.getTrailingDelimiter()) { + // Only drop the empty field produced by an actual trailing delimiter. A quoted empty + // field ("") is a real value, not a trailing delimiter, so it must be kept. + if (lastRecord && input.isEmpty() && format.getTrailingDelimiter() && !reusableToken.isQuoted) { return; } recordList.add(handleNull(input)); diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 309a073cf..051548757 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1949,6 +1949,23 @@ void testTrailingDelimiter() throws Exception { } } + @Test + void testTrailingDelimiterKeepsQuotedEmptyLastField() throws Exception { + final CSVFormat format = CSVFormat.DEFAULT.builder().setTrailingDelimiter(true).get(); + try (CSVParser parser = CSVParser.parse("a,b,\"\"", format)) { + final CSVRecord record = parser.iterator().next(); + assertEquals(3, record.size()); + assertEquals("a", record.get(0)); + assertEquals("b", record.get(1)); + assertEquals("", record.get(2)); + } + // An unquoted trailing delimiter still drops the empty field. + try (CSVParser parser = CSVParser.parse("a,b,", format)) { + final CSVRecord record = parser.iterator().next(); + assertEquals(2, record.size()); + } + } + @Test void testTrim() throws Exception { final Reader in = new StringReader("a,a,a\n\" 1 \",\" 2 \",\" 3 \"\nx,y,z"); From 53360c47dbdd5d6ba4fe5e8008f8bb3510200b33 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 22 Jun 2026 07:32:06 -0400 Subject: [PATCH 86/98] Bump actions/setup-java from 5.2.0 to 5.3.0 --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 3cb743cbf..a6154ddb1 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -53,7 +53,7 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ runner.os == 'macOS' && matrix.java == '8' && 'zulu' || 'temurin' }} java-version: ${{ matrix.java }} From fc4c0e3b43c3cba69f023d336629d2696e250294 Mon Sep 17 00:00:00 2001 From: rootvector2 Date: Mon, 22 Jun 2026 18:06:33 +0530 Subject: [PATCH 87/98] document trailing delimiter parse behavior and contrast with trailing data --- .../org/apache/commons/csv/CSVFormat.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index eaa8c8ffe..7145d23d3 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -883,6 +883,16 @@ public Builder setTrailingData(final boolean trailingData) { /** * Sets whether to add a trailing delimiter. * + *

    + * When writing, a delimiter is appended after the last value of each record. When reading, the empty field + * that such a trailing delimiter produces is dropped so the output round-trips back to the original record; + * a quoted empty trailing field ({@code ""}) is a real value rather than a trailing delimiter and is kept. + *

    + *

    + * This is unrelated to {@link #setTrailingData(boolean) trailing data}, which controls whether characters + * after the closing quote of an encapsulated value are tolerated when reading. + *

    + * * @param trailingDelimiter whether to add a trailing delimiter. * @return This instance. */ @@ -2012,6 +2022,16 @@ public boolean getTrailingData() { /** * Gets whether to add a trailing delimiter. * + *

    + * When writing, a delimiter is appended after the last value of each record. When reading, the empty field + * that such a trailing delimiter produces is dropped so the output round-trips back to the original record; + * a quoted empty trailing field ({@code ""}) is a real value rather than a trailing delimiter and is kept. + *

    + *

    + * This is unrelated to {@link #getTrailingData() trailing data}, which controls whether characters after the + * closing quote of an encapsulated value are tolerated when reading. + *

    + * * @return whether to add a trailing delimiter. * @since 1.3 */ From e729d17d5089d794c07e41dd6c9d374f9ea09e85 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 22 Jun 2026 13:49:46 +0000 Subject: [PATCH 88/98] Keep quoted empty trailing field with trailingDelimiter (#616). --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 867a20507..0d0175ccc 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -61,6 +61,7 @@ Quote value starting with comment marker in minimal quote mode (#610). Escape leading comment marker in printWithEscapes (#614). Skip byte counting at EOF in ExtendedBufferedReader.read (#615). + Keep quoted empty trailing field with trailingDelimiter (#616). Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From afbf34ad92cbf741b9dfad84699e890d8695a6f9 Mon Sep 17 00:00:00 2001 From: Naveed Khan Date: Thu, 25 Jun 2026 23:12:11 +0530 Subject: [PATCH 89/98] evaluate isDelimiter once in nextToken whitespace skip --- .../java/org/apache/commons/csv/Lexer.java | 11 +++++++++-- .../org/apache/commons/csv/LexerTest.java | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index 93a584663..fe964480a 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -277,15 +277,22 @@ Token nextToken(final Token token) throws IOException { } // Important: make sure a new char gets consumed in each iteration while (token.type == Token.Type.INVALID) { + // isDelimiter consumes the trailing characters of a multi-character delimiter as a side effect, so it must + // only be evaluated once per character. Remember a match found while skipping whitespace below. + boolean delimiter = false; // ignore whitespaces at beginning of a token if (ignoreSurroundingSpaces) { - while (Character.isWhitespace((char) c) && !isDelimiter(c) && !eol) { + while (Character.isWhitespace((char) c) && !eol) { + if (isDelimiter(c)) { + delimiter = true; + break; + } c = reader.read(); eol = readEndOfLine(c); } } // ok, start of token reached: encapsulated, or token - if (isDelimiter(c)) { + if (delimiter || isDelimiter(c)) { // empty token return TOKEN("") token.type = Token.Type.TOKEN; } else if (eol) { diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index db1ab3a6d..445f710a1 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -447,6 +447,25 @@ void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { } } + /** + * With {@code ignoreSurroundingSpaces} enabled and a multi-character delimiter whose first character is whitespace, + * the side-effecting {@link Lexer#isDelimiter(int)} must only be evaluated once per character, otherwise the + * delimiter is consumed in the whitespace-skip loop and the empty field at the boundary is dropped. + */ + @Test + void testEmptyTokenBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(" |").setIgnoreSurroundingSpaces(true).get(); + try (Lexer lexer = createLexer(" |a", format)) { + assertNextToken(TOKEN, "", lexer); + assertNextToken(EOF, "a", lexer); + } + try (Lexer lexer = createLexer("a | |b", format)) { + assertNextToken(TOKEN, "a", lexer); + assertNextToken(TOKEN, "", lexer); + assertNextToken(EOF, "b", lexer); + } + } + @Test void testReadEscapeBackspace() throws IOException { try (Lexer lexer = createLexer("b", CSVFormat.DEFAULT.withEscape('\b'))) { From c9362f76baa32534e82334a27220b698db49788c Mon Sep 17 00:00:00 2001 From: Naveed Khan Date: Fri, 26 Jun 2026 02:45:35 +0530 Subject: [PATCH 90/98] add public-api test for whitespace-prefixed multi-char delimiter exercises the empty-field-dropped regression through CSVParser, not just the lexer. --- .../org/apache/commons/csv/CSVParserTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 051548757..565e132eb 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -1758,6 +1758,26 @@ void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { } } + /** + * With {@code ignoreSurroundingSpaces} enabled and a multi-character delimiter whose first character is whitespace, + * the empty field at the delimiter boundary must survive. The delimiter look-ahead is consumed while skipping + * leading whitespace, so re-evaluating it would drop the empty field and merge the following field's value. + */ + @Test + void testEmptyFieldBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(" |").setIgnoreSurroundingSpaces(true).get(); + try (CSVParser parser = CSVParser.parse(" |a", format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertValuesEquals(new String[] { "", "a" }, records.get(0)); + } + try (CSVParser parser = CSVParser.parse("a | |b", format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertValuesEquals(new String[] { "a", "", "b" }, records.get(0)); + } + } + @Test void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); From c4113ceb3acfba0634133e47d35c40d141e18525 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 25 Jun 2026 22:11:14 +0000 Subject: [PATCH 91/98] Evaluate isDelimiter once in nextToken whitespace skip (#618). --- src/changes/changes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0d0175ccc..93952e9f1 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -62,6 +62,7 @@ Escape leading comment marker in printWithEscapes (#614). Skip byte counting at EOF in ExtendedBufferedReader.read (#615). Keep quoted empty trailing field with trailingDelimiter (#616). + Evaluate isDelimiter once in nextToken whitespace skip (#618).. Add an "Android Compatibility" section to the web site. Add CSVParser.Builder.setByteOffset(long) (#604). From e36e7f3a1d0fbe29f2ff602f041d3a3d4195b84a Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 25 Jun 2026 22:11:53 +0000 Subject: [PATCH 92/98] Sort members --- .../org/apache/commons/csv/CSVParserTest.java | 40 +++++++++---------- .../org/apache/commons/csv/LexerTest.java | 38 +++++++++--------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 565e132eb..3bea08fac 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -465,6 +465,26 @@ void testDuplicateHeadersNotAllowed() { () -> CSVParser.parse("a,b,a\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader().withAllowDuplicateHeaderNames(false))); } + /** + * With {@code ignoreSurroundingSpaces} enabled and a multi-character delimiter whose first character is whitespace, + * the empty field at the delimiter boundary must survive. The delimiter look-ahead is consumed while skipping + * leading whitespace, so re-evaluating it would drop the empty field and merge the following field's value. + */ + @Test + void testEmptyFieldBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(" |").setIgnoreSurroundingSpaces(true).get(); + try (CSVParser parser = CSVParser.parse(" |a", format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertValuesEquals(new String[] { "", "a" }, records.get(0)); + } + try (CSVParser parser = CSVParser.parse("a | |b", format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertValuesEquals(new String[] { "a", "", "b" }, records.get(0)); + } + } + @Test void testEmptyFile() throws Exception { try (CSVParser parser = CSVParser.parse(Paths.get("src/test/resources/org/apache/commons/csv/empty.txt"), StandardCharsets.UTF_8, @@ -1758,26 +1778,6 @@ void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { } } - /** - * With {@code ignoreSurroundingSpaces} enabled and a multi-character delimiter whose first character is whitespace, - * the empty field at the delimiter boundary must survive. The delimiter look-ahead is consumed while skipping - * leading whitespace, so re-evaluating it would drop the empty field and merge the following field's value. - */ - @Test - void testEmptyFieldBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(" |").setIgnoreSurroundingSpaces(true).get(); - try (CSVParser parser = CSVParser.parse(" |a", format)) { - final List records = parser.getRecords(); - assertEquals(1, records.size()); - assertValuesEquals(new String[] { "", "a" }, records.get(0)); - } - try (CSVParser parser = CSVParser.parse("a | |b", format)) { - final List records = parser.getRecords(); - assertEquals(1, records.size()); - assertValuesEquals(new String[] { "a", "", "b" }, records.get(0)); - } - } - @Test void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); diff --git a/src/test/java/org/apache/commons/csv/LexerTest.java b/src/test/java/org/apache/commons/csv/LexerTest.java index 445f710a1..a76f6e513 100644 --- a/src/test/java/org/apache/commons/csv/LexerTest.java +++ b/src/test/java/org/apache/commons/csv/LexerTest.java @@ -216,6 +216,25 @@ void testDelimiterIsWhitespace() throws IOException { } } + /** + * With {@code ignoreSurroundingSpaces} enabled and a multi-character delimiter whose first character is whitespace, + * the side-effecting {@link Lexer#isDelimiter(int)} must only be evaluated once per character, otherwise the + * delimiter is consumed in the whitespace-skip loop and the empty field at the boundary is dropped. + */ + @Test + void testEmptyTokenBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOException { + final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(" |").setIgnoreSurroundingSpaces(true).get(); + try (Lexer lexer = createLexer(" |a", format)) { + assertNextToken(TOKEN, "", lexer); + assertNextToken(EOF, "a", lexer); + } + try (Lexer lexer = createLexer("a | |b", format)) { + assertNextToken(TOKEN, "a", lexer); + assertNextToken(TOKEN, "", lexer); + assertNextToken(EOF, "b", lexer); + } + } + @Test void testEOFWithoutClosingQuote() throws Exception { final String code = "a,\"b"; @@ -447,25 +466,6 @@ void testPartialMultiCharacterDelimiterAtEOFAfterMismatch() throws IOException { } } - /** - * With {@code ignoreSurroundingSpaces} enabled and a multi-character delimiter whose first character is whitespace, - * the side-effecting {@link Lexer#isDelimiter(int)} must only be evaluated once per character, otherwise the - * delimiter is consumed in the whitespace-skip loop and the empty field at the boundary is dropped. - */ - @Test - void testEmptyTokenBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.builder().setDelimiter(" |").setIgnoreSurroundingSpaces(true).get(); - try (Lexer lexer = createLexer(" |a", format)) { - assertNextToken(TOKEN, "", lexer); - assertNextToken(EOF, "a", lexer); - } - try (Lexer lexer = createLexer("a | |b", format)) { - assertNextToken(TOKEN, "a", lexer); - assertNextToken(TOKEN, "", lexer); - assertNextToken(EOF, "b", lexer); - } - } - @Test void testReadEscapeBackspace() throws IOException { try (Lexer lexer = createLexer("b", CSVFormat.DEFAULT.withEscape('\b'))) { From 26a53751934e52f84663e5e90956db99f0eefc49 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 25 Jun 2026 22:13:08 +0000 Subject: [PATCH 93/98] Add test more assertions to CSVParserTest.testEmptyFieldBeforeWhitespacePrefixedMultiCharacterDelimiter() --- src/test/java/org/apache/commons/csv/CSVParserTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 3bea08fac..6d9bdd9e8 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -483,6 +483,11 @@ void testEmptyFieldBeforeWhitespacePrefixedMultiCharacterDelimiter() throws IOEx assertEquals(1, records.size()); assertValuesEquals(new String[] { "a", "", "b" }, records.get(0)); } + try (CSVParser parser = CSVParser.parse("a | |b |", format)) { + final List records = parser.getRecords(); + assertEquals(1, records.size()); + assertValuesEquals(new String[] { "a", "", "b", "" }, records.get(0)); + } } @Test From b346046e10a5833e5f6143fd0162155bb51ccc87 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Thu, 25 Jun 2026 18:23:59 -0400 Subject: [PATCH 94/98] Bump actions/cache from 5.0.5 to 6.0.0. --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/maven.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 08c673ee0..20f1ee2cb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 + - uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index a6154ddb1..139df406d 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -46,7 +46,7 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5 + - uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From 930bc7919db7ebb81dc566e9e278ce2bed3acf42 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Mon, 29 Jun 2026 07:29:52 -0400 Subject: [PATCH 95/98] Bump actions/cache from 6.0.0 to 6.1.0 --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/maven.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 20f1ee2cb..cca38e512 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -49,7 +49,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 139df406d..2637840b1 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -46,7 +46,7 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - - uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 #v6.1.0 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} From 6471196a31d9ea92942a634732e5350ef5253fcf Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 30 Jun 2026 13:58:31 +0000 Subject: [PATCH 96/98] Javadoc --- src/main/java/org/apache/commons/csv/CSVRecord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index 502bf318a..8dab14d90 100644 --- a/src/main/java/org/apache/commons/csv/CSVRecord.java +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java @@ -281,7 +281,7 @@ public Iterator iterator() { /** * Puts all values of this record into the given Map. * - * @param the map type. + * @param The map type. * @param map The Map to populate. * @return the given map. * @since 1.9.0 From f7efa29cd9c4d9d1c1cafdae4469cf254bf22f42 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Tue, 30 Jun 2026 20:50:12 -0400 Subject: [PATCH 97/98] Bump actions/setup-java from 5.3.0 to 5.4.0 --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 2637840b1..17ba7dd38 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -53,7 +53,7 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 + uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 with: distribution: ${{ runner.os == 'macOS' && matrix.java == '8' && 'zulu' || 'temurin' }} java-version: ${{ matrix.java }} From 4434d93b92ff3f7b0754e65eba53721dd95c59f1 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 1 Jul 2026 07:31:54 -0400 Subject: [PATCH 98/98] Bump actions/checkout from 6.0.2 to 7.0.0 --- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index bf246c140..e1868cb46 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -40,7 +40,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0 with: persist-credentials: false