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 java.util.Collection;
19  import java.util.Objects;
20  import java.util.function.Function;
21  import java.util.function.Predicate;
22  import java.util.function.Supplier;
23  import java.util.stream.Collectors;
24  import java.util.stream.Stream;
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  import org.mybatis.dynamic.sql.util.FragmentCollector;
30  
31  public abstract class AbstractListValueCondition<T> implements RenderableCondition<T> {
32      protected final Collection<T> values;
33  
34      protected AbstractListValueCondition(Collection<T> values) {
35          this.values = Objects.requireNonNull(values);
36      }
37  
38      public final Stream<T> values() {
39          return values.stream();
40      }
41  
42      @Override
43      public boolean isEmpty() {
44          return values.isEmpty();
45      }
46  
47      private <R> Collection<R> applyMapper(Function<? super T, ? extends R> mapper) {
48          Objects.requireNonNull(mapper);
49          return values().map(mapper).collect(Collectors.toList());
50      }
51  
52      private Collection<T> applyFilter(Predicate<? super T> predicate) {
53          Objects.requireNonNull(predicate);
54          return values().filter(predicate).toList();
55      }
56  
57      protected <S extends AbstractListValueCondition<T>> S filterSupport(Predicate<? super T> predicate,
58              Function<Collection<T>, S> constructor, S self, Supplier<S> emptySupplier) {
59          if (isEmpty()) {
60              return self;
61          } else {
62              Collection<T> filtered = applyFilter(predicate);
63              return filtered.isEmpty() ? emptySupplier.get() : constructor.apply(filtered);
64          }
65      }
66  
67      protected <R, S extends AbstractListValueCondition<R>> S mapSupport(Function<? super T, ? extends R> mapper,
68              Function<Collection<R>, S> constructor, Supplier<S> emptySupplier) {
69          if (isEmpty()) {
70              return emptySupplier.get();
71          } else {
72              return constructor.apply(applyMapper(mapper));
73          }
74      }
75  
76      public abstract String operator();
77  
78      @Override
79      public FragmentAndParameters renderCondition(RenderingContext renderingContext, BindableColumn<T> leftColumn) {
80          return values().map(v -> toFragmentAndParameters(v, renderingContext, leftColumn))
81                  .collect(FragmentCollector.collect())
82                  .toFragmentAndParameters(Collectors.joining(",", //$NON-NLS-1$
83                          operator() + " (", ")")); //$NON-NLS-1$ //$NON-NLS-2$
84      }
85  
86      private FragmentAndParameters toFragmentAndParameters(T value, RenderingContext renderingContext,
87                                                            BindableColumn<T> leftColumn) {
88          RenderedParameterInfo parameterInfo = renderingContext.calculateParameterInfo(leftColumn);
89          return FragmentAndParameters.withFragment(parameterInfo.renderedPlaceHolder())
90                  .withParameter(parameterInfo.parameterMapKey(), leftColumn.convertParameterType(value))
91                  .build();
92      }
93  
94      /**
95       * Conditions may implement Filterable to add optionality to rendering.
96       *
97       * <p>If a condition is Filterable, then a user may add a filter to the usage of the condition that makes a decision
98       * whether to render the condition at runtime. Conditions that fail the filter will be dropped from the
99       * rendered SQL.
100      *
101      * <p>Implementations of Filterable may call
102      * {@link AbstractListValueCondition#filterSupport(Predicate, Function, AbstractListValueCondition, Supplier)} as
103      * a common implementation of the filtering algorithm.
104      *
105      * @param <T> the Java type related to the database column type
106      */
107     public interface Filterable<T> {
108         /**
109          * If renderable and the value matches the predicate, returns this condition. Else returns a condition
110          *     that will not render.
111          *
112          * @param predicate predicate applied to the value, if renderable
113          * @return this condition if renderable and the value matches the predicate, otherwise a condition
114          *     that will not render.
115          */
116         AbstractListValueCondition<T> filter(Predicate<? super T> predicate);
117     }
118 
119     /**
120      * Conditions may implement Mappable to alter condition values or types during rendering.
121      *
122      * <p>If a condition is Mappable, then a user may add a mapper to the usage of the condition that can alter the
123      * values of a condition, or change that datatype.
124      *
125      * <p>Implementations of Mappable may call
126      * {@link AbstractListValueCondition#mapSupport(Function, Function, Supplier)} as
127      * a common implementation of the mapping algorithm.
128      *
129      * @param <T> the Java type related to the database column type
130      */
131     public interface Mappable<T> {
132         /**
133          * If renderable, apply the mapping to the value and return a new condition with the new value. Else return a
134          * condition that will not render (this).
135          *
136          * @param mapper a mapping function to apply to the value, if renderable
137          * @param <R> type of the new condition
138          * @return a new condition with the result of applying the mapper to the value of this condition,
139          *     if renderable, otherwise a condition that will not render.
140          */
141         <R> AbstractListValueCondition<R> map(Function<? super T, ? extends R> mapper);
142     }
143 }