1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache license, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the license for the specific language governing permissions and
15 * limitations under the license.
16 */
17 package org.apache.logging.log4j.core.lookup;
18
19 import java.util.Arrays;
20
21 import org.apache.logging.log4j.util.Chars;
22 import org.apache.logging.log4j.util.Strings;
23
24 /**
25 * A matcher class that can be queried to determine if a character array
26 * portion matches.
27 * <p>
28 * This class comes complete with various factory methods.
29 * If these do not suffice, you can subclass and implement your own matcher.
30 */
31 public abstract class StrMatcher {
32
33 /**
34 * Matches the comma character.
35 */
36 private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
37 /**
38 * Matches the tab character.
39 */
40 private static final StrMatcher TAB_MATCHER = new CharMatcher(Chars.TAB);
41 /**
42 * Matches the space character.
43 */
44 private static final StrMatcher SPACE_MATCHER = new CharMatcher(Chars.SPACE);
45 /**
46 * Matches the same characters as StringTokenizer,
47 * namely space, tab, newline, formfeed.
48 */
49 private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
50 /**
51 * Matches the String trim() whitespace characters.
52 */
53 private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
54 /**
55 * Matches the double quote character.
56 */
57 private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher(Chars.QUOTE);
58 /**
59 * Matches the double quote character.
60 */
61 private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher(Chars.DQUOTE);
62 /**
63 * Matches the single or double quote character.
64 */
65 private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
66 /**
67 * Matches no characters.
68 */
69 private static final StrMatcher NONE_MATCHER = new NoMatcher();
70
71 /**
72 * Constructor.
73 */
74 protected StrMatcher() {
75 }
76
77 /**
78 * Returns a matcher which matches the comma character.
79 *
80 * @return a matcher for a comma
81 */
82 public static StrMatcher commaMatcher() {
83 return COMMA_MATCHER;
84 }
85
86 /**
87 * Returns a matcher which matches the tab character.
88 *
89 * @return a matcher for a tab
90 */
91 public static StrMatcher tabMatcher() {
92 return TAB_MATCHER;
93 }
94
95 /**
96 * Returns a matcher which matches the space character.
97 *
98 * @return a matcher for a space
99 */
100 public static StrMatcher spaceMatcher() {
101 return SPACE_MATCHER;
102 }
103
104 /**
105 * Matches the same characters as StringTokenizer,
106 * namely space, tab, newline and formfeed.
107 *
108 * @return the split matcher
109 */
110 public static StrMatcher splitMatcher() {
111 return SPLIT_MATCHER;
112 }
113
114 /**
115 * Matches the String trim() whitespace characters.
116 *
117 * @return the trim matcher
118 */
119 public static StrMatcher trimMatcher() {
120 return TRIM_MATCHER;
121 }
122
123 /**
124 * Returns a matcher which matches the single quote character.
125 *
126 * @return a matcher for a single quote
127 */
128 public static StrMatcher singleQuoteMatcher() {
129 return SINGLE_QUOTE_MATCHER;
130 }
131
132 /**
133 * Returns a matcher which matches the double quote character.
134 *
135 * @return a matcher for a double quote
136 */
137 public static StrMatcher doubleQuoteMatcher() {
138 return DOUBLE_QUOTE_MATCHER;
139 }
140
141 /**
142 * Returns a matcher which matches the single or double quote character.
143 *
144 * @return a matcher for a single or double quote
145 */
146 public static StrMatcher quoteMatcher() {
147 return QUOTE_MATCHER;
148 }
149
150 /**
151 * Matches no characters.
152 *
153 * @return a matcher that matches nothing
154 */
155 public static StrMatcher noneMatcher() {
156 return NONE_MATCHER;
157 }
158
159 /**
160 * Constructor that creates a matcher from a character.
161 *
162 * @param ch the character to match, must not be null
163 * @return a new Matcher for the given char
164 */
165 public static StrMatcher charMatcher(final char ch) {
166 return new CharMatcher(ch);
167 }
168
169 /**
170 * Constructor that creates a matcher from a set of characters.
171 *
172 * @param chars the characters to match, null or empty matches nothing
173 * @return a new matcher for the given char[]
174 */
175 public static StrMatcher charSetMatcher(final char[] chars) {
176 if (chars == null || chars.length == 0) {
177 return NONE_MATCHER;
178 }
179 if (chars.length == 1) {
180 return new CharMatcher(chars[0]);
181 }
182 return new CharSetMatcher(chars);
183 }
184
185 /**
186 * Constructor that creates a matcher from a string representing a set of characters.
187 *
188 * @param chars the characters to match, null or empty matches nothing
189 * @return a new Matcher for the given characters
190 */
191 public static StrMatcher charSetMatcher(final String chars) {
192 if (Strings.isEmpty(chars)) {
193 return NONE_MATCHER;
194 }
195 if (chars.length() == 1) {
196 return new CharMatcher(chars.charAt(0));
197 }
198 return new CharSetMatcher(chars.toCharArray());
199 }
200
201 /**
202 * Constructor that creates a matcher from a string.
203 *
204 * @param str the string to match, null or empty matches nothing
205 * @return a new Matcher for the given String
206 */
207 public static StrMatcher stringMatcher(final String str) {
208 if (Strings.isEmpty(str)) {
209 return NONE_MATCHER;
210 }
211 return new StringMatcher(str);
212 }
213
214 /**
215 * Returns the number of matching characters, zero for no match.
216 * <p>
217 * This method is called to check for a match.
218 * The parameter <code>pos</code> represents the current position to be
219 * checked in the string <code>buffer</code> (a character array which must
220 * not be changed).
221 * The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
222 * <p>
223 * The character array may be larger than the active area to be matched.
224 * Only values in the buffer between the specified indices may be accessed.
225 * <p>
226 * The matching code may check one character or many.
227 * It may check characters preceding <code>pos</code> as well as those
228 * after, so long as no checks exceed the bounds specified.
229 * <p>
230 * It must return zero for no match, or a positive number if a match was found.
231 * The number indicates the number of characters that matched.
232 *
233 * @param buffer the text content to match against, do not change
234 * @param pos the starting position for the match, valid for buffer
235 * @param bufferStart the first active index in the buffer, valid for buffer
236 * @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer
237 * @return the number of matching characters, zero for no match
238 */
239 public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd);
240
241 /**
242 * Returns the number of matching characters, zero for no match.
243 * <p>
244 * This method is called to check for a match.
245 * The parameter <code>pos</code> represents the current position to be
246 * checked in the string <code>buffer</code> (a character array which must
247 * not be changed).
248 * The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
249 * <p>
250 * The matching code may check one character or many.
251 * It may check characters preceding <code>pos</code> as well as those after.
252 * <p>
253 * It must return zero for no match, or a positive number if a match was found.
254 * The number indicates the number of characters that matched.
255 *
256 * @param buffer the text content to match against, do not change
257 * @param pos the starting position for the match, valid for buffer
258 * @return the number of matching characters, zero for no match
259 * @since 2.4
260 */
261 public int isMatch(final char[] buffer, final int pos) {
262 return isMatch(buffer, pos, 0, buffer.length);
263 }
264
265 //-----------------------------------------------------------------------
266 /**
267 * Class used to define a set of characters for matching purposes.
268 */
269 static final class CharSetMatcher extends StrMatcher {
270 /** The set of characters to match. */
271 private final char[] chars;
272
273 /**
274 * Constructor that creates a matcher from a character array.
275 *
276 * @param chars the characters to match, must not be null
277 */
278 CharSetMatcher(final char[] chars) {
279 super();
280 this.chars = chars.clone();
281 Arrays.sort(this.chars);
282 }
283
284 /**
285 * Returns whether or not the given character matches.
286 *
287 * @param buffer the text content to match against, do not change
288 * @param pos the starting position for the match, valid for buffer
289 * @param bufferStart the first active index in the buffer, valid for buffer
290 * @param bufferEnd the end index of the active buffer, valid for buffer
291 * @return the number of matching characters, zero for no match
292 */
293 @Override
294 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
295 return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
296 }
297 }
298
299 //-----------------------------------------------------------------------
300 /**
301 * Class used to define a character for matching purposes.
302 */
303 static final class CharMatcher extends StrMatcher {
304 /** The character to match. */
305 private final char ch;
306
307 /**
308 * Constructor that creates a matcher that matches a single character.
309 *
310 * @param ch the character to match
311 */
312 CharMatcher(final char ch) {
313 super();
314 this.ch = ch;
315 }
316
317 /**
318 * Returns whether or not the given character matches.
319 *
320 * @param buffer the text content to match against, do not change
321 * @param pos the starting position for the match, valid for buffer
322 * @param bufferStart the first active index in the buffer, valid for buffer
323 * @param bufferEnd the end index of the active buffer, valid for buffer
324 * @return the number of matching characters, zero for no match
325 */
326 @Override
327 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
328 return ch == buffer[pos] ? 1 : 0;
329 }
330 }
331
332 //-----------------------------------------------------------------------
333 /**
334 * Class used to define a set of characters for matching purposes.
335 */
336 static final class StringMatcher extends StrMatcher {
337 /** The string to match, as a character array. */
338 private final char[] chars;
339
340 /**
341 * Constructor that creates a matcher from a String.
342 *
343 * @param str the string to match, must not be null
344 */
345 StringMatcher(final String str) {
346 super();
347 chars = str.toCharArray();
348 }
349
350 /**
351 * Returns whether or not the given text matches the stored string.
352 *
353 * @param buffer the text content to match against, do not change
354 * @param pos the starting position for the match, valid for buffer
355 * @param bufferStart the first active index in the buffer, valid for buffer
356 * @param bufferEnd the end index of the active buffer, valid for buffer
357 * @return the number of matching characters, zero for no match
358 */
359 @Override
360 public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) {
361 final int len = chars.length;
362 if (pos + len > bufferEnd) {
363 return 0;
364 }
365 for (int i = 0; i < chars.length; i++, pos++) {
366 if (chars[i] != buffer[pos]) {
367 return 0;
368 }
369 }
370 return len;
371 }
372
373 @Override
374 public String toString() {
375 return super.toString() + Chars.SPACE + Arrays.toString(chars);
376 }
377
378 }
379
380 //-----------------------------------------------------------------------
381 /**
382 * Class used to match no characters.
383 */
384 static final class NoMatcher extends StrMatcher {
385
386 /**
387 * Constructs a new instance of <code>NoMatcher</code>.
388 */
389 NoMatcher() {
390 super();
391 }
392
393 /**
394 * Always returns {@code false}.
395 *
396 * @param buffer the text content to match against, do not change
397 * @param pos the starting position for the match, valid for buffer
398 * @param bufferStart the first active index in the buffer, valid for buffer
399 * @param bufferEnd the end index of the active buffer, valid for buffer
400 * @return the number of matching characters, zero for no match
401 */
402 @Override
403 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
404 return 0;
405 }
406 }
407
408 //-----------------------------------------------------------------------
409 /**
410 * Class used to match whitespace as per trim().
411 */
412 static final class TrimMatcher extends StrMatcher {
413
414 /**
415 * Constructs a new instance of <code>TrimMatcher</code>.
416 */
417 TrimMatcher() {
418 super();
419 }
420
421 /**
422 * Returns whether or not the given character matches.
423 *
424 * @param buffer the text content to match against, do not change
425 * @param pos the starting position for the match, valid for buffer
426 * @param bufferStart the first active index in the buffer, valid for buffer
427 * @param bufferEnd the end index of the active buffer, valid for buffer
428 * @return the number of matching characters, zero for no match
429 */
430 @Override
431 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
432 return buffer[pos] <= ' ' ? 1 : 0;
433 }
434 }
435
436 }