From 9462a5ba4706758f48d79a9caf704376bcb2aae1 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 9 Jun 2017 13:46:17 +0200 Subject: [PATCH] DATACMNS-764 - Java 8 polishing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use Streamable in configuration APIs for more efficient, lazy traversal of source lists. Introduced SelectionSet.of(…) to move away from constructors. Avoid the use of null in SelectionSet. Original pull request: #201. --- ...notationRepositoryConfigurationSource.java | 26 ++++------ ...ustomRepositoryImplementationDetector.java | 45 ++++++---------- .../config/RepositoryBeanNameGenerator.java | 13 +++-- .../config/RepositoryConfigurationSource.java | 10 ++-- .../RepositoryConfigurationSourceSupport.java | 46 +++++++--------- .../data/repository/config/SelectionSet.java | 52 ++++++++----------- .../XmlRepositoryConfigurationSource.java | 11 ++-- ...epositoryConfigurationSourceUnitTests.java | 4 +- .../config/SelectionSetUnitTests.java | 43 ++++++++++----- 9 files changed, 118 insertions(+), 132 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java index 60f3f6223..967f98a88 100644 --- a/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSource.java @@ -18,7 +18,6 @@ package org.springframework.data.repository.config; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -37,6 +36,7 @@ import org.springframework.core.type.filter.AspectJTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.core.type.filter.RegexPatternTypeFilter; import org.springframework.core.type.filter.TypeFilter; +import org.springframework.data.util.Streamable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -113,7 +113,7 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBasePackages() */ - public Iterable getBasePackages() { + public Streamable getBasePackages() { String[] value = attributes.getStringArray("value"); String[] basePackages = attributes.getStringArray(BASE_PACKAGES); @@ -121,19 +121,20 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura // Default configuration - return package of annotated class if (value.length == 0 && basePackages.length == 0 && basePackageClasses.length == 0) { + String className = configMetadata.getClassName(); - return Collections.singleton(ClassUtils.getPackageName(className)); + return Streamable.of(ClassUtils.getPackageName(className)); } Set packages = new HashSet<>(); packages.addAll(Arrays.asList(value)); packages.addAll(Arrays.asList(basePackages)); - for (Class typeName : basePackageClasses) { - packages.add(ClassUtils.getPackageName(typeName)); - } + Arrays.stream(basePackageClasses)// + .map(ClassUtils::getPackageName)// + .forEach(it -> packages.add(it)); - return packages; + return Streamable.of(packages); } /* @@ -182,20 +183,15 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura * @see org.springframework.data.repository.config.RepositoryConfigurationSourceSupport#getExcludeFilters() */ @Override - public Iterable getExcludeFilters() { + public Streamable getExcludeFilters() { return parseFilters("excludeFilters"); } - private Set parseFilters(String attributeName) { + private Streamable parseFilters(String attributeName) { - Set result = new HashSet<>(); AnnotationAttributes[] filters = attributes.getAnnotationArray(attributeName); - for (AnnotationAttributes filter : filters) { - result.addAll(typeFiltersFor(filter)); - } - - return result; + return Streamable.of(() -> Arrays.stream(filters).flatMap(it -> typeFiltersFor(it).stream())); } /** diff --git a/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java index df9925006..1ab8cadbf 100644 --- a/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java +++ b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java @@ -13,16 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.repository.config; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -37,6 +33,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.RegexPatternTypeFilter; import org.springframework.core.type.filter.TypeFilter; +import org.springframework.data.util.Streamable; import org.springframework.util.Assert; /** @@ -52,6 +49,7 @@ import org.springframework.util.Assert; public class CustomRepositoryImplementationDetector { private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; + private static final String AMBIGUOUS_CUSTOM_IMPLEMENTATIONS = "Ambiguous custom implementations detected! Found %s but expected a single implementation!"; private final @NonNull MetadataReaderFactory metadataReaderFactory; private final @NonNull Environment environment; @@ -94,12 +92,10 @@ public class CustomRepositoryImplementationDetector { Set definitions = findCandidateBeanDefinitions(className, basePackages, excludeFilters); - SelectionSet selection = new SelectionSet<>( // - definitions, // - c -> c.isEmpty() ? null : throwAmbiguousCustomImplementationException(c) // - ).filterIfNecessary(bd -> beanName != null && beanName.equals(beanNameGenerator.apply(bd))); - - return Optional.ofNullable((AbstractBeanDefinition) selection.uniqueResult()); + return SelectionSet // + .of(definitions, c -> c.isEmpty() ? Optional.empty() : throwAmbiguousCustomImplementationException(c)) // + .filterIfNecessary(bd -> beanName != null && beanName.equals(beanNameGenerator.apply(bd)))// + .uniqueResult().map(it -> AbstractBeanDefinition.class.cast(it)); } Set findCandidateBeanDefinitions(String className, Iterable basePackages, @@ -116,31 +112,20 @@ public class CustomRepositoryImplementationDetector { provider.setMetadataReaderFactory(metadataReaderFactory); provider.addIncludeFilter(new RegexPatternTypeFilter(pattern)); - for (TypeFilter excludeFilter : excludeFilters) { - provider.addExcludeFilter(excludeFilter); - } + excludeFilters.forEach(it -> provider.addExcludeFilter(it)); - Set definitions = new HashSet<>(); - - for (String basePackage : basePackages) { - definitions.addAll(provider.findCandidateComponents(basePackage)); - } - - return definitions; + return Streamable.of(basePackages).stream()// + .flatMap(it -> provider.findCandidateComponents(it).stream())// + .collect(Collectors.toSet()); } - private static AbstractBeanDefinition throwAmbiguousCustomImplementationException( + private static Optional throwAmbiguousCustomImplementationException( Collection definitions) { - List implementationClassNames = new ArrayList(); - for (BeanDefinition bean : definitions) { - implementationClassNames.add(bean.getBeanClassName()); - } + String implementationNames = definitions.stream()// + .map(BeanDefinition::getBeanClassName)// + .collect(Collectors.joining(", ")); - throw new IllegalStateException( - String.format("Ambiguous custom implementations detected! Found %s but expected a single implementation!", // - definitions.stream()// - .map(BeanDefinition::getBeanClassName)// - .collect(Collectors.joining(", ")))); + throw new IllegalStateException(String.format(AMBIGUOUS_CUSTOM_IMPLEMENTATIONS, implementationNames)); } } diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java b/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java index ccfb265ba..7058ee47f 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryBeanNameGenerator.java @@ -15,10 +15,11 @@ */ package org.springframework.data.repository.config; +import lombok.RequiredArgsConstructor; + import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.ScannedGenericBeanDefinition; @@ -32,16 +33,13 @@ import org.springframework.util.ClassUtils; * @author Oliver Gierke * @author Jens Schauder */ +@RequiredArgsConstructor public class RepositoryBeanNameGenerator { private static final BeanNameGenerator DELEGATE = new AnnotationBeanNameGenerator(); private final ClassLoader beanClassLoader; - public RepositoryBeanNameGenerator(ClassLoader beanClassLoader) { - this.beanClassLoader = beanClassLoader; - } - /** * Generate a bean name for the given bean definition. * @@ -66,11 +64,13 @@ public class RepositoryBeanNameGenerator { private Class getRepositoryInterfaceFrom(BeanDefinition beanDefinition) { if (beanDefinition instanceof ScannedGenericBeanDefinition) { + try { return ((ScannedGenericBeanDefinition) beanDefinition).resolveBeanClass(beanClassLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException("Could not resolve bean class.", e); } + } else { return getRepositoryInterfaceFromFactory(beanDefinition); } @@ -81,8 +81,11 @@ public class RepositoryBeanNameGenerator { Object value = beanDefinition.getConstructorArgumentValues().getArgumentValue(0, Class.class).getValue(); if (value instanceof Class) { + return (Class) value; + } else { + try { return ClassUtils.forName(value.toString(), beanClassLoader); } catch (Exception o_O) { diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java index 76af7c005..b34674755 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSource.java @@ -15,13 +15,13 @@ */ package org.springframework.data.repository.config; -import java.util.Collection; import java.util.Optional; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.util.Streamable; /** * Interface containing the configurable options for the Spring Data repository subsystem. @@ -46,7 +46,7 @@ public interface RepositoryConfigurationSource { * * @return must not be {@literal null}. */ - Iterable getBasePackages(); + Streamable getBasePackages(); /** * Returns the {@link QueryLookupStrategy.Key} to define how query methods shall be resolved. @@ -89,7 +89,7 @@ public interface RepositoryConfigurationSource { * @param loader * @return */ - Collection getCandidates(ResourceLoader loader); + Streamable getCandidates(ResourceLoader loader); /** * Returns the value for the {@link String} attribute with the given name. The name is expected to be handed in @@ -115,12 +115,12 @@ public interface RepositoryConfigurationSource { * * @return must not be {@literal null}. */ - Iterable getExcludeFilters(); + Streamable getExcludeFilters(); /** * Returns a name for the beanDefinition. * - * @param beanDefinition Must not be {@literal null}. + * @param beanDefinition must not be {@literal null}. * @return * @since 2.0 */ diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java index b96857367..2ea1798b1 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationSourceSupport.java @@ -15,15 +15,13 @@ */ package org.springframework.data.repository.config; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; -import java.util.Set; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.filter.TypeFilter; +import org.springframework.data.util.Streamable; import org.springframework.util.Assert; /** @@ -58,27 +56,20 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository /* * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfiguration#getCandidates(org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider) + * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getCandidates(org.springframework.core.io.ResourceLoader) */ - public Collection getCandidates(ResourceLoader loader) { + @Override + public Streamable getCandidates(ResourceLoader loader) { RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters()); scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories()); scanner.setResourceLoader(loader); scanner.setEnvironment(environment); - for (TypeFilter filter : getExcludeFilters()) { - scanner.addExcludeFilter(filter); - } + getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it)); - Set result = new HashSet<>(); - - for (String basePackage : getBasePackages()) { - Set candidate = scanner.findCandidateComponents(basePackage); - result.addAll(candidate); - } - - return result; + return Streamable.of(() -> getBasePackages().stream()// + .flatMap(it -> scanner.findCandidateComponents(it).stream())); } /** @@ -87,8 +78,18 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository * * @return must not be {@literal null}. */ - public Iterable getExcludeFilters() { - return Collections.emptySet(); + @Override + public Streamable getExcludeFilters() { + return Streamable.empty(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBeanNameGenerator() + */ + @Override + public String generateBeanName(BeanDefinition beanDefinition) { + return beanNameGenerator.generateBeanName(beanDefinition); } /** @@ -110,13 +111,4 @@ public abstract class RepositoryConfigurationSourceSupport implements Repository public boolean shouldConsiderNestedRepositories() { return false; } - - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBeanNameGenerator() - */ - @Override - public String generateBeanName(BeanDefinition beanDefinition) { - return beanNameGenerator.generateBeanName(beanDefinition); - } } diff --git a/src/main/java/org/springframework/data/repository/config/SelectionSet.java b/src/main/java/org/springframework/data/repository/config/SelectionSet.java index 08faaf4ca..5feba655e 100644 --- a/src/main/java/org/springframework/data/repository/config/SelectionSet.java +++ b/src/main/java/org/springframework/data/repository/config/SelectionSet.java @@ -15,8 +15,10 @@ */ package org.springframework.data.repository.config; +import lombok.RequiredArgsConstructor; + import java.util.Collection; -import java.util.List; +import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -26,41 +28,35 @@ import java.util.stream.Collectors; * filters are ignored. * * @author Jens Schauder + * @author Oliver Gierke + * @since 2.0 */ +@RequiredArgsConstructor(staticName = "of") class SelectionSet { private final Collection collection; - private final Function, T> fallback; + private final Function, Optional> fallback; /** * creates a {@link SelectionSet} with a default fallback of {@literal null}, when no element is found and an * {@link IllegalStateException} when no element is found. */ - SelectionSet(Collection collection) { - this(collection, defaultFallback()); + static SelectionSet of(Collection collection) { + return new SelectionSet<>(collection, defaultFallback()); } /** - * @param collection The collection from which a unique element will get picked. - * @param fallback Will get called once {@literal #uniqueResult} gets called if no unique result can be determined. - */ - SelectionSet(Collection collection, Function, T> fallback) { - - this.collection = collection; - this.fallback = fallback; - } - - /** - * If this SelectionSet contains exactly one element it gets returned. If no unique result can - * be identified the fallback function passed in at the constructor gets called and its return value becomes - * the return value of this method. + * If this {@code SelectionSet} contains exactly one element it gets returned. If no unique result can be identified + * the fallback function passed in at the constructor gets called and its return value becomes the return value of + * this method. * * @return a unique result, or the result of the callback provided in the constructor. */ - T uniqueResult() { - T uniqueResult = findUniqueResult(); + Optional uniqueResult() { - return uniqueResult != null ? uniqueResult : fallback.apply(collection); + Optional uniqueResult = findUniqueResult(); + + return uniqueResult.isPresent() ? uniqueResult : fallback.apply(collection); } /** @@ -70,26 +66,22 @@ class SelectionSet { */ SelectionSet filterIfNecessary(Predicate predicate) { - if (findUniqueResult() != null) { - return this; - } - - List fillteredList = collection.stream().filter(predicate).collect(Collectors.toList()); - return new SelectionSet(fillteredList, fallback); + return findUniqueResult().map(it -> this).orElseGet( + () -> new SelectionSet(collection.stream().filter(predicate).collect(Collectors.toList()), fallback)); } - private static Function, S> defaultFallback() { + private static Function, Optional> defaultFallback() { return c -> { if (c.isEmpty()) { - return null; + return Optional.empty(); } else { throw new IllegalStateException("More then one element in collection."); } }; } - private T findUniqueResult() { - return collection.size() == 1 ? collection.iterator().next() : null; + private Optional findUniqueResult() { + return Optional.ofNullable(collection.size() == 1 ? collection.iterator().next() : null); } } diff --git a/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java index 031c98320..3dd1646dc 100644 --- a/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/config/XmlRepositoryConfigurationSource.java @@ -15,7 +15,6 @@ */ package org.springframework.data.repository.config; -import java.util.Arrays; import java.util.Collection; import java.util.Optional; @@ -26,6 +25,7 @@ import org.springframework.data.config.TypeFilterParser; import org.springframework.data.config.TypeFilterParser.Type; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.util.ParsingUtils; +import org.springframework.data.util.Streamable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -89,10 +89,11 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationSource#getBasePackages() */ - public Iterable getBasePackages() { + public Streamable getBasePackages() { String attribute = element.getAttribute(BASE_PACKAGE); - return Arrays.asList(StringUtils.delimitedListToStringArray(attribute, ",", " ")); + + return Streamable.of(StringUtils.delimitedListToStringArray(attribute, ",", " ")); } /* @@ -125,8 +126,8 @@ public class XmlRepositoryConfigurationSource extends RepositoryConfigurationSou * @see org.springframework.data.repository.config.RepositoryConfigurationSourceSupport#getExcludeFilters() */ @Override - public Iterable getExcludeFilters() { - return excludeFilters; + public Streamable getExcludeFilters() { + return Streamable.of(excludeFilters); } /* diff --git a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java index 46b9483f8..58841314c 100755 --- a/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/AnnotationRepositoryConfigurationSourceUnitTests.java @@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.*; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collection; import org.junit.Before; import org.junit.Test; @@ -32,6 +31,7 @@ import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.data.util.Streamable; /** * Unit tests for {@link AnnotationRepositoryConfigurationSource}. @@ -65,7 +65,7 @@ public class AnnotationRepositoryConfigurationSourceUnitTests { @Test // DATACMNS-47 public void evaluatesExcludeFiltersCorrectly() { - Collection candidates = source.getCandidates(new DefaultResourceLoader()); + Streamable candidates = source.getCandidates(new DefaultResourceLoader()); assertThat(candidates).hasSize(1); BeanDefinition candidate = candidates.iterator().next(); diff --git a/src/test/java/org/springframework/data/repository/config/SelectionSetUnitTests.java b/src/test/java/org/springframework/data/repository/config/SelectionSetUnitTests.java index fabd37589..7e94b7fd4 100644 --- a/src/test/java/org/springframework/data/repository/config/SelectionSetUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/SelectionSetUnitTests.java @@ -1,8 +1,25 @@ +/* + * Copyright 2017 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.config; import static java.util.Arrays.*; import static java.util.Collections.*; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.Optional; import org.junit.Test; @@ -10,53 +27,53 @@ import org.junit.Test; * Unit tests for {@link SelectionSet} * * @author Jens Schauder + * @author Oliver Gierke */ public class SelectionSetUnitTests { @Test // DATACMNS-764 public void returnsUniqueResult() { - assertEquals("single value", new SelectionSet<>(singleton("single value")).uniqueResult()); + assertThat(SelectionSet.of(singleton("single value")).uniqueResult()).hasValue("single value"); } @Test // DATACMNS-764 public void emptyCollectionReturnsNull() { - assertNull(new SelectionSet(emptySet()).uniqueResult()); + assertThat(SelectionSet.of(emptySet()).uniqueResult()).isEmpty(); } @Test(expected = IllegalStateException.class) // DATACMNS-764 public void multipleElementsThrowException() { - new SelectionSet<>(asList("one", "two")).uniqueResult(); + SelectionSet.of(asList("one", "two")).uniqueResult(); } @Test(expected = NullPointerException.class) // DATACMNS-764 public void throwsCustomExceptionWhenConfigured() { - new SelectionSet<>(asList("one", "two"), c -> { + SelectionSet.of(asList("one", "two"), c -> { throw new NullPointerException(); }).uniqueResult(); } @Test // DATACMNS-764 public void usesFallbackWhenConfigured() { - - String value = new SelectionSet<>(asList("one", "two"), c -> String.valueOf(c.size())).uniqueResult(); - - assertEquals("2", value); + assertThat(SelectionSet.of(asList("one", "two"), c -> Optional.of(String.valueOf(c.size()))).uniqueResult()) + .hasValue("2"); } @Test // DATACMNS-764 public void returnsUniqueResultAfterFilter() { - SelectionSet selection = new SelectionSet<>(asList("one", "two", "three")).filterIfNecessary(s -> s.contains("w")); + SelectionSet selection = SelectionSet.of(asList("one", "two", "three")) + .filterIfNecessary(s -> s.contains("w")); - assertEquals("two", selection.uniqueResult()); + assertThat(selection.uniqueResult()).hasValue("two"); } @Test // DATACMNS-764 public void ignoresFilterWhenResultIsAlreadyUnique() { - SelectionSet selection = new SelectionSet<>(asList("one")).filterIfNecessary(s -> s.contains("w")); + SelectionSet selection = SelectionSet.of(asList("one")).filterIfNecessary(s -> s.contains("w")); - assertEquals("one", selection.uniqueResult()); + assertThat(selection.uniqueResult()).hasValue("one"); } }