From 4525527794b75c9d06866d6561f9afb053906d8b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 6 Sep 2012 20:06:35 -0700 Subject: [PATCH] =?UTF-8?q?SpEL=20support=20for=20methods=20and=20properti?= =?UTF-8?q?es=20on=20class=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the ReflectiveMethodResolver and ReflectivePropertyAccessor to allow methods and properties of java.lang.Class to be resolved when the target object is a class. Issue: SPR-9017 Backport-Commit: d28592a6c66f62059c0cf056cc7db04cebb947c3 --- .../support/ReflectiveMethodResolver.java | 16 ++++++- .../support/ReflectivePropertyAccessor.java | 46 ++++++++++++++----- .../spel/MethodInvocationTests.java | 10 +++- .../expression/spel/PropertyAccessTests.java | 11 ++++- 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java index 33d1f510b1..90fa424088 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java @@ -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; @@ -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 methods = new HashSet(); + 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 diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 621db0ec37..23da16bd9a 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -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. @@ -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); @@ -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. A getter is defined as a method whose name start with the prefix * 'get' and the rest of the name is the same as the property name (with the first character uppercased). @@ -404,7 +428,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); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java index 9ed90cae41..d7d1c56d99 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java @@ -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. @@ -38,6 +38,8 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.testresources.PlaceOfBirth; +import static org.junit.Assert.*; + /** * Tests invocation of methods. * @@ -361,4 +363,10 @@ public class MethodInvocationTests extends ExpressionTestCase { 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"); + } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java index 2610b3a6be..1e5a0e2604 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java @@ -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. @@ -33,6 +33,8 @@ import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; +import static org.junit.Assert.*; + ///CLOVER:OFF /** @@ -155,6 +157,13 @@ public class PropertyAccessTests extends ExpressionTestCase { 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 {