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    }