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 */
017package org.apache.logging.log4j.core.lookup;
018
019import java.util.Arrays;
020
021import org.apache.logging.log4j.core.helpers.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 */
030public 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}