Merge pull request #21 from aclement/spr9038
This commit is contained in:
commit
f61410705c
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2010 the original author or authors.
|
* Copyright 2002-2011 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -38,11 +38,14 @@ import org.springframework.expression.spel.SpelMessage;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A method resolver that uses reflection to locate the method that should be invoked.
|
* Reflection-based {@link MethodResolver} used by default in
|
||||||
|
* {@link StandardEvaluationContext} unless explicit method resolvers have been specified.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Chris Beams
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see StandardEvaluationContext#addMethodResolver(MethodResolver)
|
||||||
*/
|
*/
|
||||||
public class ReflectiveMethodResolver implements MethodResolver {
|
public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
|
|
||||||
|
@ -87,7 +90,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
try {
|
try {
|
||||||
TypeConverter typeConverter = context.getTypeConverter();
|
TypeConverter typeConverter = context.getTypeConverter();
|
||||||
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
|
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
|
||||||
Method[] methods = type.getMethods();
|
Method[] methods = getMethods(type);
|
||||||
|
|
||||||
// If a filter is registered for this type, call it
|
// If a filter is registered for this type, call it
|
||||||
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
||||||
|
@ -124,7 +127,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (method.getName().equals(name)) {
|
if (method.getName().equals(name)) {
|
||||||
Class[] paramTypes = method.getParameterTypes();
|
Class<?>[] paramTypes = method.getParameterTypes();
|
||||||
List<TypeDescriptor> paramDescriptors = new ArrayList<TypeDescriptor>(paramTypes.length);
|
List<TypeDescriptor> paramDescriptors = new ArrayList<TypeDescriptor>(paramTypes.length);
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
|
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
|
||||||
|
@ -194,4 +197,16 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* in order to alter the results, e.g. specifying static methods declared elsewhere.
|
||||||
|
*
|
||||||
|
* @param type the class for which to return the methods
|
||||||
|
* @since 3.1.1
|
||||||
|
*/
|
||||||
|
protected Method[] getMethods(Class<?> type) {
|
||||||
|
return type.getMethods();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
package org.springframework.expression.spel;
|
package org.springframework.expression.spel;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
@ -38,6 +40,7 @@ import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
import org.springframework.expression.MethodExecutor;
|
import org.springframework.expression.MethodExecutor;
|
||||||
|
import org.springframework.expression.MethodResolver;
|
||||||
import org.springframework.expression.ParserContext;
|
import org.springframework.expression.ParserContext;
|
||||||
import org.springframework.expression.PropertyAccessor;
|
import org.springframework.expression.PropertyAccessor;
|
||||||
import org.springframework.expression.TypedValue;
|
import org.springframework.expression.TypedValue;
|
||||||
|
@ -179,10 +182,9 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Test
|
@Test
|
||||||
public void testSPR5804() throws Exception {
|
public void testSPR5804() throws Exception {
|
||||||
Map m = new HashMap();
|
Map<String,String> m = new HashMap<String,String>();
|
||||||
m.put("foo", "bar");
|
m.put("foo", "bar");
|
||||||
StandardEvaluationContext eContext = new StandardEvaluationContext(m); // root is a map instance
|
StandardEvaluationContext eContext = new StandardEvaluationContext(m); // root is a map instance
|
||||||
eContext.addPropertyAccessor(new MapAccessor());
|
eContext.addPropertyAccessor(new MapAccessor());
|
||||||
|
@ -239,11 +241,11 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
static class MapAccessor implements PropertyAccessor {
|
static class MapAccessor implements PropertyAccessor {
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
return new TypedValue(((Map) target).get(name));
|
return new TypedValue(((Map<?,?>) target).get(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
|
@ -252,10 +254,10 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
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) target).put(name, newValue);
|
((Map<String, Object>) target).put(name, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class[] getSpecificTargetClasses() {
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
return new Class[] {Map.class};
|
return new Class[] {Map.class};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,7 +378,6 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** $ related identifiers */
|
/** $ related identifiers */
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Test
|
@Test
|
||||||
public void testDollarPrefixedIdentifier_SPR7100() {
|
public void testDollarPrefixedIdentifier_SPR7100() {
|
||||||
Holder h = new Holder();
|
Holder h = new Holder();
|
||||||
|
@ -431,7 +432,10 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
Assert.assertEquals("wobble",name);
|
Assert.assertEquals("wobble",name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Should be accessing (setting) Goo.wibble field because 'bar' variable evaluates to "wibble" */
|
/**
|
||||||
|
* Should be accessing (setting) Goo.wibble field because 'bar' variable evaluates to
|
||||||
|
* "wibble"
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testIndexingAsAPropertyAccess_SPR6968_4() {
|
public void testIndexingAsAPropertyAccess_SPR6968_4() {
|
||||||
Goo g = Goo.instance;
|
Goo g = Goo.instance;
|
||||||
|
@ -509,7 +513,7 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
|
|
||||||
static class Holder {
|
static class Holder {
|
||||||
|
|
||||||
public Map map = new HashMap();
|
public Map<String,String> map = new HashMap<String,String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
@ -553,21 +557,6 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// @Test
|
|
||||||
// public void testFails() {
|
|
||||||
//
|
|
||||||
// StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
|
||||||
// evaluationContext.setVariable("target", new Foo2());
|
|
||||||
// for (int i = 0; i < 300000; i++) {
|
|
||||||
// evaluationContext.addPropertyAccessor(new MapAccessor());
|
|
||||||
// ExpressionParser parser = new SpelExpressionParser();
|
|
||||||
// Expression expression = parser.parseExpression("#target.execute(payload)");
|
|
||||||
// Message message = new Message();
|
|
||||||
// message.setPayload(i+"");
|
|
||||||
// expression.getValue(evaluationContext, message);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
static class Foo2 {
|
static class Foo2 {
|
||||||
public void execute(String str){
|
public void execute(String str){
|
||||||
System.out.println("Value: " + str);
|
System.out.println("Value: " + str);
|
||||||
|
@ -698,11 +687,10 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void testMapOfMap_SPR7244() throws Exception {
|
public void testMapOfMap_SPR7244() throws Exception {
|
||||||
Map<String,Object> map = new LinkedHashMap();
|
Map<String,Object> map = new LinkedHashMap<String,Object>();
|
||||||
map.put("uri", "http:");
|
map.put("uri", "http:");
|
||||||
Map nameMap = new LinkedHashMap();
|
Map<String,String> nameMap = new LinkedHashMap<String,String>();
|
||||||
nameMap.put("givenName", "Arthur");
|
nameMap.put("givenName", "Arthur");
|
||||||
map.put("value", nameMap);
|
map.put("value", nameMap);
|
||||||
|
|
||||||
|
@ -725,7 +713,7 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
SpelExpressionParser parser = new SpelExpressionParser();
|
SpelExpressionParser parser = new SpelExpressionParser();
|
||||||
String el1 = "ls.![#this.equals('abc')]";
|
String el1 = "ls.![#this.equals('abc')]";
|
||||||
SpelExpression exp = parser.parseRaw(el1);
|
SpelExpression exp = parser.parseRaw(el1);
|
||||||
List value = (List)exp.getValue(ctx);
|
List<?> value = (List<?>)exp.getValue(ctx);
|
||||||
// value is list containing [true,false]
|
// value is list containing [true,false]
|
||||||
Assert.assertEquals(Boolean.class,value.get(0).getClass());
|
Assert.assertEquals(Boolean.class,value.get(0).getClass());
|
||||||
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
|
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
|
||||||
|
@ -751,7 +739,7 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
SpelExpressionParser parser = new SpelExpressionParser();
|
SpelExpressionParser parser = new SpelExpressionParser();
|
||||||
String el1 = "ms.![key.equals('abc')]";
|
String el1 = "ms.![key.equals('abc')]";
|
||||||
SpelExpression exp = parser.parseRaw(el1);
|
SpelExpression exp = parser.parseRaw(el1);
|
||||||
List value = (List)exp.getValue(ctx);
|
List<?> value = (List<?>)exp.getValue(ctx);
|
||||||
// value is list containing [true,false]
|
// value is list containing [true,false]
|
||||||
Assert.assertEquals(Boolean.class,value.get(0).getClass());
|
Assert.assertEquals(Boolean.class,value.get(0).getClass());
|
||||||
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
|
TypeDescriptor evaluated = exp.getValueTypeDescriptor(ctx);
|
||||||
|
@ -816,8 +804,9 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether {@link ReflectiveMethodResolver} follows Java Method Invocation Conversion order. And more precisely
|
* Test whether {@link ReflectiveMethodResolver} follows Java Method Invocation
|
||||||
* that widening reference conversion is 'higher' than a unboxing conversion.
|
* Conversion order. And more precisely that widening reference conversion is 'higher'
|
||||||
|
* than a unboxing conversion.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testConversionPriority_8224() throws Exception {
|
public void testConversionPriority_8224() throws Exception {
|
||||||
|
@ -1025,9 +1014,8 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
public int DIV = 1;
|
public int DIV = 1;
|
||||||
public int div = 3;
|
public int div = 3;
|
||||||
|
|
||||||
public Map m = new HashMap();
|
public Map<String,String> m = new HashMap<String,String>();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Reserver() {
|
Reserver() {
|
||||||
m.put("NE","xyz");
|
m.put("NE","xyz");
|
||||||
}
|
}
|
||||||
|
@ -1089,21 +1077,6 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
expressionParser.parseExpression("shouldBeFourth").getValue(evaluationContext));
|
expressionParser.parseExpression("shouldBeFourth").getValue(evaluationContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
// test not quite complete, it doesn't check that the list of resolvers at the end of
|
|
||||||
// PropertyOrFieldReference.getPropertyAccessorsToTry() doesn't contain duplicates, which
|
|
||||||
// is what it is trying to test by having a property accessor that returns specific
|
|
||||||
// classes Integer and Number
|
|
||||||
// @Test
|
|
||||||
// public void testPropertyAccessorOrder_8211_2() {
|
|
||||||
// ExpressionParser expressionParser = new SpelExpressionParser();
|
|
||||||
// StandardEvaluationContext evaluationContext =
|
|
||||||
// new StandardEvaluationContext(new Integer(42));
|
|
||||||
//
|
|
||||||
// evaluationContext.addPropertyAccessor(new TestPropertyAccessor2());
|
|
||||||
//
|
|
||||||
// assertEquals("42", expressionParser.parseExpression("x").getValue(evaluationContext));
|
|
||||||
// }
|
|
||||||
|
|
||||||
class TestPropertyAccessor implements PropertyAccessor {
|
class TestPropertyAccessor implements PropertyAccessor {
|
||||||
private String mapName;
|
private String mapName;
|
||||||
public TestPropertyAccessor(String mapName) {
|
public TestPropertyAccessor(String mapName) {
|
||||||
|
@ -1140,31 +1113,6 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// class TestPropertyAccessor2 implements PropertyAccessor {
|
|
||||||
//
|
|
||||||
// public Class[] getSpecificTargetClasses() {
|
|
||||||
// return new Class[]{Integer.class,Number.class};
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
|
||||||
// return new TypedValue(target.toString(),TypeDescriptor.valueOf(String.class));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void write(EvaluationContext context, Object target, String name, Object newValue)
|
|
||||||
// throws AccessException {
|
|
||||||
// throw new UnsupportedOperationException();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
class ContextObject {
|
class ContextObject {
|
||||||
public Map<String, String> firstContext = new HashMap<String, String>();
|
public Map<String, String> firstContext = new HashMap<String, String>();
|
||||||
public Map<String, String> secondContext = new HashMap<String, String>();
|
public Map<String, String> secondContext = new HashMap<String, String>();
|
||||||
|
@ -1192,6 +1140,41 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
public Map<String, String> getFourthContext() {return fourthContext;}
|
public Map<String, String> getFourthContext() {return fourthContext;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the ability to subclass the ReflectiveMethodResolver and change how it
|
||||||
|
* determines the set of methods for a type.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCustomStaticFunctions_SPR9038() {
|
||||||
|
try {
|
||||||
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
List<MethodResolver> methodResolvers = new ArrayList<MethodResolver>();
|
||||||
|
methodResolvers.add(new ReflectiveMethodResolver() {
|
||||||
|
@Override
|
||||||
|
protected Method[] getMethods(Class<?> type) {
|
||||||
|
try {
|
||||||
|
return new Method[] {
|
||||||
|
Integer.class.getDeclaredMethod("parseInt", new Class[] {
|
||||||
|
String.class, Integer.TYPE }) };
|
||||||
|
} catch (NoSuchMethodException e1) {
|
||||||
|
return new Method[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
context.setMethodResolvers(methodResolvers);
|
||||||
|
org.springframework.expression.Expression expression =
|
||||||
|
parser.parseExpression("parseInt('-FF', 16)");
|
||||||
|
|
||||||
|
Integer result = expression.getValue(context, "", Integer.class);
|
||||||
|
assertEquals("Equal assertion failed: ", -255, result.intValue());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail("Unexpected exception: "+e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue