diff --git a/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java b/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java index 5e77903ee..bf392b4c8 100644 --- a/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java +++ b/src/main/java/org/springframework/data/repository/query/EvaluationContextExtensionInformation.java @@ -15,6 +15,8 @@ */ package org.springframework.data.repository.query; +import static org.springframework.data.util.StreamUtils.*; + import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -35,10 +37,10 @@ import org.springframework.data.repository.query.EvaluationContextExtensionInfor import org.springframework.data.repository.query.Functions.NameAndArgumentCount; import org.springframework.data.repository.query.spi.EvaluationContextExtension; import org.springframework.data.repository.query.spi.Function; -import org.springframework.data.util.MultiValueMapCollector; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.FieldFilter; @@ -55,6 +57,7 @@ import org.springframework.util.ReflectionUtils.MethodFilter; * * @author Oliver Gierke * @author Christoph Strobl + * @author Jens Schauder * @since 1.9 */ class EvaluationContextExtensionInformation { @@ -248,14 +251,11 @@ class EvaluationContextExtensionInformation { * @return the methods */ public MultiValueMap getFunctions(Optional target) { + return target.map(this::getFunctions).orElseGet(() -> new LinkedMultiValueMap<>()); + } - return target.map( // - it -> methods.stream().collect( // - new MultiValueMapCollector<>( // - m -> NameAndArgumentCount.of(m), // - m -> new Function(m, it) // - ))) // - .orElseGet(() -> CollectionUtils.toMultiValueMap(Collections.emptyMap())); + private MultiValueMap getFunctions(Object target) { + return methods.stream().collect(toMultiMap(NameAndArgumentCount::of, m -> new Function(m, target))); } /** diff --git a/src/main/java/org/springframework/data/repository/query/Functions.java b/src/main/java/org/springframework/data/repository/query/Functions.java index 51caa0d01..98b7892d5 100644 --- a/src/main/java/org/springframework/data/repository/query/Functions.java +++ b/src/main/java/org/springframework/data/repository/query/Functions.java @@ -15,12 +15,12 @@ */ package org.springframework.data.repository.query; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Value; import java.lang.reflect.Method; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -29,7 +29,7 @@ import java.util.stream.Stream; import org.springframework.core.convert.TypeDescriptor; import org.springframework.data.repository.query.spi.Function; -import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; /** @@ -38,18 +38,23 @@ import org.springframework.util.MultiValueMap; * value lists are actually unique with respect to the signature. * * @author Jens Schauder + * @author Oliver Gierke * @since 2.0 */ class Functions { - private final MultiValueMap functions = CollectionUtils - .toMultiValueMap(new HashMap<>()); + private static final String MESSAGE_TEMPLATE = "There are multiple matching methods of name '%s' for parameter types (%s), but no " + + "exact match. Make sure to provide only one matching overload or one with exactly those types."; + + private final MultiValueMap functions = new LinkedMultiValueMap<>(); void addAll(Map newFunctions) { newFunctions.forEach((n, f) -> { - NameAndArgumentCount k = new NameAndArgumentCount(n, f.getParameterCount()); + + NameAndArgumentCount k = NameAndArgumentCount.of(n, f.getParameterCount()); List currentElements = get(k); + if (!contains(currentElements, f)) { functions.add(k, f); } @@ -59,7 +64,9 @@ class Functions { void addAll(MultiValueMap newFunctions) { newFunctions.forEach((k, list) -> { + List currentElements = get(k); + list.stream() // .filter(f -> !contains(currentElements, f)) // .forEach(f -> functions.add(k, f)); @@ -82,7 +89,7 @@ class Functions { */ Optional get(String name, List argumentTypes) { - Stream candidates = get(new NameAndArgumentCount(name, argumentTypes.size())).stream() // + Stream candidates = get(NameAndArgumentCount.of(name, argumentTypes.size())).stream() // .filter(f -> f.supports(argumentTypes)); return bestMatch(candidates.collect(Collectors.toList()), argumentTypes); } @@ -96,11 +103,13 @@ class Functions { if (candidates.isEmpty()) { return Optional.empty(); } + if (candidates.size() == 1) { return Optional.of(candidates.get(0)); } Optional exactMatch = candidates.stream().filter(f -> f.supportsExact(argumentTypes)).findFirst(); + if (!exactMatch.isPresent()) { throw new IllegalStateException(createErrorMessage(candidates, argumentTypes)); } @@ -110,24 +119,22 @@ class Functions { private static String createErrorMessage(List candidates, List argumentTypes) { - String argumentTypeString = String.join( // - ",", // - argumentTypes.stream().map(TypeDescriptor::getName).collect(Collectors.toList())); + String argumentTypeString = argumentTypes.stream()// + .map(TypeDescriptor::getName)// + .collect(Collectors.joining(",")); - String messageTemplate = "There are multiple matching methods of name '%s' for parameter types (%s), but no " - + "exact match. Make sure to provide only one matching overload or one with exactly those types."; - - return String.format(messageTemplate, candidates.get(0).getName(), argumentTypeString); + return String.format(MESSAGE_TEMPLATE, candidates.get(0).getName(), argumentTypeString); } @Value - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PRIVATE, staticName = "of") static class NameAndArgumentCount { + String name; int count; static NameAndArgumentCount of(Method m) { - return new NameAndArgumentCount(m.getName(), m.getParameterCount()); + return NameAndArgumentCount.of(m.getName(), m.getParameterCount()); } } } diff --git a/src/main/java/org/springframework/data/util/MultiValueMapCollector.java b/src/main/java/org/springframework/data/util/MultiValueMapCollector.java index 597d82911..556bf49ae 100644 --- a/src/main/java/org/springframework/data/util/MultiValueMapCollector.java +++ b/src/main/java/org/springframework/data/util/MultiValueMapCollector.java @@ -15,6 +15,7 @@ */ package org.springframework.data.util; +import lombok.AccessLevel; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -36,11 +37,11 @@ import org.springframework.util.MultiValueMap; * @author Jens Schauder * @since 2.0 */ -@RequiredArgsConstructor -public class MultiValueMapCollector implements Collector, MultiValueMap> { +@RequiredArgsConstructor(access = AccessLevel.PACKAGE, staticName = "of") +class MultiValueMapCollector implements Collector, MultiValueMap> { - @NonNull private final Function keyFunction; - @NonNull private final Function valueFunction; + private final @NonNull Function keyFunction; + private final @NonNull Function valueFunction; /* * (non-Javadoc) diff --git a/src/main/java/org/springframework/data/util/StreamUtils.java b/src/main/java/org/springframework/data/util/StreamUtils.java index 45eaa8499..fa8d708d2 100644 --- a/src/main/java/org/springframework/data/util/StreamUtils.java +++ b/src/main/java/org/springframework/data/util/StreamUtils.java @@ -88,6 +88,6 @@ public interface StreamUtils { */ public static Collector, MultiValueMap> toMultiMap(Function keyFunction, Function valueFunction) { - return new MultiValueMapCollector(keyFunction, valueFunction); + return MultiValueMapCollector.of(keyFunction, valueFunction); } } diff --git a/src/test/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProviderUnitTests.java b/src/test/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProviderUnitTests.java index e508222ba..87b82a7a9 100755 --- a/src/test/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProviderUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/ExtensionAwareEvaluationContextProviderUnitTests.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.PageRequest; @@ -273,7 +272,7 @@ public class ExtensionAwareEvaluationContextProviderUnitTests { .withMessageContaining("(java.lang.Integer)"); } - private ExtensionAwareEvaluationContextProvider createContextProviderWithOverloads() { + private static ExtensionAwareEvaluationContextProvider createContextProviderWithOverloads() { return new ExtensionAwareEvaluationContextProvider(Collections.singletonList( // new DummyExtension("_first", "first") {