Browse Source

DATACMNS-1140 - Open up Query by Example to allow extension in module implementations.

We turned Example and ExampleMatcher into interfaces with default implementations created via the static of methods. The default ExampleMatcher has strongly typed default implementation. This change allows store implementations to create their very own flavor of Example and ExampleMatcher allowing eg. non typed ones.

Original pull request: #238.
pull/239/head
Christoph Strobl 8 years ago committed by Mark Paluch
parent
commit
0ff4961853
  1. 34
      src/main/java/org/springframework/data/domain/Example.java
  2. 242
      src/main/java/org/springframework/data/domain/ExampleMatcher.java
  3. 39
      src/main/java/org/springframework/data/domain/TypedExample.java
  4. 239
      src/main/java/org/springframework/data/domain/TypedExampleMatcher.java

34
src/main/java/org/springframework/data/domain/Example.java

@ -15,12 +15,6 @@ @@ -15,12 +15,6 @@
*/
package org.springframework.data.domain;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.util.ClassUtils;
/**
@ -33,13 +27,7 @@ import org.springframework.util.ClassUtils; @@ -33,13 +27,7 @@ import org.springframework.util.ClassUtils;
* @param <T> the type of the probe.
* @since 1.12
*/
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Example<T> {
private final @NonNull T probe;
private final @NonNull ExampleMatcher matcher;
public interface Example<T> {
/**
* Create a new {@link Example} including all non-null properties by default.
@ -47,8 +35,8 @@ public class Example<T> { @@ -47,8 +35,8 @@ public class Example<T> {
* @param probe must not be {@literal null}.
* @return
*/
public static <T> Example<T> of(T probe) {
return new Example<>(probe, ExampleMatcher.matching());
static <T> Example<T> of(T probe) {
return new TypedExample<>(probe, ExampleMatcher.matching());
}
/**
@ -58,8 +46,8 @@ public class Example<T> { @@ -58,8 +46,8 @@ public class Example<T> {
* @param matcher must not be {@literal null}.
* @return
*/
public static <T> Example<T> of(T probe, ExampleMatcher matcher) {
return new Example<>(probe, matcher);
static <T> Example<T> of(T probe, ExampleMatcher matcher) {
return new TypedExample<>(probe, matcher);
}
/**
@ -67,18 +55,14 @@ public class Example<T> { @@ -67,18 +55,14 @@ public class Example<T> {
*
* @return never {@literal null}.
*/
public T getProbe() {
return probe;
}
T getProbe();
/**
* Get the {@link ExampleMatcher} used.
*
* @return never {@literal null}.
*/
public ExampleMatcher getMatcher() {
return matcher;
}
ExampleMatcher getMatcher();
/**
* Get the actual type for the probe used. This is usually the given class, but the original class in case of a
@ -88,7 +72,7 @@ public class Example<T> { @@ -88,7 +72,7 @@ public class Example<T> {
* @see ClassUtils#getUserClass(Class)
*/
@SuppressWarnings("unchecked")
public Class<T> getProbeType() {
return (Class<T>) ClassUtils.getUserClass(probe.getClass());
default Class<T> getProbeType() {
return (Class<T>) ClassUtils.getUserClass(getProbe().getClass());
}
}

242
src/main/java/org/springframework/data/domain/ExampleMatcher.java

@ -18,15 +18,10 @@ package org.springframework.data.domain; @@ -18,15 +18,10 @@ package org.springframework.data.domain;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import lombok.experimental.Wither;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -37,8 +32,8 @@ import org.springframework.util.Assert; @@ -37,8 +32,8 @@ import org.springframework.util.Assert;
/**
* Specification for property path matching to use in query by example (QBE). An {@link ExampleMatcher} can be created
* for a {@link Class object type}. Instances of {@link ExampleMatcher} can be either {@link #matching()} or
* {@link #typed(Class)} and settings can be tuned {@code with...} methods in a fluent style. {@code with...} methods
* for a {@link Class object type}. Instances of {@link ExampleMatcher} can be either {@link #matchingAll()} or
* {@link #matchingAny()} and settings can be tuned {@code with...} methods in a fluent style. {@code with...} methods
* return a copy of the {@link ExampleMatcher} instance with the specified setting. Null-handling defaults to
* {@link NullHandler#IGNORE} and case-sensitive {@link StringMatcher#DEFAULT} string matching.
* <p>
@ -48,59 +43,39 @@ import org.springframework.util.Assert; @@ -48,59 +43,39 @@ import org.springframework.util.Assert;
* @author Mark Paluch
* @author Oliver Gierke
* @author Jens Schauder
* @param <T>
* @since 1.12
*/
@ToString
@EqualsAndHashCode
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class ExampleMatcher {
NullHandler nullHandler;
StringMatcher defaultStringMatcher;
PropertySpecifiers propertySpecifiers;
Set<String> ignoredPaths;
boolean defaultIgnoreCase;
@Wither(AccessLevel.PRIVATE) MatchMode mode;
private ExampleMatcher() {
this(NullHandler.IGNORE, StringMatcher.DEFAULT, new PropertySpecifiers(), Collections.emptySet(), false,
MatchMode.ALL);
}
public interface ExampleMatcher {
/**
* Create a new {@link ExampleMatcher} including all non-null properties by default exposing that all resulting
* predicates are supposed to be AND-concatenated.
*
* @param type will never be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
* @see #matchingAll()
*/
public static ExampleMatcher matching() {
static ExampleMatcher matching() {
return matchingAll();
}
/**
* Create a new {@link ExampleMatcher} including all non-null properties by default matching any predicate derived
* from the example.
* Create a new {@link ExampleMatcher} including all non-null properties by default matching <strong>any</strong>
* predicate derived from the example.
*
* @param type will never be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public static ExampleMatcher matchingAny() {
return new ExampleMatcher().withMode(MatchMode.ANY);
static ExampleMatcher matchingAny() {
return new TypedExampleMatcher().withMode(MatchMode.ANY);
}
/**
* Create a new {@link ExampleMatcher} including all non-null properties by default matching all predicates derived
* from the example.
* Create a new {@link ExampleMatcher} including all non-null properties by default matching <strong>all</strong>
* predicates derived from the example.
*
* @param type will never be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public static ExampleMatcher matchingAll() {
return new ExampleMatcher().withMode(MatchMode.ALL);
static ExampleMatcher matchingAll() {
return new TypedExampleMatcher().withMode(MatchMode.ALL);
}
/**
@ -108,42 +83,26 @@ public class ExampleMatcher { @@ -108,42 +83,26 @@ public class ExampleMatcher {
* and unaffected by this method call.
*
* @param ignoredPaths must not be {@literal null} and not empty.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withIgnorePaths(String... ignoredPaths) {
Assert.notEmpty(ignoredPaths, "IgnoredPaths must not be empty!");
Assert.noNullElements(ignoredPaths, "IgnoredPaths must not contain null elements!");
Set<String> newIgnoredPaths = new LinkedHashSet<>(this.ignoredPaths);
newIgnoredPaths.addAll(Arrays.asList(ignoredPaths));
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, newIgnoredPaths, defaultIgnoreCase,
mode);
}
ExampleMatcher withIgnorePaths(String... ignoredPaths);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified string matching of {@code defaultStringMatcher}.
* This instance is immutable and unaffected by this method call.
*
* @param defaultStringMatcher must not be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher) {
Assert.notNull(ignoredPaths, "DefaultStringMatcher must not be empty!");
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths, defaultIgnoreCase,
mode);
}
ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
/**
* Returns a copy of this {@link ExampleMatcher} with ignoring case sensitivity by default. This instance is immutable
* and unaffected by this method call.
*
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withIgnoreCase() {
default ExampleMatcher withIgnoreCase() {
return withIgnoreCase(true);
}
@ -152,12 +111,9 @@ public class ExampleMatcher { @@ -152,12 +111,9 @@ public class ExampleMatcher {
* unaffected by this method call.
*
* @param defaultIgnoreCase
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase) {
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths, defaultIgnoreCase,
mode);
}
ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
@ -165,9 +121,9 @@ public class ExampleMatcher { @@ -165,9 +121,9 @@ public class ExampleMatcher {
*
* @param propertyPath must not be {@literal null}.
* @param matcherConfigurer callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer) {
default ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(matcherConfigurer, "MatcherConfigurer must not be empty!");
@ -184,31 +140,9 @@ public class ExampleMatcher { @@ -184,31 +140,9 @@ public class ExampleMatcher {
*
* @param propertyPath must not be {@literal null}.
* @param genericPropertyMatcher callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(genericPropertyMatcher, "GenericPropertyMatcher must not be empty!");
PropertySpecifiers propertySpecifiers = new PropertySpecifiers(this.propertySpecifiers);
PropertySpecifier propertySpecifier = new PropertySpecifier(propertyPath);
if (genericPropertyMatcher.ignoreCase != null) {
propertySpecifier = propertySpecifier.withIgnoreCase(genericPropertyMatcher.ignoreCase);
}
if (genericPropertyMatcher.stringMatcher != null) {
propertySpecifier = propertySpecifier.withStringMatcher(genericPropertyMatcher.stringMatcher);
}
propertySpecifier = propertySpecifier.withValueTransformer(genericPropertyMatcher.valueTransformer);
propertySpecifiers.add(propertySpecifier);
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths, defaultIgnoreCase,
mode);
}
ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code PropertyValueTransformer} for the
@ -216,61 +150,26 @@ public class ExampleMatcher { @@ -216,61 +150,26 @@ public class ExampleMatcher {
*
* @param propertyPath must not be {@literal null}.
* @param propertyValueTransformer must not be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(propertyValueTransformer, "PropertyValueTransformer must not be empty!");
PropertySpecifiers propertySpecifiers = new PropertySpecifiers(this.propertySpecifiers);
PropertySpecifier propertySpecifier = getOrCreatePropertySpecifier(propertyPath, propertySpecifiers);
propertySpecifiers.add(propertySpecifier.withValueTransformer(propertyValueTransformer));
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths, defaultIgnoreCase,
mode);
}
ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
/**
* Returns a copy of this {@link ExampleMatcher} with ignore case sensitivity for the {@code propertyPaths}. This
* instance is immutable and unaffected by this method call.
*
* @param propertyPaths must not be {@literal null} and not empty.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withIgnoreCase(String... propertyPaths) {
Assert.notEmpty(propertyPaths, "PropertyPaths must not be empty!");
Assert.noNullElements(propertyPaths, "PropertyPaths must not contain null elements!");
PropertySpecifiers propertySpecifiers = new PropertySpecifiers(this.propertySpecifiers);
for (String propertyPath : propertyPaths) {
PropertySpecifier propertySpecifier = getOrCreatePropertySpecifier(propertyPath, propertySpecifiers);
propertySpecifiers.add(propertySpecifier.withIgnoreCase(true));
}
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths, defaultIgnoreCase,
mode);
}
private PropertySpecifier getOrCreatePropertySpecifier(String propertyPath, PropertySpecifiers propertySpecifiers) {
if (propertySpecifiers.hasSpecifierForPath(propertyPath)) {
return propertySpecifiers.getForPath(propertyPath);
}
return new PropertySpecifier(propertyPath);
}
ExampleMatcher withIgnoreCase(String... propertyPaths);
/**
* Returns a copy of this {@link ExampleMatcher} with treatment for {@literal null} values of
* {@link NullHandler#INCLUDE} . This instance is immutable and unaffected by this method call.
*
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withIncludeNullValues() {
default ExampleMatcher withIncludeNullValues() {
return withNullHandler(NullHandler.INCLUDE);
}
@ -278,9 +177,9 @@ public class ExampleMatcher { @@ -278,9 +177,9 @@ public class ExampleMatcher {
* Returns a copy of this {@link ExampleMatcher} with treatment for {@literal null} values of
* {@link NullHandler#IGNORE}. This instance is immutable and unaffected by this method call.
*
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withIgnoreNullValues() {
default ExampleMatcher withIgnoreNullValues() {
return withNullHandler(NullHandler.IGNORE);
}
@ -289,61 +188,46 @@ public class ExampleMatcher { @@ -289,61 +188,46 @@ public class ExampleMatcher {
* and unaffected by this method call.
*
* @param nullHandler must not be {@literal null}.
* @return
* @return new instance of {@link ExampleMatcher}.
*/
public ExampleMatcher withNullHandler(NullHandler nullHandler) {
Assert.notNull(nullHandler, "NullHandler must not be null!");
return new ExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths, defaultIgnoreCase,
mode);
}
ExampleMatcher withNullHandler(NullHandler nullHandler);
/**
* Get defined null handling.
*
* @return never {@literal null}
*/
public ExampleMatcher.NullHandler getNullHandler() {
return nullHandler;
}
NullHandler getNullHandler();
/**
* Get defined {@link ExampleMatcher.StringMatcher}.
*
* @return never {@literal null}.
*/
public ExampleMatcher.StringMatcher getDefaultStringMatcher() {
return defaultStringMatcher;
}
StringMatcher getDefaultStringMatcher();
/**
* @return {@literal true} if {@link String} should be matched with ignore case option.
*/
public boolean isIgnoreCaseEnabled() {
return this.defaultIgnoreCase;
}
boolean isIgnoreCaseEnabled();
/**
* @param path
* @param path must not be {@literal null}.
* @return return {@literal true} if path was set to be ignored.
*/
public boolean isIgnoredPath(String path) {
return this.ignoredPaths.contains(path);
default boolean isIgnoredPath(String path) {
return getIgnoredPaths().contains(path);
}
/**
* @return unmodifiable {@link Set} of ignored paths.
*/
public Set<String> getIgnoredPaths() {
return ignoredPaths;
}
Set<String> getIgnoredPaths();
/**
* @return the {@link PropertySpecifiers} within the {@link ExampleMatcher}.
*/
public PropertySpecifiers getPropertySpecifiers() {
return propertySpecifiers;
}
PropertySpecifiers getPropertySpecifiers();
/**
* Returns whether all of the predicates of the {@link Example} are supposed to match. If {@literal false} is
@ -351,8 +235,8 @@ public class ExampleMatcher { @@ -351,8 +235,8 @@ public class ExampleMatcher {
*
* @return whether all of the predicates of the {@link Example} are supposed to match or any of them is sufficient.
*/
public boolean isAllMatching() {
return mode.equals(MatchMode.ALL);
default boolean isAllMatching() {
return getMatchMode().equals(MatchMode.ALL);
}
/**
@ -361,16 +245,24 @@ public class ExampleMatcher { @@ -361,16 +245,24 @@ public class ExampleMatcher {
*
* @return whether it's sufficient that any of the predicates of the {@link Example} match or all need to match.
*/
public boolean isAnyMatching() {
return mode.equals(MatchMode.ANY);
default boolean isAnyMatching() {
return getMatchMode().equals(MatchMode.ANY);
}
/**
* Get the match mode of the {@link ExampleMatcher}.
*
* @return never {@literal null}.
* @since 2.0
*/
MatchMode getMatchMode();
/**
* Null handling for creating criterion out of an {@link Example}.
*
* @author Christoph Strobl
*/
public static enum NullHandler {
enum NullHandler {
INCLUDE, IGNORE
}
@ -381,7 +273,7 @@ public class ExampleMatcher { @@ -381,7 +273,7 @@ public class ExampleMatcher {
* @author Mark Paluch
* @param <T>
*/
public static interface MatcherConfigurer<T> {
interface MatcherConfigurer<T> {
void configureMatcher(T matcher);
}
@ -391,7 +283,7 @@ public class ExampleMatcher { @@ -391,7 +283,7 @@ public class ExampleMatcher {
* @author Mark Paluch
*/
@EqualsAndHashCode
public static class GenericPropertyMatcher {
class GenericPropertyMatcher {
@Nullable StringMatcher stringMatcher = null;
@Nullable Boolean ignoreCase = null;
@ -555,7 +447,7 @@ public class ExampleMatcher { @@ -555,7 +447,7 @@ public class ExampleMatcher {
*
* @author Mark Paluch
*/
public static class GenericPropertyMatchers {
class GenericPropertyMatchers {
/**
* Creates a {@link GenericPropertyMatcher} that matches string case insensitive.
@ -637,7 +529,7 @@ public class ExampleMatcher { @@ -637,7 +529,7 @@ public class ExampleMatcher {
* @author Christoph Strobl
* @author Jens Schauder
*/
public enum StringMatcher {
enum StringMatcher {
/**
* Store specific default.
@ -668,7 +560,7 @@ public class ExampleMatcher { @@ -668,7 +560,7 @@ public class ExampleMatcher {
/**
* Allows to transform the property value before it is used in the query.
*/
public static interface PropertyValueTransformer extends Function<Optional<Object>, Optional<Object>> {
interface PropertyValueTransformer extends Function<Optional<Object>, Optional<Object>> {
/**
* For backwards compatibility of clients used to invoke Spring's Converter interface.
@ -687,7 +579,7 @@ public class ExampleMatcher { @@ -687,7 +579,7 @@ public class ExampleMatcher {
* @author Oliver Gierke
* @since 1.12
*/
public static enum NoOpPropertyValueTransformer implements ExampleMatcher.PropertyValueTransformer {
enum NoOpPropertyValueTransformer implements ExampleMatcher.PropertyValueTransformer {
INSTANCE;
@ -712,7 +604,7 @@ public class ExampleMatcher { @@ -712,7 +604,7 @@ public class ExampleMatcher {
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
public static class PropertySpecifier {
class PropertySpecifier {
String path;
@Nullable StringMatcher stringMatcher;
@ -838,7 +730,7 @@ public class ExampleMatcher { @@ -838,7 +730,7 @@ public class ExampleMatcher {
* @since 1.12
*/
@EqualsAndHashCode
public static class PropertySpecifiers {
class PropertySpecifiers {
private final Map<String, PropertySpecifier> propertySpecifiers = new LinkedHashMap<>();
@ -878,7 +770,7 @@ public class ExampleMatcher { @@ -878,7 +770,7 @@ public class ExampleMatcher {
* @since 1.13
* @see ExampleMatcher#isAllMatching()
*/
private static enum MatchMode {
enum MatchMode {
ALL, ANY;
}
}

39
src/main/java/org/springframework/data/domain/TypedExample.java

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
/*
* 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.domain;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
/**
* Default implementation of {@link Example} holing instances of {@literal probe} and {@link ExampleMatcher}.
*
* @author Christoph Strobl
* @since 2.0
*/
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
@Getter
class TypedExample<T> implements Example<T> {
private final @NonNull T probe;
private final @NonNull ExampleMatcher matcher;
}

239
src/main/java/org/springframework/data/domain/TypedExampleMatcher.java

@ -0,0 +1,239 @@ @@ -0,0 +1,239 @@
/*
* 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.domain;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.experimental.Wither;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Default implementation of {@link ExampleMatcher}.
*
* @author Christoph Strobl
* @since 2.0
*/
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
class TypedExampleMatcher implements ExampleMatcher {
private final NullHandler nullHandler;
private final StringMatcher defaultStringMatcher;
private final PropertySpecifiers propertySpecifiers;
private final Set<String> ignoredPaths;
private final boolean defaultIgnoreCase;
private final @Wither(AccessLevel.PACKAGE) MatchMode mode;
TypedExampleMatcher() {
this(NullHandler.IGNORE, StringMatcher.DEFAULT, new PropertySpecifiers(), Collections.emptySet(), false,
MatchMode.ALL);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withIgnorePaths(java.lang.String...)
*/
@Override
public ExampleMatcher withIgnorePaths(String... ignoredPaths) {
Assert.notEmpty(ignoredPaths, "IgnoredPaths must not be empty!");
Assert.noNullElements(ignoredPaths, "IgnoredPaths must not contain null elements!");
Set<String> newIgnoredPaths = new LinkedHashSet<>(this.ignoredPaths);
newIgnoredPaths.addAll(Arrays.asList(ignoredPaths));
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, newIgnoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withStringMatcher(java.lang.String)
*/
@Override
public ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher) {
Assert.notNull(ignoredPaths, "DefaultStringMatcher must not be empty!");
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withIgnoreCase(boolean)
*/
@Override
public ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase) {
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withMatcher(java.lang.String, org.springframework.data.domain.ExampleMatcher.MatcherConfigurer)
*/
@Override
public ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(genericPropertyMatcher, "GenericPropertyMatcher must not be empty!");
PropertySpecifiers propertySpecifiers = new PropertySpecifiers(this.propertySpecifiers);
PropertySpecifier propertySpecifier = new PropertySpecifier(propertyPath);
if (genericPropertyMatcher.ignoreCase != null) {
propertySpecifier = propertySpecifier.withIgnoreCase(genericPropertyMatcher.ignoreCase);
}
if (genericPropertyMatcher.stringMatcher != null) {
propertySpecifier = propertySpecifier.withStringMatcher(genericPropertyMatcher.stringMatcher);
}
propertySpecifier = propertySpecifier.withValueTransformer(genericPropertyMatcher.valueTransformer);
propertySpecifiers.add(propertySpecifier);
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withTransformer(java.lang.String, org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer)
*/
@Override
public ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer) {
Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(propertyValueTransformer, "PropertyValueTransformer must not be empty!");
PropertySpecifiers propertySpecifiers = new PropertySpecifiers(this.propertySpecifiers);
PropertySpecifier propertySpecifier = getOrCreatePropertySpecifier(propertyPath, propertySpecifiers);
propertySpecifiers.add(propertySpecifier.withValueTransformer(propertyValueTransformer));
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withIgnoreCase(java.lang.String...)
*/
@Override
public ExampleMatcher withIgnoreCase(String... propertyPaths) {
Assert.notEmpty(propertyPaths, "PropertyPaths must not be empty!");
Assert.noNullElements(propertyPaths, "PropertyPaths must not contain null elements!");
PropertySpecifiers propertySpecifiers = new PropertySpecifiers(this.propertySpecifiers);
for (String propertyPath : propertyPaths) {
PropertySpecifier propertySpecifier = getOrCreatePropertySpecifier(propertyPath, propertySpecifiers);
propertySpecifiers.add(propertySpecifier.withIgnoreCase(true));
}
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#withNullHandler(org.springframework.data.domain.ExampleMatcher.NullHandler)
*/
@Override
public ExampleMatcher withNullHandler(NullHandler nullHandler) {
Assert.notNull(nullHandler, "NullHandler must not be null!");
return new TypedExampleMatcher(nullHandler, defaultStringMatcher, propertySpecifiers, ignoredPaths,
defaultIgnoreCase, mode);
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#getNullHandler()
*/
@Override
public NullHandler getNullHandler() {
return nullHandler;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#getDefaultStringMatcher()
*/
@Override
public StringMatcher getDefaultStringMatcher() {
return defaultStringMatcher;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#isIgnoreCaseEnabled()
*/
@Override
public boolean isIgnoreCaseEnabled() {
return this.defaultIgnoreCase;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#getIgnoredPaths()
*/
@Override
public Set<String> getIgnoredPaths() {
return ignoredPaths;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#getPropertySpecifiers()
*/
@Override
public PropertySpecifiers getPropertySpecifiers() {
return propertySpecifiers;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.ExampleMatcher#getMatchMode()
*/
@Override
public MatchMode getMatchMode() {
return mode;
}
private PropertySpecifier getOrCreatePropertySpecifier(String propertyPath, PropertySpecifiers propertySpecifiers) {
if (propertySpecifiers.hasSpecifierForPath(propertyPath)) {
return propertySpecifiers.getForPath(propertyPath);
}
return new PropertySpecifier(propertyPath);
}
}
Loading…
Cancel
Save