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; } }