From f357e82e7b7ee519bb6feffe5a59a43c169159f4 Mon Sep 17 00:00:00 2001 From: Sharang Gupta Date: Wed, 21 Oct 2026 23:47:24 +0100 Subject: [PATCH] Use Locale.ROOT when lower-casing camel-case parts in ParsingUtils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ParsingUtils.split(source, toLower) lower-cases each camel-case part with String.toLowerCase(), which follows the JVM's default locale. Under a Turkish default locale an uppercase I becomes a dotless ı (U+0131), so every consumer of splitCamelCaseToLower/reconcatenateCamelCase produces identifiers that do not exist: Spring Data Relational's default table and column names ("InvoiceItem" -> "ınvoice_ıtem", "invoiceId" -> "invoice_ıd"), Cassandra's SnakeCaseNamingStrategy and CamelCaseSplittingFieldNamingStrategy. The camel-case split itself uses \p{Lu} and is locale-independent; only the lower-casing is affected. Use Locale.ROOT, as AotContext, RepositoryBeanDefinitionBuilder and RepositoryConfigurationExtension already do, and add a unit test that switches the default locale to tr-TR around the conversion. Signed-off-by: Sharang Gupta --- .../springframework/data/util/ParsingUtils.java | 6 +++++- .../data/util/ParsingUtilsUnitTests.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/util/ParsingUtils.java b/src/main/java/org/springframework/data/util/ParsingUtils.java index 39a2a766a6..14175ef207 100644 --- a/src/main/java/org/springframework/data/util/ParsingUtils.java +++ b/src/main/java/org/springframework/data/util/ParsingUtils.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.regex.Pattern; import org.springframework.util.Assert; @@ -27,6 +28,7 @@ * Utility methods for {@link String} parsing. * * @author Oliver Gierke + * @author Sharang Gupta * @since 1.5 */ public abstract class ParsingUtils { @@ -84,7 +86,9 @@ private static List split(String source, boolean toLower) { List result = new ArrayList<>(parts.length); for (String part : parts) { - result.add(toLower ? part.toLowerCase() : part); + // Locale.ROOT: the parts become identifiers (table, column and field names) and must not follow the default + // locale, under which e.g. "Item" would become "ıtem" (dotless i) for tr-TR. + result.add(toLower ? part.toLowerCase(Locale.ROOT) : part); } return Collections.unmodifiableList(result); diff --git a/src/test/java/org/springframework/data/util/ParsingUtilsUnitTests.java b/src/test/java/org/springframework/data/util/ParsingUtilsUnitTests.java index 5a685e33f5..8e40c38171 100755 --- a/src/test/java/org/springframework/data/util/ParsingUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/util/ParsingUtilsUnitTests.java @@ -17,12 +17,15 @@ import static org.assertj.core.api.Assertions.*; +import java.util.Locale; + import org.junit.jupiter.api.Test; /** * Unit tests for {@link ParsingUtils}. * * @author Oliver Gierke + * @author Sharang Gupta */ public class ParsingUtilsUnitTests { @@ -50,4 +53,18 @@ public void splitsCamelCaseWithAllSortsOfCharacters() { public void reconcatenatesCamelCaseString() { assertThat(ParsingUtils.reconcatenateCamelCase("myCamelCase", "-")).isEqualTo("my-camel-case"); } + + @Test + public void lowercasesPartsRegardlessOfDefaultLocale() { + + var defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + + try { + assertThat(ParsingUtils.reconcatenateCamelCase("InvoiceItem", "_")).isEqualTo("invoice_item"); + assertThat(ParsingUtils.splitCamelCaseToLower("invoiceId")).containsExactly("invoice", "id"); + } finally { + Locale.setDefault(defaultLocale); + } + } }