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