提交 54865c0c 编写于 作者: A Andy Clement

initial typeDescriptor awareness in the EL. some basic testing of using GenericConversionService

上级 65afc808
......@@ -16,6 +16,8 @@
package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
* A property accessor is able to read (and possibly write) to object properties. The interface places no restrictions
* and so implementors are free to access properties directly as fields or through getters or in any other way they see
......@@ -41,6 +43,15 @@ public interface PropertyAccessor {
* @return an array of classes that this resolver is suitable for (or null if a general resolver)
*/
Class[] getSpecificTargetClasses();
/**
* Called to retrieve a type descriptor that describes the type of the property.
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return a type descriptor that describes the type of this property.
*/
TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name);
/**
* Called to determine if a resolver instance is able to access a specified property on a specified target object.
......
......@@ -16,6 +16,8 @@
package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
* A type converter can convert values between different types encountered
* during expression evaluation.
......@@ -26,6 +28,7 @@ package org.springframework.expression;
public interface TypeConverter {
// TODO replace this stuff with Keiths spring-binding conversion code
// TODO should ExpressionException be thrown for lost precision in the case of coercion?
// TODO could remove the methods where the target is Class and just keep the TypeDescriptor variants
/**
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
......@@ -36,6 +39,17 @@ public interface TypeConverter {
*/
<T> T convertValue(Object value, Class<T> targetType) throws EvaluationException;
/**
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
* The typeDescriptor parameter enables support for typed collections - if the caller really wishes they
* can have a List<Integer> for example, rather than simply a List.
* @param value the value to be converted
* @param typeDescriptor a type descriptor that supplies extra information about the requested result type
* @return the converted value
* @throws EvaluationException if conversion is not possible
*/
<T> T convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException;
/**
* Return true if the type converter can convert the specified type to the desired target type.
* @param sourceType the type to be converted from
......@@ -44,4 +58,12 @@ public interface TypeConverter {
*/
boolean canConvert(Class<?> sourceType, Class<?> targetType);
/**
* Return true if the type converter can convert the specified type to the desired target type.
* @param sourceType the type to be converted from
* @param typeDescriptor a type descriptor that supplies extra information about the requested result type
* @return true if that conversion can be performed
*/
boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor);
}
......@@ -20,6 +20,7 @@ import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
......@@ -99,6 +100,10 @@ public class ExpressionState {
return this.relatedContext.getTypeLocator().findType(type);
}
public <T> T convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value, targetTypeDescriptor);
}
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value, targetType);
}
......
......@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
......@@ -51,7 +52,7 @@ public class Indexer extends SpelNodeImpl {
return ((Map<?, ?>) ctx).get(index);
}
int idx = state.convertValue(index, Integer.class);
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
if (ctx.getClass().isArray()) {
return accessArrayElement(ctx, idx);
......@@ -109,7 +110,7 @@ public class Indexer extends SpelNodeImpl {
return;
}
int idx = state.convertValue(index, Integer.class);
int idx = state.convertValue(index, INTEGER_TYPE_DESCRIPTOR);
if (ctx.getClass().isArray()) {
setArrayElement(state, ctx, idx, newValue);
......
......@@ -18,8 +18,8 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* Represents the boolean AND operation.
......@@ -44,7 +44,7 @@ public class OperatorAnd extends Operator {
boolean rightValue;
try {
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class);
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
}
catch (SpelException ee) {
ee.setPosition(getLeftOperand().getCharPositionInLine());
......@@ -56,7 +56,7 @@ public class OperatorAnd extends Operator {
}
try {
rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class);
rightValue = state.convertValue(getRightOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
}
catch (SpelException ee) {
ee.setPosition(getRightOperand().getCharPositionInLine());
......
......@@ -18,8 +18,8 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* Represents a NOT operation.
......@@ -36,7 +36,7 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
try {
boolean value = state.convertValue(getChild(0).getValueInternal(state), Boolean.class);
boolean value = state.convertValue(getChild(0).getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
return !value;
}
catch (SpelException see) {
......
......@@ -18,8 +18,8 @@ package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* Represents the boolean OR operation.
......@@ -43,7 +43,7 @@ public class OperatorOr extends Operator {
boolean leftValue;
boolean rightValue;
try {
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class);
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
}
catch (SpelException see) {
see.setPosition(getLeftOperand().getCharPositionInLine());
......@@ -55,7 +55,7 @@ public class OperatorOr extends Operator {
}
try {
rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class);
rightValue = state.convertValue(getRightOperand().getValueInternal(state), BOOLEAN_TYPE_DESCRIPTOR);
}
catch (SpelException see) {
see.setPosition(getRightOperand().getCharPositionInLine());
......
......@@ -20,8 +20,10 @@ import java.util.ArrayList;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
......@@ -41,6 +43,8 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
private volatile PropertyAccessor cachedReadAccessor;
private volatile PropertyAccessor cachedWriteAccessor;
private volatile TypeDescriptor cachedTypeDescriptor;
public PropertyOrFieldReference(Token payload) {
......@@ -121,8 +125,20 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
if (accessorToUse != null) {
try {
accessorToUse.write(state.getEvaluationContext(), contextObject, name, newValue);
try {
Object possiblyConvertedValue = newValue;
if (cachedTypeDescriptor == null) {
cachedTypeDescriptor=accessorToUse.getTypeDescriptor(eContext, contextObject, name);
}
if (cachedTypeDescriptor != null) {
try {
possiblyConvertedValue = state.convertValue(newValue, cachedTypeDescriptor.getType());
} catch (EvaluationException evaluationException) {
throw new SpelException(getCharPositionInLine(), evaluationException, SpelMessages.TYPE_CONVERSION_ERROR,
newValue.getClass(), cachedTypeDescriptor.getType());
}
}
accessorToUse.write(state.getEvaluationContext(), contextObject, name, possiblyConvertedValue);
return;
}
catch (AccessException ae) {
......@@ -140,7 +156,19 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(eContext, contextObject, name)) {
this.cachedWriteAccessor = accessor;
accessor.write(eContext, contextObject, name, newValue); // TODO missing conversion of newValue to the type of the property
Object possiblyConvertedValue = newValue;
if (cachedTypeDescriptor == null) {
cachedTypeDescriptor=accessor.getTypeDescriptor(eContext, contextObject, name);
}
if (cachedTypeDescriptor != null) {
try {
possiblyConvertedValue = state.convertValue(newValue, cachedTypeDescriptor);
} catch (EvaluationException evaluationException) {
throw new SpelException(getCharPositionInLine(), evaluationException, SpelMessages.TYPE_CONVERSION_ERROR,
newValue.getClass(), cachedTypeDescriptor.getType());
}
}
accessor.write(eContext, contextObject, name, possiblyConvertedValue);
return;
}
}
......
......@@ -20,7 +20,7 @@ import java.io.Serializable;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.common.ExpressionUtils;
import org.springframework.expression.spel.ExpressionState;
......@@ -38,6 +38,9 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
*/
public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable {
protected static TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);
protected static TypeDescriptor INTEGER_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Integer.class);
/**
* The Antlr parser uses this constructor to build SpelNodes.
* @param payload the token for the node that has been parsed
......
......@@ -24,6 +24,8 @@ import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
......@@ -32,9 +34,7 @@ import org.springframework.util.StringUtils;
/**
* Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written). This
* implementation currently follows the Resolver/Executor model (it extends CacheablePropertyAccessor) - the code that
* would be used if it were a simple property accessor is shown at the end.
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written).
*
* @author Andy Clement
* @author Juergen Hoeller
......@@ -42,10 +42,11 @@ import org.springframework.util.StringUtils;
*/
public class ReflectivePropertyResolver implements PropertyAccessor {
private final Map<CacheKey, Member> readerCache = new ConcurrentHashMap<CacheKey, Member>();
private final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
protected final Map<CacheKey, Member> readerCache = new ConcurrentHashMap<CacheKey, Member>();
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey,TypeDescriptor>();
/**
* @return null which means this is a general purpose accessor
......@@ -69,12 +70,14 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
Method method = findGetterForProperty(name, type, target instanceof Class);
if (method != null) {
this.readerCache.put(cacheKey, method);
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(new MethodParameter(method,0)));
return true;
}
else {
Field field = findField(name, type, target instanceof Class);
if (field != null) {
this.readerCache.put(cacheKey, field);
this.readerCache.put(cacheKey, field);
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
return true;
}
}
......@@ -152,12 +155,14 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
Method method = findSetterForProperty(name, type, target instanceof Class);
if (method != null) {
this.writerCache.put(cacheKey, method);
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(new MethodParameter(method,0)));
return true;
}
else {
Field field = findField(name, type, target instanceof Class);
if (field != null) {
this.writerCache.put(cacheKey, field);
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
return true;
}
}
......@@ -217,7 +222,32 @@ public class ReflectivePropertyResolver implements PropertyAccessor {
throw new AccessException("Neither setter nor field found for property '" + name + "'");
}
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
if (target == null) {
return null;
}
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
if (type.isArray() && name.equals("length")) {
return TypeDescriptor.valueOf(Integer.TYPE);
}
CacheKey cacheKey = new CacheKey(type, name);
TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey);
if (typeDescriptor == null) {
// attempt to populate the cache entry
try {
if (canRead(context, target, name)) {
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
} else if (canWrite(context, target, name)) {
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
}
} catch (AccessException e) {
// continue with null typeDescriptor
}
}
return typeDescriptor;
}
/**
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
......
......@@ -16,6 +16,7 @@
package org.springframework.expression.spel.support;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelException;
......@@ -25,6 +26,7 @@ import org.springframework.util.NumberUtils;
/**
* @author Juergen Hoeller
* @author Andy Clement
* @since 3.0
*/
public class StandardTypeConverter implements TypeConverter {
......@@ -74,6 +76,11 @@ public class StandardTypeConverter implements TypeConverter {
throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType);
}
@SuppressWarnings("unchecked")
public <T> T convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException {
return (T)convertValue(value,typeDescriptor.getType());
}
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) {
return true;
......@@ -84,4 +91,8 @@ public class StandardTypeConverter implements TypeConverter {
(Boolean.class.equals(actualTargetType) && String.class.equals(sourceType)));
}
public boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor) {
return canConvert(sourceType,typeDescriptor.getType());
}
}
......@@ -16,13 +16,14 @@
package org.springframework.expression.spel;
import java.awt.*;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
......@@ -264,6 +265,10 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
public void write(EvaluationContext context, Object target, String name, Object newValue)
throws AccessException {
}
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
return null;
}
}
......@@ -301,6 +306,9 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase {
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
}
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
return null;
}
}
}
/*
* Copyright 2002-2009 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.convert.ConversionExecutor;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.service.DefaultConversionService;
import org.springframework.core.convert.service.GenericConversionService;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Expression evaluation where the TypeConverter plugged in is the {@link GenericConversionService}
*
* @author Andy Clement
*/
public class ExpressionTestsUsingCoreConversionService extends ExpressionTestCase {
private static List<String> listOfString = new ArrayList<String>();
private static TypeDescriptor typeDescriptorForListOfString = null;
private static List<Integer> listOfInteger = new ArrayList<Integer>();
private static TypeDescriptor typeDescriptorForListOfInteger = null;
static {
listOfString.add("1");
listOfString.add("2");
listOfString.add("3");
listOfInteger.add(4);
listOfInteger.add(5);
listOfInteger.add(6);
}
public void setUp() throws Exception {
super.setUp();
typeDescriptorForListOfString = new TypeDescriptor(ExpressionTestsUsingCoreConversionService.class.getDeclaredField("listOfString"));
typeDescriptorForListOfInteger = new TypeDescriptor(ExpressionTestsUsingCoreConversionService.class.getDeclaredField("listOfInteger"));
}
/**
* Test the service can convert what we are about to use in the expression evaluation tests.
*/
public void testConversionsAvailable() throws Exception {
TypeConvertorUsingConversionService tcs = new TypeConvertorUsingConversionService();
// ArrayList containing List<Integer> to List<String>
Class<?> clazz = typeDescriptorForListOfString.getElementType();
assertEquals(String.class,clazz);
ConversionExecutor executor = tcs.getConversionExecutor(ArrayList.class, typeDescriptorForListOfString);
assertNotNull(executor);
List l = (List)executor.execute(listOfInteger);
assertNotNull(l);
// ArrayList containing List<String> to List<Integer>
clazz = typeDescriptorForListOfInteger.getElementType();
assertEquals(Integer.class,clazz);
executor = tcs.getConversionExecutor(ArrayList.class, typeDescriptorForListOfInteger);
assertNotNull(executor);
l = (List)executor.execute(listOfString);
assertNotNull(l);
}
public void testSetParameterizedList() throws Exception {
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
Expression e = parser.parseExpression("listOfInteger.size()");
assertEquals(0,e.getValue(context,Integer.class).intValue());
context.setTypeConverter(new TypeConvertorUsingConversionService());
// Assign a List<String> to the List<Integer> field - the component elements should be converted
parser.parseExpression("listOfInteger").setValue(context,listOfString);
assertEquals(3,e.getValue(context,Integer.class).intValue()); // size now 3
Class clazz = parser.parseExpression("listOfInteger[1].getClass()").getValue(context,Class.class); // element type correctly Integer
assertEquals(Integer.class,clazz);
}
/**
* Type convertor that uses the core conversion service.
*/
private static class TypeConvertorUsingConversionService extends DefaultConversionService implements TypeConverter {
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
return super.canConvert(sourceType, TypeDescriptor.valueOf(targetType));
}
public boolean canConvert(Class<?> sourceType, TypeDescriptor typeDescriptor) {
return super.canConvert(sourceType, typeDescriptor);
}
@SuppressWarnings("unchecked")
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
return (T)super.executeConversion(value,TypeDescriptor.valueOf(targetType));
}
@SuppressWarnings("unchecked")
public <T> T convertValue(Object value, TypeDescriptor typeDescriptor)
throws EvaluationException {
return (T)super.executeConversion(value, typeDescriptor);
}
}
}
......@@ -18,6 +18,7 @@ package org.springframework.expression.spel;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
......@@ -61,7 +62,7 @@ public class MapAccessTests extends ExpressionTestCase {
}
private static class MapAccessor implements PropertyAccessor {
public static class MapAccessor implements PropertyAccessor {
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
return (((Map) target).containsKey(name));
......@@ -75,6 +76,7 @@ public class MapAccessTests extends ExpressionTestCase {
return true;
}
@SuppressWarnings("unchecked")
public void write(EvaluationContext context, Object target, String name, Object newValue)
throws AccessException {
((Map) target).put(name, newValue);
......@@ -83,6 +85,10 @@ public class MapAccessTests extends ExpressionTestCase {
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { Map.class };
}
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
return TypeDescriptor.valueOf(Map.class);
}
}
}
......@@ -16,6 +16,7 @@
package org.springframework.expression.spel;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
......@@ -87,6 +88,10 @@ public class PropertyAccessTests extends ExpressionTestCase {
int flibbles = 7;
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
return null;
}
public Class<?>[] getSpecificTargetClasses() {
return new Class[] { String.class };
}
......
......@@ -18,6 +18,7 @@ package org.springframework.expression.spel;
import java.lang.reflect.Method;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
......@@ -224,6 +225,11 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
public Class<?>[] getSpecificTargetClasses() {
return null;
}
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
return null;
}
}
......@@ -252,6 +258,10 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase {
public Class<?>[] getSpecificTargetClasses() {
return null;
}
public TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
return null;
}
}
......
......@@ -98,11 +98,14 @@ public class SetValueTests extends ExpressionTestCase {
setValueExpectError("'hello'[3]", 'p');
}
// public void testSetPropertyTypeCoersion() {
// setValue("publicBoolean", "true");
// }
public void testSetPropertyTypeCoersion() {
setValue("publicBoolean", "true", Boolean.TRUE);
}
public void testSetPropertyTypeCoersionThroughSetter() {
setValue("SomeProperty", "true", Boolean.TRUE);
}
/**
* Call setValue() but expect it to fail.
*/
......@@ -116,7 +119,6 @@ public class SetValueTests extends ExpressionTestCase {
SpelUtilities.printAbstractSyntaxTree(System.out, e);
}
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
// assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
e.setValue(lContext, value);
fail("expected an error");
} catch (ParseException pe) {
......@@ -148,4 +150,30 @@ public class SetValueTests extends ExpressionTestCase {
fail("Unexpected Exception: " + pe.getMessage());
}
}
/**
* For use when coercion is happening during a setValue(). The expectedValue should be
* the coerced form of the value.
*/
protected void setValue(String expression, Object value, Object expectedValue) {
try {
Expression e = parser.parseExpression(expression);
if (e == null) {
fail("Parser returned null for expression");
}
if (DEBUG) {
SpelUtilities.printAbstractSyntaxTree(System.out, e);
}
StandardEvaluationContext lContext = TestScenarioCreator.getTestEvaluationContext();
assertTrue("Expression is not writeable but should be", e.isWritable(lContext));
e.setValue(lContext, value);
assertEquals("Retrieved value was not equal to set value", expectedValue, e.getValue(lContext));
} catch (EvaluationException ee) {
ee.printStackTrace();
fail("Unexpected Exception: " + ee.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
fail("Unexpected Exception: " + pe.getMessage());
}
}
}
......@@ -21,6 +21,8 @@ public class Inventor {
private List<PlaceOfBirth> placesLivedList = new ArrayList<PlaceOfBirth>();
public ArrayContainer arrayContainer;
public boolean publicBoolean;
private boolean accessedThroughGetSet;
public List<Integer> listOfInteger = new ArrayList<Integer>();
public Inventor(String name, Date birthdate, String nationality) {
this.name = name;
......@@ -110,4 +112,12 @@ public class Inventor {
public Inventor(String... strings) {
}
public boolean getSomeProperty() {
return accessedThroughGetSet;
}
public void setSomeProperty(boolean b) {
this.accessedThroughGetSet = b;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册