SimpleEvaluationContext.Builder withRootObject/withTypedRootObject
Issue: SPR-16588
This commit is contained in:
parent
19875d8e3f
commit
c60cefa331
|
@ -23,9 +23,11 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.ConstructorResolver;
|
import org.springframework.expression.ConstructorResolver;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.MethodResolver;
|
import org.springframework.expression.MethodResolver;
|
||||||
import org.springframework.expression.OperatorOverloader;
|
import org.springframework.expression.OperatorOverloader;
|
||||||
import org.springframework.expression.PropertyAccessor;
|
import org.springframework.expression.PropertyAccessor;
|
||||||
|
@ -90,6 +92,8 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private final TypedValue rootObject;
|
||||||
|
|
||||||
private final List<PropertyAccessor> propertyAccessors;
|
private final List<PropertyAccessor> propertyAccessors;
|
||||||
|
|
||||||
private final List<MethodResolver> methodResolvers;
|
private final List<MethodResolver> methodResolvers;
|
||||||
|
@ -104,24 +108,21 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
|
|
||||||
private SimpleEvaluationContext(List<PropertyAccessor> accessors, List<MethodResolver> resolvers,
|
private SimpleEvaluationContext(List<PropertyAccessor> accessors, List<MethodResolver> resolvers,
|
||||||
@Nullable TypeConverter converter) {
|
@Nullable TypeConverter converter, @Nullable TypedValue rootObject) {
|
||||||
|
|
||||||
this.propertyAccessors = accessors;
|
this.propertyAccessors = accessors;
|
||||||
this.methodResolvers = resolvers;
|
this.methodResolvers = resolvers;
|
||||||
this.typeConverter = (converter != null ? converter : new StandardTypeConverter());
|
this.typeConverter = (converter != null ? converter : new StandardTypeConverter());
|
||||||
|
this.rootObject = (rootObject != null ? rootObject : TypedValue.NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code SimpleEvaluationContext} cannot be configured with a root object.
|
* Return the specified root object, if any.
|
||||||
* It is meant for repeated use with
|
|
||||||
* {@link org.springframework.expression.Expression Expression} method
|
|
||||||
* variants that accept both an {@code EvaluationContext} and a root object.
|
|
||||||
* @return Always returns {@link TypedValue#NULL}.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TypedValue getRootObject() {
|
public TypedValue getRootObject() {
|
||||||
return TypedValue.NULL;
|
return this.rootObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,6 +263,9 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
@Nullable
|
@Nullable
|
||||||
private TypeConverter typeConverter;
|
private TypeConverter typeConverter;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TypedValue rootObject;
|
||||||
|
|
||||||
public Builder(PropertyAccessor... accessors) {
|
public Builder(PropertyAccessor... accessors) {
|
||||||
this.accessors = Arrays.asList(accessors);
|
this.accessors = Arrays.asList(accessors);
|
||||||
}
|
}
|
||||||
|
@ -297,6 +301,18 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a custom {@link ConversionService}.
|
||||||
|
* <p>By default a {@link StandardTypeConverter} backed by a
|
||||||
|
* {@link org.springframework.core.convert.support.DefaultConversionService} is used.
|
||||||
|
* @see #withTypeConverter
|
||||||
|
* @see StandardTypeConverter#StandardTypeConverter(ConversionService)
|
||||||
|
*/
|
||||||
|
public Builder withConversionService(ConversionService conversionService) {
|
||||||
|
this.typeConverter = new StandardTypeConverter(conversionService);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Register a custom {@link TypeConverter}.
|
* Register a custom {@link TypeConverter}.
|
||||||
* <p>By default a {@link StandardTypeConverter} backed by a
|
* <p>By default a {@link StandardTypeConverter} backed by a
|
||||||
|
@ -310,19 +326,29 @@ public class SimpleEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a custom {@link ConversionService}.
|
* Specify a default root object to resolve against.
|
||||||
* <p>By default a {@link StandardTypeConverter} backed by a
|
* <p>Default is none, expecting an object argument at evaluation time.
|
||||||
* {@link org.springframework.core.convert.support.DefaultConversionService} is used.
|
* @see Expression#getValue(EvaluationContext)
|
||||||
* @see #withTypeConverter
|
* @see Expression#getValue(EvaluationContext, Object)
|
||||||
* @see StandardTypeConverter#StandardTypeConverter(ConversionService)
|
|
||||||
*/
|
*/
|
||||||
public Builder withConversionService(ConversionService conversionService) {
|
public Builder withRootObject(Object rootObject) {
|
||||||
this.typeConverter = new StandardTypeConverter(conversionService);
|
this.rootObject = new TypedValue(rootObject);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a typed root object to resolve against.
|
||||||
|
* <p>Default is none, expecting an object argument at evaluation time.
|
||||||
|
* @see Expression#getValue(EvaluationContext)
|
||||||
|
* @see Expression#getValue(EvaluationContext, Object)
|
||||||
|
*/
|
||||||
|
public Builder withTypedRootObject(Object rootObject, TypeDescriptor typeDescriptor) {
|
||||||
|
this.rootObject = new TypedValue(rootObject, typeDescriptor);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleEvaluationContext build() {
|
public SimpleEvaluationContext build() {
|
||||||
return new SimpleEvaluationContext(this.accessors, this.resolvers, this.typeConverter);
|
return new SimpleEvaluationContext(this.accessors, this.resolvers, this.typeConverter, this.rootObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -234,10 +234,29 @@ public class PropertyAccessTests extends AbstractExpressionTests {
|
||||||
assertEquals("p4", expr.getValue(context, target));
|
assertEquals("p4", expr.getValue(context, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void propertyReadWriteWithRootObject() {
|
||||||
|
Person target = new Person("p1");
|
||||||
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().withRootObject(target).build();
|
||||||
|
assertSame(target, context.getRootObject().getValue());
|
||||||
|
|
||||||
|
Expression expr = parser.parseExpression("name");
|
||||||
|
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
|
@Test
|
||||||
public void propertyAccessWithoutMethodResolver() {
|
public void propertyAccessWithoutMethodResolver() {
|
||||||
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
||||||
|
|
||||||
Person target = new Person("p1");
|
Person target = new Person("p1");
|
||||||
try {
|
try {
|
||||||
parser.parseExpression("name.substring(1)").getValue(context, target);
|
parser.parseExpression("name.substring(1)").getValue(context, target);
|
||||||
|
@ -251,11 +270,21 @@ public class PropertyAccessTests extends AbstractExpressionTests {
|
||||||
@Test
|
@Test
|
||||||
public void propertyAccessWithInstanceMethodResolver() {
|
public void propertyAccessWithInstanceMethodResolver() {
|
||||||
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
|
||||||
|
|
||||||
Person target = new Person("p1");
|
Person target = new Person("p1");
|
||||||
assertEquals("1", parser.parseExpression("name.substring(1)").getValue(context, target));
|
assertEquals("1", parser.parseExpression("name.substring(1)").getValue(context, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void propertyAccessWithInstanceMethodResolverAndTypedRootObject() {
|
||||||
|
Person target = new Person("p1");
|
||||||
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().
|
||||||
|
withInstanceMethods().withTypedRootObject(target, TypeDescriptor.valueOf(Object.class)).build();
|
||||||
|
|
||||||
|
assertEquals("1", parser.parseExpression("name.substring(1)").getValue(context, target));
|
||||||
|
assertSame(target, context.getRootObject().getValue());
|
||||||
|
assertSame(Object.class, context.getRootObject().getTypeDescriptor().getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// This can resolve the property 'flibbles' on any String (very useful...)
|
// This can resolve the property 'flibbles' on any String (very useful...)
|
||||||
private static class StringyPropertyAccessor implements PropertyAccessor {
|
private static class StringyPropertyAccessor implements PropertyAccessor {
|
||||||
|
|
Loading…
Reference in New Issue