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-beanutils.git


The following commit(s) were added to refs/heads/master by this push:
     new cb86e732 Case-fold column and bean property names with Locale.ROOT 
(#415)
cb86e732 is described below

commit cb86e7325910c1c875dc939ea0fd85a55f385bb7
Author: Naveed Khan <[email protected]>
AuthorDate: Mon Jul 13 14:34:52 2026 +0000

    Case-fold column and bean property names with Locale.ROOT (#415)
    
    default-locale toLowerCase/toUpperCase mangle identifiers under a Turkish 
locale (column ID becomes property with a dotless i, and getImages is rebuilt 
with a dotted I); fold with Locale.ROOT, matching MappedPropertyDescriptor and 
the converters.
---
 .../beanutils2/DefaultBeanIntrospector.java        |  3 +-
 .../beanutils2/sql/AbstractJdbcDynaClass.java      |  3 +-
 .../beanutils2/sql/JdbcDynaClassLocaleTest.java    | 79 ++++++++++++++++++++++
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java 
b/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java
index 9d652caa..d665f2df 100644
--- a/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java
+++ b/src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java
@@ -23,6 +23,7 @@ import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Method;
 import java.util.List;
+import java.util.Locale;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -84,7 +85,7 @@ public class DefaultBeanIntrospector implements 
BeanIntrospector {
         for (final PropertyDescriptor pd : descriptors) {
             if (pd instanceof IndexedPropertyDescriptor) {
                 final IndexedPropertyDescriptor descriptor = 
(IndexedPropertyDescriptor) pd;
-                final String propName = descriptor.getName().substring(0, 
1).toUpperCase() + descriptor.getName().substring(1);
+                final String propName = descriptor.getName().substring(0, 
1).toUpperCase(Locale.ROOT) + descriptor.getName().substring(1);
 
                 if (descriptor.getReadMethod() == null) {
                     final String methodName = 
descriptor.getIndexedReadMethod() != null ? 
descriptor.getIndexedReadMethod().getName() : "get" + propName;
diff --git 
a/src/main/java/org/apache/commons/beanutils2/sql/AbstractJdbcDynaClass.java 
b/src/main/java/org/apache/commons/beanutils2/sql/AbstractJdbcDynaClass.java
index 04f49c45..40e0774c 100644
--- a/src/main/java/org/apache/commons/beanutils2/sql/AbstractJdbcDynaClass.java
+++ b/src/main/java/org/apache/commons/beanutils2/sql/AbstractJdbcDynaClass.java
@@ -26,6 +26,7 @@ import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 
@@ -91,7 +92,7 @@ abstract class AbstractJdbcDynaClass implements DynaClass {
         if (columnName == null || columnName.trim().isEmpty()) {
             columnName = metadata.getColumnName(i);
         }
-        final String name = lowerCase ? columnName.toLowerCase() : columnName;
+        final String name = lowerCase ? columnName.toLowerCase(Locale.ROOT) : 
columnName;
         if (!name.equals(columnName)) {
             if (columnNameXref == null) {
                 columnNameXref = new HashMap<>();
diff --git 
a/src/test/java/org/apache/commons/beanutils2/sql/JdbcDynaClassLocaleTest.java 
b/src/test/java/org/apache/commons/beanutils2/sql/JdbcDynaClassLocaleTest.java
new file mode 100644
index 00000000..48915dd3
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/beanutils2/sql/JdbcDynaClassLocaleTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.beanutils2.sql;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Types;
+import java.util.Locale;
+
+import org.apache.commons.beanutils2.DynaProperty;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that column names are folded to lower case independently of the 
default {@link Locale}.
+ */
+class JdbcDynaClassLocaleTest {
+
+    private static ResultSet singleColumnResultSet(final String columnName) {
+        final InvocationHandler metaData = (proxy, method, args) -> {
+            switch (method.getName()) {
+            case "getColumnCount":
+                return Integer.valueOf(1);
+            case "getColumnLabel":
+            case "getColumnName":
+                return columnName;
+            case "getColumnType":
+                return Integer.valueOf(Types.VARCHAR);
+            case "getColumnClassName":
+                return String.class.getName();
+            default:
+                throw new UnsupportedOperationException(method.getName());
+            }
+        };
+        final ResultSetMetaData metaDataProxy = (ResultSetMetaData) 
Proxy.newProxyInstance(ResultSetMetaData.class.getClassLoader(),
+                new Class[] { ResultSetMetaData.class }, metaData);
+        final InvocationHandler resultSet = (proxy, method, args) -> {
+            if ("getMetaData".equals(method.getName())) {
+                return metaDataProxy;
+            }
+            throw new UnsupportedOperationException(method.getName());
+        };
+        return (ResultSet) 
Proxy.newProxyInstance(ResultSet.class.getClassLoader(), new Class[] { 
ResultSet.class }, resultSet);
+    }
+
+    @Test
+    void testColumnNameLowerCasedIndependentOfLocale() throws Exception {
+        final Locale save = Locale.getDefault();
+        try {
+            // Under the Turkish locale "ID".toLowerCase() is "ıd" (dotless 
i), not "id".
+            Locale.setDefault(new Locale("tr", "TR"));
+            final ResultSetDynaClass dynaClass = new 
ResultSetDynaClass(singleColumnResultSet("ID"));
+            final DynaProperty property = dynaClass.getDynaProperty("id");
+            assertNotNull(property, "column 'ID' must map to property 'id'");
+            assertEquals("id", property.getName());
+        } finally {
+            Locale.setDefault(save);
+        }
+    }
+}

Reply via email to