diff --git a/api/src/main/java/jakarta/data/metamodel/TextRestrictionRecord.java b/api/src/main/java/jakarta/data/metamodel/TextRestrictionRecord.java index 1f010b8c5..d8d96077e 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextRestrictionRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/TextRestrictionRecord.java @@ -17,7 +17,17 @@ */ package jakarta.data.metamodel; +import jakarta.data.metamodel.constraint.Between; import jakarta.data.metamodel.constraint.Constraint; +import jakarta.data.metamodel.constraint.EqualTo; +import jakarta.data.metamodel.constraint.GreaterThan; +import jakarta.data.metamodel.constraint.GreaterThanOrEqual; +import jakarta.data.metamodel.constraint.LessThan; +import jakarta.data.metamodel.constraint.LessThanOrEqual; +import jakarta.data.metamodel.constraint.Like; +import jakarta.data.metamodel.constraint.NotBetween; +import jakarta.data.metamodel.constraint.NotEqualTo; +import jakarta.data.metamodel.constraint.NotLike; import jakarta.data.metamodel.restrict.TextRestriction; import java.util.Objects; @@ -31,7 +41,40 @@ record TextRestrictionRecord(String attribute, Constraint constraint) } @Override - public TextRestrictionRecord negate() { + public TextRestriction ignoreCase() { + // TODO: Use a switch statement after we move up to Java 21 minimum + Constraint caseIgnoringConstraint; + if (constraint instanceof Like) { + caseIgnoringConstraint = ((Like) constraint).ignoreCase(); + } else if (constraint instanceof NotLike) { + caseIgnoringConstraint = ((NotLike) constraint).ignoreCase(); + } else if (constraint instanceof EqualTo) { + caseIgnoringConstraint = ((EqualTo) constraint).ignoreCase(); + } else if (constraint instanceof NotEqualTo) { + caseIgnoringConstraint = ((NotEqualTo) constraint).ignoreCase(); + } else if (constraint instanceof Between) { + caseIgnoringConstraint = ((Between) constraint).ignoreCase(); + } else if (constraint instanceof NotBetween) { + caseIgnoringConstraint = ((NotBetween) constraint).ignoreCase(); + } else if (constraint instanceof GreaterThan) { + caseIgnoringConstraint = ((GreaterThan) constraint).ignoreCase(); + } else if (constraint instanceof GreaterThanOrEqual) { + caseIgnoringConstraint = ((GreaterThanOrEqual) constraint).ignoreCase(); + } else if (constraint instanceof LessThan) { + caseIgnoringConstraint = ((LessThan) constraint).ignoreCase(); + } else if (constraint instanceof LessThanOrEqual) { + caseIgnoringConstraint = ((LessThanOrEqual) constraint).ignoreCase(); + } else { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + + constraint.getClass().getInterfaces()[0].getName() + " constraint"); + } + + return new TextRestrictionRecord<>(attribute, caseIgnoringConstraint); + } + + @Override + public TextRestriction negate() { return new TextRestrictionRecord<>(attribute, constraint.negate()); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/Between.java b/api/src/main/java/jakarta/data/metamodel/constraint/Between.java index 3c6750f79..1d8b7dc23 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/Between.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/Between.java @@ -23,6 +23,10 @@ static > Between bounds(T lower, T upper) { return new BetweenRecord<>(lower, upper); } + Between ignoreCase(); + + boolean isCaseSensitive(); + T lowerBound(); T upperBound(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/BetweenRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/BetweenRecord.java index 729149f22..35cfedb18 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/BetweenRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/BetweenRecord.java @@ -19,32 +19,40 @@ import java.util.Objects; -record BetweenRecord>(T lowerBound, T upperBound) - implements Between { +record BetweenRecord>( + T lowerBound, + T upperBound, + boolean isCaseSensitive) implements Between { + public BetweenRecord { Objects.requireNonNull(lowerBound); Objects.requireNonNull(upperBound); } - @Override - public String toString() { - return "BETWEEN " + lowerBound + " AND " + upperBound; + BetweenRecord(T lowerBound, T upperBound) { + this(lowerBound, upperBound, lowerBound instanceof String); } @Override - public boolean equals(Object obj) { - return obj instanceof BetweenRecord that - && lowerBound.equals(that.lowerBound) - && upperBound.equals(that.upperBound); + public Between ignoreCase() { + if (!(lowerBound instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + lowerBound.getClass().getName() + + " typed attribute"); + } + return new BetweenRecord<>(lowerBound, upperBound, false); } @Override - public int hashCode() { - return Objects.hash(lowerBound, upperBound); + public String toString() { + return lowerBound instanceof String + ? "BETWEEN '" + lowerBound + "' AND '" + upperBound + "'" + + (isCaseSensitive ? "" : " IGNORE CASE") + : "BETWEEN " + lowerBound + " AND " + upperBound; } @Override public NotBetween negate() { - return NotBetween.bounds(lowerBound, upperBound); + return new NotBetweenRecord<>(lowerBound, upperBound, isCaseSensitive); } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/Constraint.java b/api/src/main/java/jakarta/data/metamodel/constraint/Constraint.java index 5487d6c42..c6664e761 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/Constraint.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/Constraint.java @@ -22,7 +22,7 @@ public interface Constraint { static Constraint equalTo(T value) { - return new EqualToRecord<>(value); + return EqualTo.value(value); } @SafeVarargs @@ -38,24 +38,24 @@ static Constraint isNull() { return new NullRecord<>(); } - static > Constraint greaterThan(T bound) { - return new GreaterThanRecord<>(bound); + static > GreaterThan greaterThan(T bound) { + return GreaterThan.bound(bound); } - static > Constraint lessThan(T bound) { - return new LessThanRecord<>(bound); + static > LessThan lessThan(T bound) { + return LessThan.bound(bound); } - static > Constraint greaterThanOrEqual(T bound) { - return new GreaterThanOrEqualRecord<>(bound); + static > GreaterThanOrEqual greaterThanOrEqual(T bound) { + return GreaterThanOrEqual.min(bound); } - static > Constraint lessThanOrEqual(T bound) { - return new LessThanOrEqualRecord<>(bound); + static > LessThanOrEqual lessThanOrEqual(T bound) { + return LessThanOrEqual.max(bound); } - static > Constraint between(T lowerBound, T upperBound) { - return new BetweenRecord<>(lowerBound, upperBound); + static > Between between(T lowerBound, T upperBound) { + return Between.bounds(lowerBound, upperBound); } Constraint negate(); diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/EqualTo.java b/api/src/main/java/jakarta/data/metamodel/constraint/EqualTo.java index 7a9db74fb..f8f5bfa46 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/EqualTo.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/EqualTo.java @@ -23,5 +23,9 @@ static EqualTo value(T value) { return new EqualToRecord<>(value); } + EqualTo ignoreCase(); + + boolean isCaseSensitive(); + T value(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/EqualToRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/EqualToRecord.java index 9e1436e58..c17ba7dba 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/EqualToRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/EqualToRecord.java @@ -19,29 +19,34 @@ import java.util.Objects; -record EqualToRecord(T value) implements EqualTo { +record EqualToRecord(T value, boolean isCaseSensitive) implements EqualTo { public EqualToRecord { Objects.requireNonNull(value, "Value must not be null"); } - @Override - public NotEqualTo negate() { - return NotEqualTo.value(value); + EqualToRecord(T value) { + this(value, value instanceof String); } @Override - public String toString() { - return value instanceof String ? "= '" + value + "'" : "= " + value.toString(); + public EqualTo ignoreCase() { + if (!(value instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + value.getClass().getName() + + " typed attribute"); + } + return new EqualToRecord<>(value, false); } @Override - public boolean equals(Object obj) { - return obj instanceof EqualToRecord that - && value.equals(that.value); + public NotEqualTo negate() { + return new NotEqualToRecord<>(value, isCaseSensitive); } @Override - public int hashCode() { - return value.hashCode(); + public String toString() { + return value instanceof String + ? "= '" + value + (isCaseSensitive ? "'" : "' IGNORE CASE") + : "= " + value.toString(); } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThan.java b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThan.java index 5701bb5ac..e3259280d 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThan.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThan.java @@ -23,5 +23,9 @@ static > GreaterThan bound(T lowerBound) { return new GreaterThanRecord<>(lowerBound); } + GreaterThan ignoreCase(); + T bound(); + + boolean isCaseSensitive(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqual.java b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqual.java index c4a15732d..07233ad02 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqual.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqual.java @@ -23,5 +23,9 @@ static > GreaterThanOrEqual min(T minimum) { return new GreaterThanOrEqualRecord<>(minimum); } + GreaterThanOrEqual ignoreCase(); + T bound(); + + boolean isCaseSensitive(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqualRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqualRecord.java index 010b384c9..a0bfa0bd8 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqualRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanOrEqualRecord.java @@ -19,30 +19,34 @@ import java.util.Objects; -record GreaterThanOrEqualRecord>(T bound) +record GreaterThanOrEqualRecord>(T bound, boolean isCaseSensitive) implements GreaterThanOrEqual { public GreaterThanOrEqualRecord { Objects.requireNonNull(bound, "Lower bound must not be null"); } - @Override - public LessThan negate() { - return LessThan.bound(bound); + GreaterThanOrEqualRecord(T bound) { + this(bound, bound instanceof String); } @Override - public String toString() { - return ">= " + bound.toString(); + public GreaterThanOrEqual ignoreCase() { + if (!(bound instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + bound.getClass().getName() + + " typed attribute"); + } + return new GreaterThanOrEqualRecord<>(bound, false); } @Override - public boolean equals(Object obj) { - return obj instanceof GreaterThanOrEqualRecord that - && bound.equals(that.bound); + public LessThan negate() { + return new LessThanRecord<>(bound, isCaseSensitive); } @Override - public int hashCode() { - return bound.hashCode(); - } + public String toString() { + return bound instanceof String + ? ">= '" + bound + (isCaseSensitive ? "'" : "' IGNORE CASE") + : ">= " + bound; } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanRecord.java index e44fd1a45..0d7d70cc6 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/GreaterThanRecord.java @@ -19,30 +19,35 @@ import java.util.Objects; -record GreaterThanRecord>(T bound) +record GreaterThanRecord>(T bound, boolean isCaseSensitive) implements GreaterThan { public GreaterThanRecord { Objects.requireNonNull(bound, "Lower bound must not be null"); } - @Override - public LessThanOrEqual negate() { - return LessThanOrEqual.max(bound); + GreaterThanRecord(T bound) { + this(bound, bound instanceof String); } @Override - public String toString() { - return "> " + bound.toString(); + public GreaterThan ignoreCase() { + if (!(bound instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + bound.getClass().getName() + + " typed attribute"); + } + return new GreaterThanRecord<>(bound, false); } @Override - public boolean equals(Object obj) { - return obj instanceof GreaterThanRecord that - && bound.equals(that.bound); + public LessThanOrEqual negate() { + return new LessThanOrEqualRecord<>(bound, isCaseSensitive); } @Override - public int hashCode() { - return bound.hashCode(); + public String toString() { + return bound instanceof String + ? "> '" + bound + (isCaseSensitive ? "'" : "' IGNORE CASE") + : "> " + bound; } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/LessThan.java b/api/src/main/java/jakarta/data/metamodel/constraint/LessThan.java index 13f7ce738..e21f006e1 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/LessThan.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/LessThan.java @@ -23,5 +23,9 @@ static > LessThan bound(T upperBound) { return new LessThanRecord<>(upperBound); } + LessThan ignoreCase(); + T bound(); + + boolean isCaseSensitive(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqual.java b/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqual.java index db6886cef..9cbee4f68 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqual.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqual.java @@ -23,5 +23,9 @@ static > LessThanOrEqual max(T maximum) { return new LessThanOrEqualRecord<>(maximum); } + LessThanOrEqual ignoreCase(); + T bound(); + + boolean isCaseSensitive(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqualRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqualRecord.java index 05df0c7d9..9e568bc0b 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqualRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/LessThanOrEqualRecord.java @@ -19,30 +19,35 @@ import java.util.Objects; -record LessThanOrEqualRecord>(T bound) +record LessThanOrEqualRecord>(T bound, boolean isCaseSensitive) implements LessThanOrEqual { public LessThanOrEqualRecord { Objects.requireNonNull(bound, "Upper bound must not be null"); } - @Override - public GreaterThan negate() { - return GreaterThan.bound(bound); + LessThanOrEqualRecord(T bound) { + this(bound, bound instanceof String); } @Override - public String toString() { - return "<= " + bound.toString(); + public LessThanOrEqual ignoreCase() { + if (!(bound instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + bound.getClass().getName() + + " typed attribute"); + } + return new LessThanOrEqualRecord<>(bound, false); } @Override - public boolean equals(Object obj) { - return obj instanceof LessThanOrEqualRecord that - && bound.equals(that.bound); + public GreaterThan negate() { + return new GreaterThanRecord<>(bound, isCaseSensitive); } @Override - public int hashCode() { - return bound.hashCode(); + public String toString() { + return bound instanceof String + ? "<= '" + bound + (isCaseSensitive ? "'" : "' IGNORE CASE") + : "<= " + bound; } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/LessThanRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/LessThanRecord.java index 7a3058bf8..52cdbe8b7 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/LessThanRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/LessThanRecord.java @@ -19,30 +19,35 @@ import java.util.Objects; -record LessThanRecord>(T bound) +record LessThanRecord>(T bound, boolean isCaseSensitive) implements LessThan { public LessThanRecord { Objects.requireNonNull(bound, "Upper bound must not be null"); } - @Override - public GreaterThanOrEqual negate() { - return GreaterThanOrEqual.min(bound); + LessThanRecord(T bound) { + this(bound, bound instanceof String); } @Override - public String toString() { - return "< " + bound.toString(); + public LessThan ignoreCase() { + if (!(bound instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + bound.getClass().getName() + + " typed attribute"); + } + return new LessThanRecord<>(bound, false); } @Override - public boolean equals(Object obj) { - return obj instanceof LessThanRecord that - && bound.equals(that.bound); + public GreaterThanOrEqual negate() { + return new GreaterThanOrEqualRecord<>(bound, isCaseSensitive); } @Override - public int hashCode() { - return bound.hashCode(); + public String toString() { + return bound instanceof String + ? "< '" + bound + (isCaseSensitive ? "'" : "' IGNORE CASE") + : "< " + bound; } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/Like.java b/api/src/main/java/jakarta/data/metamodel/constraint/Like.java index b1bdf1f9f..1e6306a7b 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/Like.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/Like.java @@ -18,11 +18,17 @@ package jakarta.data.metamodel.constraint; public interface Like extends Constraint { + + Like ignoreCase(); + + boolean isCaseSensitive(); + String pattern(); + Character escape(); static Like pattern(String pattern) { - return new LikeRecord(pattern); + return new LikeRecord(pattern, null); } static Like pattern(String pattern, char charWildcard, char stringWildcard) { diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/LikeRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/LikeRecord.java index 83a7c92e3..b229a5c8c 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/LikeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/LikeRecord.java @@ -19,7 +19,7 @@ import java.util.Objects; -record LikeRecord(String pattern, Character escape) +record LikeRecord(String pattern, Character escape, boolean isCaseSensitive) implements Like { public static final char CHAR_WILDCARD = '_'; @@ -30,8 +30,8 @@ record LikeRecord(String pattern, Character escape) Objects.requireNonNull(pattern, "Pattern must not be null"); } - public LikeRecord(String pattern) { - this(pattern, null); + LikeRecord(String pattern, Character escape) { + this(pattern, escape, true); } public LikeRecord(String pattern, char charWildcard, char stringWildcard) { @@ -42,15 +42,21 @@ public LikeRecord(String pattern, char charWildcard, char stringWildcard, char e this(translate(pattern, charWildcard, stringWildcard, escape), escape); } + @Override + public Like ignoreCase() { + return new LikeRecord(pattern, escape, false); + } + @Override public NotLike negate() { - return new NotLikeRecord(pattern, escape); + return new NotLikeRecord(pattern, escape, isCaseSensitive); } @Override public String toString() { - return "LIKE '" + pattern + "'" - + (escape == null ? "" : " ESCAPE '\\'"); + return "LIKE '" + pattern + "'" + + (escape == null ? "" : " ESCAPE '\\'" + + (isCaseSensitive ? "" : " IGNORE CASE")); } public static LikeRecord prefix(String prefix) { diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/NotBetween.java b/api/src/main/java/jakarta/data/metamodel/constraint/NotBetween.java index bad92a9d5..418ee2d20 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/NotBetween.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/NotBetween.java @@ -23,6 +23,10 @@ static > NotBetween bounds(T lower, T upper) { return new NotBetweenRecord<>(lower, upper); } + NotBetween ignoreCase(); + + boolean isCaseSensitive(); + T lowerBound(); T upperBound(); diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/NotBetweenRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/NotBetweenRecord.java index cb09a87b5..61592742d 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/NotBetweenRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/NotBetweenRecord.java @@ -19,7 +19,10 @@ import java.util.Objects; -record NotBetweenRecord>(T lowerBound, T upperBound) +record NotBetweenRecord>( + T lowerBound, + T upperBound, + boolean isCaseSensitive) implements NotBetween { NotBetweenRecord { @@ -27,15 +30,30 @@ record NotBetweenRecord>(T lowerBound, T upperBound) Objects.requireNonNull(upperBound, "upperBound must not be null"); } + NotBetweenRecord(T lowerBound, T upperBound) { + this(lowerBound, upperBound, lowerBound instanceof String); + } + + @Override + public NotBetween ignoreCase() { + if (!(lowerBound instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + lowerBound.getClass().getName() + + " typed attribute"); + } + return new NotBetweenRecord<>(lowerBound, upperBound, false); + } + @Override public Between negate() { - return Between.bounds(lowerBound, upperBound); + return new BetweenRecord<>(lowerBound, upperBound, isCaseSensitive); } @Override public String toString() { return lowerBound instanceof String - ? "NOT BETWEEN '" + lowerBound + "' AND '" + upperBound + "'" + ? "NOT BETWEEN '" + lowerBound + "' AND '" + upperBound + "'" + + (isCaseSensitive ? "" : " IGNORE CASE") : "NOT BETWEEN " + lowerBound + " AND " + upperBound; } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualTo.java b/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualTo.java index 37b48435b..55bc0e355 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualTo.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualTo.java @@ -23,5 +23,9 @@ static NotEqualTo value(T value) { return new NotEqualToRecord<>(value); } + NotEqualTo ignoreCase(); + + boolean isCaseSensitive(); + T value(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualToRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualToRecord.java index 74e827e09..1d1e802c3 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualToRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/NotEqualToRecord.java @@ -19,19 +19,35 @@ import java.util.Objects; -record NotEqualToRecord(T value) implements NotEqualTo { +record NotEqualToRecord(T value, boolean isCaseSensitive) implements NotEqualTo { NotEqualToRecord { Objects.requireNonNull(value, "value must not be null"); } + NotEqualToRecord(T value) { + this(value, value instanceof String); + } + + @Override + public NotEqualTo ignoreCase() { + if (!(value instanceof String)) { + throw new UnsupportedOperationException( + "Cannot ignore case of a " + value.getClass().getName() + + " typed attribute"); + } + return new NotEqualToRecord<>(value, false); + } + @Override public EqualTo negate() { - return EqualTo.value(value); + return new EqualToRecord<>(value, isCaseSensitive); } @Override public String toString() { - return value instanceof String ? "<> '" + value + "'" : "<> " + value; + return value instanceof String + ? "<> '" + value + (isCaseSensitive ? "'" : "' IGNORE CASE") + : "<> " + value; } } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/NotLike.java b/api/src/main/java/jakarta/data/metamodel/constraint/NotLike.java index ac7c893a4..f8a026c32 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/NotLike.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/NotLike.java @@ -52,7 +52,11 @@ static NotLike suffix(String suffix) { ESCAPE); } + NotLike ignoreCase(); + Character escape(); + boolean isCaseSensitive(); + String pattern(); } diff --git a/api/src/main/java/jakarta/data/metamodel/constraint/NotLikeRecord.java b/api/src/main/java/jakarta/data/metamodel/constraint/NotLikeRecord.java index 510a1ee61..51785f570 100644 --- a/api/src/main/java/jakarta/data/metamodel/constraint/NotLikeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/constraint/NotLikeRecord.java @@ -19,20 +19,31 @@ import java.util.Objects; -record NotLikeRecord(String pattern, Character escape) implements NotLike { +record NotLikeRecord(String pattern, Character escape, boolean isCaseSensitive) + implements NotLike { NotLikeRecord { Objects.requireNonNull(pattern, "pattern must not be null"); } + NotLikeRecord(String pattern, Character escape) { + this(pattern, escape, true); + } + + @Override + public NotLike ignoreCase() { + return new NotLikeRecord(pattern, escape, false); + } + @Override public Like negate() { - return new LikeRecord(pattern, escape); + return new LikeRecord(pattern, escape, isCaseSensitive); } @Override public String toString() { return "NOT LIKE '" + pattern + "'" + - (escape == null ? "" : " ESCAPE '\\'"); + (escape == null ? "" : " ESCAPE '\\'") + + (isCaseSensitive ? "" : " IGNORE CASE"); } } diff --git a/api/src/main/java/jakarta/data/metamodel/restrict/TextRestriction.java b/api/src/main/java/jakarta/data/metamodel/restrict/TextRestriction.java index 0abcdc683..e4f8814dd 100644 --- a/api/src/main/java/jakarta/data/metamodel/restrict/TextRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/restrict/TextRestriction.java @@ -20,8 +20,12 @@ import jakarta.data.metamodel.constraint.Constraint; public interface TextRestriction extends BasicRestriction { + + TextRestriction ignoreCase(); + @Override TextRestriction negate(); + @Override Constraint constraint(); } diff --git a/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java b/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java index 5db6e1761..02a626d24 100644 --- a/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java +++ b/api/src/test/java/jakarta/data/metamodel/restrict/RestrictTest.java @@ -239,14 +239,14 @@ void shouldEscapeToLikePatternCorrectly() { @Test void shouldIgnoreCase() { - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - TextRestriction restriction = _Employee.position.contains("SOFTWARE"); - // .ignoreCase(); + TextRestriction restriction = _Employee.position.contains("SOFTWARE") + .ignoreCase(); SoftAssertions.assertSoftly(soft -> { soft.assertThat(restriction.attribute()).isEqualTo(_Employee.POSITION); - //soft.assertThat(restriction.isCaseSensitive()).isFalse(); - soft.assertThat(restriction.constraint()).isEqualTo(Like.substring("SOFTWARE")); + soft.assertThat(((Like) restriction.constraint()).isCaseSensitive()).isFalse(); + soft.assertThat(restriction.constraint()).isEqualTo( + Like.substring("SOFTWARE").ignoreCase()); }); } diff --git a/api/src/test/java/jakarta/data/metamodel/restrict/TextRestrictionRecordTest.java b/api/src/test/java/jakarta/data/metamodel/restrict/TextRestrictionRecordTest.java index d702480d3..649c371ca 100644 --- a/api/src/test/java/jakarta/data/metamodel/restrict/TextRestrictionRecordTest.java +++ b/api/src/test/java/jakarta/data/metamodel/restrict/TextRestrictionRecordTest.java @@ -58,8 +58,7 @@ void shouldCreateTextRestrictionWithDefaultValues() { SoftAssertions.assertSoftly(soft -> { soft.assertThat(restriction.attribute()).isEqualTo("title"); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //soft.assertThat(restriction.isCaseSensitive()).isTrue(); + soft.assertThat(((Like) restriction.constraint()).isCaseSensitive()).isTrue(); soft.assertThat(constraint.pattern()).isEqualTo("%Java%"); soft.assertThat(constraint.escape()).isNull(); }); @@ -73,8 +72,7 @@ void shouldCreateTextRestrictionWithExplicitNegation() { SoftAssertions.assertSoftly(soft -> { soft.assertThat(restriction.attribute()).isEqualTo("title"); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //soft.assertThat(restriction.isCaseSensitive()).isTrue(); + soft.assertThat(((NotLike) restriction.constraint()).isCaseSensitive()).isTrue(); soft.assertThat(constraint.pattern()).isEqualTo("%Java%"); soft.assertThat(constraint.escape()).isNull(); }); @@ -84,13 +82,12 @@ void shouldCreateTextRestrictionWithExplicitNegation() { void shouldIgnoreCaseForTextRestriction() { TextRestriction restriction = _Book.title.like("%Java%"); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //TextRestriction caseInsensitiveRestriction = restriction.ignoreCase(); + TextRestriction caseInsensitiveRestriction = restriction.ignoreCase(); Like constraint = (Like) restriction.constraint(); SoftAssertions.assertSoftly(soft -> { - // soft.assertThat(caseInsensitiveRestriction.attribute()).isEqualTo("title"); - // soft.assertThat(caseInsensitiveRestriction.isCaseSensitive()).isFalse(); + soft.assertThat(caseInsensitiveRestriction.attribute()).isEqualTo("title"); + soft.assertThat(((Like) caseInsensitiveRestriction.constraint()).isCaseSensitive()).isFalse(); soft.assertThat(constraint.pattern()).isEqualTo("%Java%"); soft.assertThat(constraint.escape()).isNull(); }); @@ -104,8 +101,7 @@ void shouldCreateTextRestrictionWithEscapedValue() { SoftAssertions.assertSoftly(soft -> { soft.assertThat(restriction.attribute()).isEqualTo("title"); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //soft.assertThat(restriction.isCaseSensitive()).isTrue(); + soft.assertThat(((Like) restriction.constraint()).isCaseSensitive()).isTrue(); soft.assertThat(constraint.pattern()).isEqualTo("%Java%"); soft.assertThat(constraint.escape()).isNull(); }); @@ -119,8 +115,7 @@ void shouldCreateTextRestrictionWithCustomWildcards() { SoftAssertions.assertSoftly(soft -> { soft.assertThat(restriction.attribute()).isEqualTo("title"); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //soft.assertThat(restriction.isCaseSensitive()).isTrue(); + soft.assertThat(((Like) restriction.constraint()).isCaseSensitive()).isTrue(); soft.assertThat(constraint.pattern()).isEqualTo("%Java__"); soft.assertThat(constraint.escape()).isEqualTo('$'); }); @@ -130,16 +125,15 @@ void shouldCreateTextRestrictionWithCustomWildcards() { void shouldNegateLikeRestriction() { TextRestriction likeJakartaEE = _Book.title.endsWith("Jakarta EE"); TextRestriction notLikeJakartaEE = likeJakartaEE.negate(); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //TextRestriction anyCaseNotLikeJakartaEE = likeJakartaEE.ignoreCase().negate(); - //TextRestriction notLikeJakartaEEAnyCase = likeJakartaEE.negate().ignoreCase(); - - //SoftAssertions.assertSoftly(soft -> { - // soft.assertThat(likeJakartaEE.isCaseSensitive()).isTrue(); - // soft.assertThat(notLikeJakartaEE.isCaseSensitive()).isTrue(); - // soft.assertThat(anyCaseNotLikeJakartaEE.isCaseSensitive()).isFalse(); - // soft.assertThat(notLikeJakartaEEAnyCase.isCaseSensitive()).isFalse(); - //}); + TextRestriction anyCaseNotLikeJakartaEE = likeJakartaEE.ignoreCase().negate(); + TextRestriction notLikeJakartaEEAnyCase = likeJakartaEE.negate().ignoreCase(); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(((Like) likeJakartaEE.constraint()).isCaseSensitive()).isTrue(); + soft.assertThat(((NotLike) notLikeJakartaEE.constraint()).isCaseSensitive()).isTrue(); + soft.assertThat(((NotLike) anyCaseNotLikeJakartaEE.constraint()).isCaseSensitive()).isFalse(); + soft.assertThat(((NotLike) notLikeJakartaEEAnyCase.constraint()).isCaseSensitive()).isFalse(); + }); } @Test @@ -167,8 +161,7 @@ void shouldNegateNegatedRestriction() { void shouldOutputToString() { TextRestriction titleRestriction = _Book.title.contains("Jakarta Data"); TextRestriction authorRestriction = _Book.author.equalTo("Myself") - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - // .ignoreCase() + .ignoreCase() .negate(); SoftAssertions.assertSoftly(soft -> { @@ -176,7 +169,7 @@ void shouldOutputToString() { title LIKE '%Jakarta Data%' ESCAPE '\\'\ """); soft.assertThat(authorRestriction.toString()).isEqualTo(""" - author <> 'Myself'\ + author <> 'Myself' IGNORE CASE\ """); }); } @@ -189,8 +182,7 @@ void shouldSupportNegationForTextRestriction() { SoftAssertions.assertSoftly(soft -> { soft.assertThat(restriction.attribute()).isEqualTo("author"); - // TODO TextRestriction.ignoreCase vs TextAttribute.upper/lowercased - //soft.assertThat(restriction.isCaseSensitive()).isTrue(); + soft.assertThat(constraint.isCaseSensitive()).isTrue(); soft.assertThat(constraint.value()).isEqualTo("John Doe"); }); }