diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b333e3ab..cec9f683f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,7 +103,8 @@ Runtime behavior changes: - Rendering for all the conditions (isEqualTo, etc.) has changed. This should be transparent to most users unless you have coded a direct implementation of `VisitableCondition`. The change makes it easier to code custom conditions that are not supported by the library out of the box. The statement renderers now call methods `renderCondition` and - `renderLeftColumn` that you can override to implement any rendering you need. + `renderLeftColumn` that you can override to implement any rendering you need. In addition, we've made `filter` and + `map` support optional if you implement custom conditions ## Release 1.5.2 - June 3, 2024 diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java index 23f48e6f4..41c6a56e2 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java @@ -73,16 +73,6 @@ protected > S mapSupport(Function filter(Predicate predicate); - public abstract String operator(); @Override @@ -100,4 +90,54 @@ private FragmentAndParameters toFragmentAndParameters(T value, RenderingContext .withParameter(parameterInfo.parameterMapKey(), leftColumn.convertParameterType(value)) .build(); } + + /** + * Conditions may implement Filterable to add optionality to rendering. + * + *

If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision + * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the + * rendered SQL. + * + *

Implementations of Filterable may call + * {@link AbstractListValueCondition#filterSupport(Predicate, Function, AbstractListValueCondition, Supplier)} as + * a common implementation of the filtering algorithm. + * + * @param the Java type related to the database column type + */ + public interface Filterable { + /** + * If renderable and the value matches the predicate, returns this condition. Else returns a condition + * that will not render. + * + * @param predicate predicate applied to the value, if renderable + * @return this condition if renderable and the value matches the predicate, otherwise a condition + * that will not render. + */ + AbstractListValueCondition filter(Predicate predicate); + } + + /** + * Conditions may implement Mappable to alter condition values or types during rendering. + * + *

If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the + * values of a condition, or change that datatype. + * + *

Implementations of Mappable may call + * {@link AbstractListValueCondition#mapSupport(Function, Function, Supplier)} as + * a common implementation of the mapping algorithm. + * + * @param the Java type related to the database column type + */ + public interface Mappable { + /** + * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a + * condition that will not render (this). + * + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. + */ + AbstractListValueCondition map(Function mapper); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractNoValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractNoValueCondition.java index c0e103baf..20e6251d6 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractNoValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractNoValueCondition.java @@ -38,4 +38,30 @@ protected > S filterSupport(BooleanSupplie public FragmentAndParameters renderCondition(RenderingContext renderingContext, BindableColumn leftColumn) { return FragmentAndParameters.fromFragment(operator()); } + + /** + * Conditions may implement Filterable to add optionality to rendering. + * + *

If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision + * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the + * rendered SQL. + * + *

Implementations of Filterable may call + * {@link AbstractNoValueCondition#filterSupport(BooleanSupplier, Supplier, AbstractNoValueCondition)} as + * a common implementation of the filtering algorithm. + */ + public interface Filterable { + /** + * If renderable and the supplier returns true, returns this condition. Else returns a condition that will not + * render. + * + * @param booleanSupplier + * function that specifies whether the condition should render + * @param + * condition type - not used except for compilation compliance + * + * @return this condition if renderable and the supplier returns true, otherwise a condition that will not render. + */ + AbstractNoValueCondition filter(BooleanSupplier booleanSupplier); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java index 9c8248d02..c16dbf08c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java @@ -54,16 +54,6 @@ protected > S mapSupport(Function filter(Predicate predicate); - public abstract String operator(); @Override @@ -75,4 +65,54 @@ public FragmentAndParameters renderCondition(RenderingContext renderingContext, .withParameter(parameterInfo.parameterMapKey(), leftColumn.convertParameterType(value())) .build(); } + + /** + * Conditions may implement Filterable to add optionality to rendering. + * + *

If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision + * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the + * rendered SQL. + * + *

Implementations of Filterable may call + * {@link AbstractSingleValueCondition#filterSupport(Predicate, Supplier, AbstractSingleValueCondition)} as + * a common implementation of the filtering algorithm. + * + * @param the Java type related to the database column type + */ + public interface Filterable { + /** + * If renderable and the value matches the predicate, returns this condition. Else returns a condition + * that will not render. + * + * @param predicate predicate applied to the value, if renderable + * @return this condition if renderable and the value matches the predicate, otherwise a condition + * that will not render. + */ + AbstractSingleValueCondition filter(Predicate predicate); + } + + /** + * Conditions may implement Mappable to alter condition values or types during rendering. + * + *

If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the + * values of a condition, or change that datatype. + * + *

Implementations of Mappable may call + * {@link AbstractSingleValueCondition#mapSupport(Function, Function, Supplier)} as + * a common implementation of the mapping algorithm. + * + * @param the Java type related to the database column type + */ + public interface Mappable { + /** + * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a + * condition that will not render (this). + * + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. + */ + AbstractSingleValueCondition map(Function mapper); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java index 865f7db0b..6cceff16e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java @@ -67,27 +67,6 @@ protected > S mapSupport(Function filter(BiPredicate predicate); - - /** - * If renderable and both values match the predicate, returns this condition. Else returns a condition - * that will not render. This function implements a short-circuiting test. If the - * first value does not match the predicate, then the second value will not be tested. - * - * @param predicate predicate applied to both values, if renderable - * @return this condition if renderable and the values match the predicate, otherwise a condition - * that will not render. - */ - public abstract AbstractTwoValueCondition filter(Predicate predicate); - public abstract String operator1(); public abstract String operator2(); @@ -107,4 +86,79 @@ public FragmentAndParameters renderCondition(RenderingContext renderingContext, .withParameter(parameterInfo2.parameterMapKey(), leftColumn.convertParameterType(value2())) .build(); } + + /** + * Conditions may implement Filterable to add optionality to rendering. + * + *

If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision + * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the + * rendered SQL. + * + *

Implementations of Filterable may call + * {@link AbstractTwoValueCondition#filterSupport(Predicate, Supplier, AbstractTwoValueCondition)} + * or {@link AbstractTwoValueCondition#filterSupport(BiPredicate, Supplier, AbstractTwoValueCondition)} as + * a common implementation of the filtering algorithm. + * + * @param the Java type related to the database column type + */ + public interface Filterable { + /** + * If renderable and the values match the predicate, returns this condition. Else returns a condition + * that will not render. + * + * @param predicate predicate applied to the values, if renderable + * @return this condition if renderable and the values match the predicate, otherwise a condition + * that will not render. + */ + AbstractTwoValueCondition filter(BiPredicate predicate); + + /** + * If renderable and both values match the predicate, returns this condition. Else returns a condition + * that will not render. This function implements a short-circuiting test. If the + * first value does not match the predicate, then the second value will not be tested. + * + * @param predicate predicate applied to both values, if renderable + * @return this condition if renderable and the values match the predicate, otherwise a condition + * that will not render. + */ + AbstractTwoValueCondition filter(Predicate predicate); + } + + /** + * Conditions may implement Mappable to alter condition values or types during rendering. + * + *

If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the + * values of a condition, or change that datatype. + * + *

Implementations of Mappable may call + * {@link AbstractTwoValueCondition#mapSupport(Function, Function, BiFunction, Supplier)} as + * a common implementation of the mapping algorithm. + * + * @param the Java type related to the database column type + */ + public interface Mappable { + /** + * If renderable, apply the mappings to the values and return a new condition with the new values. Else return a + * condition that will not render (this). + * + * @param mapper1 a mapping function to apply to the first value, if renderable + * @param mapper2 a mapping function to apply to the second value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mappers to the values of this condition, + * if renderable, otherwise a condition that will not render. + */ + AbstractTwoValueCondition map(Function mapper1, + Function mapper2); + + /** + * If renderable, apply the mapping to both values and return a new condition with the new values. Else return a + * condition that will not render (this). + * + * @param mapper a mapping function to apply to both values, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mappers to the values of this condition, + * if renderable, otherwise a condition that will not render. + */ + AbstractTwoValueCondition map(Function mapper); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java index 1e27db93d..2f365de1e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java @@ -882,68 +882,69 @@ static IsEqualTo isFalse() { } // conditions for strings only - static IsLikeCaseInsensitive isLikeCaseInsensitive(String value) { + static IsLikeCaseInsensitive isLikeCaseInsensitive(String value) { return IsLikeCaseInsensitive.of(value); } - static IsLikeCaseInsensitive isLikeCaseInsensitive(Supplier valueSupplier) { + static IsLikeCaseInsensitive isLikeCaseInsensitive(Supplier valueSupplier) { return isLikeCaseInsensitive(valueSupplier.get()); } - static IsLikeCaseInsensitive isLikeCaseInsensitiveWhenPresent(@Nullable String value) { + static IsLikeCaseInsensitive isLikeCaseInsensitiveWhenPresent(@Nullable String value) { return value == null ? IsLikeCaseInsensitive.empty() : IsLikeCaseInsensitive.of(value); } - static IsLikeCaseInsensitive isLikeCaseInsensitiveWhenPresent(Supplier<@Nullable String> valueSupplier) { + static IsLikeCaseInsensitive isLikeCaseInsensitiveWhenPresent(Supplier<@Nullable String> valueSupplier) { return isLikeCaseInsensitiveWhenPresent(valueSupplier.get()); } - static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(String value) { + static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(String value) { return IsNotLikeCaseInsensitive.of(value); } - static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(Supplier valueSupplier) { + static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(Supplier valueSupplier) { return isNotLikeCaseInsensitive(valueSupplier.get()); } - static IsNotLikeCaseInsensitive isNotLikeCaseInsensitiveWhenPresent(@Nullable String value) { + static IsNotLikeCaseInsensitive isNotLikeCaseInsensitiveWhenPresent(@Nullable String value) { return value == null ? IsNotLikeCaseInsensitive.empty() : IsNotLikeCaseInsensitive.of(value); } - static IsNotLikeCaseInsensitive isNotLikeCaseInsensitiveWhenPresent(Supplier<@Nullable String> valueSupplier) { + static IsNotLikeCaseInsensitive isNotLikeCaseInsensitiveWhenPresent( + Supplier<@Nullable String> valueSupplier) { return isNotLikeCaseInsensitiveWhenPresent(valueSupplier.get()); } - static IsInCaseInsensitive isInCaseInsensitive(String... values) { + static IsInCaseInsensitive isInCaseInsensitive(String... values) { return IsInCaseInsensitive.of(values); } - static IsInCaseInsensitive isInCaseInsensitive(Collection values) { + static IsInCaseInsensitive isInCaseInsensitive(Collection values) { return IsInCaseInsensitive.of(values); } - static IsInCaseInsensitiveWhenPresent isInCaseInsensitiveWhenPresent(@Nullable String... values) { + static IsInCaseInsensitiveWhenPresent isInCaseInsensitiveWhenPresent(@Nullable String... values) { return IsInCaseInsensitiveWhenPresent.of(values); } - static IsInCaseInsensitiveWhenPresent isInCaseInsensitiveWhenPresent( + static IsInCaseInsensitiveWhenPresent isInCaseInsensitiveWhenPresent( @Nullable Collection<@Nullable String> values) { return values == null ? IsInCaseInsensitiveWhenPresent.empty() : IsInCaseInsensitiveWhenPresent.of(values); } - static IsNotInCaseInsensitive isNotInCaseInsensitive(String... values) { + static IsNotInCaseInsensitive isNotInCaseInsensitive(String... values) { return IsNotInCaseInsensitive.of(values); } - static IsNotInCaseInsensitive isNotInCaseInsensitive(Collection values) { + static IsNotInCaseInsensitive isNotInCaseInsensitive(Collection values) { return IsNotInCaseInsensitive.of(values); } - static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent(@Nullable String... values) { + static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent(@Nullable String... values) { return IsNotInCaseInsensitiveWhenPresent.of(values); } - static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent( + static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent( @Nullable Collection<@Nullable String> values) { return values == null ? IsNotInCaseInsensitiveWhenPresent.empty() : IsNotInCaseInsensitiveWhenPresent.of(values); diff --git a/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java b/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java index 2b57c748e..25a7e390c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java @@ -15,6 +15,8 @@ */ package org.mybatis.dynamic.sql.util; +import java.util.function.Function; + import org.jspecify.annotations.Nullable; public interface StringUtilities { @@ -59,4 +61,8 @@ static String formatConstantForSQL(String in) { String escaped = in.replace("'", "''"); //$NON-NLS-1$ //$NON-NLS-2$ return "'" + escaped + "'"; //$NON-NLS-1$ //$NON-NLS-2$ } + + static Function mapToUpperCase(Function f) { + return f.andThen(String::toUpperCase); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/CaseInsensitiveRenderableCondition.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/CaseInsensitiveRenderableCondition.java index 2c837c4c7..977a0090b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/CaseInsensitiveRenderableCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/CaseInsensitiveRenderableCondition.java @@ -20,11 +20,11 @@ import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.FragmentAndParameters; -public interface CaseInsensitiveRenderableCondition extends RenderableCondition { +public interface CaseInsensitiveRenderableCondition extends RenderableCondition { @Override default FragmentAndParameters renderLeftColumn(RenderingContext renderingContext, - BindableColumn leftColumn) { + BindableColumn leftColumn) { return RenderableCondition.super.renderLeftColumn(renderingContext, leftColumn) .mapFragment(s -> "upper(" + s + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java index deeddb4c9..0e6700fb0 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java @@ -23,7 +23,8 @@ import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractTwoValueCondition; -public class IsBetween extends AbstractTwoValueCondition { +public class IsBetween extends AbstractTwoValueCondition + implements AbstractTwoValueCondition.Filterable, AbstractTwoValueCondition.Mappable { private static final IsBetween EMPTY = new IsBetween(-1, -1) { @Override public Object value1() { @@ -71,29 +72,12 @@ public IsBetween filter(Predicate predicate) { return filterSupport(predicate, IsBetween::empty, this); } - /** - * If renderable, apply the mappings to the values and return a new condition with the new values. Else return a - * condition that will not render (this). - * - * @param mapper1 a mapping function to apply to the first value, if renderable - * @param mapper2 a mapping function to apply to the second value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mappers to the values of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsBetween map(Function mapper1, Function mapper2) { return mapSupport(mapper1, mapper2, IsBetween::new, IsBetween::empty); } - /** - * If renderable, apply the mapping to both values and return a new condition with the new values. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to both values, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mappers to the values of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsBetween map(Function mapper) { return map(mapper, mapper); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java index 0a4b19207..51e6e4d47 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java @@ -21,7 +21,8 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsEqualTo extends AbstractSingleValueCondition { +public class IsEqualTo extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsEqualTo EMPTY = new IsEqualTo(-1) { @Override @@ -59,15 +60,7 @@ public IsEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsEqualTo::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsEqualTo map(Function mapper) { return mapSupport(mapper, IsEqualTo::new, IsEqualTo::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java index a0ed1a2bc..93be70911 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java @@ -21,7 +21,8 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsGreaterThan extends AbstractSingleValueCondition { +public class IsGreaterThan extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsGreaterThan EMPTY = new IsGreaterThan(-1) { @Override public Object value() { @@ -58,15 +59,7 @@ public IsGreaterThan filter(Predicate predicate) { return filterSupport(predicate, IsGreaterThan::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsGreaterThan map(Function mapper) { return mapSupport(mapper, IsGreaterThan::new, IsGreaterThan::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java index 59560339b..8373bf352 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java @@ -21,7 +21,8 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsGreaterThanOrEqualTo extends AbstractSingleValueCondition { +public class IsGreaterThanOrEqualTo extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsGreaterThanOrEqualTo EMPTY = new IsGreaterThanOrEqualTo(-1) { @Override public Object value() { @@ -58,15 +59,7 @@ public IsGreaterThanOrEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsGreaterThanOrEqualTo::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsGreaterThanOrEqualTo map(Function mapper) { return mapSupport(mapper, IsGreaterThanOrEqualTo::new, IsGreaterThanOrEqualTo::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java index 6ce403717..8beeacc8d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java @@ -25,7 +25,8 @@ import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.Validator; -public class IsIn extends AbstractListValueCondition { +public class IsIn extends AbstractListValueCondition + implements AbstractListValueCondition.Filterable, AbstractListValueCondition.Mappable{ private static final IsIn EMPTY = new IsIn<>(Collections.emptyList()); public static IsIn empty() { @@ -54,17 +55,9 @@ public IsIn filter(Predicate predicate) { return filterSupport(predicate, IsIn::new, this, IsIn::empty); } - /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). - * - * @param mapper a mapping function to apply to the values, if not empty - * @param type of the new condition - * @return a new condition with mapped values if renderable, otherwise an empty condition - */ + @Override public IsIn map(Function mapper) { - Function, IsIn> constructor = IsIn::new; - return mapSupport(mapper, constructor, IsIn::empty); + return mapSupport(mapper, IsIn::new, IsIn::empty); } @SafeVarargs diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java index e521d28b2..dc3b0f8f1 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java @@ -18,23 +18,26 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.StringUtilities; import org.mybatis.dynamic.sql.util.Validator; -public class IsInCaseInsensitive extends AbstractListValueCondition - implements CaseInsensitiveRenderableCondition { - private static final IsInCaseInsensitive EMPTY = new IsInCaseInsensitive(Collections.emptyList()); +public class IsInCaseInsensitive extends AbstractListValueCondition + implements CaseInsensitiveRenderableCondition, AbstractListValueCondition.Filterable, + AbstractListValueCondition.Mappable { + private static final IsInCaseInsensitive EMPTY = new IsInCaseInsensitive<>(Collections.emptyList()); - public static IsInCaseInsensitive empty() { - return EMPTY; + public static IsInCaseInsensitive empty() { + @SuppressWarnings("unchecked") + IsInCaseInsensitive t = (IsInCaseInsensitive) EMPTY; + return t; } - protected IsInCaseInsensitive(Collection values) { + protected IsInCaseInsensitive(Collection values) { super(values); } @@ -50,27 +53,34 @@ public String operator() { } @Override - public IsInCaseInsensitive filter(Predicate predicate) { + public IsInCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsInCaseInsensitive::new, this, IsInCaseInsensitive::empty); } /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). + * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a + * condition that will not render (this). * - * @param mapper a mapping function to apply to the values, if not empty - * @return a new condition with mapped values if renderable, otherwise an empty condition + *

This function DOES NOT automatically transform values to uppercase, so it potentially creates a + * case-sensitive query. For String conditions you can use {@link StringUtilities#mapToUpperCase(Function)} + * to add an uppercase transform after your mapping function. + * + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. */ - public IsInCaseInsensitive map(UnaryOperator mapper) { + @Override + public IsInCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsInCaseInsensitive::new, IsInCaseInsensitive::empty); } - public static IsInCaseInsensitive of(String... values) { + public static IsInCaseInsensitive of(String... values) { return of(Arrays.asList(values)); } - public static IsInCaseInsensitive of(Collection values) { + public static IsInCaseInsensitive of(Collection values) { // Keep the null safe upper case utility for backwards compatibility in case someone passes in a null - return new IsInCaseInsensitive(values.stream().map(StringUtilities::safelyUpperCase).toList()); + return new IsInCaseInsensitive<>(values.stream().map(StringUtilities::safelyUpperCase).toList()); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java index 7d81bba69..283e62fc7 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java @@ -18,23 +18,27 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.function.Function; import java.util.Objects; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; +import org.mybatis.dynamic.sql.util.StringUtilities; -public class IsInCaseInsensitiveWhenPresent extends AbstractListValueCondition - implements CaseInsensitiveRenderableCondition { - private static final IsInCaseInsensitiveWhenPresent EMPTY = - new IsInCaseInsensitiveWhenPresent(Collections.emptyList()); +public class IsInCaseInsensitiveWhenPresent extends AbstractListValueCondition + implements CaseInsensitiveRenderableCondition, AbstractListValueCondition.Filterable, + AbstractListValueCondition.Mappable { + private static final IsInCaseInsensitiveWhenPresent EMPTY = + new IsInCaseInsensitiveWhenPresent<>(Collections.emptyList()); - public static IsInCaseInsensitiveWhenPresent empty() { - return EMPTY; + public static IsInCaseInsensitiveWhenPresent empty() { + @SuppressWarnings("unchecked") + IsInCaseInsensitiveWhenPresent t = (IsInCaseInsensitiveWhenPresent) EMPTY; + return t; } - protected IsInCaseInsensitiveWhenPresent(Collection values) { + protected IsInCaseInsensitiveWhenPresent(Collection values) { super(values); } @@ -44,28 +48,35 @@ public String operator() { } @Override - public IsInCaseInsensitiveWhenPresent filter(Predicate predicate) { + public IsInCaseInsensitiveWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsInCaseInsensitiveWhenPresent::new, this, IsInCaseInsensitiveWhenPresent::empty); } /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). + * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a + * condition that will not render (this). * - * @param mapper a mapping function to apply to the values, if not empty - * @return a new condition with mapped values if renderable, otherwise an empty condition + *

This function DOES NOT automatically transform values to uppercase, so it potentially creates a + * case-sensitive query. For String conditions you can use {@link StringUtilities#mapToUpperCase(Function)} + * to add an uppercase transform after your mapping function. + * + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. */ - public IsInCaseInsensitiveWhenPresent map(UnaryOperator mapper) { + @Override + public IsInCaseInsensitiveWhenPresent map(Function mapper) { return mapSupport(mapper, IsInCaseInsensitiveWhenPresent::new, IsInCaseInsensitiveWhenPresent::empty); } - public static IsInCaseInsensitiveWhenPresent of(@Nullable String... values) { + public static IsInCaseInsensitiveWhenPresent of(@Nullable String... values) { return of(Arrays.asList(values)); } - public static IsInCaseInsensitiveWhenPresent of(Collection<@Nullable String> values) { - return new IsInCaseInsensitiveWhenPresent( + public static IsInCaseInsensitiveWhenPresent of(Collection<@Nullable String> values) { + return new IsInCaseInsensitiveWhenPresent<>( values.stream().filter(Objects::nonNull).map(String::toUpperCase).toList()); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java index ea1bfe509..5c431fea3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java @@ -25,7 +25,8 @@ import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; -public class IsInWhenPresent extends AbstractListValueCondition { +public class IsInWhenPresent extends AbstractListValueCondition + implements AbstractListValueCondition.Filterable, AbstractListValueCondition.Mappable{ private static final IsInWhenPresent EMPTY = new IsInWhenPresent<>(Collections.emptyList()); public static IsInWhenPresent empty() { @@ -48,17 +49,9 @@ public IsInWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsInWhenPresent::new, this, IsInWhenPresent::empty); } - /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). - * - * @param mapper a mapping function to apply to the values, if not empty - * @param type of the new condition - * @return a new condition with mapped values if renderable, otherwise an empty condition - */ + @Override public IsInWhenPresent map(Function mapper) { - Function, IsInWhenPresent> constructor = IsInWhenPresent::new; - return mapSupport(mapper, constructor, IsInWhenPresent::empty); + return mapSupport(mapper, IsInWhenPresent::new, IsInWhenPresent::empty); } @SafeVarargs diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java index 09e2e7ba6..3ed383fbd 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java @@ -21,7 +21,9 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsLessThan extends AbstractSingleValueCondition { +public class IsLessThan extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { + private static final IsLessThan EMPTY = new IsLessThan(-1) { @Override public Object value() { @@ -58,15 +60,7 @@ public IsLessThan filter(Predicate predicate) { return filterSupport(predicate, IsLessThan::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsLessThan map(Function mapper) { return mapSupport(mapper, IsLessThan::new, IsLessThan::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java index b11d06c88..1b92e0c40 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java @@ -21,7 +21,8 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsLessThanOrEqualTo extends AbstractSingleValueCondition { +public class IsLessThanOrEqualTo extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsLessThanOrEqualTo EMPTY = new IsLessThanOrEqualTo(-1) { @Override public Object value() { @@ -58,15 +59,7 @@ public IsLessThanOrEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsLessThanOrEqualTo::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsLessThanOrEqualTo map(Function mapper) { return mapSupport(mapper, IsLessThanOrEqualTo::new, IsLessThanOrEqualTo::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java index 9d79bed81..e738bda4c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java @@ -21,7 +21,9 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsLike extends AbstractSingleValueCondition { +public class IsLike extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { + private static final IsLike EMPTY = new IsLike(-1) { @Override public Object value() { @@ -58,15 +60,7 @@ public IsLike filter(Predicate predicate) { return filterSupport(predicate, IsLike::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsLike map(Function mapper) { return mapSupport(mapper, IsLike::new, IsLike::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java index de8d72cd0..6b569b32d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java @@ -16,15 +16,16 @@ package org.mybatis.dynamic.sql.where.condition; import java.util.NoSuchElementException; +import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; -public class IsLikeCaseInsensitive extends AbstractSingleValueCondition - implements CaseInsensitiveRenderableCondition { - private static final IsLikeCaseInsensitive EMPTY = new IsLikeCaseInsensitive("") { //$NON-NLS-1$ +public class IsLikeCaseInsensitive extends AbstractSingleValueCondition + implements CaseInsensitiveRenderableCondition, AbstractSingleValueCondition.Filterable, + AbstractSingleValueCondition.Mappable { + private static final IsLikeCaseInsensitive EMPTY = new IsLikeCaseInsensitive<>("") { //$NON-NLS-1$ @Override public String value() { throw new NoSuchElementException("No value present"); //$NON-NLS-1$ @@ -36,11 +37,13 @@ public boolean isEmpty() { } }; - public static IsLikeCaseInsensitive empty() { - return EMPTY; + public static IsLikeCaseInsensitive empty() { + @SuppressWarnings("unchecked") + IsLikeCaseInsensitive t = (IsLikeCaseInsensitive) EMPTY; + return t; } - protected IsLikeCaseInsensitive(String value) { + protected IsLikeCaseInsensitive(T value) { super(value); } @@ -50,7 +53,7 @@ public String operator() { } @Override - public IsLikeCaseInsensitive filter(Predicate predicate) { + public IsLikeCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsLikeCaseInsensitive::empty, this); } @@ -58,16 +61,22 @@ public IsLikeCaseInsensitive filter(Predicate predicate) { * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a * condition that will not render (this). * + *

This function DOES NOT automatically transform values to uppercase, so it potentially creates a + * case-sensitive query. For String conditions you can use {@link StringUtilities#mapToUpperCase(Function)} + * to add an uppercase transform after your mapping function. + * * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition * @return a new condition with the result of applying the mapper to the value of this condition, * if renderable, otherwise a condition that will not render. */ - public IsLikeCaseInsensitive map(UnaryOperator mapper) { + @Override + public IsLikeCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsLikeCaseInsensitive::new, IsLikeCaseInsensitive::empty); } - public static IsLikeCaseInsensitive of(String value) { + public static IsLikeCaseInsensitive of(String value) { // Keep the null safe upper case utility for backwards compatibility in case someone passes in a null - return new IsLikeCaseInsensitive(StringUtilities.safelyUpperCase(value)); + return new IsLikeCaseInsensitive<>(StringUtilities.safelyUpperCase(value)); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java index 5700de2ac..6a515d4ac 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java @@ -23,7 +23,8 @@ import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractTwoValueCondition; -public class IsNotBetween extends AbstractTwoValueCondition { +public class IsNotBetween extends AbstractTwoValueCondition + implements AbstractTwoValueCondition.Filterable, AbstractTwoValueCondition.Mappable { private static final IsNotBetween EMPTY = new IsNotBetween(-1, -1) { @Override public Object value1() { @@ -71,30 +72,13 @@ public IsNotBetween filter(Predicate predicate) { return filterSupport(predicate, IsNotBetween::empty, this); } - /** - * If renderable, apply the mappings to the values and return a new condition with the new values. Else return a - * condition that will not render (this). - * - * @param mapper1 a mapping function to apply to the first value, if renderable - * @param mapper2 a mapping function to apply to the second value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mappers to the values of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsNotBetween map(Function mapper1, Function mapper2) { return mapSupport(mapper1, mapper2, IsNotBetween::new, IsNotBetween::empty); } - /** - * If renderable, apply the mapping to both values and return a new condition with the new values. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to both values, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mappers to the values of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsNotBetween map(Function mapper) { return map(mapper, mapper); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java index 821fd019f..39070c2e8 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java @@ -21,7 +21,8 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsNotEqualTo extends AbstractSingleValueCondition { +public class IsNotEqualTo extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsNotEqualTo EMPTY = new IsNotEqualTo(-1) { @Override public Object value() { @@ -58,15 +59,7 @@ public IsNotEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsNotEqualTo::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper a mapping function to apply to the value, if renderable - * @param type of the new condition - * @return a new condition with the result of applying the mapper to the value of this condition, - * if renderable, otherwise a condition that will not render. - */ + @Override public IsNotEqualTo map(Function mapper) { return mapSupport(mapper, IsNotEqualTo::new, IsNotEqualTo::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java index e1ca07abf..33f5d14c7 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java @@ -25,7 +25,8 @@ import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.Validator; -public class IsNotIn extends AbstractListValueCondition { +public class IsNotIn extends AbstractListValueCondition + implements AbstractListValueCondition.Filterable, AbstractListValueCondition.Mappable{ private static final IsNotIn EMPTY = new IsNotIn<>(Collections.emptyList()); public static IsNotIn empty() { @@ -54,17 +55,9 @@ public IsNotIn filter(Predicate predicate) { return filterSupport(predicate, IsNotIn::new, this, IsNotIn::empty); } - /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). - * - * @param mapper a mapping function to apply to the values, if not empty - * @param type of the new condition - * @return a new condition with mapped values if renderable, otherwise an empty condition - */ + @Override public IsNotIn map(Function mapper) { - Function, IsNotIn> constructor = IsNotIn::new; - return mapSupport(mapper, constructor, IsNotIn::empty); + return mapSupport(mapper, IsNotIn::new, IsNotIn::empty); } @SafeVarargs diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java index c956b4d55..8731d5b64 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java @@ -18,23 +18,26 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.StringUtilities; import org.mybatis.dynamic.sql.util.Validator; -public class IsNotInCaseInsensitive extends AbstractListValueCondition - implements CaseInsensitiveRenderableCondition { - private static final IsNotInCaseInsensitive EMPTY = new IsNotInCaseInsensitive(Collections.emptyList()); +public class IsNotInCaseInsensitive extends AbstractListValueCondition + implements CaseInsensitiveRenderableCondition, AbstractListValueCondition.Filterable, + AbstractListValueCondition.Mappable { + private static final IsNotInCaseInsensitive EMPTY = new IsNotInCaseInsensitive<>(Collections.emptyList()); - public static IsNotInCaseInsensitive empty() { - return EMPTY; + public static IsNotInCaseInsensitive empty() { + @SuppressWarnings("unchecked") + IsNotInCaseInsensitive t = (IsNotInCaseInsensitive) EMPTY; + return t; } - protected IsNotInCaseInsensitive(Collection values) { + protected IsNotInCaseInsensitive(Collection values) { super(values); } @@ -50,27 +53,34 @@ public String operator() { } @Override - public IsNotInCaseInsensitive filter(Predicate predicate) { + public IsNotInCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsNotInCaseInsensitive::new, this, IsNotInCaseInsensitive::empty); } /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). + * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a + * condition that will not render (this). * - * @param mapper a mapping function to apply to the values, if not empty - * @return a new condition with mapped values if renderable, otherwise an empty condition + *

This function DOES NOT automatically transform values to uppercase, so it potentially creates a + * case-sensitive query. For String conditions you can use {@link StringUtilities#mapToUpperCase(Function)} + * to add an uppercase transform after your mapping function. + * + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. */ - public IsNotInCaseInsensitive map(UnaryOperator mapper) { + @Override + public IsNotInCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsNotInCaseInsensitive::new, IsNotInCaseInsensitive::empty); } - public static IsNotInCaseInsensitive of(String... values) { + public static IsNotInCaseInsensitive of(String... values) { return of(Arrays.asList(values)); } - public static IsNotInCaseInsensitive of(Collection values) { + public static IsNotInCaseInsensitive of(Collection values) { // Keep the null safe upper case utility for backwards compatibility in case someone passes in a null - return new IsNotInCaseInsensitive(values.stream().map(StringUtilities::safelyUpperCase).toList()); + return new IsNotInCaseInsensitive<>(values.stream().map(StringUtilities::safelyUpperCase).toList()); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java index 9202a2ad1..438e1c5ed 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java @@ -18,23 +18,27 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.function.Function; import java.util.Objects; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; +import org.mybatis.dynamic.sql.util.StringUtilities; -public class IsNotInCaseInsensitiveWhenPresent extends AbstractListValueCondition - implements CaseInsensitiveRenderableCondition { - private static final IsNotInCaseInsensitiveWhenPresent EMPTY = - new IsNotInCaseInsensitiveWhenPresent(Collections.emptyList()); +public class IsNotInCaseInsensitiveWhenPresent extends AbstractListValueCondition + implements CaseInsensitiveRenderableCondition, AbstractListValueCondition.Filterable, + AbstractListValueCondition.Mappable { + private static final IsNotInCaseInsensitiveWhenPresent EMPTY = + new IsNotInCaseInsensitiveWhenPresent<>(Collections.emptyList()); - public static IsNotInCaseInsensitiveWhenPresent empty() { - return EMPTY; + public static IsNotInCaseInsensitiveWhenPresent empty() { + @SuppressWarnings("unchecked") + IsNotInCaseInsensitiveWhenPresent t = (IsNotInCaseInsensitiveWhenPresent) EMPTY; + return t; } - protected IsNotInCaseInsensitiveWhenPresent(Collection values) { + protected IsNotInCaseInsensitiveWhenPresent(Collection values) { super(values); } @@ -44,28 +48,35 @@ public String operator() { } @Override - public IsNotInCaseInsensitiveWhenPresent filter(Predicate predicate) { + public IsNotInCaseInsensitiveWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotInCaseInsensitiveWhenPresent::new, this, IsNotInCaseInsensitiveWhenPresent::empty); } /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). + * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a + * condition that will not render (this). * - * @param mapper a mapping function to apply to the values, if not empty - * @return a new condition with mapped values if renderable, otherwise an empty condition + *

This function DOES NOT automatically transform values to uppercase, so it potentially creates a + * case-sensitive query. For String conditions you can use {@link StringUtilities#mapToUpperCase(Function)} + * to add an uppercase transform after your mapping function. + * + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. */ - public IsNotInCaseInsensitiveWhenPresent map(UnaryOperator mapper) { + @Override + public IsNotInCaseInsensitiveWhenPresent map(Function mapper) { return mapSupport(mapper, IsNotInCaseInsensitiveWhenPresent::new, IsNotInCaseInsensitiveWhenPresent::empty); } - public static IsNotInCaseInsensitiveWhenPresent of(@Nullable String... values) { + public static IsNotInCaseInsensitiveWhenPresent of(@Nullable String... values) { return of(Arrays.asList(values)); } - public static IsNotInCaseInsensitiveWhenPresent of(Collection<@Nullable String> values) { - return new IsNotInCaseInsensitiveWhenPresent( + public static IsNotInCaseInsensitiveWhenPresent of(Collection<@Nullable String> values) { + return new IsNotInCaseInsensitiveWhenPresent<>( values.stream().filter(Objects::nonNull).map(String::toUpperCase).toList()); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java index 1c4b570a9..ddc8e5470 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java @@ -24,7 +24,8 @@ import org.mybatis.dynamic.sql.AbstractListValueCondition; -public class IsNotInWhenPresent extends AbstractListValueCondition { +public class IsNotInWhenPresent extends AbstractListValueCondition + implements AbstractListValueCondition.Filterable, AbstractListValueCondition.Mappable{ private static final IsNotInWhenPresent EMPTY = new IsNotInWhenPresent<>(Collections.emptyList()); public static IsNotInWhenPresent empty() { @@ -47,17 +48,9 @@ public IsNotInWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotInWhenPresent::new, this, IsNotInWhenPresent::empty); } - /** - * If not empty, apply the mapping to each value in the list return a new condition with the mapped values. - * Else return an empty condition (this). - * - * @param mapper a mapping function to apply to the values, if not empty - * @param type of the new condition - * @return a new condition with mapped values if renderable, otherwise an empty condition - */ + @Override public IsNotInWhenPresent map(Function mapper) { - Function, IsNotInWhenPresent> constructor = IsNotInWhenPresent::new; - return mapSupport(mapper, constructor, IsNotInWhenPresent::empty); + return mapSupport(mapper, IsNotInWhenPresent::new, IsNotInWhenPresent::empty); } @SafeVarargs diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java index 7379a316b..a62dc3e9e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java @@ -21,7 +21,8 @@ import org.mybatis.dynamic.sql.AbstractSingleValueCondition; -public class IsNotLike extends AbstractSingleValueCondition { +public class IsNotLike extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsNotLike EMPTY = new IsNotLike(-1) { @Override public Object value() { @@ -58,18 +59,7 @@ public IsNotLike filter(Predicate predicate) { return filterSupport(predicate, IsNotLike::empty, this); } - /** - * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a - * condition that will not render (this). - * - * @param mapper - * a mapping function to apply to the value, if renderable - * @param - * type of the new condition - * - * @return a new condition with the result of applying the mapper to the value of this condition, if renderable, - * otherwise a condition that will not render. - */ + @Override public IsNotLike map(Function mapper) { return mapSupport(mapper, IsNotLike::new, IsNotLike::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java index c26168e42..8eb65d772 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java @@ -16,15 +16,16 @@ package org.mybatis.dynamic.sql.where.condition; import java.util.NoSuchElementException; +import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.UnaryOperator; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; -public class IsNotLikeCaseInsensitive extends AbstractSingleValueCondition - implements CaseInsensitiveRenderableCondition { - private static final IsNotLikeCaseInsensitive EMPTY = new IsNotLikeCaseInsensitive("") { //$NON-NLS-1$ +public class IsNotLikeCaseInsensitive extends AbstractSingleValueCondition + implements CaseInsensitiveRenderableCondition, AbstractSingleValueCondition.Filterable, + AbstractSingleValueCondition.Mappable { + private static final IsNotLikeCaseInsensitive EMPTY = new IsNotLikeCaseInsensitive<>("") { //$NON-NLS-1$ @Override public String value() { throw new NoSuchElementException("No value present"); //$NON-NLS-1$ @@ -36,11 +37,13 @@ public boolean isEmpty() { } }; - public static IsNotLikeCaseInsensitive empty() { - return EMPTY; + public static IsNotLikeCaseInsensitive empty() { + @SuppressWarnings("unchecked") + IsNotLikeCaseInsensitive t = (IsNotLikeCaseInsensitive) EMPTY; + return t; } - protected IsNotLikeCaseInsensitive(String value) { + protected IsNotLikeCaseInsensitive(T value) { super(value); } @@ -50,7 +53,7 @@ public String operator() { } @Override - public IsNotLikeCaseInsensitive filter(Predicate predicate) { + public IsNotLikeCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsNotLikeCaseInsensitive::empty, this); } @@ -58,18 +61,22 @@ public IsNotLikeCaseInsensitive filter(Predicate predicate) { * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a * condition that will not render (this). * - * @param mapper - * a mapping function to apply to the value, if renderable + *

This function DOES NOT automatically transform values to uppercase, so it potentially creates a + * case-sensitive query. For String conditions you can use {@link StringUtilities#mapToUpperCase(Function)} + * to add an uppercase transform after your mapping function. * - * @return a new condition with the result of applying the mapper to the value of this condition, if renderable, - * otherwise a condition that will not render. + * @param mapper a mapping function to apply to the value, if renderable + * @param type of the new condition + * @return a new condition with the result of applying the mapper to the value of this condition, + * if renderable, otherwise a condition that will not render. */ - public IsNotLikeCaseInsensitive map(UnaryOperator mapper) { + @Override + public IsNotLikeCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsNotLikeCaseInsensitive::new, IsNotLikeCaseInsensitive::empty); } - public static IsNotLikeCaseInsensitive of(String value) { + public static IsNotLikeCaseInsensitive of(String value) { // Keep the null safe upper case utility for backwards compatibility in case someone passes in a null - return new IsNotLikeCaseInsensitive(StringUtilities.safelyUpperCase(value)); + return new IsNotLikeCaseInsensitive<>(StringUtilities.safelyUpperCase(value)); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotNull.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotNull.java index 03d558387..1c1f3139d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotNull.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotNull.java @@ -19,7 +19,7 @@ import org.mybatis.dynamic.sql.AbstractNoValueCondition; -public class IsNotNull extends AbstractNoValueCondition { +public class IsNotNull extends AbstractNoValueCondition implements AbstractNoValueCondition.Filterable { private static final IsNotNull EMPTY = new IsNotNull<>() { @Override public boolean isEmpty() { @@ -42,17 +42,7 @@ public String operator() { return "is not null"; //$NON-NLS-1$ } - /** - * If renderable and the supplier returns true, returns this condition. Else returns a condition that will not - * render. - * - * @param booleanSupplier - * function that specifies whether the condition should render - * @param - * condition type - not used except for compilation compliance - * - * @return this condition if renderable and the supplier returns true, otherwise a condition that will not render. - */ + @Override public IsNotNull filter(BooleanSupplier booleanSupplier) { @SuppressWarnings("unchecked") IsNotNull self = (IsNotNull) this; diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNull.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNull.java index 36c68aa34..a27b7dc2a 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNull.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNull.java @@ -19,7 +19,7 @@ import org.mybatis.dynamic.sql.AbstractNoValueCondition; -public class IsNull extends AbstractNoValueCondition { +public class IsNull extends AbstractNoValueCondition implements AbstractNoValueCondition.Filterable { private static final IsNull EMPTY = new IsNull<>() { @Override public boolean isEmpty() { @@ -42,17 +42,7 @@ public String operator() { return "is null"; //$NON-NLS-1$ } - /** - * If renderable and the supplier returns true, returns this condition. Else returns a condition that will not - * render. - * - * @param booleanSupplier - * function that specifies whether the condition should render - * @param - * condition type - not used except for compilation compliance - * - * @return this condition if renderable and the supplier returns true, otherwise a condition that will not render. - */ + @Override public IsNull filter(BooleanSupplier booleanSupplier) { @SuppressWarnings("unchecked") IsNull self = (IsNull) this; diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt index 536032605..530c962ed 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt @@ -324,50 +324,53 @@ fun isTrue(): IsEqualTo = isEqualTo(true) fun isFalse(): IsEqualTo = isEqualTo(false) // conditions for strings only -fun isLikeCaseInsensitive(value: String): IsLikeCaseInsensitive = SqlBuilder.isLikeCaseInsensitive(value) +fun isLikeCaseInsensitive(value: String): IsLikeCaseInsensitive = SqlBuilder.isLikeCaseInsensitive(value) -fun isLikeCaseInsensitiveWhenPresent(value: String?): IsLikeCaseInsensitive = +fun isLikeCaseInsensitiveWhenPresent(value: String?): IsLikeCaseInsensitive = SqlBuilder.isLikeCaseInsensitiveWhenPresent(value) -fun isNotLikeCaseInsensitive(value: String): IsNotLikeCaseInsensitive = SqlBuilder.isNotLikeCaseInsensitive(value) +fun isNotLikeCaseInsensitive(value: String): IsNotLikeCaseInsensitive = SqlBuilder.isNotLikeCaseInsensitive(value) -fun isNotLikeCaseInsensitiveWhenPresent(value: String?): IsNotLikeCaseInsensitive = +fun isNotLikeCaseInsensitiveWhenPresent(value: String?): IsNotLikeCaseInsensitive = SqlBuilder.isNotLikeCaseInsensitiveWhenPresent(value) -fun isInCaseInsensitive(vararg values: String): IsInCaseInsensitive = isInCaseInsensitive(values.asList()) +fun isInCaseInsensitive(vararg values: String): IsInCaseInsensitive = isInCaseInsensitive(values.asList()) @JvmName("isInArrayCaseInsensitive") -fun isInCaseInsensitive(values: Array): IsInCaseInsensitive = SqlBuilder.isInCaseInsensitive(values.asList()) +fun isInCaseInsensitive(values: Array): IsInCaseInsensitive = + SqlBuilder.isInCaseInsensitive(values.asList()) -fun isInCaseInsensitive(values: Collection): IsInCaseInsensitive = SqlBuilder.isInCaseInsensitive(values) +fun isInCaseInsensitive(values: Collection): IsInCaseInsensitive = + SqlBuilder.isInCaseInsensitive(values) -fun isInCaseInsensitiveWhenPresent(vararg values: String?): IsInCaseInsensitiveWhenPresent = +fun isInCaseInsensitiveWhenPresent(vararg values: String?): IsInCaseInsensitiveWhenPresent = isInCaseInsensitiveWhenPresent(values.asList()) @JvmName("isInArrayCaseInsensitiveWhenPresent") -fun isInCaseInsensitiveWhenPresent(values: Array?): IsInCaseInsensitiveWhenPresent = +fun isInCaseInsensitiveWhenPresent(values: Array?): IsInCaseInsensitiveWhenPresent = SqlBuilder.isInCaseInsensitiveWhenPresent(values?.asList()) -fun isInCaseInsensitiveWhenPresent(values: Collection?): IsInCaseInsensitiveWhenPresent = +fun isInCaseInsensitiveWhenPresent(values: Collection?): IsInCaseInsensitiveWhenPresent = SqlBuilder.isInCaseInsensitiveWhenPresent(values) -fun isNotInCaseInsensitive(vararg values: String): IsNotInCaseInsensitive = isNotInCaseInsensitive(values.asList()) +fun isNotInCaseInsensitive(vararg values: String): IsNotInCaseInsensitive = + isNotInCaseInsensitive(values.asList()) @JvmName("isNotInArrayCaseInsensitive") -fun isNotInCaseInsensitive(values: Array): IsNotInCaseInsensitive = +fun isNotInCaseInsensitive(values: Array): IsNotInCaseInsensitive = SqlBuilder.isNotInCaseInsensitive(values.asList()) -fun isNotInCaseInsensitive(values: Collection): IsNotInCaseInsensitive = +fun isNotInCaseInsensitive(values: Collection): IsNotInCaseInsensitive = SqlBuilder.isNotInCaseInsensitive(values) -fun isNotInCaseInsensitiveWhenPresent(vararg values: String?): IsNotInCaseInsensitiveWhenPresent = +fun isNotInCaseInsensitiveWhenPresent(vararg values: String?): IsNotInCaseInsensitiveWhenPresent = isNotInCaseInsensitiveWhenPresent(values.asList()) @JvmName("isNotInArrayCaseInsensitiveWhenPresent") -fun isNotInCaseInsensitiveWhenPresent(values: Array?): IsNotInCaseInsensitiveWhenPresent = +fun isNotInCaseInsensitiveWhenPresent(values: Array?): IsNotInCaseInsensitiveWhenPresent = SqlBuilder.isNotInCaseInsensitiveWhenPresent(values?.asList()) -fun isNotInCaseInsensitiveWhenPresent(values: Collection?): IsNotInCaseInsensitiveWhenPresent = +fun isNotInCaseInsensitiveWhenPresent(values: Collection?): IsNotInCaseInsensitiveWhenPresent = SqlBuilder.isNotInCaseInsensitiveWhenPresent(values) // order by support diff --git a/src/site/markdown/docs/extending.md b/src/site/markdown/docs/extending.md index eabee1e03..a1fee9adb 100644 --- a/src/site/markdown/docs/extending.md +++ b/src/site/markdown/docs/extending.md @@ -304,7 +304,8 @@ Here's an example of implementing a LIKE condition that supports ESCAPE: ```java @NullMarked -public class IsLikeEscape extends AbstractSingleValueCondition { +public class IsLikeEscape extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsLikeEscape EMPTY = new IsLikeEscape(-1, null) { @Override public Object value() { @@ -354,6 +355,7 @@ public class IsLikeEscape extends AbstractSingleValueCondition { return filterSupport(predicate, IsLikeEscape::empty, this); } + @Override public IsLikeEscape map(Function mapper) { return mapSupport(mapper, v -> new IsLikeEscape<>(v, escapeCharacter), IsLikeEscape::empty); } @@ -374,4 +376,5 @@ Important notes: 2. The class constructor accepts an escape character that will be rendered into an ESCAPE phrase 3. The class overrides `renderCondition` and changes the library generated `FragmentAndParameters` to add the ESCAPE phrase. **This is the key to what's needed to implement a custom condition.** -4. The class provides `map` and `filter` functions as is expected for any condition in the library +4. The class implements `Filterable` and `Mappable` to provide `filter` and `map` functions as is expected for most + conditions in the library diff --git a/src/test/java/examples/mysql/IsLikeEscape.java b/src/test/java/examples/mysql/IsLikeEscape.java index 19e1a085c..6b8799abb 100644 --- a/src/test/java/examples/mysql/IsLikeEscape.java +++ b/src/test/java/examples/mysql/IsLikeEscape.java @@ -27,7 +27,8 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters; @NullMarked -public class IsLikeEscape extends AbstractSingleValueCondition { +public class IsLikeEscape extends AbstractSingleValueCondition + implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable { private static final IsLikeEscape EMPTY = new IsLikeEscape(-1, null) { @Override public Object value() { @@ -77,6 +78,7 @@ public IsLikeEscape filter(Predicate predicate) { return filterSupport(predicate, IsLikeEscape::empty, this); } + @Override public IsLikeEscape map(Function mapper) { return mapSupport(mapper, v -> new IsLikeEscape<>(v, escapeCharacter), IsLikeEscape::empty); } diff --git a/src/test/java/org/mybatis/dynamic/sql/where/condition/FilterAndMapTest.java b/src/test/java/org/mybatis/dynamic/sql/where/condition/FilterAndMapTest.java index bd5f2e2cd..7d4e4505e 100644 --- a/src/test/java/org/mybatis/dynamic/sql/where/condition/FilterAndMapTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/where/condition/FilterAndMapTest.java @@ -24,18 +24,19 @@ import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.SqlBuilder; +import org.mybatis.dynamic.sql.util.StringUtilities; class FilterAndMapTest { @Test void testTypeConversion() { - IsEqualTo cond = SqlBuilder.isEqualTo("1").map(Integer::parseInt); + var cond = SqlBuilder.isEqualTo("1").map(Integer::parseInt); assertThat(cond.isEmpty()).isFalse(); assertThat(cond.value()).isEqualTo(1); } @Test void testTypeConversionWithNullThrowsException() { - IsEqualTo cond = SqlBuilder.isEqualTo((String) null); + var cond = SqlBuilder.isEqualTo((String) null); assertThatExceptionOfType(NumberFormatException.class).isThrownBy(() -> cond.map(Integer::parseInt) ); @@ -43,7 +44,7 @@ void testTypeConversionWithNullThrowsException() { @Test void testTypeConversionWithNullAndFilterDoesNotThrowException() { - IsEqualTo cond = SqlBuilder.isEqualTo((String) null).filter(Objects::nonNull).map(Integer::parseInt); + var cond = SqlBuilder.isEqualTo((String) null).filter(Objects::nonNull).map(Integer::parseInt); assertThat(cond.isEmpty()).isTrue(); } @@ -328,8 +329,8 @@ void testIsLikeMapUnRenderableShouldNotThrowNullPointerException() { @Test void testIsLikeCaseInsensitiveRenderableTruePredicateShouldReturnSameObject() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive("Fred"); - IsLikeCaseInsensitive filtered = cond.filter(s -> true); + var cond = SqlBuilder.isLikeCaseInsensitive("Fred"); + var filtered = cond.filter(s -> true); assertThat(filtered.value()).isEqualTo("FRED"); assertThat(filtered.isEmpty()).isFalse(); assertThat(cond).isSameAs(filtered); @@ -337,24 +338,24 @@ void testIsLikeCaseInsensitiveRenderableTruePredicateShouldReturnSameObject() { @Test void testIsLikeCaseInsensitiveRenderableFalsePredicate() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive("Fred"); - IsLikeCaseInsensitive filtered = cond.filter(s -> false); + var cond = SqlBuilder.isLikeCaseInsensitive("Fred"); + var filtered = cond.filter(s -> false); assertThat(cond.isEmpty()).isFalse(); assertThat(filtered.isEmpty()).isTrue(); } @Test void testIsLikeCaseInsensitiveFilterUnRenderableShouldReturnSameObject() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive("Fred").filter(s -> false); - IsLikeCaseInsensitive filtered = cond.filter(s -> true); + var cond = SqlBuilder.isLikeCaseInsensitive("Fred").filter(s -> false); + var filtered = cond.filter(s -> true); assertThat(filtered.isEmpty()).isTrue(); assertThat(cond).isSameAs(filtered); } @Test void testIsLikeCaseInsensitiveMapUnRenderableShouldNotThrowNullPointerException() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive("Fred").filter(s -> false); - IsLikeCaseInsensitive mapped = cond.map(String::toUpperCase); + IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive("Fred").filter(s -> false); + IsLikeCaseInsensitive mapped = cond.map(String::toUpperCase); assertThat(cond.isEmpty()).isTrue(); assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(cond::value); assertThat(cond).isSameAs(mapped); @@ -395,8 +396,8 @@ void testIsNotLikeMapUnRenderableShouldNotThrowNullPointerException() { @Test void testIsNotLikeCaseInsensitiveRenderableTruePredicateShouldReturnSameObject() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive("Fred"); - IsNotLikeCaseInsensitive filtered = cond.filter(s -> true); + var cond = SqlBuilder.isNotLikeCaseInsensitive("Fred"); + var filtered = cond.filter(s -> true); assertThat(filtered.value()).isEqualTo("FRED"); assertThat(filtered.isEmpty()).isFalse(); assertThat(cond).isSameAs(filtered); @@ -404,24 +405,24 @@ void testIsNotLikeCaseInsensitiveRenderableTruePredicateShouldReturnSameObject() @Test void testIsNotLikeCaseInsensitiveRenderableFalsePredicate() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive("Fred"); - IsNotLikeCaseInsensitive filtered = cond.filter(s -> false); + var cond = SqlBuilder.isNotLikeCaseInsensitive("Fred"); + var filtered = cond.filter(s -> false); assertThat(cond.isEmpty()).isFalse(); assertThat(filtered.isEmpty()).isTrue(); } @Test void testIsNotLikeCaseInsensitiveFilterUnRenderableShouldReturnSameObject() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive("Fred").filter(s -> false); - IsNotLikeCaseInsensitive filtered = cond.filter(s -> true); + var cond = SqlBuilder.isNotLikeCaseInsensitive("Fred").filter(s -> false); + var filtered = cond.filter(s -> true); assertThat(filtered.isEmpty()).isTrue(); assertThat(cond).isSameAs(filtered); } @Test void testIsNotLikeCaseInsensitiveMapUnRenderableShouldNotThrowNullPointerException() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive("Fred").filter(s -> false); - IsNotLikeCaseInsensitive mapped = cond.map(String::toUpperCase); + var cond = SqlBuilder.isNotLikeCaseInsensitive("Fred").filter(s -> false); + var mapped = cond.map(String::toUpperCase); assertThat(cond.isEmpty()).isTrue(); assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(cond::value); assertThat(cond).isSameAs(mapped); @@ -447,25 +448,25 @@ void testIsNotInRenderableMapShouldReturnMappedObject() { @Test void testIsNotInCaseInsensitiveRenderableMapShouldReturnMappedObject() { - IsNotInCaseInsensitive cond = SqlBuilder.isNotInCaseInsensitive("Fred ", "Wilma "); - List values = cond.values().toList(); + var cond = SqlBuilder.isNotInCaseInsensitive("Fred ", "Wilma "); + var values = cond.values().toList(); assertThat(values).containsExactly("FRED ", "WILMA "); assertThat(cond.isEmpty()).isFalse(); - IsNotInCaseInsensitive mapped = cond.map(String::trim); - List mappedValues = mapped.values().toList(); + var mapped = cond.map(String::trim); + var mappedValues = mapped.values().toList(); assertThat(mappedValues).containsExactly("FRED", "WILMA"); } @Test void testIsInCaseInsensitiveRenderableMapShouldReturnMappedObject() { - IsInCaseInsensitive cond = SqlBuilder.isInCaseInsensitive("Fred ", "Wilma "); - List values = cond.values().toList(); + var cond = SqlBuilder.isInCaseInsensitive("Fred ", "Wilma "); + var values = cond.values().toList(); assertThat(values).containsExactly("FRED ", "WILMA "); assertThat(cond.isEmpty()).isFalse(); - IsInCaseInsensitive mapped = cond.map(String::trim); - List mappedValues = mapped.values().toList(); + var mapped = cond.map(String::trim); + var mappedValues = mapped.values().toList(); assertThat(mappedValues).containsExactly("FRED", "WILMA"); } @@ -533,4 +534,40 @@ void testNotBetweenMapWithSingleMapper() { assertThat(cond.value2()).isEqualTo(4); } + @Test + void testMappingAnEmptyListCondition() { + var cond = SqlBuilder.isNotIn("Fred", "Wilma"); + var filtered = cond.filter(s -> false); + var mapped = filtered.map(s -> s); + assertThat(mapped.isEmpty()).isTrue(); + assertThat(filtered).isSameAs(mapped); + } + + @Test + void testIsInCaseInsensitiveWhenPresentMap() { + var cond = SqlBuilder.isInCaseInsensitiveWhenPresent("Fred", "Wilma"); + var mapped = cond.map(s -> s + " Flintstone"); + assertThat(mapped.values().toList()).containsExactly("FRED Flintstone", "WILMA Flintstone"); + } + + @Test + void testIsInCaseInsensitiveWhenPresentMapCaseInsensitive() { + var cond = SqlBuilder.isInCaseInsensitiveWhenPresent("Fred", "Wilma"); + var mapped = cond.map(StringUtilities.mapToUpperCase(s -> s + " Flintstone")); + assertThat(mapped.values().toList()).containsExactly("FRED FLINTSTONE", "WILMA FLINTSTONE"); + } + + @Test + void testIsNotInCaseInsensitiveWhenPresentMap() { + var cond = SqlBuilder.isNotInCaseInsensitiveWhenPresent("Fred", "Wilma"); + var mapped = cond.map(s -> s + " Flintstone"); + assertThat(mapped.values().toList()).containsExactly("FRED Flintstone", "WILMA Flintstone"); + } + + @Test + void testIsNotInCaseInsensitiveWhenPresentMapCaseInsensitive() { + var cond = SqlBuilder.isNotInCaseInsensitiveWhenPresent("Fred", "Wilma"); + var mapped = cond.map(StringUtilities.mapToUpperCase(s -> s + " Flintstone")); + assertThat(mapped.values().toList()).containsExactly("FRED FLINTSTONE", "WILMA FLINTSTONE"); + } } diff --git a/src/test/java/org/mybatis/dynamic/sql/where/condition/SupplierTest.java b/src/test/java/org/mybatis/dynamic/sql/where/condition/SupplierTest.java index e1362d48f..b85a7f412 100644 --- a/src/test/java/org/mybatis/dynamic/sql/where/condition/SupplierTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/where/condition/SupplierTest.java @@ -260,28 +260,28 @@ void testIsLikeNull() { @Test void testIsLikeCaseInsensitive() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive(() -> "%f%"); + IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive(() -> "%f%"); assertThat(cond.value()).isEqualTo("%F%"); assertThat(cond.isEmpty()).isFalse(); } @Test void testIsLikeCaseInsensitiveNull() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive(() -> null); + IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitive(() -> null); assertThat(cond.value()).isNull(); assertThat(cond.isEmpty()).isFalse(); } @Test void testIsLikeCaseInsensitiveWhenPresent() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitiveWhenPresent(() -> "%f%"); + IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitiveWhenPresent(() -> "%f%"); assertThat(cond.value()).isEqualTo("%F%"); assertThat(cond.isEmpty()).isFalse(); } @Test void testIsLikeCaseInsensitiveWhenPresentNull() { - IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitiveWhenPresent(() -> null); + IsLikeCaseInsensitive cond = SqlBuilder.isLikeCaseInsensitiveWhenPresent(() -> null); assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(cond::value); assertThat(cond.isEmpty()).isTrue(); } @@ -330,28 +330,28 @@ void testIsNotLikeWhenPresentNull() { @Test void testIsNotLikeCaseInsensitive() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive(() -> "%f%"); + IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive(() -> "%f%"); assertThat(cond.value()).isEqualTo("%F%"); assertThat(cond.isEmpty()).isFalse(); } @Test void testIsNotLikeCaseInsensitiveNull() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive(() -> null); + IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitive(() -> null); assertThat(cond.value()).isNull(); assertThat(cond.isEmpty()).isFalse(); } @Test void testIsNotLikeCaseInsensitiveWhenPresent() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitiveWhenPresent(() -> "%f%"); + IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitiveWhenPresent(() -> "%f%"); assertThat(cond.value()).isEqualTo("%F%"); assertThat(cond.isEmpty()).isFalse(); } @Test void testIsNotLikeCaseInsensitiveWhenPresentNull() { - IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitiveWhenPresent(() -> null); + IsNotLikeCaseInsensitive cond = SqlBuilder.isNotLikeCaseInsensitiveWhenPresent(() -> null); assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(cond::value); assertThat(cond.isEmpty()).isTrue(); } diff --git a/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt b/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt index e018a8329..b47f09110 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt @@ -10,7 +10,8 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters sealed class KIsLikeEscape( value: T, private val escapeCharacter: Char? = null -) : AbstractSingleValueCondition(value) { +) : AbstractSingleValueCondition(value), AbstractSingleValueCondition.Filterable, + AbstractSingleValueCondition.Mappable { override fun operator(): String = "like" @@ -24,7 +25,7 @@ sealed class KIsLikeEscape( override fun filter(predicate: Predicate): KIsLikeEscape = filterSupport(predicate, EmptyIsLikeEscape::empty, this) - fun map(mapper : Function): KIsLikeEscape = + override fun map(mapper : Function): KIsLikeEscape = mapSupport(mapper, { r -> ConcreteIsLikeEscape(r, escapeCharacter) }, EmptyIsLikeEscape::empty) companion object {