001: /*
002: *
003: * The DbUnit Database Testing Framework
004: * Copyright (C)2002-2004, DbUnit.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: */
021: package org.dbunit.dataset.filter;
022:
023: import org.slf4j.Logger;
024: import org.slf4j.LoggerFactory;
025:
026: import java.util.Set;
027: import java.util.HashSet;
028: import java.util.Iterator;
029:
030: /**
031: * @author Manuel Laflamme
032: * @since Apr 17, 2004
033: * @version $Revision: 554 $
034: */
035: class PatternMatcher {
036:
037: /**
038: * Logger for this class
039: */
040: private static final Logger logger = LoggerFactory
041: .getLogger(PatternMatcher.class);
042:
043: private final Set _acceptedNames = new HashSet();
044: private final Set _acceptedPatterns = new HashSet();
045:
046: /**
047: * Add a new accepted pattern.
048: * The following wildcard characters are supported:
049: * '*' matches zero or more characters,
050: * '?' matches one character.
051: */
052: public void addPattern(String patternName) {
053: logger.debug("addPattern(patternName=" + patternName
054: + ") - start");
055:
056: if (patternName.indexOf("*") != -1
057: || patternName.indexOf("?") != -1) {
058: _acceptedPatterns.add(patternName);
059: } else {
060: _acceptedNames.add(patternName.toUpperCase());
061: }
062: }
063:
064: public boolean isEmpty() {
065: logger.debug("isEmpty() - start");
066:
067: if (_acceptedNames.isEmpty() && _acceptedPatterns.isEmpty()) {
068: return true;
069: }
070:
071: return false;
072: }
073:
074: public boolean accept(String name) {
075: logger.debug("accept(name=" + name + ") - start");
076:
077: if (_acceptedNames.contains(name.toUpperCase())) {
078: return true;
079: }
080:
081: if (_acceptedPatterns.size() > 0) {
082: for (Iterator it = _acceptedPatterns.iterator(); it
083: .hasNext();) {
084: String pattern = (String) it.next();
085: if (match(pattern, name, false)) {
086: return true;
087: }
088: }
089: }
090:
091: return false;
092: }
093:
094: /**
095: * Matches a string against a pattern. The pattern contains two special
096: * characters:
097: * '*' which means zero or more characters,
098: * '?' which means one and only one character.
099: *
100: * @param pattern the (non-null) pattern to match against
101: * @param str the (non-null) string that must be matched against the
102: * pattern
103: *
104: * @return <code>true</code> when the string matches against the pattern,
105: * <code>false</code> otherwise.
106: */
107: private boolean match(String pattern, String str,
108: boolean isCaseSensitive) {
109: logger.debug("match(pattern=" + pattern + ", str=" + str
110: + ", isCaseSensitive=" + isCaseSensitive + ") - start");
111:
112: /* Following pattern matching code taken from the Apache Ant project. */
113:
114: char[] patArr = pattern.toCharArray();
115: char[] strArr = str.toCharArray();
116: int patIdxStart = 0;
117: int patIdxEnd = patArr.length - 1;
118: int strIdxStart = 0;
119: int strIdxEnd = strArr.length - 1;
120: char ch;
121:
122: boolean containsStar = false;
123: for (int i = 0; i < patArr.length; i++) {
124: if (patArr[i] == '*') {
125: containsStar = true;
126: break;
127: }
128: }
129:
130: if (!containsStar) {
131: // No '*'s, so we make a shortcut
132: if (patIdxEnd != strIdxEnd) {
133: return false; // Pattern and string do not have the same size
134: }
135: for (int i = 0; i <= patIdxEnd; i++) {
136: ch = patArr[i];
137: if (ch != '?') {
138: if (isCaseSensitive && ch != strArr[i]) {
139: return false;// Character mismatch
140: }
141: if (!isCaseSensitive
142: && Character.toUpperCase(ch) != Character
143: .toUpperCase(strArr[i])) {
144: return false; // Character mismatch
145: }
146: }
147: }
148: return true; // String matches against pattern
149: }
150:
151: if (patIdxEnd == 0) {
152: return true; // Pattern contains only '*', which matches anything
153: }
154:
155: // Process characters before first star
156: while ((ch = patArr[patIdxStart]) != '*'
157: && strIdxStart <= strIdxEnd) {
158: if (ch != '?') {
159: if (isCaseSensitive && ch != strArr[strIdxStart]) {
160: return false;// Character mismatch
161: }
162: if (!isCaseSensitive
163: && Character.toUpperCase(ch) != Character
164: .toUpperCase(strArr[strIdxStart])) {
165: return false;// Character mismatch
166: }
167: }
168: patIdxStart++;
169: strIdxStart++;
170: }
171: if (strIdxStart > strIdxEnd) {
172: // All characters in the string are used. Check if only '*'s are
173: // left in the pattern. If so, we succeeded. Otherwise failure.
174: for (int i = patIdxStart; i <= patIdxEnd; i++) {
175: if (patArr[i] != '*') {
176: return false;
177: }
178: }
179: return true;
180: }
181:
182: // Process characters after last star
183: while ((ch = patArr[patIdxEnd]) != '*'
184: && strIdxStart <= strIdxEnd) {
185: if (ch != '?') {
186: if (isCaseSensitive && ch != strArr[strIdxEnd]) {
187: return false;// Character mismatch
188: }
189: if (!isCaseSensitive
190: && Character.toUpperCase(ch) != Character
191: .toUpperCase(strArr[strIdxEnd])) {
192: return false;// Character mismatch
193: }
194: }
195: patIdxEnd--;
196: strIdxEnd--;
197: }
198: if (strIdxStart > strIdxEnd) {
199: // All characters in the string are used. Check if only '*'s are
200: // left in the pattern. If so, we succeeded. Otherwise failure.
201: for (int i = patIdxStart; i <= patIdxEnd; i++) {
202: if (patArr[i] != '*') {
203: return false;
204: }
205: }
206: return true;
207: }
208:
209: // process pattern between stars. padIdxStart and patIdxEnd point
210: // always to a '*'.
211: while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
212: int patIdxTmp = -1;
213: for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
214: if (patArr[i] == '*') {
215: patIdxTmp = i;
216: break;
217: }
218: }
219: if (patIdxTmp == patIdxStart + 1) {
220: // Two stars next to each other, skip the first one.
221: patIdxStart++;
222: continue;
223: }
224: // Find the pattern between padIdxStart & padIdxTmp in str between
225: // strIdxStart & strIdxEnd
226: int patLength = (patIdxTmp - patIdxStart - 1);
227: int strLength = (strIdxEnd - strIdxStart + 1);
228: int foundIdx = -1;
229: strLoop: for (int i = 0; i <= strLength - patLength; i++) {
230: for (int j = 0; j < patLength; j++) {
231: ch = patArr[patIdxStart + j + 1];
232: if (ch != '?') {
233: if (isCaseSensitive
234: && ch != strArr[strIdxStart + i + j]) {
235: continue strLoop;
236: }
237: if (!isCaseSensitive
238: && Character.toUpperCase(ch) != Character
239: .toUpperCase(strArr[strIdxStart
240: + i + j])) {
241: continue strLoop;
242: }
243: }
244: }
245:
246: foundIdx = strIdxStart + i;
247: break;
248: }
249:
250: if (foundIdx == -1) {
251: return false;
252: }
253:
254: patIdxStart = patIdxTmp;
255: strIdxStart = foundIdx + patLength;
256: }
257:
258: // All characters in the string are used. Check if only '*'s are left
259: // in the pattern. If so, we succeeded. Otherwise failure.
260: for (int i = patIdxStart; i <= patIdxEnd; i++) {
261: if (patArr[i] != '*') {
262: return false;
263: }
264: }
265: return true;
266: }
267:
268: }
|