Skip to content

fix: align unicode case folding with String.regionMatches - #1302

Open
hanzhe-one wants to merge 3 commits into
apache:mainfrom
hanzhe-one:fix/unicode-region-matches
Open

hanzhe-one wants to merge 3 commits into
apache:mainfrom
hanzhe-one:fix/unicode-region-matches

Conversation

@hanzhe-one

Copy link
Copy Markdown

Purpose of the pull request

Closed: #1301

StringUtils.regionMatches delegates to String.regionMatches when both values are Strings, but uses a manual fallback for other CharSequence types. The fallback compared the uppercase forms, then lowercased the original characters. For İ (U+0130) and ı (U+0131) that differs from the JDK: the uppercase forms differ and the directly lowercased forms differ, but lowercasing the uppercase results yields i for both. As a result, the String path returned true while the StringBuilder path returned false.

What's changed?

  • Compare the lowercase forms of the uppercase results in the fallback, matching the JDK's second-stage case folding.
  • Keep the existing String fast path, parameter checks, and NPE behavior unchanged.
  • Add a focused JUnit test covering String and StringBuilder inputs in both directions, with case-sensitive controls.

Validation:

  • ./mvnw -B -pl fesod-common test: 61 tests pass, including the new regression test.
  • ./mvnw -B spotless:check: passes.
  • The new test fails against the unfixed implementation and passes with the change.

No production caller currently uses ignoreCase=true, so this fixes the public utility's contract consistency rather than an observed spreadsheet data-loss path.

Opening as a draft while the approach on #1301 is still awaiting maintainer confirmation.

Checklist

  • I have read the Contributor Guide.
  • I have written the necessary doc or comment.
  • I have added the necessary unit tests and all cases have passed.

@hanzhe-one
hanzhe-one marked this pull request as ready for review November 2, 2026 11:24

@Mikkey-f Mikkey-f left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hanzhe-one — I verified the bug and the fix locally (Windows 11, JDK 17).

Root cause: The fallback lowercased the original characters, while String.regionMatches lowercases the uppercased forms. For İ (U+0130) and ı (U+0131) the uppercase forms differ (İ vs I) and the direct lowercase forms differ (i vs ı), but toLowerCase(toUpperCase(...)) yields i for both — so the String fast path returned true and the StringBuilder path returned false.

Verification

  • Red: with only StringUtils reverted (the new test kept), test_regionMatches_unicodeIgnoreCase fails with expected: <true> but was: <false>.
  • Green: the test passes; StringUtilsTest 9/9.
  • Full suite: 61 + 1,789 tests, 0 failures; spotless:check clean.

Code review: The new comparison is byte-for-byte the JDK's second-stage check, and the fast path, parameter validation, and NPE behavior are untouched. I also confirmed no production caller passes ignoreCase=true today (the only in-repo caller is StringUtils.equals, case-sensitive), so this aligns the public helper's contract with the JDK without changing a spreadsheet code path.

One nit (non-blocking): The contract is "same result as String.regionMatches"; the test currently hard-codes expected values. Comparing against ((String) cs).regionMatches(...) for the same inputs would pin the equivalence itself.

LGTM — minimal change that matches the JDK, with both directions covered.

@hanzhe-one
hanzhe-one requested a review from Mikkey-f November 5, 2026 13:59
@alaahong
alaahong requested a balanced review from Copilot and removed request for Mikkey-f November 7, 2026 01:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are reported for the Unicode case-folding consistency fix.

No open issues 🎉

What changed in this PR

Fixes #1301 by aligning the non-String CharSequence case-folding fallback with String.regionMatches for dotted and dotless Unicode I characters.

Changes:

  • Compare lowercase forms of the uppercase characters, preserving the String fast path and existing parameter handling.
  • Add regression coverage for String and StringBuilder combinations in both directions, including case-sensitive controls.
File Description
fesod-common/​src/​test/​java/​org/​apache/​fesod/​common/​util/​StringUtilsTest.java Tests Unicode comparison consistency across input types and directions.
fesod-common/​src/​main/​java/​org/​apache/​fesod/​common/​util/​StringUtils.java Corrects second-stage case folding in the fallback.

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Unicode case-insensitive regionMatches differs between String and StringBuilder

4 participants