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 }