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    }