Browse Source

Retrofit ExampleMatcher with TypedPropertyPath.

issue/3400
Mark Paluch 2 months ago
parent
commit
31cf50192c
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 11
      src/main/java/org/springframework/data/core/TypedPropertyPath.java
  2. 10
      src/main/java/org/springframework/data/core/TypedPropertyPaths.java
  3. 78
      src/main/java/org/springframework/data/domain/ExampleMatcher.java
  4. 6
      src/main/java/org/springframework/data/domain/Sort.java
  5. 2
      src/test/java/org/springframework/data/core/TypedPropertyPathUnitTests.java
  6. 61
      src/test/java/org/springframework/data/domain/ExampleMatcherUnitTests.java
  7. 3
      src/test/java/org/springframework/data/domain/SortUnitTests.java

11
src/main/java/org/springframework/data/mapping/TypedPropertyPath.java → src/main/java/org/springframework/data/core/TypedPropertyPath.java

@ -13,14 +13,13 @@ @@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
package org.springframework.data.core;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import org.jspecify.annotations.Nullable;
import org.springframework.data.util.TypeInformation;
/**
* Type-safe representation of a property path expressed through method references.
@ -32,13 +31,13 @@ import org.springframework.data.util.TypeInformation; @@ -32,13 +31,13 @@ import org.springframework.data.util.TypeInformation;
* <p>
* Typed property paths can be created directly they are accepted used or conveniently using the static factory method
* {@link #of(TypedPropertyPath)} with method references:
*
*
* <pre class="code">
* PropertyPath.of(Person::getName);
* </pre>
*
*
* Property paths can be composed to navigate nested properties using {@link #then(TypedPropertyPath)}:
*
*
* <pre class="code">
* PropertyPath.of(Person::getAddress).then(Address::getCountry).then(Country::getName);
* </pre>
@ -56,7 +55,7 @@ import org.springframework.data.util.TypeInformation; @@ -56,7 +55,7 @@ import org.springframework.data.util.TypeInformation;
* <p>
* Note that using lambda expressions requires bytecode analysis of the declaration site classes and therefore presence
* of their class files.
*
*
* @param <T> the owning type of the property path segment, but typically the root type for composed property paths.
* @param <P> the property value type at this path segment.
* @author Mark Paluch

10
src/main/java/org/springframework/data/mapping/TypedPropertyPaths.java → src/main/java/org/springframework/data/core/TypedPropertyPaths.java

@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
package org.springframework.data.core;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
@ -46,8 +46,6 @@ import org.springframework.asm.Type; @@ -46,8 +46,6 @@ import org.springframework.asm.Type;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ObjectUtils;
@ -59,7 +57,7 @@ import org.springframework.util.ReflectionUtils; @@ -59,7 +57,7 @@ import org.springframework.util.ReflectionUtils;
class TypedPropertyPaths {
private static final Map<ClassLoader, Map<Object, PropertyPathInformation>> lambdas = new WeakHashMap<>();
private static final Map<ClassLoader, Map<TypedPropertyPath, ResolvedTypedPropertyPath<?, ?>>> resolved = new WeakHashMap<>();
private static final Map<ClassLoader, Map<TypedPropertyPath<?, ?>, ResolvedTypedPropertyPath<?, ?>>> resolved = new WeakHashMap<>();
/**
* Retrieve {@link PropertyPathInformation} for a given {@link TypedPropertyPath}.
@ -92,7 +90,7 @@ class TypedPropertyPaths { @@ -92,7 +90,7 @@ class TypedPropertyPaths {
return lambda;
}
Map<TypedPropertyPath, ResolvedTypedPropertyPath<?, ?>> cache;
Map<TypedPropertyPath<?, ?>, ResolvedTypedPropertyPath<?, ?>> cache;
synchronized (resolved) {
cache = resolved.computeIfAbsent(lambda.getClass().getClassLoader(), k -> new ConcurrentReferenceHashMap<>());
}
@ -168,7 +166,7 @@ class TypedPropertyPaths { @@ -168,7 +166,7 @@ class TypedPropertyPaths {
Field field = ReflectionUtils.findField(owner, fieldName, fieldType);
if (field == null) {
throw new IllegalArgumentException("Field %s.%s() not found".formatted(owner.getName(), field));
throw new IllegalArgumentException("Field %s.%s() not found".formatted(owner.getName(), fieldName));
}
return new PropertyPathInformation(TypeInformation.of(owner),

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

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package org.springframework.data.domain;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@ -24,6 +25,7 @@ import java.util.function.Function; @@ -24,6 +25,7 @@ import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.data.core.TypedPropertyPath;
import org.springframework.lang.CheckReturnValue;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
@ -77,6 +79,21 @@ public interface ExampleMatcher { @@ -77,6 +79,21 @@ public interface ExampleMatcher {
return new TypedExampleMatcher().withMode(MatchMode.ALL);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code propertyPaths}. This instance is immutable
* and unaffected by this method call.
*
* @param ignoredPaths must not be {@literal null} and not empty.
* @return new instance of {@link ExampleMatcher}.
* @since 4.1
*/
@Contract("_ -> new")
@CheckReturnValue
default <T> ExampleMatcher withIgnorePaths(TypedPropertyPath<T, ?>... ignoredPaths) {
return withIgnorePaths(Arrays.stream(ignoredPaths).map(TypedPropertyPath::of).map(TypedPropertyPath::toDotPath)
.toArray(String[]::new));
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code propertyPaths}. This instance is immutable
* and unaffected by this method call.
@ -122,6 +139,22 @@ public interface ExampleMatcher { @@ -122,6 +139,22 @@ public interface ExampleMatcher {
@CheckReturnValue
ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
*
* @param propertyPath must not be {@literal null}.
* @param matcherConfigurer callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
* @since 4.1
*/
@Contract("_, _ -> new")
@CheckReturnValue
default <T, P> ExampleMatcher withMatcher(TypedPropertyPath<T, P> propertyPath,
MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer) {
return withMatcher(propertyPath.toDotPath(), matcherConfigurer);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
@ -143,6 +176,21 @@ public interface ExampleMatcher { @@ -143,6 +176,21 @@ public interface ExampleMatcher {
return withMatcher(propertyPath, genericPropertyMatcher);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
*
* @param propertyPath must not be {@literal null}.
* @param genericPropertyMatcher callback to configure a {@link GenericPropertyMatcher}, must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
*/
@Contract("_, _ -> new")
@CheckReturnValue
default <T, P> ExampleMatcher withMatcher(TypedPropertyPath<T, P> propertyPath,
GenericPropertyMatcher genericPropertyMatcher) {
return withMatcher(propertyPath.toDotPath(), genericPropertyMatcher);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code GenericPropertyMatcher} for the
* {@code propertyPath}. This instance is immutable and unaffected by this method call.
@ -155,6 +203,22 @@ public interface ExampleMatcher { @@ -155,6 +203,22 @@ public interface ExampleMatcher {
@CheckReturnValue
ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code PropertyValueTransformer} for the
* {@code propertyPath}.
*
* @param propertyPath must not be {@literal null}.
* @param propertyValueTransformer must not be {@literal null}.
* @return new instance of {@link ExampleMatcher}.
* @since 4.1
*/
@Contract("_, _ -> new")
@CheckReturnValue
default <T, P> ExampleMatcher withTransformer(TypedPropertyPath<T, P> propertyPath,
PropertyValueTransformer propertyValueTransformer) {
return withTransformer(propertyPath.toDotPath(), propertyValueTransformer);
}
/**
* Returns a copy of this {@link ExampleMatcher} with the specified {@code PropertyValueTransformer} for the
* {@code propertyPath}.
@ -167,6 +231,20 @@ public interface ExampleMatcher { @@ -167,6 +231,20 @@ public interface ExampleMatcher {
@CheckReturnValue
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 new instance of {@link ExampleMatcher}.
*/
@Contract("_ -> new")
@CheckReturnValue
default <T> ExampleMatcher withIgnoreCase(TypedPropertyPath<T, ?>... propertyPaths) {
return withIgnoreCase(Arrays.stream(propertyPaths).map(TypedPropertyPath::of).map(TypedPropertyPath::toDotPath)
.toArray(String[]::new));
}
/**
* 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.

6
src/main/java/org/springframework/data/domain/Sort.java

@ -29,11 +29,11 @@ import java.util.stream.Collectors; @@ -29,11 +29,11 @@ import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.core.PropertyPath;
import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.util.MethodInvocationRecorder;
import org.springframework.data.util.MethodInvocationRecorder.Recorded;
import org.springframework.data.util.PropertyPath;
import org.springframework.data.util.Streamable;
import org.springframework.data.util.TypedPropertyPath;
import org.springframework.lang.CheckReturnValue;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
@ -797,7 +797,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord @@ -797,7 +797,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @author Oliver Gierke
* @since 2.2
* @soundtrack The Intersphere - Linger (The Grand Delusion)
* @deprecated since 4.1 in favor of {@link Sort#by(org.springframework.data.util.TypedPropertyPath...)}
* @deprecated since 4.1 in favor of {@link Sort#by(org.springframework.data.core.TypedPropertyPath...)}
*/
@Deprecated(since = "4.1")
public static class TypedSort<T> extends Sort {

2
src/test/java/org/springframework/data/mapping/TypedPropertyPathUnitTests.java → src/test/java/org/springframework/data/core/TypedPropertyPathUnitTests.java

@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
package org.springframework.data.core;
import static org.assertj.core.api.Assertions.*;

61
src/test/java/org/springframework/data/domain/ExampleMatcherUnitTests.java

@ -121,6 +121,15 @@ class ExampleMatcherUnitTests { @@ -121,6 +121,15 @@ class ExampleMatcherUnitTests {
assertThat(matcher.getIgnoredPaths()).hasSize(2);
}
@Test //
void ignoredPropertyPathsShouldReturnUniqueProperties() {
matcher = matching().withIgnorePaths(Person::getFirstname, Person::getLastname, Person::getFirstname);
assertThat(matcher.getIgnoredPaths()).contains("firstname", "lastname");
assertThat(matcher.getIgnoredPaths()).hasSize(2);
}
@Test // DATACMNS-810
void withCreatesNewInstance() {
@ -160,11 +169,11 @@ class ExampleMatcherUnitTests { @@ -160,11 +169,11 @@ class ExampleMatcherUnitTests {
void shouldCompareUsingHashCodeAndEquals() {
matcher = matching() //
.withIgnorePaths("foo", "bar", "baz") //
.withIgnorePaths(Random::getFoo, Random::getBar, Random::getBaz) //
.withNullHandler(NullHandler.IGNORE) //
.withIgnoreCase("ignored-case") //
.withMatcher("hello", GenericPropertyMatchers.contains().caseSensitive()) //
.withMatcher("world", GenericPropertyMatcher::endsWith);
.withMatcher(Random::getHello, GenericPropertyMatchers.contains().caseSensitive()) //
.withMatcher(Random::getWorld, GenericPropertyMatcher::endsWith);
var sameAsMatcher = matching() //
.withIgnorePaths("foo", "bar", "baz") //
@ -182,8 +191,54 @@ class ExampleMatcherUnitTests { @@ -182,8 +191,54 @@ class ExampleMatcherUnitTests {
assertThat(matcher).isEqualTo(sameAsMatcher).isNotEqualTo(different);
}
static class Random {
String foo;
String bar;
String baz;
String hello;
String world;
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public String getBaz() {
return baz;
}
public String getHello() {
return hello;
}
public String getWorld() {
return world;
}
}
static class Person {
String firstname;
String lastname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
}

3
src/test/java/org/springframework/data/domain/SortUnitTests.java

@ -21,11 +21,12 @@ import static org.springframework.data.domain.Sort.NullHandling.*; @@ -21,11 +21,12 @@ import static org.springframework.data.domain.Sort.NullHandling.*;
import java.util.Collection;
import org.junit.jupiter.api.Test;
import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.geo.Circle;
import org.springframework.data.mapping.Person;
import org.springframework.data.util.TypedPropertyPath;
/**
* Unit test for {@link Sort}.

Loading…
Cancel
Save