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