001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017 package org.apache.logging.log4j.core.lookup;
018
019 import java.util.Arrays;
020
021 import org.apache.logging.log4j.util.Chars;
022 import org.apache.logging.log4j.util.Strings;
023
024 /**
025 * A matcher class that can be queried to determine if a character array
026 * portion matches.
027 * <p>
028 * This class comes complete with various factory methods.
029 * If these do not suffice, you can subclass and implement your own matcher.
030 */
031 public abstract class StrMatcher {
032
033 /**
034 * Matches the comma character.
035 */
036 private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
037 /**
038 * Matches the tab character.
039 */
040 private static final StrMatcher TAB_MATCHER = new CharMatcher(Chars.TAB);
041 /**
042 * Matches the space character.
043 */
044 private static final StrMatcher SPACE_MATCHER = new CharMatcher(Chars.SPACE);
045 /**
046 * Matches the same characters as StringTokenizer,
047 * namely space, tab, newline, formfeed.
048 */
049 private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
050 /**
051 * Matches the String trim() whitespace characters.
052 */
053 private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
054 /**
055 * Matches the double quote character.
056 */
057 private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher(Chars.QUOTE);
058 /**
059 * Matches the double quote character.
060 */
061 private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher(Chars.DQUOTE);
062 /**
063 * Matches the single or double quote character.
064 */
065 private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
066 /**
067 * Matches no characters.
068 */
069 private static final StrMatcher NONE_MATCHER = new NoMatcher();
070
071 /**
072 * Constructor.
073 */
074 protected StrMatcher() {
075 }
076
077 /**
078 * Returns a matcher which matches the comma character.
079 *
080 * @return a matcher for a comma
081 */
082 public static StrMatcher commaMatcher() {
083 return COMMA_MATCHER;
084 }
085
086 /**
087 * Returns a matcher which matches the tab character.
088 *
089 * @return a matcher for a tab
090 */
091 public static StrMatcher tabMatcher() {
092 return TAB_MATCHER;
093 }
094
095 /**
096 * Returns a matcher which matches the space character.
097 *
098 * @return a matcher for a space
099 */
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
374 //-----------------------------------------------------------------------
375 /**
376 * Class used to match no characters.
377 */
378 static final class NoMatcher extends StrMatcher {
379
380 /**
381 * Constructs a new instance of <code>NoMatcher</code>.
382 */
383 NoMatcher() {
384 super();
385 }
386
387 /**
388 * Always returns {@code false}.
389 *
390 * @param buffer the text content to match against, do not change
391 * @param pos the starting position for the match, valid for buffer
392 * @param bufferStart the first active index in the buffer, valid for buffer
393 * @param bufferEnd the end index of the active buffer, valid for buffer
394 * @return the number of matching characters, zero for no match
395 */
396 @Override
397 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
398 return 0;
399 }
400 }
401
402 //-----------------------------------------------------------------------
403 /**
404 * Class used to match whitespace as per trim().
405 */
406 static final class TrimMatcher extends StrMatcher {
407
408 /**
409 * Constructs a new instance of <code>TrimMatcher</code>.
410 */
411 TrimMatcher() {
412 super();
413 }
414
415 /**
416 * Returns whether or not the given character matches.
417 *
418 * @param buffer the text content to match against, do not change
419 * @param pos the starting position for the match, valid for buffer
420 * @param bufferStart the first active index in the buffer, valid for buffer
421 * @param bufferEnd the end index of the active buffer, valid for buffer
422 * @return the number of matching characters, zero for no match
423 */
424 @Override
425 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
426 return buffer[pos] <= ' ' ? 1 : 0;
427 }
428 }
429
430 }