Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 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.
Expand Down Expand Up @@ -35,18 +35,30 @@
* <p>
* Specifications can be composed into higher order functions from other specifications using
* {@link #and(DeleteSpecification)}, {@link #or(DeleteSpecification)} or factory methods such as
* {@link #allOf(Iterable)}. Composition considers whether one or more specifications contribute to the overall
* predicate by returning a {@link Predicate} or {@literal null}. Specifications returning {@literal null} are
* considered to not contribute to the overall predicate and their result is not considered in the final predicate.
* {@link #allOf(Iterable)}.
* <p>
* Composition considers whether one or more specifications contribute to the overall predicate by returning a
* {@link Predicate} or {@literal null}. Specifications returning {@literal null}, such as {@link #unrestricted()}, are
* considered to not contribute to the overall predicate, and their result is not considered in the final predicate.
*
* @author Mark Paluch
* @author Peter Aisher
* @since 4.0
*/
@FunctionalInterface
public interface DeleteSpecification<T> extends Serializable {

/**
* Simple static factory method to create a specification deleting all objects.
* Simple static factory method to create a specification which does not participate in matching. The specification
* returned is {@code null}-like, and is elided in all operations.
*
* <pre>
* {@code
* unrestricted().and(other) // consider only `other`
* unrestricted().or(other) // consider only `other`
* not(unrestricted()) // equivalent to `unrestricted()`
* }
* </pre>
*
* @param <T> the type of the {@link Root} the resulting {@literal DeleteSpecification} operates on.
* @return guaranteed to be not {@literal null}.
Expand Down Expand Up @@ -159,7 +171,7 @@ static <T> DeleteSpecification<T> not(DeleteSpecification<T> spec) {
return (root, delete, builder) -> {

Predicate predicate = spec.toPredicate(root, delete, builder);
return predicate != null ? builder.not(predicate) : builder.disjunction();
return predicate != null ? builder.not(predicate) : null;
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 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.
Expand Down Expand Up @@ -34,18 +34,30 @@
* <p>
* Specifications can be composed into higher order functions from other specifications using
* {@link #and(PredicateSpecification)}, {@link #or(PredicateSpecification)} or factory methods such as
* {@link #allOf(Iterable)}. Composition considers whether one or more specifications contribute to the overall
* predicate by returning a {@link Predicate} or {@literal null}. Specifications returning {@literal null} are
* considered to not contribute to the overall predicate and their result is not considered in the final predicate.
* {@link #allOf(Iterable)}.
* <p>
* Composition considers whether one or more specifications contribute to the overall predicate by returning a
* {@link Predicate} or {@literal null}. Specifications returning {@literal null}, such as {@link #unrestricted()}, are
* considered to not contribute to the overall predicate, and their result is not considered in the final predicate.
*
* @author Mark Paluch
* @author Peter Aisher
* @since 4.0
*/
@FunctionalInterface
public interface PredicateSpecification<T> extends Serializable {

/**
* Simple static factory method to create a specification matching all objects.
* Simple static factory method to create a specification which does not participate in matching. The specification
* returned is {@code null}-like, and is elided in all operations.
*
* <pre>
* {@code
* unrestricted().and(other) // consider only `other`
* unrestricted().or(other) // consider only `other`
* not(unrestricted()) // equivalent to `unrestricted()`
* }
* </pre>
*
* @param <T> the type of the {@link Root} the resulting {@literal PredicateSpecification} operates on.
* @return guaranteed to be not {@literal null}.
Expand Down Expand Up @@ -113,7 +125,7 @@ static <T> PredicateSpecification<T> not(PredicateSpecification<T> spec) {
return (root, builder) -> {

Predicate predicate = spec.toPredicate(root, builder);
return predicate != null ? builder.not(predicate) : builder.disjunction();
return predicate != null ? builder.not(predicate) : null;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
* <p>
* Specifications can be composed into higher order functions from other specifications using
* {@link #and(Specification)}, {@link #or(Specification)} or factory methods such as {@link #allOf(Iterable)}.
* <p>
* Composition considers whether one or more specifications contribute to the overall predicate by returning a
* {@link Predicate} or {@literal null}. Specifications returning {@literal null} are considered to not contribute to
* the overall predicate and their result is not considered in the final predicate.
* {@link Predicate} or {@literal null}. Specifications returning {@literal null}, such as {@link #unrestricted()}, are
* considered to not contribute to the overall predicate, and their result is not considered in the final predicate.
*
* @author Oliver Gierke
* @author Thomas Darimont
Expand All @@ -49,12 +50,22 @@
* @author Daniel Shuy
* @author Sergey Rukin
* @author Heeeun Cho
* @author Peter Aisher
*/
@FunctionalInterface
public interface Specification<T> extends Serializable {

/**
* Simple static factory method to create a specification matching all objects.
* Simple static factory method to create a specification which does not participate in matching. The specification
* returned is {@code null}-like, and is elided in all operations.
*
* <pre>
* {@code
* unrestricted().and(other) // consider only `other`
* unrestricted().or(other) // consider only `other`
* not(unrestricted()) // equivalent to `unrestricted()`
* }
* </pre>
*
* @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
* @return guaranteed to be not {@literal null}.
Expand Down Expand Up @@ -175,7 +186,7 @@ static <T> Specification<T> not(Specification<T> spec) {
return (root, query, builder) -> {

Predicate predicate = spec.toPredicate(root, query, builder);
return predicate != null ? builder.not(predicate) : builder.disjunction();
return predicate != null ? builder.not(predicate) : null;
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 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.
Expand Down Expand Up @@ -35,18 +35,30 @@
* <p>
* Specifications can be composed into higher order functions from other specifications using
* {@link #and(UpdateSpecification)}, {@link #or(UpdateSpecification)} or factory methods such as
* {@link #allOf(Iterable)}. Composition considers whether one or more specifications contribute to the overall
* predicate by returning a {@link Predicate} or {@literal null}. Specifications returning {@literal null} are
* considered to not contribute to the overall predicate and their result is not considered in the final predicate.
* {@link #allOf(Iterable)}.
* <p>
* Composition considers whether one or more specifications contribute to the overall predicate by returning a
* {@link Predicate} or {@literal null}. Specifications returning {@literal null}, such as {@link #unrestricted()}, are
* considered to not contribute to the overall predicate, and their result is not considered in the final predicate.
*
* @author Mark Paluch
* @author Peter Aisher
* @since 4.0
*/
@FunctionalInterface
public interface UpdateSpecification<T> extends Serializable {

/**
* Simple static factory method to create a specification updating all objects.
* Simple static factory method to create a specification which does not participate in matching. The specification
* returned is {@code null}-like, and is elided in all operations.
*
* <pre>
* {@code
* unrestricted().and(other) // consider only `other`
* unrestricted().or(other) // consider only `other`
* not(unrestricted()) // equivalent to `unrestricted()`
* }
* </pre>
*
* @param <T> the type of the {@link Root} the resulting {@literal UpdateSpecification} operates on.
* @return guaranteed to be not {@literal null}.
Expand Down Expand Up @@ -180,7 +192,7 @@ static <T> UpdateSpecification<T> not(UpdateSpecification<T> spec) {
return (root, update, builder) -> {

Predicate predicate = spec.toPredicate(root, update, builder);
return predicate != null ? builder.not(predicate) : builder.disjunction();
return predicate != null ? builder.not(predicate) : null;
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 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.
Expand Down Expand Up @@ -38,6 +38,7 @@
* Unit tests for {@link DeleteSpecification}.
*
* @author Mark Paluch
* @author Peter Aisher
*/
@SuppressWarnings({ "unchecked", "deprecation" })
@ExtendWith(MockitoExtension.class)
Expand Down Expand Up @@ -158,15 +159,13 @@ void orCombinesSpecificationsInOrder() {
verify(builder).or(firstPredicate, secondPredicate);
}

@Test // GH-3849
@Test // GH-3849, GH-4023
void notWithNullPredicate() {

when(builder.disjunction()).thenReturn(mock(Predicate.class));

DeleteSpecification<Object> notSpec = DeleteSpecification.not((r, q, cb) -> null);

assertThat(notSpec.toPredicate(root, delete, builder)).isNotNull();
verify(builder).disjunction();
assertThat(notSpec.toPredicate(root, delete, builder)).isNull();
verifyNoInteractions(builder);
}

static class SerializableSpecification implements Serializable, DeleteSpecification<Object> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 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.
Expand Down Expand Up @@ -37,6 +37,7 @@
* Unit tests for {@link PredicateSpecification}.
*
* @author Mark Paluch
* @author Peter Aisher
*/
@SuppressWarnings({ "unchecked", "deprecation" })
@ExtendWith(MockitoExtension.class)
Expand Down Expand Up @@ -156,15 +157,13 @@ void orCombinesSpecificationsInOrder() {
verify(builder).or(firstPredicate, secondPredicate);
}

@Test // GH-3849
@Test // GH-3849, GH-4023
void notWithNullPredicate() {

when(builder.disjunction()).thenReturn(mock(Predicate.class));

PredicateSpecification<Object> notSpec = PredicateSpecification.not((r, cb) -> null);

assertThat(notSpec.toPredicate(root, builder)).isNotNull();
verify(builder).disjunction();
assertThat(notSpec.toPredicate(root, builder)).isNull();
verifyNoInteractions(builder);
}

static class SerializableSpecification implements Serializable, PredicateSpecification<Object> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
* @author Mark Paluch
* @author Daniel Shuy
* @author Heeeun Cho
* @author Peter Aisher
*/
@SuppressWarnings({ "unchecked", "deprecation" })
@ExtendWith(MockitoExtension.class)
Expand Down Expand Up @@ -127,15 +128,13 @@ void orCombinesSpecificationsInOrder() {
verify(builder).or(firstPredicate, secondPredicate);
}

@Test // GH-3849
@Test // GH-3849, GH-4023
void notWithNullPredicate() {

when(builder.disjunction()).thenReturn(mock(Predicate.class));
Specification<Object> notSpec = Specification.not(Specification.unrestricted());

Specification<Object> notSpec = Specification.not((r, q, cb) -> null);

assertThat(notSpec.toPredicate(root, query, builder)).isNotNull();
verify(builder).disjunction();
assertThat(notSpec.toPredicate(root, query, builder)).isNull();
verifyNoInteractions(builder);
}

@Test // GH-3992
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 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.
Expand Down Expand Up @@ -38,6 +38,7 @@
* Unit tests for {@link UpdateSpecification}.
*
* @author Mark Paluch
* @author Peter Aisher
*/
@SuppressWarnings({ "unchecked", "deprecation" })
@ExtendWith(MockitoExtension.class)
Expand Down Expand Up @@ -158,15 +159,13 @@ void orCombinesSpecificationsInOrder() {
verify(builder).or(firstPredicate, secondPredicate);
}

@Test // GH-3849
@Test // GH-3849, GH-4023
void notWithNullPredicate() {

when(builder.disjunction()).thenReturn(mock(Predicate.class));

UpdateSpecification<Object> notSpec = UpdateSpecification.not((r, q, cb) -> null);

assertThat(notSpec.toPredicate(root, update, builder)).isNotNull();
verify(builder).disjunction();
assertThat(notSpec.toPredicate(root, update, builder)).isNull();
verifyNoInteractions(builder);
}

static class SerializableSpecification implements Serializable, UpdateSpecification<Object> {
Expand Down