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.mybatis.dynamic.sql.render.RenderedParameterInfo;
27 import org.mybatis.dynamic.sql.render.RenderingContext;
28 import org.mybatis.dynamic.sql.util.FragmentAndParameters;
29
30 public abstract class AbstractTwoValueCondition<T> implements RenderableCondition<T> {
31 protected final T value1;
32 protected final T value2;
33
34 protected AbstractTwoValueCondition(T value1, T value2) {
35 this.value1 = value1;
36 this.value2 = value2;
37 }
38
39 public T value1() {
40 return value1;
41 }
42
43 public T value2() {
44 return value2;
45 }
46
47 protected <S extends AbstractTwoValueCondition<T>> S filterSupport(BiPredicate<? super T, ? super T> predicate,
48 Supplier<S> emptySupplier, S self) {
49 if (isEmpty()) {
50 return self;
51 } else {
52 return predicate.test(value1, value2) ? self : emptySupplier.get();
53 }
54 }
55
56 protected <S extends AbstractTwoValueCondition<T>> S filterSupport(Predicate<? super T> predicate,
57 Supplier<S> emptySupplier, S self) {
58 return filterSupport((v1, v2) -> predicate.test(v1) && predicate.test(v2), emptySupplier, self);
59 }
60
61 protected <R, S extends AbstractTwoValueCondition<R>> S mapSupport(Function<? super T, ? extends R> mapper1,
62 Function<? super T, ? extends R> mapper2, BiFunction<R, R, S> constructor, Supplier<S> emptySupplier) {
63 if (isEmpty()) {
64 return emptySupplier.get();
65 } else {
66 return constructor.apply(mapper1.apply(value1), mapper2.apply(value2));
67 }
68 }
69
70 public abstract String operator1();
71
72 public abstract String operator2();
73
74 @Override
75 public FragmentAndParameters renderCondition(RenderingContext renderingContext, BindableColumn<T> leftColumn) {
76 RenderedParameterInfo parameterInfo1 = renderingContext.calculateParameterInfo(leftColumn);
77 RenderedParameterInfo parameterInfo2 = renderingContext.calculateParameterInfo(leftColumn);
78
79 String finalFragment = operator1()
80 + spaceBefore(parameterInfo1.renderedPlaceHolder())
81 + spaceBefore(operator2())
82 + spaceBefore(parameterInfo2.renderedPlaceHolder());
83
84 return FragmentAndParameters.withFragment(finalFragment)
85 .withParameter(parameterInfo1.parameterMapKey(), leftColumn.convertParameterType(value1()))
86 .withParameter(parameterInfo2.parameterMapKey(), leftColumn.convertParameterType(value2()))
87 .build();
88 }
89
90 /**
91 * Conditions may implement Filterable to add optionality to rendering.
92 *
93 * <p>If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision
94 * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the
95 * rendered SQL.
96 *
97 * <p>Implementations of Filterable may call
98 * {@link AbstractTwoValueCondition#filterSupport(Predicate, Supplier, AbstractTwoValueCondition)}
99 * or {@link AbstractTwoValueCondition#filterSupport(BiPredicate, Supplier, AbstractTwoValueCondition)} as
100 * a common implementation of the filtering algorithm.
101 *
102 * @param <T> the Java type related to the database column type
103 */
104 public interface Filterable<T> {
105 /**
106 * If renderable and the values match the predicate, returns this condition. Else returns a condition
107 * that will not render.
108 *
109 * @param predicate predicate applied to the values, if renderable
110 * @return this condition if renderable and the values match the predicate, otherwise a condition
111 * that will not render.
112 */
113 AbstractTwoValueCondition<T> filter(BiPredicate<? super T, ? super T> predicate);
114
115 /**
116 * If renderable and both values match the predicate, returns this condition. Else returns a condition
117 * that will not render. This function implements a short-circuiting test. If the
118 * first value does not match the predicate, then the second value will not be tested.
119 *
120 * @param predicate predicate applied to both values, if renderable
121 * @return this condition if renderable and the values match the predicate, otherwise a condition
122 * that will not render.
123 */
124 AbstractTwoValueCondition<T> filter(Predicate<? super T> predicate);
125 }
126
127 /**
128 * Conditions may implement Mappable to alter condition values or types during rendering.
129 *
130 * <p>If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the
131 * values of a condition, or change that datatype.
132 *
133 * <p>Implementations of Mappable may call
134 * {@link AbstractTwoValueCondition#mapSupport(Function, Function, BiFunction, Supplier)} as
135 * a common implementation of the mapping algorithm.
136 *
137 * @param <T> the Java type related to the database column type
138 */
139 public interface Mappable<T> {
140 /**
141 * If renderable, apply the mappings to the values and return a new condition with the new values. Else return a
142 * condition that will not render (this).
143 *
144 * @param mapper1 a mapping function to apply to the first value, if renderable
145 * @param mapper2 a mapping function to apply to the second value, if renderable
146 * @param <R> type of the new condition
147 * @return a new condition with the result of applying the mappers to the values of this condition,
148 * if renderable, otherwise a condition that will not render.
149 */
150 <R> AbstractTwoValueCondition<R> map(Function<? super T, ? extends R> mapper1,
151 Function<? super T, ? extends R> mapper2);
152
153 /**
154 * If renderable, apply the mapping to both values and return a new condition with the new values. Else return a
155 * condition that will not render (this).
156 *
157 * @param mapper a mapping function to apply to both values, if renderable
158 * @param <R> type of the new condition
159 * @return a new condition with the result of applying the mappers to the values of this condition,
160 * if renderable, otherwise a condition that will not render.
161 */
162 <R> AbstractTwoValueCondition<R> map(Function<? super T, ? extends R> mapper);
163 }
164 }