1 /*
2 * Copyright 2016-2025 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.mybatis.dynamic.sql;
17
18 import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore;
19
20 import java.util.function.BiFunction;
21 import java.util.function.BiPredicate;
22 import java.util.function.Function;
23 import java.util.function.Predicate;
24 import java.util.function.Supplier;
25
26 import org.jspecify.annotations.NonNull;
27 import org.mybatis.dynamic.sql.render.RenderedParameterInfo;
28 import org.mybatis.dynamic.sql.render.RenderingContext;
29 import org.mybatis.dynamic.sql.util.FragmentAndParameters;
30
31 public abstract class AbstractTwoValueCondition<T> implements RenderableCondition<T> {
32 protected final T value1;
33 protected final T value2;
34
35 protected AbstractTwoValueCondition(T value1, T value2) {
36 this.value1 = value1;
37 this.value2 = value2;
38 }
39
40 public T value1() {
41 return value1;
42 }
43
44 public T value2() {
45 return value2;
46 }
47
48 protected <S extends AbstractTwoValueCondition<T>> S filterSupport(BiPredicate<? super T, ? super T> predicate,
49 Supplier<S> emptySupplier, S self) {
50 if (isEmpty()) {
51 return self;
52 } else {
53 return predicate.test(value1, value2) ? self : emptySupplier.get();
54 }
55 }
56
57 protected <S extends AbstractTwoValueCondition<T>> S filterSupport(Predicate<? super T> predicate,
58 Supplier<S> emptySupplier, S self) {
59 return filterSupport((v1, v2) -> predicate.test(v1) && predicate.test(v2), emptySupplier, self);
60 }
61
62 protected <R, S extends AbstractTwoValueCondition<R>> S mapSupport(Function<? super T, ? extends R> mapper1,
63 Function<? super T, ? extends R> mapper2, BiFunction<R, R, S> constructor, Supplier<S> emptySupplier) {
64 if (isEmpty()) {
65 return emptySupplier.get();
66 } else {
67 return constructor.apply(mapper1.apply(value1), mapper2.apply(value2));
68 }
69 }
70
71 public abstract String operator1();
72
73 public abstract String operator2();
74
75 @Override
76 public FragmentAndParameters renderCondition(RenderingContext renderingContext, BindableColumn<T> leftColumn) {
77 RenderedParameterInfo parameterInfo1 = renderingContext.calculateParameterInfo(leftColumn);
78 RenderedParameterInfo parameterInfo2 = renderingContext.calculateParameterInfo(leftColumn);
79
80 String finalFragment = operator1()
81 + spaceBefore(parameterInfo1.renderedPlaceHolder())
82 + spaceBefore(operator2())
83 + spaceBefore(parameterInfo2.renderedPlaceHolder());
84
85 return FragmentAndParameters.withFragment(finalFragment)
86 .withParameter(parameterInfo1.parameterMapKey(), leftColumn.convertParameterType(value1()))
87 .withParameter(parameterInfo2.parameterMapKey(), leftColumn.convertParameterType(value2()))
88 .build();
89 }
90
91 /**
92 * Conditions may implement Filterable to add optionality to rendering.
93 *
94 * <p>If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision
95 * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the
96 * rendered SQL.
97 *
98 * <p>Implementations of Filterable may call
99 * {@link AbstractTwoValueCondition#filterSupport(Predicate, Supplier, AbstractTwoValueCondition)}
100 * or {@link AbstractTwoValueCondition#filterSupport(BiPredicate, Supplier, AbstractTwoValueCondition)} as
101 * a common implementation of the filtering algorithm.
102 *
103 * @param <T> the Java type related to the database column type
104 */
105 public interface Filterable<T> {
106 /**
107 * If renderable and the values match the predicate, returns this condition. Else returns a condition
108 * that will not render.
109 *
110 * @param predicate predicate applied to the values, if renderable
111 * @return this condition if renderable and the values match the predicate, otherwise a condition
112 * that will not render.
113 */
114 AbstractTwoValueCondition<T> filter(BiPredicate<? super @NonNull T, ? super @NonNull T> predicate);
115
116 /**
117 * If renderable and both values match the predicate, returns this condition. Else returns a condition
118 * that will not render. This function implements a short-circuiting test. If the
119 * first value does not match the predicate, then the second value will not be tested.
120 *
121 * @param predicate predicate applied to both values, if renderable
122 * @return this condition if renderable and the values match the predicate, otherwise a condition
123 * that will not render.
124 */
125 AbstractTwoValueCondition<T> filter(Predicate<? super @NonNull T> predicate);
126 }
127
128 /**
129 * Conditions may implement Mappable to alter condition values or types during rendering.
130 *
131 * <p>If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the
132 * values of a condition, or change that datatype.
133 *
134 * <p>Implementations of Mappable may call
135 * {@link AbstractTwoValueCondition#mapSupport(Function, Function, BiFunction, Supplier)} as
136 * a common implementation of the mapping algorithm.
137 *
138 * @param <T> the Java type related to the database column type
139 */
140 public interface Mappable<T> {
141 /**
142 * If renderable, apply the mappings to the values and return a new condition with the new values. Else return a
143 * condition that will not render (this).
144 *
145 * @param mapper1 a mapping function to apply to the first value, if renderable
146 * @param mapper2 a mapping function to apply to the second value, if renderable
147 * @param <R> type of the new condition
148 * @return a new condition with the result of applying the mappers to the values of this condition,
149 * if renderable, otherwise a condition that will not render.
150 */
151 <R> AbstractTwoValueCondition<R> map(Function<? super @NonNull T, ? extends R> mapper1,
152 Function<? super @NonNull T, ? extends R> mapper2);
153
154 /**
155 * If renderable, apply the mapping to both values and return a new condition with the new values. Else return a
156 * condition that will not render (this).
157 *
158 * @param mapper a mapping function to apply to both values, if renderable
159 * @param <R> type of the new condition
160 * @return a new condition with the result of applying the mappers to the values of this condition,
161 * if renderable, otherwise a condition that will not render.
162 */
163 <R> AbstractTwoValueCondition<R> map(Function<? super @NonNull T, ? extends R> mapper);
164 }
165 }