View Javadoc
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         StringBuffer sb = new StringBuffer();
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 }