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