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 }