DATACMNS-1108 - Polishing.

Move loop to Set lookup, update Javadoc and remove unused imports.

Original Pull Request: #454
This commit is contained in:
Christoph Strobl
2020-07-20 13:22:41 +02:00
parent e9901e7df8
commit fb15a9647d
8 changed files with 182 additions and 42 deletions

View File

@@ -28,9 +28,12 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import org.springframework.beans.BeanUtils;
import org.springframework.data.spel.EvaluationContextExtensionInformation.ExtensionTypeInformation.PublicMethodAndFieldFilter;
import org.springframework.data.spel.ExpressionDependencies.ExpressionDependency;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.data.spel.spi.Function;
import org.springframework.data.util.Streamable;
@@ -102,14 +105,13 @@ class EvaluationContextExtensionInformation {
}
/**
* Return {@literal true} if this extension provides {@link ExpressionDependencies.ExpressionDependency}.
* Return {@literal true} if this extension provides {@link ExpressionDependency}.
*
* @param dependency the expression dependency.
* @return {@literal true} if {@link ExpressionDependencies.ExpressionDependency} is provided by this root object or
* the extension itself.
* @return {@literal true} if {@link ExpressionDependency} is provided by this root object or the extension itself.
* @since 2.4
*/
public boolean provides(ExpressionDependencies.ExpressionDependency dependency) {
public boolean provides(ExpressionDependency dependency) {
// We don't know, extension declares Object getRootObject
if (!rootObjectInformation.isPresent()) {
@@ -159,13 +161,13 @@ class EvaluationContextExtensionInformation {
}
/**
* Return {@literal true} if this extension provides {@link ExpressionDependencies.ExpressionDependency}.
* Return {@literal true} if this extension provides {@link ExpressionDependency}.
*
* @param dependency the expression dependency.
* @return {@literal true} if {@link ExpressionDependencies.ExpressionDependency} is provided by this root object.
* @return {@literal true} if {@link ExpressionDependency} is provided by this root object.
* @since 2.4
*/
public boolean provides(ExpressionDependencies.ExpressionDependency dependency) {
public boolean provides(ExpressionDependency dependency) {
if (dependency.isPropertyOrField()) {
return properties.containsKey(dependency.getSymbol());
@@ -199,12 +201,12 @@ class EvaluationContextExtensionInformation {
static class PublicMethodAndFieldFilter implements MethodFilter, FieldFilter {
public static final PublicMethodAndFieldFilter STATIC = new PublicMethodAndFieldFilter(true);
public static final PublicMethodAndFieldFilter NON_STATIC = new PublicMethodAndFieldFilter(false);
static final PublicMethodAndFieldFilter STATIC = new PublicMethodAndFieldFilter(true);
static final PublicMethodAndFieldFilter NON_STATIC = new PublicMethodAndFieldFilter(false);
private final boolean staticOnly;
public PublicMethodAndFieldFilter(boolean staticOnly) {
PublicMethodAndFieldFilter(boolean staticOnly) {
this.staticOnly = staticOnly;
}
@@ -251,7 +253,7 @@ class EvaluationContextExtensionInformation {
private static final RootObjectInformation NONE = new RootObjectInformation(Object.class);
private final Map<String, Method> accessors;
private final Collection<Method> methods;
private final Methods methods;
private final Collection<Field> fields;
/**
@@ -260,12 +262,12 @@ class EvaluationContextExtensionInformation {
*
* @param type must not be {@literal null}.
*/
public RootObjectInformation(Class<?> type) {
RootObjectInformation(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
this.accessors = new HashMap<>();
this.methods = new HashSet<>();
this.methods = new Methods();
this.fields = new ArrayList<>();
if (Object.class.equals(type)) {
@@ -293,7 +295,7 @@ class EvaluationContextExtensionInformation {
* @param target can be {@literal null}.
* @return the methods
*/
public MultiValueMap<String, Function> getFunctions(Optional<Object> target) {
MultiValueMap<String, Function> getFunctions(Optional<Object> target) {
return target.map(this::getFunctions).orElseGet(LinkedMultiValueMap::new);
}
@@ -307,7 +309,7 @@ class EvaluationContextExtensionInformation {
*
* @return the properties
*/
public Map<String, Object> getProperties(Optional<Object> target) {
Map<String, Object> getProperties(Optional<Object> target) {
return target.map(it -> {
@@ -323,12 +325,17 @@ class EvaluationContextExtensionInformation {
}
/**
* Returns whether this root object information provides {@link ExpressionDependencies.ExpressionDependency}.
* Returns whether this root object information provides {@link ExpressionDependency}.
*
* @param dependency the expression dependency.
* @return {@literal true} if {@link ExpressionDependencies.ExpressionDependency} is provided by this root object.
* @return {@literal true} if {@link ExpressionDependency} is provided by this root object.
* @since 2.4
*/
public boolean provides(ExpressionDependencies.ExpressionDependency dependency) {
boolean provides(ExpressionDependency dependency) {
if (!dependency.isMethod() && !dependency.isPropertyOrField()) {
return false;
}
if (dependency.isPropertyOrField()) {
@@ -346,14 +353,7 @@ class EvaluationContextExtensionInformation {
}
if (dependency.isMethod()) {
for (Method method : methods) {
if (method.getName().equals(dependency.getSymbol())) {
return true;
}
}
return false;
return methods.containsMethodName(dependency.getSymbol());
}
return false;
@@ -369,4 +369,34 @@ class EvaluationContextExtensionInformation {
return map.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(map);
}
/**
* @since 2.4
* @author Christpoh Strobl
*/
static class Methods {
private final Collection<Method> methods;
private final Set<String> methodNames;
Methods() {
this.methods = new HashSet<>();
this.methodNames = new HashSet<>();
}
void add(Method method) {
methods.add(method);
methodNames.add(method.getName());
}
boolean containsMethodName(String name) {
return methodNames.contains(name);
}
public Stream<Method> stream() {
return methods.stream();
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.spel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -37,6 +38,7 @@ import org.springframework.util.ObjectUtils;
* Value object capturing dependencies to a method or property/field that is referenced from a SpEL expression.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.4
*/
public class ExpressionDependencies implements Streamable<ExpressionDependencies.ExpressionDependency> {
@@ -52,12 +54,47 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
/**
* Return an empty {@link ExpressionDependencies} object.
*
* @return the empty dependencies.
* @return empty dependencies.
*/
public static ExpressionDependencies empty() {
public static ExpressionDependencies none() {
return EMPTY;
}
/**
* Return an {@link ExpressionDependencies} object representing the given {@link Collection} of
* {@link ExpressionDependency dependencies}.
*
* @return {@link ExpressionDependencies} holding the given {@link ExpressionDependency dependencies} or
* {@link ExpressionDependencies#none() none} if the collection is {@link Collection#isEmpty() empty}.
*/
public static ExpressionDependencies of(Collection<ExpressionDependency> dependencies) {
if (dependencies.isEmpty()) {
return EMPTY;
}
return new ExpressionDependencies(new ArrayList<>(new LinkedHashSet<>(dependencies)));
}
/**
* Return an {@link ExpressionDependencies} object representing the merged collection of {@link ExpressionDependency
* dependencies} withing the given {@link ExpressionDependencies} collection.
*
* @return {@link ExpressionDependencies} holding a set of merged {@link ExpressionDependency dependencies} or
* {@link ExpressionDependencies#none() none} if the given {@link Iterable} is {@link Collection#isEmpty()
* empty}.
*/
public static ExpressionDependencies merged(Iterable<ExpressionDependencies> dependencies) {
if (!dependencies.iterator().hasNext()) {
return EMPTY;
}
List<ExpressionDependency> dependencySet = new ArrayList<>();
dependencies.forEach(it -> dependencySet.addAll(it.dependencies));
return ExpressionDependencies.of(dependencySet);
}
/**
* Discover all expression dependencies that are referenced in the {@link SpelNode expression root}.
*
@@ -65,7 +102,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
* @return a set of {@link ExpressionDependencies}.
*/
public static ExpressionDependencies discover(Expression expression) {
return expression instanceof SpelExpression ? discover(((SpelExpression) expression).getAST(), true) : empty();
return expression instanceof SpelExpression ? discover(((SpelExpression) expression).getAST(), true) : none();
}
/**
@@ -171,6 +208,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
private final int nestLevel;
private ExpressionDependency(DependencyType type, String symbol, int nestLevel) {
this.symbol = symbol;
this.nestLevel = nestLevel;
this.type = type;

View File

@@ -50,6 +50,7 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* An {@link EvaluationContextProvider} that assembles an {@link EvaluationContext} from a list of
@@ -64,7 +65,7 @@ import org.springframework.util.Assert;
*/
public class ExtensionAwareEvaluationContextProvider implements EvaluationContextProvider {
private final Map<Class<?>, EvaluationContextExtensionInformation> extensionInformationCache = new ConcurrentHashMap<>();
private final Map<String, EvaluationContextExtensionInformation> extensionInformationCache = new ConcurrentHashMap<>();
private final Lazy<? extends Collection<? extends ExtensionIdAware>> extensions;
private ListableBeanFactory beanFactory;
@@ -181,7 +182,7 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
* @return
*/
EvaluationContextExtensionInformation getOrCreateInformation(Class<? extends EvaluationContextExtension> extension) {
return extensionInformationCache.computeIfAbsent(extension,
return extensionInformationCache.computeIfAbsent(ClassUtils.getUserClass(extension).getName(),
type -> new EvaluationContextExtensionInformation(extension));
}

View File

@@ -18,13 +18,13 @@ package org.springframework.data.spel.spi;
import java.util.Collections;
import java.util.Map;
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.expression.EvaluationContext;
import org.springframework.lang.Nullable;
/**
* SPI to allow adding a set of properties and function definitions accessible via the root of an
* {@link EvaluationContext} provided by a {@link ExtensionAwareQueryMethodEvaluationContextProvider}.
* {@link EvaluationContext} provided by an
* {@link org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider}.
* <p>
* Extensions can be ordered by following Spring's {@link org.springframework.core.Ordered} conventions.
*

View File

@@ -29,7 +29,7 @@ public interface ExtensionIdAware {
* Return the identifier of the extension. The id can be leveraged by users to fully qualify property lookups and thus
* overcome ambiguities in case multiple extensions expose properties with the same name.
*
* @return the extension id, must not be {@literal null}.
* @return the extension id, never {@literal null}.
*/
String getExtensionId();
}

View File

@@ -17,12 +17,12 @@ package org.springframework.data.spel.spi;
import reactor.core.publisher.Mono;
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.expression.EvaluationContext;
/**
* SPI to resolve a {@link EvaluationContextExtension} to make it accessible via the root of an
* {@link EvaluationContext} provided by a {@link ExtensionAwareQueryMethodEvaluationContextProvider}.
* {@link EvaluationContext} provided by a
* {@link org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider}.
* <p>
* Extensions can be ordered by following Spring's {@link org.springframework.core.Ordered} conventions.
*