Relax JavaBean rules for SpEL property access
Relax the method search algorithm used by `ReflectivePropertyAccessor` to include methods of the form `getXY()` for properties of the form `xy`. Although the JavaBean specification indicates that a property `xy` should use the accessors `getxY()` and `setxY()`, in practice many developers choose to have an uppercase first character. The `ReflectivePropertyAccessor` will now consider these style methods if the traditional conventions fail to find a match. Issue: SPR-10716
This commit is contained in:
parent
59fcf5014f
commit
b25e91a550
|
@ -318,42 +318,34 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
* Find a getter method for the specified property.
|
* Find a getter method for the specified property.
|
||||||
*/
|
*/
|
||||||
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
||||||
Method[] ms = getSortedClassMethods(clazz);
|
return findMethodForProperty(getPropertyMethodSuffixes(propertyName),
|
||||||
String propertyMethodSuffix = getPropertyMethodSuffix(propertyName);
|
new String[] { "get", "is" }, clazz, mustBeStatic, 0);
|
||||||
|
|
||||||
// Try "get*" method...
|
|
||||||
String getterName = "get" + propertyMethodSuffix;
|
|
||||||
for (Method method : ms) {
|
|
||||||
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
|
|
||||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Try "is*" method...
|
|
||||||
getterName = "is" + propertyMethodSuffix;
|
|
||||||
for (Method method : ms) {
|
|
||||||
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
|
|
||||||
(boolean.class.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType())) &&
|
|
||||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a setter method for the specified property.
|
* Find a setter method for the specified property.
|
||||||
*/
|
*/
|
||||||
protected Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
protected Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
||||||
|
return findMethodForProperty(getPropertyMethodSuffixes(propertyName),
|
||||||
|
new String[] { "set" }, clazz, mustBeStatic, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method findMethodForProperty(String[] methodSuffixes, String[] prefixes, Class<?> clazz,
|
||||||
|
boolean mustBeStatic, int numberOfParams) {
|
||||||
Method[] methods = getSortedClassMethods(clazz);
|
Method[] methods = getSortedClassMethods(clazz);
|
||||||
String setterName = "set" + getPropertyMethodSuffix(propertyName);
|
for (String methodSuffix : methodSuffixes) {
|
||||||
|
for (String prefix : prefixes) {
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
if (method.getName().equals(setterName) && method.getParameterTypes().length == 1 &&
|
if (method.getName().equals(prefix + methodSuffix)
|
||||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
&& method.getParameterTypes().length == numberOfParams
|
||||||
|
&& (!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,14 +362,30 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the method suffixes for a given property name. The default implementation
|
||||||
|
* uses JavaBean conventions with additional support for properties of the form 'xY'
|
||||||
|
* where the method 'getXY()' is used in preference to the JavaBean convention of
|
||||||
|
* 'getxY()'.
|
||||||
|
*/
|
||||||
|
protected String[] getPropertyMethodSuffixes(String propertyName) {
|
||||||
|
String suffix = getPropertyMethodSuffix(propertyName);
|
||||||
|
if (suffix.length() > 0 && Character.isUpperCase(suffix.charAt(0))) {
|
||||||
|
return new String[] { suffix };
|
||||||
|
}
|
||||||
|
return new String[] { suffix, StringUtils.capitalize(suffix) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the method suffix for a given property name. The default implementation
|
||||||
|
* uses JavaBean conventions.
|
||||||
|
*/
|
||||||
protected String getPropertyMethodSuffix(String propertyName) {
|
protected String getPropertyMethodSuffix(String propertyName) {
|
||||||
if (propertyName.length() > 1 && Character.isUpperCase(propertyName.charAt(1))) {
|
if (propertyName.length() > 1 && Character.isUpperCase(propertyName.charAt(1))) {
|
||||||
return propertyName;
|
return propertyName;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return StringUtils.capitalize(propertyName);
|
return StringUtils.capitalize(propertyName);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a field of a certain name on a specified class
|
* Find a field of a certain name on a specified class
|
||||||
|
|
|
@ -335,6 +335,12 @@ public class ReflectionHelperTests extends ExpressionTestCase {
|
||||||
assertEquals("id",rpr.read(ctx,t,"Id").getValue());
|
assertEquals("id",rpr.read(ctx,t,"Id").getValue());
|
||||||
assertTrue(rpr.canRead(ctx,t,"Id"));
|
assertTrue(rpr.canRead(ctx,t,"Id"));
|
||||||
|
|
||||||
|
// repro SPR-10994
|
||||||
|
assertEquals("xyZ",rpr.read(ctx,t,"xyZ").getValue());
|
||||||
|
assertTrue(rpr.canRead(ctx,t,"xyZ"));
|
||||||
|
assertEquals("xY",rpr.read(ctx,t,"xY").getValue());
|
||||||
|
assertTrue(rpr.canRead(ctx,t,"xY"));
|
||||||
|
|
||||||
// SPR-10122, ReflectivePropertyAccessor JavaBean property names compliance tests - setters
|
// SPR-10122, ReflectivePropertyAccessor JavaBean property names compliance tests - setters
|
||||||
rpr.write(ctx, t, "pEBS","Test String");
|
rpr.write(ctx, t, "pEBS","Test String");
|
||||||
assertEquals("Test String",rpr.read(ctx,t,"pEBS").getValue());
|
assertEquals("Test String",rpr.read(ctx,t,"pEBS").getValue());
|
||||||
|
@ -429,6 +435,8 @@ public class ReflectionHelperTests extends ExpressionTestCase {
|
||||||
String id = "id";
|
String id = "id";
|
||||||
String ID = "ID";
|
String ID = "ID";
|
||||||
String pEBS = "pEBS";
|
String pEBS = "pEBS";
|
||||||
|
String xY = "xY";
|
||||||
|
String xyZ = "xyZ";
|
||||||
|
|
||||||
public String getProperty() { return property; }
|
public String getProperty() { return property; }
|
||||||
public void setProperty(String value) { property = value; }
|
public void setProperty(String value) { property = value; }
|
||||||
|
@ -445,6 +453,10 @@ public class ReflectionHelperTests extends ExpressionTestCase {
|
||||||
|
|
||||||
public String getID() { return ID; }
|
public String getID() { return ID; }
|
||||||
|
|
||||||
|
public String getXY() { return xY; }
|
||||||
|
|
||||||
|
public String getXyZ() { return xyZ; }
|
||||||
|
|
||||||
public String getpEBS() {
|
public String getpEBS() {
|
||||||
return pEBS;
|
return pEBS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue