View Javadoc
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 
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 }