From d28592a6c66f62059c0cf056cc7db04cebb947c3 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 --- .../support/ReflectiveMethodResolver.java | 16 ++++++- .../support/ReflectivePropertyAccessor.java | 44 ++++++++++++++----- .../spel/MethodInvocationTests.java | 10 ++++- .../expression/spel/PropertyAccessTests.java | 11 ++++- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java index dd8045430d..c086771cbe 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java +++ b/spring-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/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 108e926e09..0dd11d23ca 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-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,7 +82,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) { TypeDescriptor typeDescriptor = new TypeDescriptor(field); this.readerCache.put(cacheKey, new InvokerPair(field,typeDescriptor)); @@ -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. */ @@ -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); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java index 75ccfb3b4a..6134533eb1 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java +++ b/spring-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. @@ -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; @@ -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/spring-expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java index d49e69d991..3cb81de5e9 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java +++ b/spring-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. @@ -16,6 +16,8 @@ package org.springframework.expression.spel; +import static org.junit.Assert.*; + import java.util.ArrayList; import java.util.List; @@ -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 {