From 0c2d47ec8e5f07b3f52778e1dce1c2f575e103cd Mon Sep 17 00:00:00 2001 From: Andrei Sereda <25229979+asereda-gs@users.noreply.github.com> Date: Sat, 2 May 2020 17:14:49 -0400 Subject: [PATCH] Add syntax sugar to Optional matchers with is(Optional) / isNot(Optional) methods Helpful for nullable arguments which get converted to isPresent / isAbsent equivalent depending on optional value. value == Optional.empty() changed to isAbsent() value == Optional.of(123) changed to is(123) This allows making calling API like: person.name.is(Optional.ofNullable(nullableName)); --- .../criteria/matcher/ObjectMatcher.java | 13 ++++ .../matcher/OptionalBigDecimalMatcher.java | 4 +- .../matcher/OptionalBigIntegerMatcher.java | 4 +- .../matcher/OptionalBooleanMatcher.java | 19 +++++- .../matcher/OptionalComparableMatcher.java | 6 +- .../matcher/OptionalDoubleMatcher.java | 11 +++- .../matcher/OptionalIntegerMatcher.java | 11 +++- .../criteria/matcher/OptionalLongMatcher.java | 11 +++- .../matcher/OptionalNumberMatcher.java | 6 +- ...atcher.java => OptionalObjectMatcher.java} | 6 +- .../matcher/OptionalStringMatcher.java | 4 +- .../matcher/OptionalValueMatcher.java | 61 +++++++++++++++++++ .../processor/CriteriaModelProcessorTest.java | 18 +++--- .../criteria/typemodel/BooleanTemplate.java | 10 +++ .../criteria/typemodel/DateTemplate.java | 10 +++ .../criteria/typemodel/EnumTemplate.java | 11 ++++ .../criteria/typemodel/InstantTemplate.java | 10 +++ .../criteria/typemodel/LocalDateTemplate.java | 10 +++ .../typemodel/LocalDateTimeTemplate.java | 30 ++++++--- .../criteria/typemodel/StringTemplate.java | 16 ++++- .../value/processor/meta/CriteriaModel.java | 2 +- 21 files changed, 228 insertions(+), 45 deletions(-) rename criteria/common/src/org/immutables/criteria/matcher/{OptionalMatcher.java => OptionalObjectMatcher.java} (83%) create mode 100644 criteria/common/src/org/immutables/criteria/matcher/OptionalValueMatcher.java diff --git a/criteria/common/src/org/immutables/criteria/matcher/ObjectMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/ObjectMatcher.java index 05cc4e130..95182e28a 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/ObjectMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/ObjectMatcher.java @@ -34,11 +34,21 @@ */ public interface ObjectMatcher extends Matcher { + /** + * Equivalent to {@code this == value} ({@code value} can't be null) + * @throws NullPointerException if value argument is null + */ default R is(V value) { + Objects.requireNonNull(value,"value"); return Matchers.extract(this).applyAndCreateRoot(e -> Expressions.call(Operators.EQUAL, e, Expressions.constant(value))); } + /** + * Equivalent to {@code this != value} ({@code value} can't be null) + * @throws NullPointerException if value argument is null + */ default R isNot(V value) { + Objects.requireNonNull(value,"value"); return Matchers.extract(this).applyAndCreateRoot(e -> Expressions.call(Operators.NOT_EQUAL, e, Expressions.constant(value))); } @@ -60,6 +70,9 @@ default R notIn(V v1, V v2, V ... rest) { return notIn(values); } + /** + * Equivalent to {@code this in $values} + */ default R in(Iterable values) { Objects.requireNonNull(values, "values"); return Matchers.extract(this).applyAndCreateRoot(e -> Expressions.call(Operators.IN, e, Expressions.constant(ImmutableList.copyOf(values)))); diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalBigDecimalMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalBigDecimalMatcher.java index 1367368de..8030a20e6 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalBigDecimalMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalBigDecimalMatcher.java @@ -20,9 +20,9 @@ import java.util.Optional; /** - * Intersection type between {@link OptionalMatcher} and {@link NumberMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link NumberMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalBigIntegerMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalBigIntegerMatcher.java index f79532782..908cd5661 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalBigIntegerMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalBigIntegerMatcher.java @@ -21,9 +21,9 @@ import java.util.Optional; /** - * Intersection type between {@link OptionalMatcher} and {@link NumberMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link NumberMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalBooleanMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalBooleanMatcher.java index a38c7e116..4fe6de0a7 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalBooleanMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalBooleanMatcher.java @@ -16,13 +16,30 @@ package org.immutables.criteria.matcher; +import java.util.Objects; +import java.util.Optional; + /** - * Intersection type between {@link OptionalMatcher} and {@link BooleanMatcher} + * Intersection type between {@link OptionalValueMatcher} and {@link BooleanMatcher} * * @param root criteria type */ public interface OptionalBooleanMatcher extends BooleanMatcher, PresentAbsentMatcher { + /** + * Match current boolean attribute given an optional boolean parameter. If optional is + * empty, matching is equivalent to {@link #isAbsent()} otherwise standard + * {@link #is(boolean)} matching is used. + * + * @param optional argument to match with + * @throws NullPointerException if argument is null + */ + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + default R is(Optional optional) { + Objects.requireNonNull(optional, "optional"); + return optional.map(this::is).orElseGet(this::isAbsent); + } + interface Self extends Template {} interface Template extends OptionalBooleanMatcher, WithMatcher, NotMatcher, Projection

, Aggregation.Count {} diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalComparableMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalComparableMatcher.java index 78ca950a2..411f1566d 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalComparableMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalComparableMatcher.java @@ -19,14 +19,14 @@ import java.util.Optional; /** - * Intersection type between {@link OptionalMatcher} and {@link ComparableMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link ComparableMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type */ -public interface OptionalComparableMatcher> extends ComparableMatcher, PresentAbsentMatcher { +public interface OptionalComparableMatcher> extends ComparableMatcher, OptionalValueMatcher { /** * Self-type for this matcher diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalDoubleMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalDoubleMatcher.java index 4f6c86e80..db4f2687a 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalDoubleMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalDoubleMatcher.java @@ -16,18 +16,25 @@ package org.immutables.criteria.matcher; +import java.util.Objects; import java.util.OptionalDouble; /** - * Intersection type between {@link OptionalMatcher} and {@link NumberMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link NumberMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type */ public interface OptionalDoubleMatcher extends OptionalNumberMatcher { + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + default R is(OptionalDouble optional) { + Objects.requireNonNull(optional, "optional"); + return optional.isPresent() ? is(optional.getAsDouble()) : isAbsent(); + } + /** * Self-type for this matcher */ diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalIntegerMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalIntegerMatcher.java index e408a41a2..42904b7ac 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalIntegerMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalIntegerMatcher.java @@ -16,20 +16,27 @@ package org.immutables.criteria.matcher; +import java.util.Objects; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; /** - * Intersection type between {@link OptionalMatcher} and {@link NumberMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link NumberMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type */ public interface OptionalIntegerMatcher extends OptionalNumberMatcher { + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + default R is(OptionalInt optional) { + Objects.requireNonNull(optional, "optional"); + return optional.isPresent() ? is(optional.getAsInt()) : isAbsent(); + } + /** * Self-type for this matcher */ diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalLongMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalLongMatcher.java index cdd70b1c7..ea2706168 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalLongMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalLongMatcher.java @@ -16,19 +16,26 @@ package org.immutables.criteria.matcher; +import java.util.Objects; import java.util.OptionalDouble; import java.util.OptionalLong; /** - * Intersection type between {@link OptionalMatcher} and {@link NumberMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link NumberMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type */ public interface OptionalLongMatcher extends OptionalNumberMatcher { + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + default R is(OptionalLong optional) { + Objects.requireNonNull(optional, "optional"); + return optional.isPresent() ? is(optional.getAsLong()) : isAbsent(); + } + /** * Self-type for this matcher */ diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalNumberMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalNumberMatcher.java index d155fb737..c07dc96fa 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalNumberMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalNumberMatcher.java @@ -20,14 +20,14 @@ import java.util.OptionalDouble; /** - * Intersection type between {@link OptionalMatcher} and {@link NumberMatcher}. + * Intersection type between {@link OptionalObjectMatcher} and {@link NumberMatcher}. * - *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalMatcher} + *

Syntax sugar to avoid chaining {@code value()} method from {@link OptionalObjectMatcher} * on long expressions with many optional elements. * * @param root criteria type */ -public interface OptionalNumberMatcher> extends NumberMatcher, PresentAbsentMatcher { +public interface OptionalNumberMatcher> extends NumberMatcher, OptionalValueMatcher { /** * Self-type for this matcher diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalObjectMatcher.java similarity index 83% rename from criteria/common/src/org/immutables/criteria/matcher/OptionalMatcher.java rename to criteria/common/src/org/immutables/criteria/matcher/OptionalObjectMatcher.java index c1e4194a6..009d75039 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalObjectMatcher.java @@ -17,9 +17,9 @@ package org.immutables.criteria.matcher; /** - * Matcher for optional attributes + * Matcher for optional (non-scalar) attributes */ -public interface OptionalMatcher extends PresentAbsentMatcher, Matcher { +public interface OptionalObjectMatcher extends PresentAbsentMatcher, Matcher { /** * Apply context-specific matcher if value is present @@ -34,7 +34,7 @@ default S value() { */ interface Self extends Template, S, Void>, Disjunction> {} - interface Template extends OptionalMatcher, Projection

, Aggregation.Count {} + interface Template extends OptionalObjectMatcher, Projection

, Aggregation.Count {} @SuppressWarnings("unchecked") static CriteriaCreator creator() { diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalStringMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalStringMatcher.java index fec8c5aeb..ab995d9d9 100644 --- a/criteria/common/src/org/immutables/criteria/matcher/OptionalStringMatcher.java +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalStringMatcher.java @@ -19,10 +19,10 @@ import java.util.Optional; /** - * Intersection type between {@link OptionalMatcher} and {@link StringMatcher} + * Intersection type between {@link OptionalValueMatcher} and {@link StringMatcher} * @param root criteria type */ -public interface OptionalStringMatcher extends StringMatcher, PresentAbsentMatcher { +public interface OptionalStringMatcher extends StringMatcher, OptionalValueMatcher { interface Self extends Template, Disjunction> {} diff --git a/criteria/common/src/org/immutables/criteria/matcher/OptionalValueMatcher.java b/criteria/common/src/org/immutables/criteria/matcher/OptionalValueMatcher.java new file mode 100644 index 000000000..df5d5c449 --- /dev/null +++ b/criteria/common/src/org/immutables/criteria/matcher/OptionalValueMatcher.java @@ -0,0 +1,61 @@ +/* + * Copyright 2020 Immutables Authors and Contributors + * + * 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.immutables.criteria.matcher; + +import java.util.Objects; +import java.util.Optional; + +/** + * Optional matcher for scalar (string, int, comparable etc.) attributes. + * In addition to standard {@link ObjectMatcher} contract offers similar methods but with + * {@link Optional} arguments. Depending on optional value (empty / present) matcher will + * delegate to {@link #isPresent()}, {@link #isAbsent()} or {@link #is(Object)} + */ +public interface OptionalValueMatcher extends ObjectMatcher, PresentAbsentMatcher { + + /** + * Match current attribute given an optional parameter. If optional is + * empty, matching is equivalent to {@link #isAbsent()} otherwise standard + * {@link #is(Object)} matching is used. + * + * @param optional argument to match with + * @throws NullPointerException if argument is null + */ + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + default R is(Optional optional) { + Objects.requireNonNull(optional, "optional"); + return optional.map(this::is).orElseGet(this::isAbsent); + } + + /** + * Match current attribute given an optional parameter. If optional is + * empty, matching is equivalent to {@link #isPresent()} ()} otherwise standard + * {@link #isNot(Object)} matching is used. + * + *

Note Different backends might treat null (missing/unknown) value differently. Negations + * might or might not return missing value. + * For SQL there is Three-Valued-Logic. + * while Mongo is two-valued. + * @throws NullPointerException if argument is null + */ + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + default R isNot(Optional optional) { + Objects.requireNonNull(optional, "optional"); + return optional.map(this::isNot).orElseGet(this::isPresent); + } + +} diff --git a/criteria/common/test/org/immutables/criteria/processor/CriteriaModelProcessorTest.java b/criteria/common/test/org/immutables/criteria/processor/CriteriaModelProcessorTest.java index 9c0e254e3..c7396a1aa 100644 --- a/criteria/common/test/org/immutables/criteria/processor/CriteriaModelProcessorTest.java +++ b/criteria/common/test/org/immutables/criteria/processor/CriteriaModelProcessorTest.java @@ -80,10 +80,10 @@ public void timeZone() { assertAttribute("timeZone", "org.immutables.criteria.matcher.ObjectMatcher.Template"); assertAttribute("optionalTimeZone", - "org.immutables.criteria.matcher.OptionalMatcher.Template,java.util.Optional>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template,java.util.Optional>"); checkCreator("optionalTimeZone").contains("ObjectMatcher.creator()"); assertAttribute("nullableTimeZone", - "org.immutables.criteria.matcher.OptionalMatcher.Template,java.util.TimeZone>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template,java.util.TimeZone>"); checkCreator("nullableTimeZone").contains("ObjectMatcher.creator()"); checkCreator("listTimeZone").contains("ObjectMatcher.creator()"); checkCreator("arrayTimeZone").contains("ObjectMatcher.creator()"); @@ -99,15 +99,15 @@ public void array() { @Test public void wierd() { assertAttribute("weird1", - "org.immutables.criteria.matcher.OptionalMatcher.Template>,java.util.Optional>>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template>,java.util.Optional>>"); // Optional> weird2(); assertAttribute("weird2", - "org.immutables.criteria.matcher.OptionalMatcher.Template,java.lang.String,java.util.List>,java.util.Optional>>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template,java.lang.String,java.util.List>,java.util.Optional>>"); // List> weird3(); assertAttribute("weird3", "org.immutables.criteria.matcher.IterableMatcher.Template>,java.util.Optional,java.util.List>>"); assertAttribute("weird4", - "org.immutables.criteria.matcher.OptionalMatcher.Template,java.util.Optional>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template,java.util.Optional>"); } @Test @@ -208,16 +208,16 @@ public void havingCriteria() { assertAttribute("nullableFoo", - "org.immutables.criteria.matcher.OptionalMatcher.Template,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo>"); checkCreator("nullableFoo").contains("FooCriteria.creator()"); - checkCreator("nullableFoo").contains("OptionalMatcher.creator()"); + checkCreator("nullableFoo").contains("OptionalObjectMatcher.creator()"); assertAttribute("optionalFoo", - "org.immutables.criteria.matcher.OptionalMatcher.Template,java.util.Optional>"); + "org.immutables.criteria.matcher.OptionalObjectMatcher.Template,java.util.Optional>"); checkCreator("optionalFoo").contains("FooCriteria.creator()"); - checkCreator("optionalFoo").contains("OptionalMatcher.creator()"); + checkCreator("optionalFoo").contains("OptionalObjectMatcher.creator()"); assertAttribute("listFoo", "org.immutables.criteria.matcher.IterableMatcher.Template,org.immutables.criteria.processor.CriteriaModelProcessorTest.Foo,java.util.List>"); diff --git a/criteria/common/test/org/immutables/criteria/typemodel/BooleanTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/BooleanTemplate.java index a6d3e1c1b..387177eb7 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/BooleanTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/BooleanTemplate.java @@ -82,6 +82,11 @@ protected void optional() { ids(holder.optional.isTrue()).hasContentInAnyOrder("id2"); ids(holder.optional.is(false)).hasContentInAnyOrder("id1"); ids(holder.optional.isFalse()).hasContentInAnyOrder("id1"); + + // using OptionalValue matcher API + ids(holder.optional.is(Optional.empty())).isOf("id3"); + ids(holder.optional.is(Optional.of(true))).isOf("id2"); + ids(holder.optional.is(Optional.of(false))).isOf("id1"); } @Test @@ -96,6 +101,11 @@ void nullable() { ids(holder.nullable.isTrue()).hasContentInAnyOrder("id2"); ids(holder.nullable.is(false)).hasContentInAnyOrder("id1"); ids(holder.nullable.isFalse()).hasContentInAnyOrder("id1"); + + // using OptionalValue matcher API + ids(holder.nullable.is(Optional.empty())).isOf("id3"); + ids(holder.nullable.is(Optional.of(true))).isOf("id2"); + ids(holder.nullable.is(Optional.of(false))).isOf("id1"); } @Test diff --git a/criteria/common/test/org/immutables/criteria/typemodel/DateTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/DateTemplate.java index 087c05562..9a9940d43 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/DateTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/DateTemplate.java @@ -127,6 +127,11 @@ void nullable() { ids(criteria.nullable.atMost(date)).hasContentInAnyOrder("id2"); ids(criteria.nullable.greaterThan(date)).isEmpty(); ids(criteria.nullable.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(criteria.nullable.is(Optional.empty())).isOf("id1"); + ids(criteria.nullable.is(Optional.of(date))).isOf("id2"); + ids(criteria.nullable.isNot(Optional.empty())).isOf("id2"); } @Test @@ -141,6 +146,11 @@ protected void optional() { ids(criteria.optional.atMost(date)).hasContentInAnyOrder("id2"); ids(criteria.optional.greaterThan(date)).isEmpty(); ids(criteria.optional.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(criteria.optional.is(Optional.empty())).isOf("id1"); + ids(criteria.optional.is(Optional.of(date))).isOf("id2"); + ids(criteria.optional.isNot(Optional.empty())).isOf("id2"); } @Test diff --git a/criteria/common/test/org/immutables/criteria/typemodel/EnumTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/EnumTemplate.java index bc82d44a8..64092029b 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/EnumTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/EnumTemplate.java @@ -91,6 +91,17 @@ void projection() { } + @Test + void nullable() { + repository.insert(generator.get().withId("id1").withNullable(TypeHolder.Foo.ONE)); + repository.insert(generator.get().withId("id2").withNullable(null)); + repository.insert(generator.get().withId("id3").withNullable(TypeHolder.Foo.TWO)); + + ids(criteria.nullable.is(Optional.of(TypeHolder.Foo.ONE))).isOf("id1"); + ids(criteria.nullable.is(Optional.empty())).isOf("id2"); + ids(criteria.nullable.is(Optional.of(TypeHolder.Foo.TWO))).isOf("id3"); + } + private IterableChecker, String> ids(EnumHolderCriteria criteria) { return CriteriaChecker.ofReader(repository.find(criteria)).toList(TypeHolder.EnumHolder::id); } diff --git a/criteria/common/test/org/immutables/criteria/typemodel/InstantTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/InstantTemplate.java index b4e3a57c5..63583a7bd 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/InstantTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/InstantTemplate.java @@ -124,6 +124,11 @@ void nullable() { ids(holder.nullable.atMost(date)).hasContentInAnyOrder("id2"); ids(holder.nullable.greaterThan(date)).isEmpty(); ids(holder.nullable.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(holder.nullable.is(Optional.empty())).isOf("id1"); + ids(holder.nullable.is(Optional.of(date))).isOf("id2"); + ids(holder.nullable.isNot(Optional.empty())).isOf("id2"); } @Test @@ -138,6 +143,11 @@ protected void optional() { ids(holder.optional.atMost(date)).hasContentInAnyOrder("id2"); ids(holder.optional.greaterThan(date)).isEmpty(); ids(holder.optional.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(holder.optional.is(Optional.empty())).isOf("id1"); + ids(holder.optional.is(Optional.of(date))).isOf("id2"); + ids(holder.optional.isNot(Optional.empty())).isOf("id2"); } @Test diff --git a/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTemplate.java index d0128d840..98ecd21a9 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTemplate.java @@ -121,6 +121,11 @@ void nullable() { ids(holder.nullable.atMost(date)).hasContentInAnyOrder("id2"); ids(holder.nullable.greaterThan(date)).isEmpty(); ids(holder.nullable.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(holder.nullable.is(Optional.empty())).isOf("id1"); + ids(holder.nullable.is(Optional.of(date))).isOf("id2"); + ids(holder.nullable.isNot(Optional.empty())).isOf("id2"); } @Test @@ -135,6 +140,11 @@ protected void optional() { ids(holder.optional.atMost(date)).hasContentInAnyOrder("id2"); ids(holder.optional.greaterThan(date)).isEmpty(); ids(holder.optional.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(holder.optional.is(Optional.empty())).isOf("id1"); + ids(holder.optional.is(Optional.of(date))).isOf("id2"); + ids(holder.optional.isNot(Optional.empty())).isOf("id2"); } @Test diff --git a/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTimeTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTimeTemplate.java index 2f5eb02ac..468a1c9bb 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTimeTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/LocalDateTimeTemplate.java @@ -114,13 +114,18 @@ void nullable() { repository.insert(generator.get().withId("id1").withNullable(null)); repository.insert(generator.get().withId("id2").withNullable(date)); - ids(holder.nullable.isPresent()).hasContentInAnyOrder("id2"); - ids(holder.nullable.isAbsent()).hasContentInAnyOrder("id1"); - ids(holder.nullable.is(date)).hasContentInAnyOrder("id2"); - ids(holder.nullable.atLeast(date)).hasContentInAnyOrder("id2"); - ids(holder.nullable.atMost(date)).hasContentInAnyOrder("id2"); + ids(holder.nullable.isPresent()).isOf("id2"); + ids(holder.nullable.isAbsent()).isOf("id1"); + ids(holder.nullable.is(date)).isOf("id2"); + ids(holder.nullable.atLeast(date)).isOf("id2"); + ids(holder.nullable.atMost(date)).isOf("id2"); ids(holder.nullable.greaterThan(date)).isEmpty(); ids(holder.nullable.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(holder.nullable.is(Optional.empty())).isOf("id1"); + ids(holder.nullable.is(Optional.of(date))).isOf("id2"); + ids(holder.nullable.isNot(Optional.empty())).isOf("id2"); } @Test @@ -128,13 +133,18 @@ protected void optional() { LocalDateTime date = LocalDateTime.now(); repository.insert(generator.get().withId("id1").withOptional(Optional.empty())); repository.insert(generator.get().withId("id2").withOptional(Optional.of(date))); - ids(holder.optional.isPresent()).hasContentInAnyOrder("id2"); - ids(holder.optional.isAbsent()).hasContentInAnyOrder("id1"); - ids(holder.optional.is(date)).hasContentInAnyOrder("id2"); - ids(holder.optional.atLeast(date)).hasContentInAnyOrder("id2"); - ids(holder.optional.atMost(date)).hasContentInAnyOrder("id2"); + ids(holder.optional.isPresent()).isOf("id2"); + ids(holder.optional.isAbsent()).isOf("id1"); + ids(holder.optional.is(date)).isOf("id2"); + ids(holder.optional.atLeast(date)).isOf("id2"); + ids(holder.optional.atMost(date)).isOf("id2"); ids(holder.optional.greaterThan(date)).isEmpty(); ids(holder.optional.lessThan(date)).isEmpty(); + + // using OptionalValue matcher API + ids(holder.optional.is(Optional.empty())).isOf("id1"); + ids(holder.optional.is(Optional.of(date))).isOf("id2"); + ids(holder.optional.isNot(Optional.empty())).isOf("id2"); } @Test diff --git a/criteria/common/test/org/immutables/criteria/typemodel/StringTemplate.java b/criteria/common/test/org/immutables/criteria/typemodel/StringTemplate.java index e71c41915..347b67f25 100644 --- a/criteria/common/test/org/immutables/criteria/typemodel/StringTemplate.java +++ b/criteria/common/test/org/immutables/criteria/typemodel/StringTemplate.java @@ -198,6 +198,11 @@ protected void nullable() { values(string.nullable.is("")).isEmpty(); values(string.value.is("null")).hasContentInAnyOrder("null"); values(string.value.is("notnull")).hasContentInAnyOrder("notnull"); + + // using OptionalValue matcher API + values(string.nullable.is(Optional.empty())).isOf("null"); + values(string.nullable.isNot(Optional.empty())).isOf("notnull"); + values(string.nullable.is(Optional.of("notnull"))).isOf("notnull"); } @Test @@ -205,11 +210,16 @@ protected void optional() { repository.insert(generator.get().withValue("null").withNullable(null).withOptional(Optional.empty())); repository.insert(generator.get().withValue("notnull").withNullable("notnull").withOptional("notempty")); - values(string.optional.isAbsent()).hasContentInAnyOrder("null"); - values(string.optional.isPresent()).hasContentInAnyOrder("notnull"); + values(string.optional.isAbsent()).isOf("null"); + values(string.optional.isPresent()).isOf("notnull"); values(string.optional.is("null")).isEmpty(); - values(string.optional.is("notempty")).hasContentInAnyOrder("notnull"); + values(string.optional.is("notempty")).isOf("notnull"); values(string.optional.is("")).isEmpty(); + + // using OptionalValue matcher API + values(string.optional.is(Optional.empty())).isOf("null"); + values(string.optional.isNot(Optional.empty())).isOf("notnull"); + values(string.optional.is(Optional.of("notempty"))).isOf("notnull"); } /** diff --git a/value-processor/src/org/immutables/value/processor/meta/CriteriaModel.java b/value-processor/src/org/immutables/value/processor/meta/CriteriaModel.java index aa6f89783..0fdd89ab9 100644 --- a/value-processor/src/org/immutables/value/processor/meta/CriteriaModel.java +++ b/value-processor/src/org/immutables/value/processor/meta/CriteriaModel.java @@ -309,7 +309,7 @@ private Type.Parameterized matcherType(IntrospectedType introspected) { } else if (param.isComparable()) { name = "org.immutables.criteria.matcher.OptionalComparableMatcher.Template"; } else { - name = "org.immutables.criteria.matcher.OptionalMatcher.Template"; + name = "org.immutables.criteria.matcher.OptionalObjectMatcher.Template"; } } else if (introspected.hasCriteria()) { name = topLevelCriteriaClassName(type);