    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    }