KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > webdav > ant > Scanner


1 /*
2  * $Header: /home/cvs/jakarta-slide/webdavclient/ant/src/java/org/apache/webdav/ant/Scanner.java,v 1.3 2004/07/28 09:31:49 ib Exp $
3  * $Revision: 1.3 $
4  * $Date: 2004/07/28 09:31:49 $
5  * ========================================================================
6  * Copyright 2004 The Apache Software Foundation
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * 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, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ========================================================================
20  */

21 package org.apache.webdav.ant;
22
23 import java.util.ArrayList JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26
27 import org.apache.tools.ant.types.selectors.SelectorUtils;
28
29 /**
30  * Class for scanning a directory for files/directories that match a certain
31  * criteria.
32  * <p>
33  * These criteria consist of a set of include and exclude patterns. With these
34  * patterns, you can select which files you want to have included, and which
35  * files you want to have excluded.
36  * <p>
37  * The idea is simple. A given directory is recursively scanned for all files
38  * and directories. Each file/directory is matched against a set of include
39  * and exclude patterns. Only files/directories that match at least one
40  * pattern of the include pattern list, and don't match a pattern of the
41  * exclude pattern list will be placed in the list of files/directories found.
42  * <p>
43  * When no list of include patterns is supplied, "**" will be used, which
44  * means that everything will be matched. When no list of exclude patterns is
45  * supplied, an empty list is used, such that nothing will be excluded.
46  * <p>
47  * The pattern matching is done as follows:
48  * The name to be matched is split up in path segments. A path segment is the
49  * name of a directory or file, which is bounded by
50  * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
51  * E.g. "abc/def/ghi/xyz.java" is split up in the segments "abc", "def", "ghi"
52  * and "xyz.java".
53  * The same is done for the pattern against which should be matched.
54  * <p>
55  * Then the segments of the name and the pattern will be matched against each
56  * other. When '**' is used for a path segment in the pattern, then it matches
57  * zero or more path segments of the name.
58  * <p>
59  * There are special case regarding the use of <code>File.separator</code>s at
60  * the beginningof the pattern and the string to match:<br>
61  * When a pattern starts with a <code>File.separator</code>, the string
62  * to match must also start with a <code>File.separator</code>.
63  * When a pattern does not start with a <code>File.separator</code>, the
64  * string to match may not start with a <code>File.separator</code>.
65  * When one of these rules is not obeyed, the string will not
66  * match.
67  * <p>
68  * When a name path segment is matched against a pattern path segment, the
69  * following special characters can be used:
70  * '*' matches zero or more characters,
71  * '?' matches one character.
72  * <p>
73  * Examples:
74  * <p>
75  * "**\*.class" matches all .class files/dirs in a directory tree.
76  * <p>
77  * "test\a??.java" matches all files/dirs which start with an 'a', then two
78  * more characters and then ".java", in a directory called test.
79  * <p>
80  * "**" matches everything in a directory tree.
81  * <p>
82  * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where
83  * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
84  * <p>
85  * Example of usage:
86  * <pre>
87  * String[] includes = {"**\\*.class"};
88  * String[] excludes = {"modules\\*\\**"};
89  * ds.setIncludes(includes);
90  * ds.setExcludes(excludes);
91  * ds.setBasedir(new File("test"));
92  * ds.scan();
93  *
94  * System.out.println("FILES:");
95  * String[] files = ds.getIncludedFiles();
96  * for (int i = 0; i < files.length;i++) {
97  * System.out.println(files[i]);
98  * }
99  * </pre>
100  * This will scan a directory called test for .class files, but excludes all
101  * .class files in all directories under a directory called "modules"
102  *
103  */

104 public abstract class Scanner {
105    
106    protected static final String JavaDoc SEPARATOR = "/";
107    protected static final char SEPARATOR_CHAR = '/';
108
109 // /**
110
// * Patterns that should be excluded by default.
111
// *
112
// * @see #addDefaultExcludes()
113
// */
114
// private final static String[] DEFAULTEXCLUDES = {
115
// "**/*~",
116
// "**/#*#",
117
// "**/%*%",
118
// "**/CVS",
119
// "**/CVS/*",
120
// "**/.cvsignore"
121
// };
122

123     /**
124      * The patterns for the files that should be included.
125      */

126     private List JavaDoc includes = new ArrayList JavaDoc();
127
128     /**
129      * The patterns for the files that should be excluded.
130      */

131     private List JavaDoc excludes = new ArrayList JavaDoc();
132
133     /**
134      * The files that where found and matched at least one includes, and matched
135      * no excludes.
136      */

137     protected List JavaDoc filesIncluded;
138
139     /**
140      * The files that where found and did not match any includes.
141      */

142     protected List JavaDoc filesNotIncluded;
143
144     /**
145      * The files that where found and matched at least one includes, and also
146      * matched at least one excludes.
147      */

148     protected List JavaDoc filesExcluded;
149
150     /**
151      * The directories that where found and matched at least one includes, and
152      * matched no excludes.
153      */

154     protected List JavaDoc dirsIncluded;
155
156     /**
157      * The directories that where found and did not match any includes.
158      */

159     protected List JavaDoc dirsNotIncluded;
160
161     /**
162      * The files that where found and matched at least one includes, and also
163      * matched at least one excludes.
164      */

165     protected List JavaDoc dirsExcluded;
166     
167     protected boolean isCaseSensitive = true;
168
169
170
171     /**
172      * Constructor.
173      */

174     public Scanner() {
175     }
176
177
178
179     /**
180      * Sets the set of include patterns to use. All '/' and '\' characters are
181      * replaced by <code>File.separatorChar</code>. So the separator used need
182      * not match <code>File.separatorChar</code>.
183      * <p>
184      * When a pattern ends with a '/' or '\', "**" is appended.
185      *
186      * @param includes list of include patterns
187      */

188     public void setIncludes(String JavaDoc[] includes) {
189        this.includes = new ArrayList JavaDoc();
190        addIncludes(includes);
191     }
192     public void addIncludes(String JavaDoc[] includes) {
193        if (includes != null) {
194           for(int i = 0; i < includes.length ; i++) {
195              this.includes.add(noramlizePattern(includes[i]));
196           }
197        }
198     }
199
200     /**
201      * Sets the set of exclude patterns to use. All '/' and '\' characters are
202      * replaced by <code>File.separatorChar</code>. So the separator used need
203      * not match <code>File.separatorChar</code>.
204      * <p>
205      * When a pattern ends with a '/' or '\', "**" is appended.
206      *
207      * @param excludes list of exclude patterns (a list of Strings)
208      */

209     public void setExcludes(String JavaDoc[] excludes) {
210        this.excludes = new ArrayList JavaDoc();
211        addExcludes(excludes);
212     }
213     public void addExcludes(String JavaDoc[] excludes) {
214        if (excludes != null) {
215           for (int i = 0; i < excludes.length; i++) {
216              this.excludes.add(noramlizePattern(excludes[i]));
217           }
218        }
219     }
220     
221     private String JavaDoc noramlizePattern(String JavaDoc pattern) {
222 // pattern = pattern.replace('/',getSeparatorChar()).
223
// replace('\\',getSeparatorChar());
224
if (pattern.endsWith(SEPARATOR)) {
225          pattern += "**";
226       }
227       return pattern;
228     }
229
230
231     public void setCaseSensitive(boolean val) {
232        this.isCaseSensitive = val;
233     }
234
235
236     /**
237      * Scans the base directory for files that match at least one include
238      * pattern, and don't match any exclude patterns.
239      *
240      * @exception IllegalStateException when basedir was set incorrecly
241      */

242     public abstract void scan();
243
244
245
246     /**
247      * Tests whether a name matches against at least one include pattern.
248      *
249      * @param name the name to match
250      * @return <code>true</code> when the name matches against at least one
251      * include pattern, <code>false</code> otherwise.
252      */

253     protected boolean isIncluded(String JavaDoc name) {
254         for (Iterator JavaDoc i = this.includes.iterator(); i.hasNext();) {
255             if (matchPath((String JavaDoc)i.next(), name, this.isCaseSensitive)) {
256                 return true;
257             }
258         }
259         return false;
260     }
261
262
263
264     /**
265      * Tests whether a name matches against at least one exclude pattern.
266      *
267      * @param name the name to match
268      * @return <code>true</code> when the name matches against at least one
269      * exclude pattern, <code>false</code> otherwise.
270      */

271     protected boolean isExcluded(String JavaDoc name) {
272         for (Iterator JavaDoc i = this.excludes.iterator(); i.hasNext();) {
273            if (matchPath((String JavaDoc)i.next(), name, this.isCaseSensitive)) {
274               return true;
275            }
276         }
277         return false;
278     }
279
280
281
282     /**
283      * Get the names of the files that matched at least one of the include
284      * patterns, an matched none of the exclude patterns.
285      * The names are relative to the basedir.
286      *
287      * @return the names of the files
288      */

289     public String JavaDoc[] getIncludedFiles() {
290         int count = filesIncluded.size();
291         String JavaDoc[] files = new String JavaDoc[count];
292         for (int i = 0; i < count; i++) {
293             files[i] = (String JavaDoc)filesIncluded.get(i);
294         }
295         return files;
296     }
297
298
299
300     /**
301      * Get the names of the files that matched at none of the include patterns.
302      * The names are relative to the basedir.
303      *
304      * @return the names of the files
305      */

306     public String JavaDoc[] getNotIncludedFiles() {
307         int count = filesNotIncluded.size();
308         String JavaDoc[] files = new String JavaDoc[count];
309         for (int i = 0; i < count; i++) {
310             files[i] = (String JavaDoc)filesNotIncluded.get(i);
311         }
312         return files;
313     }
314
315
316
317     /**
318      * Get the names of the files that matched at least one of the include
319      * patterns, an matched also at least one of the exclude patterns.
320      * The names are relative to the basedir.
321      *
322      * @return the names of the files
323      */

324     public String JavaDoc[] getExcludedFiles() {
325         int count = filesExcluded.size();
326         String JavaDoc[] files = new String JavaDoc[count];
327         for (int i = 0; i < count; i++) {
328             files[i] = (String JavaDoc)filesExcluded.get(i);
329         }
330         return files;
331     }
332
333
334
335     /**
336      * Get the names of the directories that matched at least one of the include
337      * patterns, an matched none of the exclude patterns.
338      * The names are relative to the basedir.
339      *
340      * @return the names of the directories
341      */

342     public String JavaDoc[] getIncludedDirectories() {
343         int count = dirsIncluded.size();
344         String JavaDoc[] directories = new String JavaDoc[count];
345         for (int i = 0; i < count; i++) {
346             directories[i] = (String JavaDoc)dirsIncluded.get(i);
347         }
348         return directories;
349     }
350
351
352
353     /**
354      * Get the names of the directories that matched at none of the include
355      * patterns.
356      * The names are relative to the basedir.
357      *
358      * @return the names of the directories
359      */

360     public String JavaDoc[] getNotIncludedDirectories() {
361         int count = dirsNotIncluded.size();
362         String JavaDoc[] directories = new String JavaDoc[count];
363         for (int i = 0; i < count; i++) {
364             directories[i] = (String JavaDoc)dirsNotIncluded.get(i);
365         }
366         return directories;
367     }
368
369
370
371     /**
372      * Get the names of the directories that matched at least one of the include
373      * patterns, an matched also at least one of the exclude patterns.
374      * The names are relative to the basedir.
375      *
376      * @return the names of the directories
377      */

378     public String JavaDoc[] getExcludedDirectories() {
379         int count = dirsExcluded.size();
380         String JavaDoc[] directories = new String JavaDoc[count];
381         for (int i = 0; i < count; i++) {
382             directories[i] = (String JavaDoc)dirsExcluded.get(i);
383         }
384         return directories;
385     }
386
387
388
389 // /**
390
// * Adds the array with default exclusions to the current exclusions set.
391
// *
392
// */
393
// public void addDefaultExcludes() {
394
// if (this.excludes == null) {
395
// this.excludes = new ArrayList();
396
// }
397
// excludes.addAll(Arrays.asList(DEFAULTEXCLUDES));
398
// }
399

400 // protected abstract String getSeparator();
401
//
402
// protected char getSeparatorChar() {
403
// return ((getSeparator().length() > 0) ? getSeparator().charAt(0) : '/');
404
// }
405

406
407     /**
408      * Tests whether or not a string matches against a pattern.
409      * The pattern may contain two special characters:<br>
410      * '*' means zero or more characters<br>
411      * '?' means one and only one character
412      *
413      * @param pattern The pattern to match against.
414      * Must not be <code>null</code>.
415      * @param str The string which must be matched against the pattern.
416      * Must not be <code>null</code>.
417      * @param isCaseSensitive Whether or not matching should be performed
418      * case sensitively.
419      * @see SelectorUtils#match(java.lang.String, java.lang.String, boolean)
420      *
421      * @return <code>true</code> if the string matches against the pattern,
422      * or <code>false</code> otherwise.
423      */

424     public static boolean match(String JavaDoc pattern, String JavaDoc str,
425                                 boolean isCaseSensitive)
426     {
427        return SelectorUtils.match(pattern, str, isCaseSensitive);
428     }
429
430    /**
431      * Tests whether or not a given path matches a given pattern.
432      *
433      * @param pattern The pattern to match against. Must not be
434      * <code>null</code>.
435      * @param str The path to match, as a String. Must not be
436      * <code>null</code>.
437      * @param isCaseSensitive Whether or not matching should be performed
438      * case sensitively.
439      *
440      * @return <code>true</code> if the pattern matches against the string,
441      * or <code>false</code> otherwise.
442      * @see SelectorUtils#matchPath(java.lang.String, java.lang.String, boolean)
443      * (but this uses always File.Selector)
444      */

445     public static boolean matchPath(String JavaDoc pattern, String JavaDoc str,
446                                     boolean isCaseSensitive)
447     {
448         // When str starts with a File.separator, pattern has to start with a
449
// File.separator.
450
// When pattern starts with a File.separator, str has to start with a
451
// File.separator.
452
if (str.startsWith(SEPARATOR) != pattern.startsWith(SEPARATOR)) {
453             return false;
454         }
455    
456         String JavaDoc[] patDirs = tokenizePathAsArray(pattern);
457         String JavaDoc[] strDirs = tokenizePathAsArray(str);
458    
459         int patIdxStart = 0;
460         int patIdxEnd = patDirs.length - 1;
461         int strIdxStart = 0;
462         int strIdxEnd = strDirs.length - 1;
463    
464         // up to first '**'
465
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
466             String JavaDoc patDir = patDirs[patIdxStart];
467             if (patDir.equals("**")) {
468                 break;
469             }
470             if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
471                 patDirs = null;
472                 strDirs = null;
473                 return false;
474             }
475             patIdxStart++;
476             strIdxStart++;
477         }
478         if (strIdxStart > strIdxEnd) {
479             // String is exhausted
480
for (int i = patIdxStart; i <= patIdxEnd; i++) {
481                 if (!patDirs[i].equals("**")) {
482                     patDirs = null;
483                     strDirs = null;
484                     return false;
485                 }
486             }
487             return true;
488         } else {
489             if (patIdxStart > patIdxEnd) {
490                 // String not exhausted, but pattern is. Failure.
491
patDirs = null;
492                 strDirs = null;
493                 return false;
494             }
495         }
496    
497         // up to last '**'
498
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
499             String JavaDoc patDir = patDirs[patIdxEnd];
500             if (patDir.equals("**")) {
501                 break;
502             }
503             if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
504                 patDirs = null;
505                 strDirs = null;
506                 return false;
507             }
508             patIdxEnd--;
509             strIdxEnd--;
510         }
511         if (strIdxStart > strIdxEnd) {
512             // String is exhausted
513
for (int i = patIdxStart; i <= patIdxEnd; i++) {
514                 if (!patDirs[i].equals("**")) {
515                     patDirs = null;
516                     strDirs = null;
517                     return false;
518                 }
519             }
520             return true;
521         }
522    
523         while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
524             int patIdxTmp = -1;
525             for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
526                 if (patDirs[i].equals("**")) {
527                     patIdxTmp = i;
528                     break;
529                 }
530             }
531             if (patIdxTmp == patIdxStart + 1) {
532                 // '**/**' situation, so skip one
533
patIdxStart++;
534                 continue;
535             }
536             // Find the pattern between padIdxStart & padIdxTmp in str between
537
// strIdxStart & strIdxEnd
538
int patLength = (patIdxTmp - patIdxStart - 1);
539             int strLength = (strIdxEnd - strIdxStart + 1);
540             int foundIdx = -1;
541             strLoop:
542                         for (int i = 0; i <= strLength - patLength; i++) {
543                             for (int j = 0; j < patLength; j++) {
544                                 String JavaDoc subPat = patDirs[patIdxStart + j + 1];
545                                 String JavaDoc subStr = strDirs[strIdxStart + i + j];
546                                 if (!match(subPat, subStr, isCaseSensitive)) {
547                                     continue strLoop;
548                                 }
549                             }
550    
551                             foundIdx = strIdxStart + i;
552                             break;
553                         }
554    
555             if (foundIdx == -1) {
556                 patDirs = null;
557                 strDirs = null;
558                 return false;
559             }
560    
561             patIdxStart = patIdxTmp;
562             strIdxStart = foundIdx + patLength;
563         }
564    
565         for (int i = patIdxStart; i <= patIdxEnd; i++) {
566             if (!patDirs[i].equals("**")) {
567                 patDirs = null;
568                 strDirs = null;
569                 return false;
570             }
571         }
572    
573         return true;
574     }
575     
576     private static String JavaDoc[] tokenizePathAsArray(String JavaDoc path) {
577        char sep = SEPARATOR_CHAR;
578        int start = 0;
579        int len = path.length();
580        int count = 0;
581        for (int pos = 0; pos < len; pos++) {
582            if (path.charAt(pos) == sep) {
583                if (pos != start) {
584                    count++;
585                }
586                start = pos + 1;
587            }
588        }
589        if (len != start) {
590            count++;
591        }
592        String JavaDoc[] l = new String JavaDoc[count];
593        count = 0;
594        start = 0;
595        for (int pos = 0; pos < len; pos++) {
596            if (path.charAt(pos) == sep) {
597                if (pos != start) {
598                    String JavaDoc tok = path.substring(start, pos);
599                    l[count++] = tok;
600                }
601                start = pos + 1;
602            }
603        }
604        if (len != start) {
605            String JavaDoc tok = path.substring(start);
606            l[count/*++*/] = tok;
607        }
608        return l;
609    }
610 }
611
Popular Tags