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);
+ }
+ }
+}