From 94436dd609a0522eb705e697f6d64dc3cf2cabd9 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 10 Jul 2014 10:37:12 +0200 Subject: [PATCH] DATACMNS-533 - Implemented handling of root objects. Refactored the implementation of the dynamic lookup of properties and functions on EvaluationContextExtensions. All the reflective lookup logic has moved into an EvaluationContextExtensionAdapter. We now also distinguish between the meta-information that can be looked up once and cached (so that we avoid reflection overhead for subsequent context creations). Related pull request: spring-projects/spring-data-jpa#101 Related ticket: DATAJPA-564 --- ...EvaluationContextExtensionInformation.java | 331 ++++++++++++++++++ ...tensionAwareEvaluationContextProvider.java | 319 +++++++++++++---- .../EvaluationContextExtensionSupport.java | 71 +--- 3 files changed, 585 insertions(+), 136 deletions(-) create mode 100644 src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java diff --git a/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java b/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java new file mode 100644 index 000000000..71d2cf31b --- /dev/null +++ b/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java @@ -0,0 +1,331 @@ +/* + * Copyright 2014 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.data.repository.query; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; + +import org.springframework.beans.BeanUtils; +import org.springframework.data.repository.query.EvaluationContextExtensionInformation.ExtensionTypeInformation.PublicMethodAndFieldFilter; +import org.springframework.data.repository.query.spi.EvaluationContextExtension; +import org.springframework.data.repository.query.spi.Function; +import org.springframework.expression.EvaluationContext; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; +import org.springframework.util.ReflectionUtils.FieldFilter; +import org.springframework.util.ReflectionUtils.MethodCallback; +import org.springframework.util.ReflectionUtils.MethodFilter; + +/** + * Inspects the configured {@link EvaluationContextExtension} type for static methods and fields to avoid repeated + * reflection lookups. Also inspects the return type of the {@link EvaluationContextExtension#getRootObject()} method + * and captures the methods declared on it as {@link Function}s. + *

+ * The type basically allows us to cache the type based information within + * {@link ExtensionAwareEvaluationContextProvider} to avoid repeated reflection lookups for every creation of an + * {@link EvaluationContext}. + * + * @author Oliver Gierke + * @since 1.9 + */ +class EvaluationContextExtensionInformation { + + private final ExtensionTypeInformation extensionTypeInformation; + private final RootObjectInformation rootObjectInformation; + + /** + * Creates a new {@link EvaluationContextExtension} for the given extension type. + * + * @param type must not be {@literal null}. + */ + public EvaluationContextExtensionInformation(Class type) { + + Assert.notNull(type, "Extension type must not be null!"); + Class rootObjectType = getRootObjectMethod(type).getReturnType(); + + this.rootObjectInformation = Object.class.equals(rootObjectType) ? null : new RootObjectInformation(rootObjectType); + this.extensionTypeInformation = new ExtensionTypeInformation(type); + } + + /** + * Returns the {@link ExtensionTypeInformation} for the extension. + * + * @return + */ + public ExtensionTypeInformation getExtensionTypeInformation() { + return this.extensionTypeInformation; + } + + /** + * Returns the {@link RootObjectInformation} for the given target object. If the information has been pre-computed + * earlier, the existing one will be used. + * + * @param target + * @return + */ + public RootObjectInformation getRootObjectInformation(Object target) { + return target == null ? RootObjectInformation.NONE : rootObjectInformation == null ? new RootObjectInformation( + target.getClass()) : rootObjectInformation; + } + + private static Method getRootObjectMethod(Class type) { + + try { + return type.getMethod("getRootObject"); + } catch (Exception e) { + return null; + } + } + + /** + * Static information about the given {@link EvaluationContextExtension} type. Discovers public static methods and + * fields. The fields' values are obtained directly, the methods are exposed {@link Function} invocations. + * + * @author Oliver Gierke + */ + public static class ExtensionTypeInformation { + + private final Map properties; + private final Map functions; + + /** + * Creates a new {@link ExtensionTypeInformation} fir the given type. + * + * @param type must not be {@literal null}. + */ + public ExtensionTypeInformation(Class type) { + + Assert.notNull(type, "Extension type must not be null!"); + + this.functions = discoverDeclaredFunctions(type); + this.properties = discoverDeclaredProperties(type, PublicMethodAndFieldFilter.STATIC); + } + + /** + * Returns the statically defined properties of the extension type. + * + * @return the properties will never be {@literal null}. + */ + public Map getProperties() { + return properties; + } + + /** + * Returns the statically exposed functions of the extension type. + * + * @return the functions will never be {@literal null}. + */ + public Map getFunctions() { + return functions; + } + + private static Map discoverDeclaredFunctions(Class type) { + + final Map map = new HashMap(); + + ReflectionUtils.doWithMethods(type, new MethodCallback() { + + @Override + public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) { + map.put(method.getName(), new Function(method, null)); + } + } + }); + + return map.isEmpty() ? Collections. emptyMap() : Collections.unmodifiableMap(map); + } + + static class PublicMethodAndFieldFilter implements MethodFilter, FieldFilter { + + public static PublicMethodAndFieldFilter STATIC = new PublicMethodAndFieldFilter(true); + public static PublicMethodAndFieldFilter NON_STATIC = new PublicMethodAndFieldFilter(false); + + private final boolean staticOnly; + + /** + * @param staticOnly + */ + public PublicMethodAndFieldFilter(boolean forStatic) { + this.staticOnly = forStatic; + } + + /* + * (non-Javadoc) + * @see org.springframework.util.ReflectionUtils.MethodFilter#matches(java.lang.reflect.Method) + */ + @Override + public boolean matches(Method method) { + + if (ReflectionUtils.isObjectMethod(method)) { + return false; + } + + boolean methodStatic = Modifier.isStatic(method.getModifiers()); + boolean staticMatch = staticOnly ? methodStatic : !methodStatic; + + return Modifier.isPublic(method.getModifiers()) && staticMatch; + } + + /* + * (non-Javadoc) + * @see org.springframework.util.ReflectionUtils.FieldFilter#matches(java.lang.reflect.Field) + */ + @Override + public boolean matches(Field field) { + + if (field.getDeclaringClass().equals(Object.class)) { + + } + + boolean fieldStatic = Modifier.isStatic(field.getModifiers()); + boolean staticMatch = staticOnly ? fieldStatic : !fieldStatic; + + return Modifier.isPublic(field.getModifiers()) && staticMatch; + } + } + } + + /** + * Information about the root object of an extension. + * + * @author Oliver Gierke + */ + public static class RootObjectInformation { + + static RootObjectInformation NONE = new RootObjectInformation(Object.class); + + private final Map accessors; + private final Collection methods; + private final Collection fields; + + /** + * Creates a new {@link RootObjectInformation} for the given type. Inspects public methods and fields to register + * them as {@link Function}s and properties. + * + * @param type must not be {@literal null}. + */ + public RootObjectInformation(Class type) { + + Assert.notNull(type, "Type must not be null!"); + + this.accessors = new HashMap(); + this.methods = new HashSet(); + this.fields = new ArrayList(); + + if (Object.class.equals(type)) { + return; + } + + final PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(type); + + ReflectionUtils.doWithMethods(type, new MethodCallback() { + + @Override + public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + + RootObjectInformation.this.methods.add(method); + + for (PropertyDescriptor descriptor : descriptors) { + if (method.equals(descriptor.getReadMethod())) { + RootObjectInformation.this.accessors.put(descriptor.getName(), method); + } + } + } + }, PublicMethodAndFieldFilter.NON_STATIC); + + ReflectionUtils.doWithFields(type, new FieldCallback() { + + @Override + public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { + RootObjectInformation.this.fields.add(field); + } + }, PublicMethodAndFieldFilter.NON_STATIC); + } + + /** + * Returns {@link Function} instances that wrap method invocations on the given target object. + * + * @param target can be {@literal null}. + * @return the methods + */ + public Map getFunctions(Object target) { + + if (target == null) { + return Collections.emptyMap(); + } + + Map functions = new HashMap(methods.size()); + + for (Method method : methods) { + functions.put(method.getName(), new Function(method, target)); + } + + return Collections.unmodifiableMap(functions); + + } + + /** + * Returns the properties of the target object. This will also include {@link Function} instances for all properties + * with accessor methods that need to be resolved downstream. + * + * @return the properties + */ + public Map getProperties(Object target) { + + if (target == null) { + return Collections.emptyMap(); + } + + Map properties = new HashMap(); + + for (Entry method : accessors.entrySet()) { + properties.put(method.getKey(), new Function(method.getValue(), target)); + } + + for (Field field : fields) { + properties.put(field.getName(), ReflectionUtils.getField(field, target)); + } + + return Collections.unmodifiableMap(properties); + } + } + + private static Map discoverDeclaredProperties(Class type, FieldFilter filter) { + + final Map map = new HashMap(); + + ReflectionUtils.doWithFields(type, new FieldCallback() { + + @Override + public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { + map.put(field.getName(), field.get(null)); + } + }, filter); + + return map.isEmpty() ? Collections. emptyMap() : Collections.unmodifiableMap(map); + } +} diff --git a/src/main/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProvider.java b/src/main/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProvider.java index 48284a5ca..690b58fc2 100644 --- a/src/main/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProvider.java +++ b/src/main/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProvider.java @@ -15,8 +15,8 @@ */ package org.springframework.data.repository.query; -import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -30,7 +30,10 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.data.repository.query.EvaluationContextExtensionInformation.ExtensionTypeInformation; +import org.springframework.data.repository.query.EvaluationContextExtensionInformation.RootObjectInformation; import org.springframework.data.repository.query.spi.EvaluationContextExtension; +import org.springframework.data.repository.query.spi.Function; import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.MethodExecutor; @@ -43,7 +46,6 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import org.springframework.util.TypeUtils; /** * An {@link EvaluationContextProvider} that assembles an {@link EvaluationContext} from a list of @@ -55,28 +57,28 @@ import org.springframework.util.TypeUtils; */ public class ExtensionAwareEvaluationContextProvider implements EvaluationContextProvider, ApplicationContextAware { - private List extensions; + private final Map, EvaluationContextExtensionInformation> extensionInformationCache = new HashMap, EvaluationContextExtensionInformation>(); + + private List extensions; private ListableBeanFactory beanFactory; /** * Creates a new {@link ExtensionAwareEvaluationContextProvider}. Extensions are being looked up lazily from the * {@link BeanFactory} configured. */ - public ExtensionAwareEvaluationContextProvider() {} + public ExtensionAwareEvaluationContextProvider() { + this.extensions = null; + } /** * Creates a new {@link ExtensionAwareEvaluationContextProvider} for the given {@link EvaluationContextExtension}s. * - * @param extensions must not be {@literal null}. + * @param adapters must not be {@literal null}. */ public ExtensionAwareEvaluationContextProvider(List extensions) { Assert.notNull(extensions, "List of EvaluationContextExtensions must not be null!"); - - List extensionsToSet = new ArrayList(extensions); - Collections.sort(extensionsToSet, AnnotationAwareOrderComparator.INSTANCE); - - this.extensions = Collections.unmodifiableList(extensionsToSet); + this.extensions = extensions; } /* @@ -152,7 +154,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex * * @return */ - private List getExtensions() { + private List getExtensions() { if (this.extensions != null) { return this.extensions; @@ -163,57 +165,81 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex return this.extensions; } - List extensions = new ArrayList(beanFactory.getBeansOfType( + this.extensions = new ArrayList(beanFactory.getBeansOfType( EvaluationContextExtension.class, true, false).values()); - Collections.sort(extensions, AnnotationAwareOrderComparator.INSTANCE); - this.extensions = extensions; return extensions; } + /** + * Looks up the {@link EvaluationContextExtensionInformation} for the given {@link EvaluationContextExtension} from + * the cache or creates a new one and caches that for later lookup. + * + * @param extension must not be {@literal null}. + * @return + */ + private EvaluationContextExtensionInformation getOrCreateInformation(EvaluationContextExtension extension) { + + Class extensionType = extension.getClass(); + EvaluationContextExtensionInformation information = extensionInformationCache.get(extensionType); + + if (information != null) { + return information; + } + + information = new EvaluationContextExtensionInformation(extensionType); + extensionInformationCache.put(extensionType, information); + return information; + } + + /** + * Creates {@link EvaluationContextExtensionAdapter}s for the given {@link EvaluationContextExtension}s. + * + * @param extensions + * @return + */ + private List toAdapters(Collection extensions) { + + List extensionsToSet = new ArrayList(extensions); + Collections.sort(extensionsToSet, AnnotationAwareOrderComparator.INSTANCE); + + List adapters = new ArrayList( + extensions.size()); + + for (EvaluationContextExtension extension : extensionsToSet) { + adapters.add(new EvaluationContextExtensionAdapter(extension, getOrCreateInformation(extension))); + } + + return adapters; + } + /** * @author Thomas Darimont * @author Oliver Gierke * @see 1.9 */ - private static class ExtensionAwarePropertyAccessor implements PropertyAccessor, MethodResolver { + private class ExtensionAwarePropertyAccessor implements PropertyAccessor, MethodResolver { - private final Map extensionMap; - private final List extensions; - private final Map functions; + private final List adapters; + private final Map adapterMap; /** * Creates a new {@link ExtensionAwarePropertyAccessor} for the given {@link EvaluationContextExtension}s. * - * @param extensions must not be {@literal null}. + * @param adapters must not be {@literal null}. */ public ExtensionAwarePropertyAccessor(List extensions) { Assert.notNull(extensions, "Extensions must not be null!"); - Map functions = new HashMap(); + this.adapters = toAdapters(extensions); + this.adapterMap = new HashMap(extensions.size()); - for (EvaluationContextExtension ext : extensions) { - - Map extFunctions = ext.getFunctions(); - - if (ext.getExtensionId() != null) { - functions.put(ext.getExtensionId(), extFunctions); - } - - functions.putAll(extFunctions); + for (EvaluationContextExtensionAdapter adapter : adapters) { + this.adapterMap.put(adapter.getExtensionId(), adapter); } - this.functions = functions; - - this.extensions = new ArrayList(extensions); - Collections.reverse(this.extensions); - - this.extensionMap = new HashMap(extensions.size()); - - for (EvaluationContextExtension extension : extensions) { - this.extensionMap.put(extension.getExtensionId(), extension); - } + Collections.reverse(this.adapters); } /* @@ -227,11 +253,11 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex return true; } - if (extensionMap.containsKey(name)) { + if (adapterMap.containsKey(name)) { return true; } - for (EvaluationContextExtension extension : extensions) { + for (EvaluationContextExtensionAdapter extension : adapters) { if (extension.getProperties().containsKey(name)) { return true; } @@ -247,20 +273,20 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex @Override public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { - if (target instanceof EvaluationContextExtension) { - return new TypedValue(((EvaluationContextExtension) target).getProperties().get(name)); + if (target instanceof EvaluationContextExtensionAdapter) { + return lookupPropertyFrom(((EvaluationContextExtensionAdapter) target), name); } - if (extensionMap.containsKey(name)) { - return new TypedValue(extensionMap.get(name)); + if (adapterMap.containsKey(name)) { + return new TypedValue(adapterMap.get(name)); } - for (EvaluationContextExtension extension : extensions) { + for (EvaluationContextExtensionAdapter extension : adapters) { Map properties = extension.getProperties(); if (properties.containsKey(name)) { - return new TypedValue(properties.get(name)); + return lookupPropertyFrom(extension, name); } } @@ -272,44 +298,23 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex * @see org.springframework.expression.MethodResolver#resolve(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String, java.util.List) */ @Override - public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, + public MethodExecutor resolve(EvaluationContext context, Object target, final String name, List argumentTypes) throws AccessException { - final Method function = targetObject instanceof Map && ((Map) targetObject).containsKey(name) ? (Method) ((Map) targetObject) - .get(name) : (Method) functions.get(name); - - if (function == null) { - return null; + if (target instanceof EvaluationContextExtensionAdapter) { + return getMethodExecutor((EvaluationContextExtensionAdapter) target, name, argumentTypes); } - Class[] parameterTypes = function.getParameterTypes(); - if (parameterTypes.length != argumentTypes.size()) { - return null; - } + for (EvaluationContextExtensionAdapter adapter : adapters) { - for (int i = 0; i < parameterTypes.length; i++) { - if (!TypeUtils.isAssignable(parameterTypes[i], argumentTypes.get(i).getType())) { - return null; + MethodExecutor executor = getMethodExecutor(adapter, name, argumentTypes); + + if (executor != null) { + return executor; } } - return new MethodExecutor() { - - /* - * (non-Javadoc) - * @see org.springframework.expression.MethodExecutor#execute(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object[]) - */ - @Override - public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { - - try { - return new TypedValue(function.invoke(null, arguments)); - } catch (Exception e) { - throw new SpelEvaluationException(e, SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, function.getName(), - function.getDeclaringClass()); - } - } - }; + return null; } /* @@ -338,5 +343,165 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex public Class[] getSpecificTargetClasses() { return null; } + + /** + * Returns a {@link MethodExecutor} + * + * @param adapter + * @param name + * @param argumentTypes + * @return + */ + private MethodExecutor getMethodExecutor(EvaluationContextExtensionAdapter adapter, String name, + List argumentTypes) { + + Map functions = adapter.getFunctions(); + + if (!functions.containsKey(name)) { + return null; + } + + Function function = functions.get(name); + + if (!function.supports(argumentTypes)) { + return null; + } + + return new FunctionMethodExecutor(function); + } + + /** + * Looks up the property value for the property of the given name from the given extension. Takes care of resolving + * {@link Function} values transitively. + * + * @param extension must not be {@literal null}. + * @param name must not be {@literal null} or empty. + * @return + */ + private TypedValue lookupPropertyFrom(EvaluationContextExtensionAdapter extension, String name) { + + Object value = extension.getProperties().get(name); + + if (!(value instanceof Function)) { + return new TypedValue(value); + } + + Function function = (Function) value; + + try { + return new TypedValue(function.invoke(new Object[0])); + } catch (Exception e) { + throw new SpelEvaluationException(e, SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, + function.getDeclaringClass()); + } + } + } + + /** + * {@link MethodExecutor} to invoke {@link Function} instances. + * + * @author Oliver Gierke + * @since 1.9 + */ + private static class FunctionMethodExecutor implements MethodExecutor { + + private final Function function; + + /** + * Creates a new {@link FunctionMethodExecutor} for the given {@link Function}. + * + * @param function must not be {@literal null}. + */ + public FunctionMethodExecutor(Function function) { + this.function = function; + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.MethodExecutor#execute(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object[]) + */ + @Override + public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { + + try { + return new TypedValue(function.invoke(arguments)); + } catch (Exception e) { + throw new SpelEvaluationException(e, SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, function.getName(), + function.getDeclaringClass()); + } + } + } + + /** + * Adapter to expose a unified view on {@link EvaluationContextExtension} based on some reflective inspection of the + * extension (see {@link EvaluationContextExtensionInformation}) as well as the values exposed by the extension + * itself. + * + * @author Oliver Gierke + * @since 1.9 + */ + private static class EvaluationContextExtensionAdapter { + + private final EvaluationContextExtension extension; + + private final Map functions; + private final Map properties; + + /** + * Creates a new {@link EvaluationContextExtensionAdapter} for the given {@link EvaluationContextExtension} and + * {@link EvaluationContextExtensionInformation}. + * + * @param extension must not be {@literal null}. + * @param information must not be {@literal null}. + */ + public EvaluationContextExtensionAdapter(EvaluationContextExtension extension, + EvaluationContextExtensionInformation information) { + + Assert.notNull(extension, "Extenstion must not be null!"); + Assert.notNull(information, "Extension information must not be null!"); + + Object target = extension.getRootObject(); + ExtensionTypeInformation extensionTypeInformation = information.getExtensionTypeInformation(); + RootObjectInformation rootObjectInformation = information.getRootObjectInformation(target); + + this.functions = new HashMap(); + this.functions.putAll(extensionTypeInformation.getFunctions()); + this.functions.putAll(rootObjectInformation.getFunctions(target)); + this.functions.putAll(extension.getFunctions()); + + this.properties = new HashMap(); + this.properties.putAll(extensionTypeInformation.getProperties()); + this.properties.putAll(rootObjectInformation.getProperties(target)); + this.properties.putAll(extension.getProperties()); + + this.extension = extension; + } + + /** + * Returns the extension identifier. + * + * @return + */ + public String getExtensionId() { + return extension.getExtensionId(); + } + + /** + * Returns all functions exposed. + * + * @return + */ + public Map getFunctions() { + return this.functions; + } + + /** + * Returns all properties exposed. Note, the value of a property can be a {@link Function} in turn + * + * @return + */ + public Map getProperties() { + return this.properties; + } } } diff --git a/src/main/java/org/springframework/data/repository/query/spi/EvaluationContextExtensionSupport.java b/src/main/java/org/springframework/data/repository/query/spi/EvaluationContextExtensionSupport.java index 6afdd6d09..875c74be6 100644 --- a/src/main/java/org/springframework/data/repository/query/spi/EvaluationContextExtensionSupport.java +++ b/src/main/java/org/springframework/data/repository/query/spi/EvaluationContextExtensionSupport.java @@ -15,17 +15,9 @@ */ package org.springframework.data.repository.query.spi; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.Collections; -import java.util.HashMap; import java.util.Map; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.FieldCallback; -import org.springframework.util.ReflectionUtils.MethodCallback; - /** * A base class for {@link EvaluationContextExtension}s. * @@ -35,61 +27,13 @@ import org.springframework.util.ReflectionUtils.MethodCallback; */ public abstract class EvaluationContextExtensionSupport implements EvaluationContextExtension { - private final Map declaredProperties; - private final Map declaredFunctions; - - /** - * Creates a new {@link EvaluationContextExtensionSupport}. - */ - public EvaluationContextExtensionSupport() { - - this.declaredProperties = discoverDeclaredProperties(); - this.declaredFunctions = discoverDeclaredFunctions(); - } - - private Map discoverDeclaredProperties() { - - final Map map = new HashMap(); - - ReflectionUtils.doWithFields(getClass(), new FieldCallback() { - - @Override - public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { - - if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) { - map.put(field.getName(), field.get(null)); - } - } - }); - - return map.isEmpty() ? Collections. emptyMap() : Collections.unmodifiableMap(map); - } - - private Map discoverDeclaredFunctions() { - - final Map map = new HashMap(); - - ReflectionUtils.doWithMethods(getClass(), new MethodCallback() { - - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - - if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) { - map.put(method.getName(), method); - } - } - }); - - return map.isEmpty() ? Collections. emptyMap() : Collections.unmodifiableMap(map); - } - /* * (non-Javadoc) * @see org.springframework.data.repository.query.EvaluationContextExtension#getProperties() */ @Override public Map getProperties() { - return this.declaredProperties; + return Collections.emptyMap(); } /* @@ -97,7 +41,16 @@ public abstract class EvaluationContextExtensionSupport implements EvaluationCon * @see org.springframework.data.repository.query.EvaluationContextExtension#getFunctions() */ @Override - public Map getFunctions() { - return this.declaredFunctions; + public Map getFunctions() { + return Collections.emptyMap(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.spi.EvaluationContextExtension#getRootObject() + */ + @Override + public Object getRootObject() { + return null; } }