diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index e1b8c34a3f..478b717294 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -186,9 +186,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { } } - Class contextObjectClass = getObjectClass(contextObject.getValue()); - List accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors()); - + List 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 accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors()); + List 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 resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObjectValue), eContext.getPropertyAccessors()); - if (resolversToTry != null) { - for (PropertyAccessor pfResolver : resolversToTry) { + List 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 getPropertyAccessorsToTry(Class targetType, List propertyAccessors) { + private List getPropertyAccessorsToTry(Object contextObject, List propertyAccessors) { + Class targetType = (contextObject != null ? contextObject.getClass() : null); + List specificAccessors = new ArrayList(); List generalAccessors = new ArrayList(); 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); } } } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index 18bf68bd18..0c88e05359 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -286,6 +286,11 @@ public class SpelReproTests extends AbstractExpressionTests { 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)); @@ -306,11 +311,6 @@ public class SpelReproTests extends AbstractExpressionTests { public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { ((Map) target).put(name, newValue); } - - @Override - public Class[] getSpecificTargetClasses() { - return new Class[] { Map.class }; - } } @@ -1774,10 +1774,10 @@ public class SpelReproTests extends AbstractExpressionTests { public void SPR10486() throws Exception { SpelExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); - SPR10486 rootObject = new SPR10486(); + Spr10486 rootObject = new Spr10486(); Expression classNameExpression = parser.parseExpression("class.name"); Expression nameExpression = parser.parseExpression("name"); - assertThat(classNameExpression.getValue(context, rootObject), equalTo((Object) SPR10486.class.getName())); + assertThat(classNameExpression.getValue(context, rootObject), equalTo((Object) Spr10486.class.getName())); assertThat(nameExpression.getValue(context, rootObject), equalTo((Object) "name")); } @@ -1785,7 +1785,7 @@ public class SpelReproTests extends AbstractExpressionTests { public void SPR11142() throws Exception { SpelExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); - SPR11142 rootObject = new SPR11142(); + Spr11142 rootObject = new Spr11142(); Expression expression = parser.parseExpression("something"); thrown.expect(SpelEvaluationException.class); thrown.expectMessage("'something' cannot be found"); @@ -1837,26 +1837,6 @@ public class SpelReproTests extends AbstractExpressionTests { assertEquals(1, expr.getValue(context)); } - - static class Spr11445Class implements BeanResolver { - - private final AtomicInteger counter = new AtomicInteger(); - - public int echo(int invocation) { - return invocation; - } - - public int parameter() { - return counter.incrementAndGet(); - } - - @Override - public Object resolve(EvaluationContext context, String beanName) throws AccessException { - return beanName.equals("bean") ? this : null; - } - } - - @Test public void SPR11494() { Expression exp = new SpelExpressionParser().parseExpression("T(java.util.Arrays).asList('a','b')"); @@ -1864,6 +1844,15 @@ public class SpelReproTests extends AbstractExpressionTests { assertThat(list.size(), is(2)); } + @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(sec)); + } + private static enum ABC { A, B, C } @@ -1939,7 +1928,7 @@ public class SpelReproTests extends AbstractExpressionTests { } - public static class SPR10486 { + public static class Spr10486 { private String name = "name"; @@ -1953,7 +1942,7 @@ public class SpelReproTests extends AbstractExpressionTests { } - static class SPR11142 { + static class Spr11142 { public String isSomething() { return ""; @@ -1983,4 +1972,29 @@ public class SpelReproTests extends AbstractExpressionTests { } } + + static class Spr11445Class implements BeanResolver { + + private final AtomicInteger counter = new AtomicInteger(); + + public int echo(int invocation) { + return invocation; + } + + public int parameter() { + return counter.incrementAndGet(); + } + + @Override + public Object resolve(EvaluationContext context, String beanName) throws AccessException { + return beanName.equals("bean") ? this : null; + } + } + + + public static class MapWithConstant extends HashMap { + + public static final int X = 1; + } + }