From 76c322c66f197a0401b9107a11b712d3f472e219 Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Sat, 13 Jun 2026 12:13:56 +0530 Subject: [PATCH 1/3] use locale-independent lowercasing in case conversion helpers --- .../java/org/apache/commons/text/CaseUtils.java | 3 ++- .../java/org/apache/commons/text/WordUtils.java | 3 ++- .../commons/text/lookup/StringLookupFactory.java | 2 +- .../org/apache/commons/text/CaseUtilsTest.java | 14 ++++++++++++++ .../org/apache/commons/text/WordUtilsTest.java | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/text/CaseUtils.java b/src/main/java/org/apache/commons/text/CaseUtils.java index d079fb8b84..075903a948 100644 --- a/src/main/java/org/apache/commons/text/CaseUtils.java +++ b/src/main/java/org/apache/commons/text/CaseUtils.java @@ -17,6 +17,7 @@ package org.apache.commons.text; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import org.apache.commons.lang3.ArrayUtils; @@ -70,7 +71,7 @@ public static String toCamelCase(String str, final boolean capitalizeFirstLetter if (StringUtils.isEmpty(str)) { return str; } - str = str.toLowerCase(); + str = str.toLowerCase(Locale.ROOT); final int strLen = str.length(); final int[] newCodePoints = new int[strLen]; int outOffset = 0; diff --git a/src/main/java/org/apache/commons/text/WordUtils.java b/src/main/java/org/apache/commons/text/WordUtils.java index 720ca5f98c..f76f028859 100644 --- a/src/main/java/org/apache/commons/text/WordUtils.java +++ b/src/main/java/org/apache/commons/text/WordUtils.java @@ -17,6 +17,7 @@ package org.apache.commons.text; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -250,7 +251,7 @@ public static String capitalizeFully(String str, final char... delimiters) { if (StringUtils.isEmpty(str)) { return str; } - str = str.toLowerCase(); + str = str.toLowerCase(Locale.ROOT); return capitalize(str, delimiters); } diff --git a/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java b/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java index 699b7deab7..a9c7b0ba10 100644 --- a/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java +++ b/src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java @@ -328,7 +328,7 @@ private static Map parseStringLookups(final String str) { try { for (final String lookupName : str.split("[\\s,]+")) { if (!lookupName.isEmpty()) { - addLookup(DefaultStringLookup.valueOf(lookupName.toUpperCase()), lookupMap); + addLookup(DefaultStringLookup.valueOf(lookupName.toUpperCase(Locale.ROOT)), lookupMap); } } } catch (final IllegalArgumentException exc) { diff --git a/src/test/java/org/apache/commons/text/CaseUtilsTest.java b/src/test/java/org/apache/commons/text/CaseUtilsTest.java index 899e5e8d22..b39c979fbd 100644 --- a/src/test/java/org/apache/commons/text/CaseUtilsTest.java +++ b/src/test/java/org/apache/commons/text/CaseUtilsTest.java @@ -24,6 +24,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; +import java.util.Locale; import org.junit.jupiter.api.Test; @@ -77,4 +78,17 @@ void testToCamelCase() { assertEquals("\uD800\uDF00\uD800\uDF01\uD800\uDF02\uD800\uDF03", CaseUtils.toCamelCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03", true, '\uD800', '\uDF14')); } + + @Test + void testToCamelCaseLocaleIndependent() { + final Locale dflt = Locale.getDefault(); + try { + // Turkish lower-cases 'I' (U+0049) to dotless 'i' (U+0131), which would otherwise leak into the result. + Locale.setDefault(new Locale("tr", "TR")); + assertEquals("TipTop", CaseUtils.toCamelCase("TIP.TOP", true, '.')); + assertEquals("toCamelCase", CaseUtils.toCamelCase("TO CAMEL CASE", false, null)); + } finally { + Locale.setDefault(dflt); + } + } } diff --git a/src/test/java/org/apache/commons/text/WordUtilsTest.java b/src/test/java/org/apache/commons/text/WordUtilsTest.java index 429a8934ff..7cbd4b4791 100644 --- a/src/test/java/org/apache/commons/text/WordUtilsTest.java +++ b/src/test/java/org/apache/commons/text/WordUtilsTest.java @@ -27,6 +27,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.time.Duration; +import java.util.Locale; import java.util.stream.IntStream; import org.apache.commons.lang3.ArrayUtils; @@ -124,6 +125,19 @@ void testCapitalizeFully_String() { } + @Test + void testCapitalizeFully_LocaleIndependent() { + final Locale dflt = Locale.getDefault(); + try { + // Turkish lower-cases 'I' (U+0049) to dotless 'i' (U+0131), which would otherwise leak into the result. + Locale.setDefault(new Locale("tr", "TR")); + assertEquals("Heli World", WordUtils.capitalizeFully("HELI WORLD")); + assertEquals("I Am Here 123", WordUtils.capitalizeFully("I AM HERE 123")); + } finally { + Locale.setDefault(dflt); + } + } + @Test void testCapitalizeFully_Text88() { assertEquals("I am fine now", WordUtils.capitalizeFully("i am fine now", new char[] {})); From e1d33d5794665354e3dcbdb27782f0b61fc4c5cb Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Sat, 13 Jun 2026 16:54:50 +0530 Subject: [PATCH 2/3] use junit pioneer @DefaultLocale to manage the default locale in tests --- .../org/apache/commons/text/CaseUtilsTest.java | 15 +++++---------- .../org/apache/commons/text/WordUtilsTest.java | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/test/java/org/apache/commons/text/CaseUtilsTest.java b/src/test/java/org/apache/commons/text/CaseUtilsTest.java index b39c979fbd..f9d7237d5b 100644 --- a/src/test/java/org/apache/commons/text/CaseUtilsTest.java +++ b/src/test/java/org/apache/commons/text/CaseUtilsTest.java @@ -24,9 +24,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; -import java.util.Locale; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.DefaultLocale; /** * Tests {@link CaseUtils} class. @@ -80,15 +80,10 @@ void testToCamelCase() { } @Test + @DefaultLocale(language = "tr", country = "TR") void testToCamelCaseLocaleIndependent() { - final Locale dflt = Locale.getDefault(); - try { - // Turkish lower-cases 'I' (U+0049) to dotless 'i' (U+0131), which would otherwise leak into the result. - Locale.setDefault(new Locale("tr", "TR")); - assertEquals("TipTop", CaseUtils.toCamelCase("TIP.TOP", true, '.')); - assertEquals("toCamelCase", CaseUtils.toCamelCase("TO CAMEL CASE", false, null)); - } finally { - Locale.setDefault(dflt); - } + // Turkish lower-cases 'I' (U+0049) to dotless 'i' (U+0131), which would otherwise leak into the result. + assertEquals("TipTop", CaseUtils.toCamelCase("TIP.TOP", true, '.')); + assertEquals("toCamelCase", CaseUtils.toCamelCase("TO CAMEL CASE", false, null)); } } diff --git a/src/test/java/org/apache/commons/text/WordUtilsTest.java b/src/test/java/org/apache/commons/text/WordUtilsTest.java index 7cbd4b4791..85b8d1d462 100644 --- a/src/test/java/org/apache/commons/text/WordUtilsTest.java +++ b/src/test/java/org/apache/commons/text/WordUtilsTest.java @@ -27,12 +27,12 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.time.Duration; -import java.util.Locale; import java.util.stream.IntStream; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.DefaultLocale; /** * Tests {@link WordUtils}. @@ -126,16 +126,11 @@ void testCapitalizeFully_String() { } @Test + @DefaultLocale(language = "tr", country = "TR") void testCapitalizeFully_LocaleIndependent() { - final Locale dflt = Locale.getDefault(); - try { - // Turkish lower-cases 'I' (U+0049) to dotless 'i' (U+0131), which would otherwise leak into the result. - Locale.setDefault(new Locale("tr", "TR")); - assertEquals("Heli World", WordUtils.capitalizeFully("HELI WORLD")); - assertEquals("I Am Here 123", WordUtils.capitalizeFully("I AM HERE 123")); - } finally { - Locale.setDefault(dflt); - } + // Turkish lower-cases 'I' (U+0049) to dotless 'i' (U+0131), which would otherwise leak into the result. + assertEquals("Heli World", WordUtils.capitalizeFully("HELI WORLD")); + assertEquals("I Am Here 123", WordUtils.capitalizeFully("I AM HERE 123")); } @Test From a1047fe29c919e016ff947cb70b51a3d382b740a Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Sun, 14 Jun 2026 10:04:53 +0530 Subject: [PATCH 3/3] add locale-independent test for default string lookups parsing --- .../commons/text/lookup/StringLookupFactoryTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java b/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java index caeccd8289..e0fdf8abe7 100644 --- a/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java +++ b/src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java @@ -32,6 +32,7 @@ import javax.xml.XMLConstants; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.DefaultLocale; import org.junitpioneer.jupiter.SetSystemProperty; /** @@ -173,6 +174,16 @@ void testDefaultStringLookupsHolder_givenSingleLookup() { checkDefaultStringLookupsHolder(props, "base64", StringLookupFactory.KEY_BASE64_ENCODER); } + @Test + @DefaultLocale(language = "tr", country = "TR") + void testDefaultStringLookupsHolder_givenSingleLookup_localeIndependent() { + // Turkish upper-cases 'i' (U+0069) to dotted 'İ' (U+0130), so "file" would fold to "FİLE" + // and fail to resolve against the DefaultStringLookup enum without Locale.ROOT. + final Properties props = new Properties(); + props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "file"); + checkDefaultStringLookupsHolder(props, StringLookupFactory.KEY_FILE); + } + @Test void testDefaultStringLookupsHolder_givenSingleLookup_weirdString() { final Properties props = new Properties();