View Javadoc

1   /*
2    * $Id: WildcardFileFilter.java,v 1.4 2006/09/06 17:48:19 edankert Exp $
3    *
4    * The contents of this file are subject to the Mozilla Public License 
5    * Version 1.1 (the "License"); you may not use this file except in 
6    * compliance with the License. You may obtain a copy of the License at 
7    * http://www.mozilla.org/MPL/ 
8    *
9    * Software distributed under the License is distributed on an "AS IS" basis, 
10   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
11   * for the specific language governing rights and limitations under the License.
12   *
13   * The Original Code is XML Hammer code. (org.xmlhammer.*)
14   *
15   * The Initial Developer of the Original Code is Edwin Dankert. Portions created 
16   * by the Initial Developer are Copyright (C) 2002 - 2006 the Initial Developer. 
17   * All Rights Reserved.
18   *
19   * Contributor(s): Edwin Dankert <edankert@gmail.com>
20   */
21  
22  
23  package org.xmlhammer.gui.util;
24  
25  import java.io.File;
26  import java.util.ArrayList;
27  import java.util.List;
28  
29  import org.apache.commons.io.filefilter.AbstractFileFilter;
30  
31  /***
32   * Filters files using supplied wildcard(s).
33   * <p/>
34   * See org.apache.commons.io.find.FilenameUtils.wildcardMatch() for wildcard matching rules
35   * <p/>
36   *
37   * <p/>
38   * e.g.
39   * <pre>
40   * File dir = new File(".");
41   * FileFilter fileFilter = new WildcardFilter("*test*.java~*~");
42   * File[] files = dir.listFiles(fileFilter);
43   * for (int i = 0; i < files.length; i++) {
44   *   System.out.println(files[i]);
45   * }
46   * </pre>
47   *
48   * @author Jason Anderson
49   * @version $Revision: 1.4 $ $Date: 2006/09/06 17:48:19 $
50   * @since Commons IO 1.1
51   */
52  public class WildcardFileFilter extends AbstractFileFilter {
53  
54      /*** The patterns that will be used to match filenames */
55      private String[] patterns = null;
56  
57      /***
58       * Construct a new wildcard filter for a single wildcard
59       *
60       * @param wildcard wildcard to match
61       * @throws IllegalArgumentException if the pattern is null
62       */
63      public WildcardFileFilter( String pattern) {
64          if (pattern == null) {
65              throw new java.lang.IllegalArgumentException();
66          }
67      
68          patterns = new String[] { pattern };
69      }
70  
71      /***
72       * Construct a new wildcard filter for an array of wildcards
73       *
74       * @param wildcards wildcards to match
75       * @throws IllegalArgumentException if the pattern array is null
76       */
77      public WildcardFileFilter(String[] pattern) {
78          if (pattern == null) {
79              throw new java.lang.IllegalArgumentException();
80          }
81      
82          this.patterns = pattern;
83      }
84  
85      /***
86       * Construct a new wildcard filter for a list of wildcards
87       *
88       * @param wildcards list of wildcards to match
89       * @throws IllegalArgumentException if the pattern list is null
90       * @throws ClassCastException if the list does not contain Strings
91       */
92      public WildcardFileFilter( List<String> patterns) {
93          if ( patterns == null) {
94              throw new java.lang.IllegalArgumentException();
95          }
96      
97          this.patterns = patterns.toArray(new String[patterns.size()]);
98      }
99  
100     /***
101      * Checks to see if the filename matches one of the wildcards.
102      *
103      * @param dir   the file directory
104      * @param name  the filename
105      * @return true if the filename matches one of the wildcards
106      */
107     public boolean accept(File dir, String name) {
108         if (dir != null && new File(dir, name).isDirectory()) {
109             return false;
110         }
111     
112         for (int i = 0; i < patterns.length; i++) {
113             if ( WildcardFileFilter.wildcardMatch(name, patterns[i])) {
114                 return true;
115             }
116         }
117     
118         return false;
119     }
120 
121     /***
122      * Checks to see if the filename matches one of the wildcards.
123      *
124      * @param file the file to check
125      * @return true if the filename matches one of the wildcards
126      */
127     public boolean accept(File file) {
128         if (file.isDirectory()) {
129             return false;
130         }
131     
132         for (int i = 0; i < patterns.length; i++) {
133             if ( wildcardMatch(file.getName(), patterns[i])) {
134                 return true;
135             }
136         }
137     
138         return false;
139     }
140     
141     //-----------------------------------------------------------------------
142     /***
143      * Checks a filename to see if it matches the specified wildcard matcher.
144      * <p>
145      * The wildcard matcher uses the characters '?' and '*' to represent a
146      * single or multiple wildcard characters.
147      * This is the same as often found on Dos/Unix command lines.
148      * The extension check is case sensitive on Unix and case insensitive on Windows.
149      * <pre>
150      * wildcardMatch("c.txt", "*.txt")      --> true
151      * wildcardMatch("c.txt", "*.jpg")      --> false
152      * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
153      * wildcardMatch("c.txt", "*.???")      --> true
154      * wildcardMatch("c.txt", "*.????")     --> false
155      * </pre>
156      * 
157      * @param filename  the filename to match on
158      * @param wildcardMatcher  the wildcard string to match against
159      * @return true if the filename matches the wilcard string
160      */
161     public static boolean wildcardMatch(String filename, String wildcardMatcher) {
162         if (filename == null && wildcardMatcher == null) {
163             return true;
164         }
165         if (filename == null || wildcardMatcher == null) {
166             return false;
167         }
168         if ( File.separatorChar == '//') {
169             filename = filename.toLowerCase();
170             wildcardMatcher = wildcardMatcher.toLowerCase();
171         }
172         String[] wcs = splitOnTokens(wildcardMatcher);
173         boolean anyChars = false;
174         int textIdx = 0;
175         int wcsIdx = 0;
176   
177         // loop whilst tokens and text left to process
178         while (wcsIdx < wcs.length && textIdx < filename.length()) {
179   
180             if (wcs[wcsIdx].equals("?")) {
181                 // ? so move to next text char
182                 textIdx++;
183                 anyChars = false;
184                 
185             } else if (wcs[wcsIdx].equals("*")) {
186                 // set any chars status
187                 anyChars = true;
188                 
189             } else {
190                 // matching text token
191                 if (anyChars) {
192                     // any chars then try to locate text token
193                     textIdx = filename.indexOf(wcs[wcsIdx], textIdx);
194                     if (textIdx == -1) {
195                         // token not found
196                         return false;
197                     }
198                 } else {
199                     // matching from current position
200                     if (!filename.startsWith(wcs[wcsIdx], textIdx)) {
201                         // couldnt match token
202                         return false;
203                     }
204                 }
205   
206                 // matched text token, move text index to end of matched token
207                 textIdx += wcs[wcsIdx].length();
208                 anyChars = false;
209             }
210   
211             wcsIdx++;
212         }
213 
214         // didnt match all wildcards
215         if (wcsIdx < wcs.length) {
216             // ok if one remaining and wildcard or empty
217             if (wcsIdx + 1 != wcs.length || !(wcs[wcsIdx].equals("*") || wcs[wcsIdx].equals("")) ) {
218                 return false;
219             }
220         }
221   
222         // ran out of text chars
223         if (textIdx > filename.length()) {
224            return false;
225         }
226   
227         // didnt match all text chars, only ok if any chars set
228         if (textIdx < filename.length() && !anyChars) {
229             return false;
230         }
231   
232         return true;
233     }
234 
235     // used by wildcardMatch
236     // package level so a unit test may run on this
237     static String[] splitOnTokens(String text) {
238         if (text.indexOf("?") == -1 && text.indexOf("*") == -1) {
239             return new String[] { text };
240         }
241 
242         char[] array = text.toCharArray();
243         ArrayList<String> list = new ArrayList<String>();
244         StringBuffer buffer = new StringBuffer();
245         for (int i = 0; i < array.length; i++) {
246 			if (array[i] == '?' || array[i] == '*') {
247 				if (buffer.length() != 0) {
248 					list.add(buffer.toString());
249 					buffer.setLength(0);
250 				}
251 				list.add(new String(new char[] { array[i] }));
252 			} else {
253 				buffer.append(array[i]);
254 			}
255 		}
256         if (buffer.length() != 0) {
257             list.add(buffer.toString());
258         }
259 
260         return list.toArray(new String[0]);
261     }
262 }