Backported DataBindingPropertyAccessor and DataBindingMethodResolver

Issue: SPR-16588
This commit is contained in:
Juergen Hoeller
2018-03-28 01:22:59 +02:00
parent f046a066ec
commit 65a8aa1c09
8 changed files with 602 additions and 123 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,9 @@ import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.Person;
import static org.junit.Assert.*;
@@ -73,7 +75,7 @@ public class PropertyAccessTests extends AbstractExpressionTests {
* supplied resolver might be able to - so null shouldn't crash the reflection resolver.
*/
@Test
public void testAccessingOnNullObject() throws Exception {
public void testAccessingOnNullObject() {
SpelExpression expr = (SpelExpression)parser.parseExpression("madeup");
EvaluationContext context = new StandardEvaluationContext(null);
try {
@@ -85,7 +87,7 @@ public class PropertyAccessTests extends AbstractExpressionTests {
}
assertFalse(expr.isWritable(context));
try {
expr.setValue(context,"abc");
expr.setValue(context, "abc");
fail("Should have failed - default property resolver cannot resolve on null");
}
catch (Exception ex) {
@@ -93,19 +95,19 @@ public class PropertyAccessTests extends AbstractExpressionTests {
}
}
private void checkException(Exception e, SpelMessage expectedMessage) {
if (e instanceof SpelEvaluationException) {
SpelMessage sm = ((SpelEvaluationException)e).getMessageCode();
assertEquals("Expected exception type did not occur",expectedMessage,sm);
private void checkException(Exception ex, SpelMessage expectedMessage) {
if (ex instanceof SpelEvaluationException) {
SpelMessage sm = ((SpelEvaluationException) ex).getMessageCode();
assertEquals("Expected exception type did not occur", expectedMessage, sm);
}
else {
fail("Should be a SpelException "+e);
fail("Should be a SpelException " + ex);
}
}
@Test
// Adding a new property accessor just for a particular type
public void testAddingSpecificPropertyAccessor() throws Exception {
public void testAddingSpecificPropertyAccessor() {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext ctx = new StandardEvaluationContext();
@@ -115,7 +117,7 @@ public class PropertyAccessTests extends AbstractExpressionTests {
ctx.addPropertyAccessor(new StringyPropertyAccessor());
Expression expr = parser.parseRaw("new String('hello').flibbles");
Integer i = expr.getValue(ctx, Integer.class);
assertEquals((int) i, 7);
assertEquals(7, (int) i);
// The reflection one will be used for other properties...
expr = parser.parseRaw("new String('hello').CASE_INSENSITIVE_ORDER");
@@ -125,7 +127,7 @@ public class PropertyAccessTests extends AbstractExpressionTests {
expr = parser.parseRaw("new String('hello').flibbles");
expr.setValue(ctx, 99);
i = expr.getValue(ctx, Integer.class);
assertEquals((int) i, 99);
assertEquals(99, (int) i);
// Cannot set it to a string value
try {
@@ -162,10 +164,10 @@ public class PropertyAccessTests extends AbstractExpressionTests {
}
@Test
public void testAccessingPropertyOfClass() throws Exception {
public void testAccessingPropertyOfClass() {
Expression expression = parser.parseExpression("name");
Object value = expression.getValue(new StandardEvaluationContext(String.class));
assertEquals(value, "java.lang.String");
assertEquals("java.lang.String", value);
}
@Test
@@ -182,6 +184,78 @@ public class PropertyAccessTests extends AbstractExpressionTests {
assertEquals("Jens", expression.getValue(context));
}
@Test
public void standardGetClassAccess() {
assertEquals(String.class.getName(), parser.parseExpression("'a'.class.name").getValue());
}
@Test(expected = SpelEvaluationException.class)
public void noGetClassAccess() {
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
parser.parseExpression("'a'.class.name").getValue(context);
}
@Test
public void propertyReadOnly() {
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Expression expr = parser.parseExpression("name");
Person target = new Person("p1");
assertEquals("p1", expr.getValue(context, target));
target.setName("p2");
assertEquals("p2", expr.getValue(context, target));
try {
parser.parseExpression("name='p3'").getValue(context, target);
fail("Should have thrown SpelEvaluationException");
}
catch (SpelEvaluationException ex) {
// expected
}
}
@Test
public void propertyReadWrite() {
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
Expression expr = parser.parseExpression("name");
Person target = new Person("p1");
assertEquals("p1", expr.getValue(context, target));
target.setName("p2");
assertEquals("p2", expr.getValue(context, target));
parser.parseExpression("name='p3'").getValue(context, target);
assertEquals("p3", target.getName());
assertEquals("p3", expr.getValue(context, target));
expr.setValue(context, target, "p4");
assertEquals("p4", target.getName());
assertEquals("p4", expr.getValue(context, target));
}
@Test
public void propertyAccessWithoutMethodResolver() {
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Person target = new Person("p1");
try {
parser.parseExpression("name.substring(1)").getValue(context, target);
fail("Should have thrown SpelEvaluationException");
}
catch (SpelEvaluationException ex) {
// expected
}
}
@Test
public void propertyAccessWithInstanceMethodResolver() {
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
Person target = new Person("p1");
assertEquals("1", parser.parseExpression("name.substring(1)").getValue(context, target));
}
// This can resolve the property 'flibbles' on any String (very useful...)
private static class StringyPropertyAccessor implements PropertyAccessor {
@@ -223,7 +297,8 @@ public class PropertyAccessTests extends AbstractExpressionTests {
throw new RuntimeException("Assertion Failed! name should be flibbles");
}
try {
flibbles = (Integer) context.getTypeConverter().convertValue(newValue, TypeDescriptor.forObject(newValue), TypeDescriptor.valueOf(Integer.class));
flibbles = (Integer) context.getTypeConverter().convertValue(newValue,
TypeDescriptor.forObject(newValue), TypeDescriptor.valueOf(Integer.class));
}
catch (EvaluationException ex) {
throw new AccessException("Cannot set flibbles to an object of type '" + newValue.getClass() + "'");