Skip to content

Commit cb86e73

Browse files
authored
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.
1 parent 163ff0e commit cb86e73

3 files changed

Lines changed: 83 additions & 2 deletions

File tree

src/main/java/org/apache/commons/beanutils2/DefaultBeanIntrospector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.beans.PropertyDescriptor;
2424
import java.lang.reflect.Method;
2525
import java.util.List;
26+
import java.util.Locale;
2627

2728
import org.apache.commons.logging.Log;
2829
import org.apache.commons.logging.LogFactory;
@@ -84,7 +85,7 @@ private void handleIndexedPropertyDescriptors(final Class<?> beanClass, final Pr
8485
for (final PropertyDescriptor pd : descriptors) {
8586
if (pd instanceof IndexedPropertyDescriptor) {
8687
final IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor) pd;
87-
final String propName = descriptor.getName().substring(0, 1).toUpperCase() + descriptor.getName().substring(1);
88+
final String propName = descriptor.getName().substring(0, 1).toUpperCase(Locale.ROOT) + descriptor.getName().substring(1);
8889

8990
if (descriptor.getReadMethod() == null) {
9091
final String methodName = descriptor.getIndexedReadMethod() != null ? descriptor.getIndexedReadMethod().getName() : "get" + propName;

src/main/java/org/apache/commons/beanutils2/sql/AbstractJdbcDynaClass.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.ArrayList;
2727
import java.util.HashMap;
2828
import java.util.List;
29+
import java.util.Locale;
2930
import java.util.Map;
3031
import java.util.Objects;
3132

@@ -91,7 +92,7 @@ protected DynaProperty createDynaProperty(final ResultSetMetaData metadata, fina
9192
if (columnName == null || columnName.trim().isEmpty()) {
9293
columnName = metadata.getColumnName(i);
9394
}
94-
final String name = lowerCase ? columnName.toLowerCase() : columnName;
95+
final String name = lowerCase ? columnName.toLowerCase(Locale.ROOT) : columnName;
9596
if (!name.equals(columnName)) {
9697
if (columnNameXref == null) {
9798
columnNameXref = new HashMap<>();
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.beanutils2.sql;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertNotNull;
22+
23+
import java.lang.reflect.InvocationHandler;
24+
import java.lang.reflect.Proxy;
25+
import java.sql.ResultSet;
26+
import java.sql.ResultSetMetaData;
27+
import java.sql.Types;
28+
import java.util.Locale;
29+
30+
import org.apache.commons.beanutils2.DynaProperty;
31+
import org.junit.jupiter.api.Test;
32+
33+
/**
34+
* Tests that column names are folded to lower case independently of the default {@link Locale}.
35+
*/
36+
class JdbcDynaClassLocaleTest {
37+
38+
private static ResultSet singleColumnResultSet(final String columnName) {
39+
final InvocationHandler metaData = (proxy, method, args) -> {
40+
switch (method.getName()) {
41+
case "getColumnCount":
42+
return Integer.valueOf(1);
43+
case "getColumnLabel":
44+
case "getColumnName":
45+
return columnName;
46+
case "getColumnType":
47+
return Integer.valueOf(Types.VARCHAR);
48+
case "getColumnClassName":
49+
return String.class.getName();
50+
default:
51+
throw new UnsupportedOperationException(method.getName());
52+
}
53+
};
54+
final ResultSetMetaData metaDataProxy = (ResultSetMetaData) Proxy.newProxyInstance(ResultSetMetaData.class.getClassLoader(),
55+
new Class[] { ResultSetMetaData.class }, metaData);
56+
final InvocationHandler resultSet = (proxy, method, args) -> {
57+
if ("getMetaData".equals(method.getName())) {
58+
return metaDataProxy;
59+
}
60+
throw new UnsupportedOperationException(method.getName());
61+
};
62+
return (ResultSet) Proxy.newProxyInstance(ResultSet.class.getClassLoader(), new Class[] { ResultSet.class }, resultSet);
63+
}
64+
65+
@Test
66+
void testColumnNameLowerCasedIndependentOfLocale() throws Exception {
67+
final Locale save = Locale.getDefault();
68+
try {
69+
// Under the Turkish locale "ID".toLowerCase() is "ıd" (dotless i), not "id".
70+
Locale.setDefault(new Locale("tr", "TR"));
71+
final ResultSetDynaClass dynaClass = new ResultSetDynaClass(singleColumnResultSet("ID"));
72+
final DynaProperty property = dynaClass.getDynaProperty("id");
73+
assertNotNull(property, "column 'ID' must map to property 'id'");
74+
assertEquals("id", property.getName());
75+
} finally {
76+
Locale.setDefault(save);
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)