vjsai opened a new pull request, #761:
URL: https://github.com/apache/commons-text/pull/761

   `WordUtils.abbreviate()` slices the input at raw `char` offsets, so an upper 
limit that lands between a high surrogate and its trailing low surrogate leaves 
an **unpaired surrogate** at the end of the result.
   
   ## Reproduction
   
   ```java
   WordUtils.abbreviate("😀😀😀", 0, 3, "");
   ```
   
   The string is 6 `char`s long and contains no space, so `abbreviate` appends 
`str[0, 3)` — which is one whole emoji plus the **high surrogate** of the 
second one.
   
   - Before: `"😀\uD83D"` — one emoji followed by a lone high surrogate (renders 
as `😀?`)
   - After: `"😀"` — `"😀"`
   
   Both call sites are affected: the no-space-found branch (cut at `upper`) and 
the space-found branch (cut at `min(index, upper)`).
   
   ## Fix
   
   When the cut would split a pair, back it off by one `char` so the whole pair 
is dropped, and the result stays within the requested upper limit. This is the 
same rule the surrounding code already applies elsewhere — `WordUtils.wrap()` 
keeps a pair whole at a hard break, and `StringUtils.abbreviate()` in Commons 
Lang backs its head cut off a pair boundary via a `splitsSurrogatePair` helper. 
This PR adds the same private helper to `WordUtils`, so the class now handles 
surrogate pairs consistently across `wrap`, `initials`, and `abbreviate`.
   
   This is a follow-on to the same class of fix already applied in this repo to 
`WordUtils.wrap` (#755) and `TextStringBuilder.reverse` (#756); `abbreviate` 
was missed by that sweep.
   
   ## Test
   
   `WordUtilsTest.testAbbreviateSurrogatePairs()` covers a cut inside a pair 
(with and without `appendToEnd`), a cut short of a space, and a limit that 
already falls between two pairs (which must be left alone).
   
   The test **fails before** the change and **passes after**:
   
   ```
   [ERROR] WordUtilsTest.testAbbreviateSurrogatePairs:92 expected: <😀> but was: 
<😀?>
   [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
   ```
   
   After the fix, the default `mvn` goal is green on JDK 17 (`clean verify 
apache-rat:check japicmp:cmp checkstyle:check pmd:check spotbugs:check 
javadoc:javadoc`):
   
   ```
   [INFO] Tests run: 1889, Failures: 0, Errors: 0, Skipped: 3
   [INFO] BUILD SUCCESS
   ```
   
   Behavior for strings without surrogate pairs is unchanged, and the change is 
binary compatible (the new helper is private) — `japicmp` reports no issues.
   
   ---
   
   - [x] Read the [contribution guidelines](CONTRIBUTING.md) for this project.
   - [x] Read the [ASF Generative Tooling 
Guidance](https://www.apache.org/legal/generative-tooling.html) if you use 
Artificial Intelligence (AI).
   - [x] **I used AI to create any part of, or all of, this pull request. Which 
AI tool was used to create this pull request, and to what extent did it 
contribute?**
         Claude Code (Anthropic) was used to help locate the bug, draft the fix 
and the test, and run the build. I reviewed and verified the change myself: I 
confirmed the failing assertion before the fix, confirmed the full default-goal 
build passes after it, and checked the fix against the existing surrogate-pair 
handling in `WordUtils.wrap` and `StringUtils.abbreviate`. I understand and 
take responsibility for the contents of this PR.
   - [x] Run a successful build using the default 
[Maven](https://maven.apache.org/) goal with `mvn`; that's `mvn` on the command 
line by itself.
   - [x] Write unit tests that match behavioral changes, where the tests fail 
if the changes to the runtime are not applied.
   - [x] Write a pull request description that is detailed enough to understand 
what the pull request does, how, and why.
   - [x] Each commit in the pull request should have a meaningful subject line 
and body.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to