001    package org.apache.commons.digester3;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.apache.commons.logging.Log;
023    import org.apache.commons.logging.LogFactory;
024    
025    /**
026     * <p>
027     * Simple regex pattern matching algorithm.
028     * </p>
029     * <p>
030     * This uses just two wildcards:
031     * <ul>
032     * <li><code>*</code> matches any sequence of none, one or more characters
033     * <li><code>?</code> matches any one character
034     * </ul>
035     * Escaping these wildcards is not supported .
036     * </p>
037     * 
038     * @since 1.5
039     */
040    public class SimpleRegexMatcher
041        extends RegexMatcher
042    {
043    
044        // --------------------------------------------------------- Fields
045    
046        /** Default log (class wide) */
047        private static final Log BASE_LOG = LogFactory.getLog( SimpleRegexMatcher.class );
048    
049        /** Custom log (can be set per object) */
050        private Log log = BASE_LOG;
051    
052        // --------------------------------------------------------- Properties
053    
054        /**
055         * Gets the <code>Log</code> implementation.
056         *
057         * @return the <code>Log</code> implementation.
058         */
059        public Log getLog()
060        {
061            return log;
062        }
063    
064        /**
065         * Sets the current <code>Log</code> implementation used by this class.
066         *
067         * @param log the current <code>Log</code> implementation used by this class.
068         */
069        public void setLog( Log log )
070        {
071            this.log = log;
072        }
073    
074        // --------------------------------------------------------- Public Methods
075    
076        /**
077         * {@inheritDoc}
078         */
079        @Override
080        public boolean match( String basePattern, String regexPattern )
081        {
082            // check for nulls
083            if ( basePattern == null || regexPattern == null )
084            {
085                return false;
086            }
087            return match( basePattern, regexPattern, 0, 0 );
088        }
089    
090        // --------------------------------------------------------- Implementations Methods
091    
092        /**
093         * Implementation of regex matching algorithm. This calls itself recursively.
094         *
095         * @param basePattern the standard digester path representing the element
096         * @param regexPattern the regex pattern the path will be tested against
097         * @param baseAt FIXME
098         * @param regexAt FIXME
099         */
100        private boolean match( String basePattern, String regexPattern, int baseAt, int regexAt )
101        {
102            if ( log.isTraceEnabled() )
103            {
104                log.trace( "Base: " + basePattern );
105                log.trace( "Regex: " + regexPattern );
106                log.trace( "Base@" + baseAt );
107                log.trace( "Regex@" + regexAt );
108            }
109    
110            // check bounds
111            if ( regexAt >= regexPattern.length() )
112            {
113                // maybe we've got a match
114                if ( baseAt >= basePattern.length() )
115                {
116                    // ok!
117                    return true;
118                }
119                // run out early
120                return false;
121    
122            }
123            if ( baseAt >= basePattern.length() )
124            {
125                // run out early
126                return false;
127            }
128    
129            // ok both within bounds
130            char regexCurrent = regexPattern.charAt( regexAt );
131            switch ( regexCurrent )
132            {
133                case '*':
134                    // this is the tricky case
135                    // check for terminal
136                    if ( ++regexAt >= regexPattern.length() )
137                    {
138                        // this matches anything let - so return true
139                        return true;
140                    }
141                    // go through every subsequent apperance of the next character
142                    // and so if the rest of the regex matches
143                    char nextRegex = regexPattern.charAt( regexAt );
144                    if ( log.isTraceEnabled() )
145                    {
146                        log.trace( "Searching for next '" + nextRegex + "' char" );
147                    }
148                    int nextMatch = basePattern.indexOf( nextRegex, baseAt );
149                    while ( nextMatch != -1 )
150                    {
151                        if ( log.isTraceEnabled() )
152                        {
153                            log.trace( "Trying '*' match@" + nextMatch );
154                        }
155                        if ( match( basePattern, regexPattern, nextMatch, regexAt ) )
156                        {
157                            return true;
158                        }
159                        nextMatch = basePattern.indexOf( nextRegex, nextMatch + 1 );
160                    }
161                    log.trace( "No matches found." );
162                    return false;
163    
164                case '?':
165                    // this matches anything
166                    return match( basePattern, regexPattern, ++baseAt, ++regexAt );
167    
168                default:
169                    if ( log.isTraceEnabled() )
170                    {
171                        log.trace( "Camparing " + regexCurrent + " to " + basePattern.charAt( baseAt ) );
172                    }
173                    if ( regexCurrent == basePattern.charAt( baseAt ) )
174                    {
175                        // still got more to go
176                        return match( basePattern, regexPattern, ++baseAt, ++regexAt );
177                    }
178                    return false;
179            }
180        }
181    
182    }