View Javadoc
1   /*
2    *    Copyright 2018-2022 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.scripting.thymeleaf.expression;
17  
18  import java.util.Arrays;
19  import java.util.Collections;
20  import java.util.Optional;
21  import java.util.Set;
22  import java.util.function.Function;
23  import java.util.stream.Collectors;
24  
25  /**
26   * The expression utility object that provide helper method for like feature. <br>
27   * This object can be access using {@code #likes}) as expression utility object.
28   *
29   * @author Kazuki Shimizu
30   *
31   * @version 1.0.0
32   */
33  public class Likes {
34  
35    private char escapeChar = '\\';
36  
37    private Set<Character> additionalEscapeTargetChars = Collections.emptySet();
38  
39    private Function<Character, String> escapeClauseSupplier = targetEscapeChar -> "ESCAPE '" + targetEscapeChar + "'";
40  
41    /**
42     * Construct new instance that corresponds with specified configuration.
43     */
44    private Likes() {
45      // NOP
46    }
47  
48    /**
49     * Escape for LIKE condition value. <br>
50     * By default configuration, this method escape the {@code "%"} and {@code "_"} using {@code "\"}.
51     *
52     * @param value
53     *          A target condition value
54     *
55     * @return A escaped value
56     */
57    public String escapeWildcard(String value) {
58      if (value == null || value.isEmpty()) {
59        return "";
60      }
61      StringBuilder sb = new StringBuilder(value.length() + 16);
62      for (char c : value.toCharArray()) {
63        if (c == escapeChar || c == '%' || c == '_' || additionalEscapeTargetChars.contains(c)) {
64          sb.append(escapeChar);
65        }
66        sb.append(c);
67      }
68      return sb.toString();
69    }
70  
71    /**
72     * Return a escape clause string of LIKE. <br>
73     * By default configuration, this method return {@code "ESCAPE '\'"}.
74     *
75     * @return A escape clause string of LIKE
76     */
77    public String escapeClause() {
78      return escapeClauseSupplier.apply(escapeChar);
79    }
80  
81    /**
82     * Creates a new builder instance for {@link Likes}.
83     *
84     * @return a new builder instance
85     */
86    public static Builder newBuilder() {
87      return new Builder();
88    }
89  
90    /**
91     * The builder class for {@link Likes}.
92     */
93    public static class Builder {
94  
95      private final Likes instance = new Likes();
96  
97      private Builder() {
98        // NOP
99      }
100 
101     /**
102      * Set an escape character for wildcard of LIKE. <br>
103      * The default value is {@code '\'} (backslash)
104      *
105      * @param escapeChar
106      *          A escape character
107      *
108      * @return A self instance
109      */
110     public Builder escapeChar(Character escapeChar) {
111       Optional.ofNullable(escapeChar).ifPresent(v -> instance.escapeChar = v);
112       return this;
113     }
114 
115     /**
116      * Set additional escape target characters(custom wildcard characters) for LIKE condition. <br>
117      * The default value is nothing.
118      *
119      * @param additionalEscapeTargetChars
120      *          escape target characters(custom wildcard characters)
121      *
122      * @return A self instance
123      */
124     public Builder additionalEscapeTargetChars(Character... additionalEscapeTargetChars) {
125       Optional.ofNullable(additionalEscapeTargetChars)
126           .ifPresent(v -> instance.additionalEscapeTargetChars = Arrays.stream(v).collect(Collectors.toSet()));
127       return this;
128     }
129 
130     /**
131      * Set a format of escape clause. <br>
132      * The default value is {@code "ESCAPE '%s'"}.
133      *
134      * @param escapeClauseFormat
135      *          a format of escape clause
136      *
137      * @return A self instance
138      */
139     public Builder escapeClauseFormat(String escapeClauseFormat) {
140       Optional.ofNullable(escapeClauseFormat)
141           .ifPresent(v -> instance.escapeClauseSupplier = escapeChar -> String.format(v, escapeChar));
142       return this;
143     }
144 
145     /**
146      * Return a {@link Likes} instance .
147      *
148      * @return A {@link Likes} instance corresponding with specified option
149      */
150     public Likes build() {
151       return instance;
152     }
153 
154   }
155 
156 }