Browse Source

Correctly resolve accessors for static properties on Class

Issue: SPR-11609
pull/500/head
Juergen Hoeller 12 years ago
parent
commit
3af8a3260a
  1. 49
      spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
  2. 74
      spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java

49
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<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then // 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 // get the accessor and use it. If they are not cacheable but report they can read the property
// then ask them to read it // then ask them to read it
@ -214,7 +212,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
} }
else { else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, name, 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(contextObject.getValue(), eContext.getPropertyAccessors());
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
if (accessorsToTry != null) { if (accessorsToTry != null) {
try { try {
for (PropertyAccessor accessor : accessorsToTry) { for (PropertyAccessor accessor : accessorsToTry) {
@ -259,18 +255,16 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
} }
else { else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name, 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 { public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext eContext) throws SpelEvaluationException {
Object contextObjectValue = contextObject.getValue(); List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
// TypeDescriptor td = state.getActiveContextObject().getTypeDescriptor(); if (accessorsToTry != null) {
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObjectValue), eContext.getPropertyAccessors()); for (PropertyAccessor accessor : accessorsToTry) {
if (resolversToTry != null) {
for (PropertyAccessor pfResolver : resolversToTry) {
try { try {
if (pfResolver.canWrite(eContext, contextObjectValue, name)) { if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
return true; 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 * 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 * 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. * 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 * @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> specificAccessors = new ArrayList<PropertyAccessor>();
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>(); List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
for (PropertyAccessor resolver : propertyAccessors) { for (PropertyAccessor resolver : propertyAccessors) {
Class<?>[] targets = resolver.getSpecificTargetClasses(); 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); generalAccessors.add(resolver);
} }
else { else if (targetType != null) {
if (targetType != null) { for (Class<?> clazz : targets) {
for (Class<?> clazz : targets) { if (clazz == targetType) {
if (clazz == targetType) { specificAccessors.add(resolver);
specificAccessors.add( resolver); break;
break; }
} else if (clazz.isAssignableFrom(targetType)) {
else if (clazz.isAssignableFrom(targetType)) { generalAccessors.add(resolver);
generalAccessors.add(resolver);
}
} }
} }
} }

74
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 { static class MapAccessor implements PropertyAccessor {
@Override
public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[] {Map.class};
}
@Override @Override
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return (((Map<?, ?>) target).containsKey(name)); 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 { public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
((Map<String, Object>) target).put(name, newValue); ((Map<String, Object>) 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 { public void SPR10486() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser(); SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
SPR10486 rootObject = new SPR10486(); Spr10486 rootObject = new Spr10486();
Expression classNameExpression = parser.parseExpression("class.name"); Expression classNameExpression = parser.parseExpression("class.name");
Expression nameExpression = parser.parseExpression("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")); assertThat(nameExpression.getValue(context, rootObject), equalTo((Object) "name"));
} }
@ -1785,7 +1785,7 @@ public class SpelReproTests extends AbstractExpressionTests {
public void SPR11142() throws Exception { public void SPR11142() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser(); SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
SPR11142 rootObject = new SPR11142(); Spr11142 rootObject = new Spr11142();
Expression expression = parser.parseExpression("something"); Expression expression = parser.parseExpression("something");
thrown.expect(SpelEvaluationException.class); thrown.expect(SpelEvaluationException.class);
thrown.expectMessage("'something' cannot be found"); thrown.expectMessage("'something' cannot be found");
@ -1837,26 +1837,6 @@ public class SpelReproTests extends AbstractExpressionTests {
assertEquals(1, expr.getValue(context)); 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 @Test
public void SPR11494() { public void SPR11494() {
Expression exp = new SpelExpressionParser().parseExpression("T(java.util.Arrays).asList('a','b')"); 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)); 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 } 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"; private String name = "name";
@ -1953,7 +1942,7 @@ public class SpelReproTests extends AbstractExpressionTests {
} }
static class SPR11142 { static class Spr11142 {
public String isSomething() { public String isSomething() {
return ""; 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;
}
} }

Loading…
Cancel
Save