Correctly resolve accessors for static properties on Class
Issue: SPR-11609
(cherry picked from commit 3af8a32)
This commit is contained in:
@@ -186,9 +186,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
|
||||
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
|
||||
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
|
||||
// get the accessor and use it. If they are not cacheable but report they can read the property
|
||||
// then ask them to read it
|
||||
@@ -214,7 +212,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, name,
|
||||
FormatHelper.formatClassNameForMessage(contextObjectClass));
|
||||
FormatHelper.formatClassNameForMessage(getObjectClass(contextObject.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,9 +234,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
|
||||
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
|
||||
if (accessorsToTry != null) {
|
||||
try {
|
||||
for (PropertyAccessor accessor : accessorsToTry) {
|
||||
@@ -259,18 +255,16 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name,
|
||||
FormatHelper.formatClassNameForMessage(contextObjectClass));
|
||||
FormatHelper.formatClassNameForMessage(getObjectClass(contextObject.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext eContext) throws SpelEvaluationException {
|
||||
Object contextObjectValue = contextObject.getValue();
|
||||
// TypeDescriptor td = state.getActiveContextObject().getTypeDescriptor();
|
||||
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObjectValue), eContext.getPropertyAccessors());
|
||||
if (resolversToTry != null) {
|
||||
for (PropertyAccessor pfResolver : resolversToTry) {
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
|
||||
if (accessorsToTry != null) {
|
||||
for (PropertyAccessor accessor : accessorsToTry) {
|
||||
try {
|
||||
if (pfResolver.canWrite(eContext, contextObjectValue, name)) {
|
||||
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -290,27 +284,28 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
* the start of the list. In addition, there are specific resolvers that exactly name the class in question and
|
||||
* resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
|
||||
* specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
|
||||
* @param targetType the type upon which property access is being attempted
|
||||
* @param contextObject the object upon which property access is being attempted
|
||||
* @return a list of resolvers that should be tried in order to access the property
|
||||
*/
|
||||
private List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
|
||||
private List<PropertyAccessor> getPropertyAccessorsToTry(Object contextObject, List<PropertyAccessor> propertyAccessors) {
|
||||
Class<?> targetType = (contextObject != null ? contextObject.getClass() : null);
|
||||
|
||||
List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
|
||||
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
|
||||
for (PropertyAccessor resolver : propertyAccessors) {
|
||||
Class<?>[] targets = resolver.getSpecificTargetClasses();
|
||||
if (targets == null) { // generic resolver that says it can be used for any type
|
||||
if (targets == null) {
|
||||
// generic resolver that says it can be used for any type
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
else {
|
||||
if (targetType != null) {
|
||||
for (Class<?> clazz : targets) {
|
||||
if (clazz == targetType) {
|
||||
specificAccessors.add( resolver);
|
||||
break;
|
||||
}
|
||||
else if (clazz.isAssignableFrom(targetType)) {
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
else if (targetType != null) {
|
||||
for (Class<?> clazz : targets) {
|
||||
if (clazz == targetType) {
|
||||
specificAccessors.add(resolver);
|
||||
break;
|
||||
}
|
||||
else if (clazz.isAssignableFrom(targetType)) {
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,6 +285,11 @@ public class SpelReproTests extends ExpressionTestCase {
|
||||
|
||||
static class MapAccessor implements PropertyAccessor {
|
||||
|
||||
@Override
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return new Class<?>[] {Map.class};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
return (((Map<?, ?>) target).containsKey(name));
|
||||
@@ -305,11 +310,6 @@ public class SpelReproTests extends ExpressionTestCase {
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
((Map<String, Object>) target).put(name, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return new Class[] { Map.class };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1828,9 +1828,11 @@ public class SpelReproTests extends ExpressionTestCase {
|
||||
|
||||
@Test
|
||||
public void SPR11609() {
|
||||
StandardEvaluationContext sec = new StandardEvaluationContext();
|
||||
sec.addPropertyAccessor(new MapAccessor());
|
||||
Expression exp = new SpelExpressionParser().parseExpression(
|
||||
"T(org.springframework.expression.spel.SpelReproTests$MapWithConstant).X");
|
||||
assertEquals(1, exp.getValue());
|
||||
assertEquals(1, exp.getValue(sec));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user