View Javadoc
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.Function;
21  import java.util.function.Predicate;
22  import java.util.function.Supplier;
23  
24  import org.jspecify.annotations.NonNull;
25  import org.mybatis.dynamic.sql.render.RenderedParameterInfo;
26  import org.mybatis.dynamic.sql.render.RenderingContext;
27  import org.mybatis.dynamic.sql.util.FragmentAndParameters;
28  
29  public abstract class AbstractSingleValueCondition<T> implements RenderableCondition<T> {
30      protected final T value;
31  
32      protected AbstractSingleValueCondition(T value) {
33          this.value = value;
34      }
35  
36      public T value() {
37          return value;
38      }
39  
40      protected <S extends AbstractSingleValueCondition<T>> S filterSupport(Predicate<? super T> predicate,
41              Supplier<S> emptySupplier, S self) {
42          if (isEmpty()) {
43              return self;
44          } else {
45              return predicate.test(value) ? self : emptySupplier.get();
46          }
47      }
48  
49      protected <R, S extends AbstractSingleValueCondition<R>> S mapSupport(Function<? super T, ? extends R> mapper,
50              Function<R, S> constructor, Supplier<S> emptySupplier) {
51          if (isEmpty()) {
52              return emptySupplier.get();
53          } else {
54              return constructor.apply(mapper.apply(value));
55          }
56      }
57  
58      public abstract String operator();
59  
60      @Override
61      public FragmentAndParameters renderCondition(RenderingContext renderingContext, BindableColumn<T> leftColumn) {
62          RenderedParameterInfo parameterInfo = renderingContext.calculateParameterInfo(leftColumn);
63          String finalFragment = operator() + spaceBefore(parameterInfo.renderedPlaceHolder());
64  
65          return FragmentAndParameters.withFragment(finalFragment)
66                  .withParameter(parameterInfo.parameterMapKey(), leftColumn.convertParameterType(value()))
67                  .build();
68      }
69  
70      /**
71       * Conditions may implement Filterable to add optionality to rendering.
72       *
73       * <p>If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision
74       * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the
75       * rendered SQL.
76       *
77       * <p>Implementations of Filterable may call
78       * {@link AbstractSingleValueCondition#filterSupport(Predicate, Supplier, AbstractSingleValueCondition)} as
79       * a common implementation of the filtering algorithm.
80       *
81       * @param <T> the Java type related to the database column type
82       */
83      public interface Filterable<T> {
84          /**
85           * If renderable and the value matches the predicate, returns this condition. Else returns a condition
86           *     that will not render.
87           *
88           * @param predicate predicate applied to the value, if renderable
89           * @return this condition if renderable and the value matches the predicate, otherwise a condition
90           *     that will not render.
91           */
92          AbstractSingleValueCondition<T> filter(Predicate<? super @NonNull T> predicate);
93      }
94  
95      /**
96       * Conditions may implement Mappable to alter condition values or types during rendering.
97       *
98       * <p>If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the
99       * values of a condition, or change that datatype.
100      *
101      * <p>Implementations of Mappable may call
102      * {@link AbstractSingleValueCondition#mapSupport(Function, Function, Supplier)} as
103      * a common implementation of the mapping algorithm.
104      *
105      * @param <T> the Java type related to the database column type
106      */
107     public interface Mappable<T> {
108         /**
109          * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a
110          * condition that will not render (this).
111          *
112          * @param mapper a mapping function to apply to the value, if renderable
113          * @param <R> type of the new condition
114          * @return a new condition with the result of applying the mapper to the value of this condition,
115          *     if renderable, otherwise a condition that will not render.
116          */
117         <R> AbstractSingleValueCondition<R> map(Function<? super @NonNull T, ? extends R> mapper);
118     }
119 }