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 /**
022 * A matcher class that can be queried to determine if a character array
023 * portion matches.
024 * <p>
025 * This class comes complete with various factory methods.
026 * If these do not suffice, you can subclass and implement your own matcher.
027 */
028 public abstract class StrMatcher {
029
030 /**
031 * Matches the comma character.
032 */
033 private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
034 /**
035 * Matches the tab character.
036 */
037 private static final StrMatcher TAB_MATCHER = new CharMatcher('\t');
038 /**
039 * Matches the space character.
040 */
041 private static final StrMatcher SPACE_MATCHER = new CharMatcher(' ');
042 /**
043 * Matches the same characters as StringTokenizer,
044 * namely space, tab, newline, formfeed.
045 */
046 private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
047 /**
048 * Matches the String trim() whitespace characters.
049 */
050 private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
051 /**
052 * Matches the double quote character.
053 */
054 private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\'');
055 /**
056 * Matches the double quote character.
057 */
058 private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"');
059 /**
060 * Matches the single or double quote character.
061 */
062 private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
063 /**
064 * Matches no characters.
065 */
066 private static final StrMatcher NONE_MATCHER = new NoMatcher();
067
068 /**
069 * Constructor.
070 */
071 protected StrMatcher() {
072 }
073
074 /**
075 * Returns a matcher which matches the comma character.
076 *
077 * @return a matcher for a comma
078 */
079 public static StrMatcher commaMatcher() {
080 return COMMA_MATCHER;
081 }
082
083 /**
084 * Returns a matcher which matches the tab character.
085 *
086 * @return a matcher for a tab
087 */
088 public static StrMatcher tabMatcher() {
089 return TAB_MATCHER;
090 }
091
092 /**
093 * Returns a matcher which matches the space character.
094 *
095 * @return a matcher for a space
096 */
097 public static StrMatcher spaceMatcher() {
098 return SPACE_MATCHER;
099 }
100
101 /**
102 * Matches the same characters as StringTokenizer,
103 * namely space, tab, newline and formfeed.
104 *
105 * @return the split matcher
106 */
107 public static StrMatcher splitMatcher() {
108 return SPLIT_MATCHER;
109 }
110
111 /**
112 * Matches the String trim() whitespace characters.
113 *
114 * @return the trim matcher
115 */
116 public static StrMatcher trimMatcher() {
117 return TRIM_MATCHER;
118 }
119
120 /**
121 * Returns a matcher which matches the single quote character.
122 *
123 * @return a matcher for a single quote
124 */
125 public static StrMatcher singleQuoteMatcher() {
126 return SINGLE_QUOTE_MATCHER;
127 }
128
129 /**
130 * Returns a matcher which matches the double quote character.
131 *
132 * @return a matcher for a double quote
133 */
134 public static StrMatcher doubleQuoteMatcher() {
135 return DOUBLE_QUOTE_MATCHER;
136 }
137
138 /**
139 * Returns a matcher which matches the single or double quote character.
140 *
141 * @return a matcher for a single or double quote
142 */
143 public static StrMatcher quoteMatcher() {
144 return QUOTE_MATCHER;
145 }
146
147 /**
148 * Matches no characters.
149 *
150 * @return a matcher that matches nothing
151 */
152 public static StrMatcher noneMatcher() {
153 return NONE_MATCHER;
154 }
155
156 /**
157 * Constructor that creates a matcher from a character.
158 *
159 * @param ch the character to match, must not be null
160 * @return a new Matcher for the given char
161 */
162 public static StrMatcher charMatcher(final char ch) {
163 return new CharMatcher(ch);
164 }
165
166 /**
167 * Constructor that creates a matcher from a set of characters.
168 *
169 * @param chars the characters to match, null or empty matches nothing
170 * @return a new matcher for the given char[]
171 */
172 public static StrMatcher charSetMatcher(final char[] chars) {
173 if (chars == null || chars.length == 0) {
174 return NONE_MATCHER;
175 }
176 if (chars.length == 1) {
177 return new CharMatcher(chars[0]);
178 }
179 return new CharSetMatcher(chars);
180 }
181
182 /**
183 * Constructor that creates a matcher from a string representing a set of characters.
184 *
185 * @param chars the characters to match, null or empty matches nothing
186 * @return a new Matcher for the given characters
187 */
188 public static StrMatcher charSetMatcher(final String chars) {
189 if (chars == null || chars.length() == 0) {
190 return NONE_MATCHER;
191 }
192 if (chars.length() == 1) {
193 return new CharMatcher(chars.charAt(0));
194 }
195 return new CharSetMatcher(chars.toCharArray());
196 }
197
198 /**
199 * Constructor that creates a matcher from a string.
200 *
201 * @param str the string to match, null or empty matches nothing
202 * @return a new Matcher for the given String
203 */
204 public static StrMatcher stringMatcher(final String str) {
205 if (str == null || str.length() == 0) {
206 return NONE_MATCHER;
207 }
208 return new StringMatcher(str);
209 }
210
211 /**
212 * Returns the number of matching characters, zero for no match.
213 * <p>
214 * This method is called to check for a match.
215 * The parameter <code>pos</code> represents the current position to be
216 * checked in the string <code>buffer</code> (a character array which must
217 * not be changed).
218 * The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
219 * <p>
220 * The character array may be larger than the active area to be matched.
221 * Only values in the buffer between the specified indices may be accessed.
222 * <p>
223 * The matching code may check one character or many.
224 * It may check characters preceding <code>pos</code> as well as those
225 * after, so long as no checks exceed the bounds specified.
226 * <p>
227 * It must return zero for no match, or a positive number if a match was found.
228 * The number indicates the number of characters that matched.
229 *
230 * @param buffer the text content to match against, do not change
231 * @param pos the starting position for the match, valid for buffer
232 * @param bufferStart the first active index in the buffer, valid for buffer
233 * @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer
234 * @return the number of matching characters, zero for no match
235 */
236 public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd);
237
238 /**
239 * Returns the number of matching characters, zero for no match.
240 * <p>
241 * This method is called to check for a match.
242 * The parameter <code>pos</code> represents the current position to be
243 * checked in the string <code>buffer</code> (a character array which must
244 * not be changed).
245 * The API guarantees that <code>pos</code> is a valid index for <code>buffer</code>.
246 * <p>
247 * The matching code may check one character or many.
248 * It may check characters preceding <code>pos</code> as well as those after.
249 * <p>
250 * It must return zero for no match, or a positive number if a match was found.
251 * The number indicates the number of characters that matched.
252 *
253 * @param buffer the text content to match against, do not change
254 * @param pos the starting position for the match, valid for buffer
255 * @return the number of matching characters, zero for no match
256 * @since 2.4
257 */
258 public int isMatch(final char[] buffer, final int pos) {
259 return isMatch(buffer, pos, 0, buffer.length);
260 }
261
262 //-----------------------------------------------------------------------
263 /**
264 * Class used to define a set of characters for matching purposes.
265 */
266 static final class CharSetMatcher extends StrMatcher {
267 /** The set of characters to match. */
268 private final char[] chars;
269
270 /**
271 * Constructor that creates a matcher from a character array.
272 *
273 * @param chars the characters to match, must not be null
274 */
275 CharSetMatcher(final char[] chars) {
276 super();
277 this.chars = chars.clone();
278 Arrays.sort(this.chars);
279 }
280
281 /**
282 * Returns whether or not the given character matches.
283 *
284 * @param buffer the text content to match against, do not change
285 * @param pos the starting position for the match, valid for buffer
286 * @param bufferStart the first active index in the buffer, valid for buffer
287 * @param bufferEnd the end index of the active buffer, valid for buffer
288 * @return the number of matching characters, zero for no match
289 */
290 @Override
291 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
292 return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
293 }
294 }
295
296 //-----------------------------------------------------------------------
297 /**
298 * Class used to define a character for matching purposes.
299 */
300 static final class CharMatcher extends StrMatcher {
301 /** The character to match. */
302 private final char ch;
303
304 /**
305 * Constructor that creates a matcher that matches a single character.
306 *
307 * @param ch the character to match
308 */
309 CharMatcher(final char ch) {
310 super();
311 this.ch = ch;
312 }
313
314 /**
315 * Returns whether or not the given character matches.
316 *
317 * @param buffer the text content to match against, do not change
318 * @param pos the starting position for the match, valid for buffer
319 * @param bufferStart the first active index in the buffer, valid for buffer
320 * @param bufferEnd the end index of the active buffer, valid for buffer
321 * @return the number of matching characters, zero for no match
322 */
323 @Override
324 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
325 return ch == buffer[pos] ? 1 : 0;
326 }
327 }
328
329 //-----------------------------------------------------------------------
330 /**
331 * Class used to define a set of characters for matching purposes.
332 */
333 static final class StringMatcher extends StrMatcher {
334 /** The string to match, as a character array. */
335 private final char[] chars;
336
337 /**
338 * Constructor that creates a matcher from a String.
339 *
340 * @param str the string to match, must not be null
341 */
342 StringMatcher(final String str) {
343 super();
344 chars = str.toCharArray();
345 }
346
347 /**
348 * Returns whether or not the given text matches the stored string.
349 *
350 * @param buffer the text content to match against, do not change
351 * @param pos the starting position for the match, valid for buffer
352 * @param bufferStart the first active index in the buffer, valid for buffer
353 * @param bufferEnd the end index of the active buffer, valid for buffer
354 * @return the number of matching characters, zero for no match
355 */
356 @Override
357 public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) {
358 final int len = chars.length;
359 if (pos + len > bufferEnd) {
360 return 0;
361 }
362 for (int i = 0; i < chars.length; i++, pos++) {
363 if (chars[i] != buffer[pos]) {
364 return 0;
365 }
366 }
367 return len;
368 }
369 }
370
371 //-----------------------------------------------------------------------
372 /**
373 * Class used to match no characters.
374 */
375 static final class NoMatcher extends StrMatcher {
376
377 /**
378 * Constructs a new instance of <code>NoMatcher</code>.
379 */
380 NoMatcher() {
381 super();
382 }
383
384 /**
385 * Always returns {@code false}.
386 *
387 * @param buffer the text content to match against, do not change
388 * @param pos the starting position for the match, valid for buffer
389 * @param bufferStart the first active index in the buffer, valid for buffer
390 * @param bufferEnd the end index of the active buffer, valid for buffer
391 * @return the number of matching characters, zero for no match
392 */
393 @Override
394 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
395 return 0;
396 }
397 }
398
399 //-----------------------------------------------------------------------
400 /**
401 * Class used to match whitespace as per trim().
402 */
403 static final class TrimMatcher extends StrMatcher {
404
405 /**
406 * Constructs a new instance of <code>TrimMatcher</code>.
407 */
408 TrimMatcher() {
409 super();
410 }
411
412 /**
413 * Returns whether or not the given character matches.
414 *
415 * @param buffer the text content to match against, do not change
416 * @param pos the starting position for the match, valid for buffer
417 * @param bufferStart the first active index in the buffer, valid for buffer
418 * @param bufferEnd the end index of the active buffer, valid for buffer
419 * @return the number of matching characters, zero for no match
420 */
421 @Override
422 public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
423 return buffer[pos] <= ' ' ? 1 : 0;
424 }
425 }
426
427 }