diff --git a/src/main/java/org/springframework/data/domain/Example.java b/src/main/java/org/springframework/data/domain/Example.java index 589730011..6256ceb61 100644 --- a/src/main/java/org/springframework/data/domain/Example.java +++ b/src/main/java/org/springframework/data/domain/Example.java @@ -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; * @param the type of the probe. * @since 1.12 */ -@ToString -@EqualsAndHashCode -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public class Example { - - private final @NonNull T probe; - private final @NonNull ExampleMatcher matcher; +public interface Example { /** * Create a new {@link Example} including all non-null properties by default. @@ -47,8 +35,8 @@ public class Example { * @param probe must not be {@literal null}. * @return */ - public static Example of(T probe) { - return new Example<>(probe, ExampleMatcher.matching()); + static Example of(T probe) { + return new TypedExample<>(probe, ExampleMatcher.matching()); } /** @@ -58,8 +46,8 @@ public class Example { * @param matcher must not be {@literal null}. * @return */ - public static Example of(T probe, ExampleMatcher matcher) { - return new Example<>(probe, matcher); + static Example of(T probe, ExampleMatcher matcher) { + return new TypedExample<>(probe, matcher); } /** @@ -67,18 +55,14 @@ public class Example { * * @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 { * @see ClassUtils#getUserClass(Class) */ @SuppressWarnings("unchecked") - public Class getProbeType() { - return (Class) ClassUtils.getUserClass(probe.getClass()); + default Class getProbeType() { + return (Class) ClassUtils.getUserClass(getProbe().getClass()); } } diff --git a/src/main/java/org/springframework/data/domain/ExampleMatcher.java b/src/main/java/org/springframework/data/domain/ExampleMatcher.java index 609d252c9..9614ffcc6 100644 --- a/src/main/java/org/springframework/data/domain/ExampleMatcher.java +++ b/src/main/java/org/springframework/data/domain/ExampleMatcher.java @@ -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; /** * 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. *

@@ -48,59 +43,39 @@ import org.springframework.util.Assert; * @author Mark Paluch * @author Oliver Gierke * @author Jens Schauder - * @param * @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 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 any + * 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 all + * 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 { * 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 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 { * 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 { * * @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 matcherConfigurer) { + default ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer matcherConfigurer) { Assert.hasText(propertyPath, "PropertyPath must not be empty!"); Assert.notNull(matcherConfigurer, "MatcherConfigurer must not be empty!"); @@ -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 { * * @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 { * 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 { * 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 getIgnoredPaths() { - return ignoredPaths; - } + Set 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 { * * @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 { * * @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 { * @author Mark Paluch * @param */ - public static interface MatcherConfigurer { + interface MatcherConfigurer { void configureMatcher(T matcher); } @@ -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 { * * @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 { * @author Christoph Strobl * @author Jens Schauder */ - public enum StringMatcher { + enum StringMatcher { /** * Store specific default. @@ -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> { + interface PropertyValueTransformer extends Function, Optional> { /** * For backwards compatibility of clients used to invoke Spring's Converter interface. @@ -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 { @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 { * @since 1.12 */ @EqualsAndHashCode - public static class PropertySpecifiers { + class PropertySpecifiers { private final Map propertySpecifiers = new LinkedHashMap<>(); @@ -878,7 +770,7 @@ public class ExampleMatcher { * @since 1.13 * @see ExampleMatcher#isAllMatching() */ - private static enum MatchMode { + enum MatchMode { ALL, ANY; } } diff --git a/src/main/java/org/springframework/data/domain/TypedExample.java b/src/main/java/org/springframework/data/domain/TypedExample.java new file mode 100644 index 000000000..a9cb055fc --- /dev/null +++ b/src/main/java/org/springframework/data/domain/TypedExample.java @@ -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 implements Example { + + private final @NonNull T probe; + private final @NonNull ExampleMatcher matcher; +} diff --git a/src/main/java/org/springframework/data/domain/TypedExampleMatcher.java b/src/main/java/org/springframework/data/domain/TypedExampleMatcher.java new file mode 100644 index 000000000..561d62e40 --- /dev/null +++ b/src/main/java/org/springframework/data/domain/TypedExampleMatcher.java @@ -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 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 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 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); + } +}