1 package org.apache.commons.digester3;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24
25 /**
26 * <p>
27 * Simple regex pattern matching algorithm.
28 * </p>
29 * <p>
30 * This uses just two wildcards:
31 * <ul>
32 * <li><code>*</code> matches any sequence of none, one or more characters
33 * <li><code>?</code> matches any one character
34 * </ul>
35 * Escaping these wildcards is not supported .
36 * </p>
37 *
38 * @since 1.5
39 */
40 public class SimpleRegexMatcher
41 extends RegexMatcher
42 {
43
44 // --------------------------------------------------------- Fields
45
46 /** Default log (class wide) */
47 private static final Log BASE_LOG = LogFactory.getLog( SimpleRegexMatcher.class );
48
49 /** Custom log (can be set per object) */
50 private Log log = BASE_LOG;
51
52 // --------------------------------------------------------- Properties
53
54 /**
55 * Gets the <code>Log</code> implementation.
56 *
57 * @return the <code>Log</code> implementation.
58 */
59 public Log getLog()
60 {
61 return log;
62 }
63
64 /**
65 * Sets the current <code>Log</code> implementation used by this class.
66 *
67 * @param log the current <code>Log</code> implementation used by this class.
68 */
69 public void setLog( Log log )
70 {
71 this.log = log;
72 }
73
74 // --------------------------------------------------------- Public Methods
75
76 /**
77 * {@inheritDoc}
78 */
79 @Override
80 public boolean match( String basePattern, String regexPattern )
81 {
82 // check for nulls
83 if ( basePattern == null || regexPattern == null )
84 {
85 return false;
86 }
87 return match( basePattern, regexPattern, 0, 0 );
88 }
89
90 // --------------------------------------------------------- Implementations Methods
91
92 /**
93 * Implementation of regex matching algorithm. This calls itself recursively.
94 *
95 * @param basePattern the standard digester path representing the element
96 * @param regexPattern the regex pattern the path will be tested against
97 * @param baseAt FIXME
98 * @param regexAt FIXME
99 */
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 }