1 /*
2 *
3 * The DbUnit Database Testing Framework
4 * Copyright (C)2002-2004, DbUnit.org
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21 package org.dbunit.dataset.filter;
22
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import java.util.Set;
27 import java.util.HashSet;
28 import java.util.Iterator;
29
30 /**
31 * @author Manuel Laflamme
32 * @since Apr 17, 2004
33 * @version $Revision$
34 */
35 class PatternMatcher
36 {
37
38 /**
39 * Logger for this class
40 */
41 private static final Logger logger = LoggerFactory.getLogger(PatternMatcher.class);
42
43 private final Set _acceptedNames = new HashSet();
44 private final Set _acceptedPatterns = new HashSet();
45
46 /**
47 * Add a new accepted pattern.
48 * The following wildcard characters are supported:
49 * '*' matches zero or more characters,
50 * '?' matches one character.
51 */
52 public void addPattern(String patternName)
53 {
54 logger.debug("addPattern(patternName={}) - start", patternName);
55
56 if (patternName.indexOf("*") != -1 || patternName.indexOf("?") != -1)
57 {
58 _acceptedPatterns.add(patternName);
59 }
60 else
61 {
62 _acceptedNames.add(patternName.toUpperCase());
63 }
64 }
65
66 public boolean isEmpty()
67 {
68 logger.debug("isEmpty() - start");
69
70 if (_acceptedNames.isEmpty() && _acceptedPatterns.isEmpty())
71 {
72 return true;
73 }
74
75 return false;
76 }
77
78 public boolean accept(String name)
79 {
80 logger.debug("accept(name={}) - start", name);
81
82 if (_acceptedNames.contains(name.toUpperCase()))
83 {
84 return true;
85 }
86
87 if (_acceptedPatterns.size() > 0)
88 {
89 for (Iterator it = _acceptedPatterns.iterator(); it.hasNext();)
90 {
91 String pattern = (String)it.next();
92 if (match(pattern, name, false))
93 {
94 return true;
95 }
96 }
97 }
98
99 return false;
100 }
101
102 /**
103 * Matches a string against a pattern. The pattern contains two special
104 * characters:
105 * '*' which means zero or more characters,
106 * '?' which means one and only one character.
107 *
108 * @param pattern the (non-null) pattern to match against
109 * @param str the (non-null) string that must be matched against the
110 * pattern
111 *
112 * @return <code>true</code> when the string matches against the pattern,
113 * <code>false</code> otherwise.
114 */
115 private boolean match(String pattern, String str, boolean isCaseSensitive)
116 {
117 if(logger.isDebugEnabled())
118 logger.debug("match(pattern={}, str={}, isCaseSensitive={}) - start",
119 new Object[]{pattern, str, String.valueOf(isCaseSensitive)});
120
121 /* Following pattern matching code taken from the Apache Ant project. */
122
123 char[] patArr = pattern.toCharArray();
124 char[] strArr = str.toCharArray();
125 int patIdxStart = 0;
126 int patIdxEnd = patArr.length - 1;
127 int strIdxStart = 0;
128 int strIdxEnd = strArr.length - 1;
129 char ch;
130
131 boolean containsStar = false;
132 for (int i = 0; i < patArr.length; i++)
133 {
134 if (patArr[i] == '*')
135 {
136 containsStar = true;
137 break;
138 }
139 }
140
141 if (!containsStar)
142 {
143 // No '*'s, so we make a shortcut
144 if (patIdxEnd != strIdxEnd)
145 {
146 return false; // Pattern and string do not have the same size
147 }
148 for (int i = 0; i <= patIdxEnd; i++)
149 {
150 ch = patArr[i];
151 if (ch != '?')
152 {
153 if (isCaseSensitive && ch != strArr[i])
154 {
155 return false;// Character mismatch
156 }
157 if (!isCaseSensitive && Character.toUpperCase(ch) !=
158 Character.toUpperCase(strArr[i]))
159 {
160 return false; // Character mismatch
161 }
162 }
163 }
164 return true; // String matches against pattern
165 }
166
167 if (patIdxEnd == 0)
168 {
169 return true; // Pattern contains only '*', which matches anything
170 }
171
172 // Process characters before first star
173 while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd)
174 {
175 if (ch != '?')
176 {
177 if (isCaseSensitive && ch != strArr[strIdxStart])
178 {
179 return false;// Character mismatch
180 }
181 if (!isCaseSensitive && Character.toUpperCase(ch) !=
182 Character.toUpperCase(strArr[strIdxStart]))
183 {
184 return false;// Character mismatch
185 }
186 }
187 patIdxStart++;
188 strIdxStart++;
189 }
190 if (strIdxStart > strIdxEnd)
191 {
192 // All characters in the string are used. Check if only '*'s are
193 // left in the pattern. If so, we succeeded. Otherwise failure.
194 for (int i = patIdxStart; i <= patIdxEnd; i++)
195 {
196 if (patArr[i] != '*')
197 {
198 return false;
199 }
200 }
201 return true;
202 }
203
204 // Process characters after last star
205 while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd)
206 {
207 if (ch != '?')
208 {
209 if (isCaseSensitive && ch != strArr[strIdxEnd])
210 {
211 return false;// Character mismatch
212 }
213 if (!isCaseSensitive && Character.toUpperCase(ch) !=
214 Character.toUpperCase(strArr[strIdxEnd]))
215 {
216 return false;// Character mismatch
217 }
218 }
219 patIdxEnd--;
220 strIdxEnd--;
221 }
222 if (strIdxStart > strIdxEnd)
223 {
224 // All characters in the string are used. Check if only '*'s are
225 // left in the pattern. If so, we succeeded. Otherwise failure.
226 for (int i = patIdxStart; i <= patIdxEnd; i++)
227 {
228 if (patArr[i] != '*')
229 {
230 return false;
231 }
232 }
233 return true;
234 }
235
236 // process pattern between stars. padIdxStart and patIdxEnd point
237 // always to a '*'.
238 while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd)
239 {
240 int patIdxTmp = -1;
241 for (int i = patIdxStart + 1; i <= patIdxEnd; i++)
242 {
243 if (patArr[i] == '*')
244 {
245 patIdxTmp = i;
246 break;
247 }
248 }
249 if (patIdxTmp == patIdxStart + 1)
250 {
251 // Two stars next to each other, skip the first one.
252 patIdxStart++;
253 continue;
254 }
255 // Find the pattern between padIdxStart & padIdxTmp in str between
256 // strIdxStart & strIdxEnd
257 int patLength = (patIdxTmp - patIdxStart - 1);
258 int strLength = (strIdxEnd - strIdxStart + 1);
259 int foundIdx = -1;
260 strLoop:
261 for (int i = 0; i <= strLength - patLength; i++)
262 {
263 for (int j = 0; j < patLength; j++)
264 {
265 ch = patArr[patIdxStart + j + 1];
266 if (ch != '?')
267 {
268 if (isCaseSensitive && ch != strArr[strIdxStart + i + j])
269 {
270 continue strLoop;
271 }
272 if (!isCaseSensitive && Character.toUpperCase(ch) !=
273 Character.toUpperCase(strArr[strIdxStart + i + j]))
274 {
275 continue strLoop;
276 }
277 }
278 }
279
280 foundIdx = strIdxStart + i;
281 break;
282 }
283
284 if (foundIdx == -1)
285 {
286 return false;
287 }
288
289 patIdxStart = patIdxTmp;
290 strIdxStart = foundIdx + patLength;
291 }
292
293 // All characters in the string are used. Check if only '*'s are left
294 // in the pattern. If so, we succeeded. Otherwise failure.
295 for (int i = patIdxStart; i <= patIdxEnd; i++)
296 {
297 if (patArr[i] != '*')
298 {
299 return false;
300 }
301 }
302 return true;
303 }
304
305
306 public String toString()
307 {
308 final StringBuilder sb = new StringBuilder();
309 sb.append(getClass().getName()).append("[");
310 sb.append("_acceptedNames=").append(_acceptedNames);
311 sb.append(", _acceptedPatterns=").append(_acceptedPatterns);
312 sb.append("]");
313 return sb.toString();
314 }
315
316
317 }