You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2026/07/30 11:22:53 UTC
(commons-lang) branch master updated: Keep abbreviateMiddle off surrogate pair boundaries (#1758)
This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 4c83821cf Keep abbreviateMiddle off surrogate pair boundaries (#1758)
4c83821cf is described below
commit 4c83821cf01bfd13314ffb6a026b9c2e96fd2b63
Author: kdelay <90...@users.noreply.github.com>
AuthorDate: Thu Jul 30 20:22:48 2026 +0900
Keep abbreviateMiddle off surrogate pair boundaries (#1758)
---
.../java/org/apache/commons/lang3/StringUtils.java | 11 +++++++--
.../commons/lang3/StringUtilsAbbreviateTest.java | 28 ++++++++++++++++++++++
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java
index 17cdad6d9..5b8b03d72 100644
--- a/src/main/java/org/apache/commons/lang3/StringUtils.java
+++ b/src/main/java/org/apache/commons/lang3/StringUtils.java
@@ -429,8 +429,15 @@ public static String abbreviateMiddle(final String str, final String middle, fin
return str;
}
final int targetString = length - middle.length();
- final int startOffset = targetString / 2 + targetString % 2;
- final int endOffset = str.length() - targetString / 2;
+ int startOffset = targetString / 2 + targetString % 2;
+ int endOffset = str.length() - targetString / 2;
+ // keep both cuts off the middle of a surrogate pair so the result is never left holding a lone surrogate
+ if (splitsSurrogatePair(str, startOffset)) {
+ startOffset--;
+ }
+ if (splitsSurrogatePair(str, endOffset)) {
+ endOffset++;
+ }
return str.substring(0, startOffset) + middle + str.substring(endOffset);
}
diff --git a/src/test/java/org/apache/commons/lang3/StringUtilsAbbreviateTest.java b/src/test/java/org/apache/commons/lang3/StringUtilsAbbreviateTest.java
index 771cf1955..55d2f0fa2 100644
--- a/src/test/java/org/apache/commons/lang3/StringUtilsAbbreviateTest.java
+++ b/src/test/java/org/apache/commons/lang3/StringUtilsAbbreviateTest.java
@@ -207,6 +207,34 @@ void testAbbreviateMiddle() {
assertEquals("ab.ef", StringUtils.abbreviateMiddle("abcdef", ".", 5));
}
+ @Test
+ void testAbbreviateMiddleSurrogatePair() {
+ // U+1F600 GRINNING FACE is a single supplementary code point stored as a surrogate pair
+ final String grin = "😀";
+ // the head cut backs off the pair so the middle is never preceded by a lone high surrogate
+ assertEquals("a." + "b", StringUtils.abbreviateMiddle("a" + grin + grin + "b", ".", 4));
+ // the tail cut skips the orphaned low surrogate so the middle is never followed by one
+ assertEquals(grin + "..", StringUtils.abbreviateMiddle(grin + "abc" + grin, "..", 5));
+ // a cut that lands between two whole code points is unchanged
+ assertEquals("ab.d", StringUtils.abbreviateMiddle("ab" + grin + "cd", ".", 4));
+ assertEquals("ab.f", StringUtils.abbreviateMiddle("abcdef", ".", 4));
+ // results stay within length and never contain an unpaired surrogate
+ final String source = "a" + grin + "b" + grin + "cd" + grin + "ef";
+ for (int len = 3; len < source.length(); len++) {
+ final String result = StringUtils.abbreviateMiddle(source, ".", len);
+ assertTrue(result.length() <= len, () -> "result longer than length: " + result);
+ for (int i = 0; i < result.length(); i++) {
+ final char ch = result.charAt(i);
+ if (Character.isHighSurrogate(ch)) {
+ assertTrue(i + 1 < result.length() && Character.isLowSurrogate(result.charAt(i + 1)), "lone high surrogate in: " + result);
+ i++; // skip the paired low surrogate
+ } else {
+ assertFalse(Character.isLowSurrogate(ch), "lone low surrogate in: " + result);
+ }
+ }
+ }
+ }
+
@Test
void testAbbreviateSurrogatePair() {
// U+1F600 GRINNING FACE is a single supplementary code point stored as a surrogate pair