Merge pull request #136 from philwebb/SPR-9017
* SPR-9017: SpEL support for methods and properties on class … Remove trailing whitespace
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -21,8 +21,10 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -53,7 +55,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
|
||||
private Map<Class<?>, MethodFilter> filters = null;
|
||||
|
||||
// Using distance will ensure a more accurate match is discovered,
|
||||
// Using distance will ensure a more accurate match is discovered,
|
||||
// more closely following the Java rules.
|
||||
private boolean useDistance = false;
|
||||
|
||||
@@ -66,7 +68,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
* This constructors allows the ReflectiveMethodResolver to be configured such that it will
|
||||
* use a distance computation to check which is the better of two close matches (when there
|
||||
* are multiple matches). Using the distance computation is intended to ensure matches
|
||||
* are more closely representative of what a Java compiler would do when taking into
|
||||
* are more closely representative of what a Java compiler would do when taking into
|
||||
* account boxing/unboxing and whether the method candidates are declared to handle a
|
||||
* supertype of the type (of the argument) being passed in.
|
||||
* @param useDistance true if distance computation should be used when calculating matches
|
||||
@@ -90,7 +92,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
try {
|
||||
TypeConverter typeConverter = context.getTypeConverter();
|
||||
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
|
||||
Method[] methods = getMethods(type);
|
||||
Method[] methods = getMethods(type, targetObject);
|
||||
|
||||
// If a filter is registered for this type, call it
|
||||
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
||||
@@ -197,6 +199,16 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private Method[] getMethods(Class<?> type, Object targetObject) {
|
||||
if(targetObject instanceof Class) {
|
||||
Set<Method> methods = new HashSet<Method>();
|
||||
methods.addAll(Arrays.asList(getMethods(type)));
|
||||
methods.addAll(Arrays.asList(getMethods(targetObject.getClass())));
|
||||
return methods.toArray(new Method[methods.size()]);
|
||||
}
|
||||
return getMethods(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of methods for this type. The default implementation returns the
|
||||
* result of Class#getMethods for the given {@code type}, but subclasses may override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -37,8 +37,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Simple PropertyAccessor 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).
|
||||
*
|
||||
* 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
|
||||
* @since 3.0
|
||||
@@ -48,7 +48,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
protected final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>();
|
||||
|
||||
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
|
||||
|
||||
|
||||
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>();
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (this.readerCache.containsKey(cacheKey)) {
|
||||
return true;
|
||||
}
|
||||
Method method = findGetterForProperty(name, type, target instanceof Class);
|
||||
Method method = findGetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
// Treat it like a property
|
||||
// The readerCache will only contain gettable properties (let's not worry about setters for now)
|
||||
@@ -82,10 +82,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Field field = findField(name, type, target instanceof Class);
|
||||
Field field = findField(name, type, target);
|
||||
if (field != null) {
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(field);
|
||||
this.readerCache.put(cacheKey, new InvokerPair(field,typeDescriptor));
|
||||
this.readerCache.put(cacheKey, new InvokerPair(field,typeDescriptor));
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (invoker == null || invoker.member instanceof Method) {
|
||||
Method method = (Method) (invoker != null ? invoker.member : null);
|
||||
if (method == null) {
|
||||
method = findGetterForProperty(name, type, target instanceof Class);
|
||||
method = findGetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
// TODO remove the duplication here between canRead and read
|
||||
// Treat it like a property
|
||||
@@ -138,7 +138,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (invoker == null || invoker.member instanceof Field) {
|
||||
Field field = (Field) (invoker == null ? null : invoker.member);
|
||||
if (field == null) {
|
||||
field = findField(name, type, target instanceof Class);
|
||||
field = findField(name, type, target);
|
||||
if (field != null) {
|
||||
invoker = new InvokerPair(field, new TypeDescriptor(field));
|
||||
this.readerCache.put(cacheKey, invoker);
|
||||
@@ -168,7 +168,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (this.writerCache.containsKey(cacheKey)) {
|
||||
return true;
|
||||
}
|
||||
Method method = findSetterForProperty(name, type, target instanceof Class);
|
||||
Method method = findSetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
// Treat it like a property
|
||||
Property property = new Property(type, null, method);
|
||||
@@ -178,7 +178,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Field field = findField(name, type, target instanceof Class);
|
||||
Field field = findField(name, type, target);
|
||||
if (field != null) {
|
||||
this.writerCache.put(cacheKey, field);
|
||||
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
|
||||
@@ -211,7 +211,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (cachedMember == null || cachedMember instanceof Method) {
|
||||
Method method = (Method) cachedMember;
|
||||
if (method == null) {
|
||||
method = findSetterForProperty(name, type, target instanceof Class);
|
||||
method = findSetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
cachedMember = method;
|
||||
this.writerCache.put(cacheKey, cachedMember);
|
||||
@@ -232,7 +232,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (cachedMember == null || cachedMember instanceof Field) {
|
||||
Field field = (Field) cachedMember;
|
||||
if (field == null) {
|
||||
field = findField(name, type, target instanceof Class);
|
||||
field = findField(name, type, target);
|
||||
if (field != null) {
|
||||
cachedMember = field;
|
||||
this.writerCache.put(cacheKey, cachedMember);
|
||||
@@ -252,7 +252,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
throw new AccessException("Neither setter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
|
||||
private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
@@ -281,6 +281,30 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return typeDescriptor;
|
||||
}
|
||||
|
||||
private Method findGetterForProperty(String propertyName, Class<?> clazz, Object target) {
|
||||
Method method = findGetterForProperty(propertyName, clazz, target instanceof Class);
|
||||
if(method == null && target instanceof Class) {
|
||||
method = findGetterForProperty(propertyName, target.getClass(), false);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private Method findSetterForProperty(String propertyName, Class<?> clazz, Object target) {
|
||||
Method method = findSetterForProperty(propertyName, clazz, target instanceof Class);
|
||||
if(method == null && target instanceof Class) {
|
||||
method = findSetterForProperty(propertyName, target.getClass(), false);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private Field findField(String name, Class<?> clazz, Object target) {
|
||||
Field field = findField(name, clazz, target instanceof Class);
|
||||
if(field == null && target instanceof Class) {
|
||||
field = findField(name, target.getClass(), false);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a getter method for the specified property.
|
||||
*/
|
||||
@@ -340,22 +364,22 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Captures the member (method/field) to call reflectively to access a property value and the type descriptor for the
|
||||
* value returned by the reflective call.
|
||||
*/
|
||||
private static class InvokerPair {
|
||||
|
||||
|
||||
final Member member;
|
||||
|
||||
|
||||
final TypeDescriptor typeDescriptor;
|
||||
|
||||
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
|
||||
this.member = member;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CacheKey {
|
||||
@@ -387,9 +411,9 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Attempt to create an optimized property accessor tailored for a property of a particular name on
|
||||
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
|
||||
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
|
||||
* due to the need to lookup which reflective member (method/field) to use each time read() is called.
|
||||
* This method will just return the ReflectivePropertyAccessor instance if it is unable to build
|
||||
* something more optimal.
|
||||
@@ -410,7 +434,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (invocationTarget == null || invocationTarget.member instanceof Method) {
|
||||
Method method = (Method) (invocationTarget==null?null:invocationTarget.member);
|
||||
if (method == null) {
|
||||
method = findGetterForProperty(name, type, target instanceof Class);
|
||||
method = findGetterForProperty(name, type, target);
|
||||
if (method != null) {
|
||||
invocationTarget = new InvokerPair(method,new TypeDescriptor(new MethodParameter(method,-1)));
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
@@ -438,10 +462,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An optimized form of a PropertyAccessor that will use reflection but only knows how to access a particular property
|
||||
* on a particular class. This is unlike the general ReflectivePropertyResolver which manages a cache of methods/fields that
|
||||
* An optimized form of a PropertyAccessor that will use reflection but only knows how to access a particular property
|
||||
* on a particular class. This is unlike the general ReflectivePropertyResolver which manages a cache of methods/fields that
|
||||
* may be invoked to access different properties on different classes. This optimal accessor exists because looking up
|
||||
* the appropriate reflective object by class/name on each read is not cheap.
|
||||
*/
|
||||
@@ -449,13 +473,13 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
private final Member member;
|
||||
private final TypeDescriptor typeDescriptor;
|
||||
private final boolean needsToBeMadeAccessible;
|
||||
|
||||
|
||||
OptimalPropertyAccessor(InvokerPair target) {
|
||||
this.member = target.member;
|
||||
this.member = target.member;
|
||||
this.typeDescriptor = target.typeDescriptor;
|
||||
if (this.member instanceof Field) {
|
||||
Field field = (Field)member;
|
||||
needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
|
||||
needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
|
||||
&& !field.isAccessible();
|
||||
}
|
||||
else {
|
||||
@@ -468,7 +492,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
public Class[] getSpecificTargetClasses() {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
|
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (target == null) {
|
||||
return false;
|
||||
@@ -504,7 +528,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access property '" + name + "' through getter", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (member instanceof Field) {
|
||||
try {
|
||||
if (needsToBeMadeAccessible) {
|
||||
@@ -523,7 +547,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
||||
throws AccessException {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -40,7 +42,7 @@ import org.springframework.expression.spel.testresources.PlaceOfBirth;
|
||||
|
||||
/**
|
||||
* Tests invocation of methods.
|
||||
*
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class MethodInvocationTests extends ExpressionTestCase {
|
||||
@@ -89,7 +91,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
evaluate("new String('hello 2.0 to you').startsWith(7.0d)", false, Boolean.class);
|
||||
evaluate("new String('7.0 foobar').startsWith(7.0d)", true, Boolean.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMethodThrowingException_SPR6760() {
|
||||
// Test method on inventor: throwException()
|
||||
@@ -97,7 +99,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
// On 2 it will throw a RuntimeException
|
||||
// On 3 it will exit normally
|
||||
// In each case it increments the Inventor field 'counter' when invoked
|
||||
|
||||
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
Expression expr = parser.parseExpression("throwException(#bar)");
|
||||
|
||||
@@ -115,18 +117,18 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
o = expr.getValue(eContext);
|
||||
Assert.assertEquals("London", o);
|
||||
// That confirms the logic to mark the cached reference stale and retry is working
|
||||
|
||||
|
||||
|
||||
|
||||
// Now let's cause the method to exit via exception and ensure it doesn't cause
|
||||
// a retry.
|
||||
|
||||
|
||||
// First, switch back to throwException(int)
|
||||
eContext.setVariable("bar",3);
|
||||
o = expr.getValue(eContext);
|
||||
Assert.assertEquals(3, o);
|
||||
Assert.assertEquals(2,parser.parseExpression("counter").getValue(eContext));
|
||||
|
||||
|
||||
|
||||
// Now cause it to throw an exception:
|
||||
eContext.setVariable("bar",1);
|
||||
try {
|
||||
@@ -157,7 +159,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
// If counter is 5 then the method got called twice!
|
||||
Assert.assertEquals(4,parser.parseExpression("counter").getValue(eContext));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check on first usage (when the cachedExecutor in MethodReference is null) that the exception is not wrapped.
|
||||
*/
|
||||
@@ -168,10 +170,10 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
// On 2 it will throw a RuntimeException
|
||||
// On 3 it will exit normally
|
||||
// In each case it increments the Inventor field 'counter' when invoked
|
||||
|
||||
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
Expression expr = parser.parseExpression("throwException(#bar)");
|
||||
|
||||
|
||||
eContext.setVariable("bar",2);
|
||||
try {
|
||||
expr.getValue(eContext);
|
||||
@@ -184,7 +186,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
// normal
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMethodThrowingException_SPR6941_2() {
|
||||
// Test method on inventor: throwException()
|
||||
@@ -192,10 +194,10 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
// On 2 it will throw a RuntimeException
|
||||
// On 3 it will exit normally
|
||||
// In each case it increments the Inventor field 'counter' when invoked
|
||||
|
||||
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
Expression expr = parser.parseExpression("throwException(#bar)");
|
||||
|
||||
|
||||
eContext.setVariable("bar",4);
|
||||
try {
|
||||
expr.getValue(eContext);
|
||||
@@ -207,7 +209,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
}
|
||||
Assert.fail("Should not be a SpelEvaluationException");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMethodFiltering_SPR6764() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
@@ -215,13 +217,13 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
context.setRootObject(new TestObject());
|
||||
LocalFilter filter = new LocalFilter();
|
||||
context.registerMethodFilter(TestObject.class,filter);
|
||||
|
||||
|
||||
// Filter will be called but not do anything, so first doit() will be invoked
|
||||
SpelExpression expr = (SpelExpression) parser.parseExpression("doit(1)");
|
||||
String result = expr.getValue(context,String.class);
|
||||
Assert.assertEquals("1",result);
|
||||
Assert.assertTrue(filter.filterCalled);
|
||||
|
||||
|
||||
// Filter will now remove non @Anno annotated methods
|
||||
filter.removeIfNotAnnotated = true;
|
||||
filter.filterCalled = false;
|
||||
@@ -229,7 +231,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
result = expr.getValue(context,String.class);
|
||||
Assert.assertEquals("double 1.0",result);
|
||||
Assert.assertTrue(filter.filterCalled);
|
||||
|
||||
|
||||
// check not called for other types
|
||||
filter.filterCalled=false;
|
||||
context.setRootObject(new String("abc"));
|
||||
@@ -237,7 +239,7 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
result = expr.getValue(context,String.class);
|
||||
Assert.assertEquals("a",result);
|
||||
Assert.assertFalse(filter.filterCalled);
|
||||
|
||||
|
||||
// check de-registration works
|
||||
filter.filterCalled = false;
|
||||
context.registerMethodFilter(TestObject.class,null);//clear filter
|
||||
@@ -247,14 +249,14 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
Assert.assertEquals("1",result);
|
||||
Assert.assertFalse(filter.filterCalled);
|
||||
}
|
||||
|
||||
|
||||
// Simple filter
|
||||
static class LocalFilter implements MethodFilter {
|
||||
|
||||
|
||||
public boolean removeIfNotAnnotated = false;
|
||||
|
||||
|
||||
public boolean filterCalled = false;
|
||||
|
||||
|
||||
private boolean isAnnotated(Method m) {
|
||||
Annotation[] annos = m.getAnnotations();
|
||||
if (annos==null) {
|
||||
@@ -282,53 +284,53 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Anno {}
|
||||
|
||||
|
||||
class TestObject {
|
||||
public int doit(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
@Anno
|
||||
public String doit(double d) {
|
||||
return "double "+d;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAddingMethodResolvers() {
|
||||
StandardEvaluationContext ctx = new StandardEvaluationContext();
|
||||
|
||||
|
||||
// reflective method accessor is the only one by default
|
||||
List<MethodResolver> methodResolvers = ctx.getMethodResolvers();
|
||||
Assert.assertEquals(1,methodResolvers.size());
|
||||
|
||||
|
||||
MethodResolver dummy = new DummyMethodResolver();
|
||||
ctx.addMethodResolver(dummy);
|
||||
Assert.assertEquals(2,ctx.getMethodResolvers().size());
|
||||
|
||||
|
||||
List<MethodResolver> copy = new ArrayList<MethodResolver>();
|
||||
copy.addAll(ctx.getMethodResolvers());
|
||||
Assert.assertTrue(ctx.removeMethodResolver(dummy));
|
||||
Assert.assertFalse(ctx.removeMethodResolver(dummy));
|
||||
Assert.assertEquals(1,ctx.getMethodResolvers().size());
|
||||
|
||||
|
||||
ctx.setMethodResolvers(copy);
|
||||
Assert.assertEquals(2,ctx.getMethodResolvers().size());
|
||||
}
|
||||
|
||||
|
||||
static class DummyMethodResolver implements MethodResolver {
|
||||
|
||||
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
|
||||
List<TypeDescriptor> argumentTypes) throws AccessException {
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -355,10 +357,16 @@ public class MethodInvocationTests extends ExpressionTestCase {
|
||||
evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class);
|
||||
// evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvocationOnNullContextObject() {
|
||||
evaluateAndCheckError("null.toString()",SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodOfClass() throws Exception {
|
||||
Expression expression = parser.parseExpression("getName()");
|
||||
Object value = expression.getValue(new StandardEvaluationContext(String.class));
|
||||
assertEquals(value, "java.lang.String");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -37,7 +39,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
/**
|
||||
* Tests accessing of properties.
|
||||
*
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class PropertyAccessTests extends ExpressionTestCase {
|
||||
@@ -65,9 +67,9 @@ public class PropertyAccessTests extends ExpressionTestCase {
|
||||
// name is ok but foobar does not exist:
|
||||
evaluateAndCheckError("name.foobar", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 5);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The standard reflection resolver cannot find properties on null objects but some
|
||||
* The standard reflection resolver cannot find properties on null objects but some
|
||||
* supplied resolver might be able to - so null shouldn't crash the reflection resolver.
|
||||
*/
|
||||
@Test
|
||||
@@ -132,29 +134,36 @@ public class PropertyAccessTests extends ExpressionTestCase {
|
||||
// System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAddingRemovingAccessors() {
|
||||
StandardEvaluationContext ctx = new StandardEvaluationContext();
|
||||
|
||||
|
||||
// reflective property accessor is the only one by default
|
||||
List<PropertyAccessor> propertyAccessors = ctx.getPropertyAccessors();
|
||||
Assert.assertEquals(1,propertyAccessors.size());
|
||||
|
||||
|
||||
StringyPropertyAccessor spa = new StringyPropertyAccessor();
|
||||
ctx.addPropertyAccessor(spa);
|
||||
Assert.assertEquals(2,ctx.getPropertyAccessors().size());
|
||||
|
||||
|
||||
List<PropertyAccessor> copy = new ArrayList<PropertyAccessor>();
|
||||
copy.addAll(ctx.getPropertyAccessors());
|
||||
Assert.assertTrue(ctx.removePropertyAccessor(spa));
|
||||
Assert.assertFalse(ctx.removePropertyAccessor(spa));
|
||||
Assert.assertEquals(1,ctx.getPropertyAccessors().size());
|
||||
|
||||
|
||||
ctx.setPropertyAccessors(copy);
|
||||
Assert.assertEquals(2,ctx.getPropertyAccessors().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessingPropertyOfClass() throws Exception {
|
||||
Expression expression = parser.parseExpression("name");
|
||||
Object value = expression.getValue(new StandardEvaluationContext(String.class));
|
||||
assertEquals(value, "java.lang.String");
|
||||
}
|
||||
|
||||
|
||||
// This can resolve the property 'flibbles' on any String (very useful...)
|
||||
private static class StringyPropertyAccessor implements PropertyAccessor {
|
||||
|
||||
Reference in New Issue
Block a user