KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > DirectoryScanner


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant;
20
21 import java.io.File JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.Vector JavaDoc;
31
32 import org.apache.tools.ant.taskdefs.condition.Os;
33 import org.apache.tools.ant.types.Resource;
34 import org.apache.tools.ant.types.ResourceFactory;
35 import org.apache.tools.ant.types.resources.FileResource;
36 import org.apache.tools.ant.types.selectors.FileSelector;
37 import org.apache.tools.ant.types.selectors.SelectorScanner;
38 import org.apache.tools.ant.types.selectors.SelectorUtils;
39 import org.apache.tools.ant.util.FileUtils;
40
41 /**
42  * Class for scanning a directory for files/directories which match certain
43  * criteria.
44  * <p>
45  * These criteria consist of selectors and patterns which have been specified.
46  * With the selectors you can select which files you want to have included.
47  * Files which are not selected are excluded. With patterns you can include
48  * or exclude files based on their filename.
49  * <p>
50  * The idea is simple. A given directory is recursively scanned for all files
51  * and directories. Each file/directory is matched against a set of selectors,
52  * including special support for matching against filenames with include and
53  * and exclude patterns. Only files/directories which match at least one
54  * pattern of the include pattern list or other file selector, and don't match
55  * any pattern of the exclude pattern list or fail to match against a required
56  * selector will be placed in the list of files/directories found.
57  * <p>
58  * When no list of include patterns is supplied, "**" will be used, which
59  * means that everything will be matched. When no list of exclude patterns is
60  * supplied, an empty list is used, such that nothing will be excluded. When
61  * no selectors are supplied, none are applied.
62  * <p>
63  * The filename pattern matching is done as follows:
64  * The name to be matched is split up in path segments. A path segment is the
65  * name of a directory or file, which is bounded by
66  * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
67  * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
68  * "def","ghi" and "xyz.java".
69  * The same is done for the pattern against which should be matched.
70  * <p>
71  * The segments of the name and the pattern are then matched against each
72  * other. When '**' is used for a path segment in the pattern, it matches
73  * zero or more path segments of the name.
74  * <p>
75  * There is a special case regarding the use of <code>File.separator</code>s
76  * at the beginning of the pattern and the string to match:<br>
77  * When a pattern starts with a <code>File.separator</code>, the string
78  * to match must also start with a <code>File.separator</code>.
79  * When a pattern does not start with a <code>File.separator</code>, the
80  * string to match may not start with a <code>File.separator</code>.
81  * When one of these rules is not obeyed, the string will not
82  * match.
83  * <p>
84  * When a name path segment is matched against a pattern path segment, the
85  * following special characters can be used:<br>
86  * '*' matches zero or more characters<br>
87  * '?' matches one character.
88  * <p>
89  * Examples:
90  * <p>
91  * "**\*.class" matches all .class files/dirs in a directory tree.
92  * <p>
93  * "test\a??.java" matches all files/dirs which start with an 'a', then two
94  * more characters and then ".java", in a directory called test.
95  * <p>
96  * "**" matches everything in a directory tree.
97  * <p>
98  * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
99  * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
100  * <p>
101  * Case sensitivity may be turned off if necessary. By default, it is
102  * turned on.
103  * <p>
104  * Example of usage:
105  * <pre>
106  * String[] includes = {"**\\*.class"};
107  * String[] excludes = {"modules\\*\\**"};
108  * ds.setIncludes(includes);
109  * ds.setExcludes(excludes);
110  * ds.setBasedir(new File("test"));
111  * ds.setCaseSensitive(true);
112  * ds.scan();
113  *
114  * System.out.println("FILES:");
115  * String[] files = ds.getIncludedFiles();
116  * for (int i = 0; i < files.length; i++) {
117  * System.out.println(files[i]);
118  * }
119  * </pre>
120  * This will scan a directory called test for .class files, but excludes all
121  * files in all proper subdirectories of a directory called "modules"
122  *
123  */

124 public class DirectoryScanner
125        implements FileScanner, SelectorScanner, ResourceFactory {
126
127     /** Is OpenVMS the operating system we're running on? */
128     private static final boolean ON_VMS = Os.isFamily("openvms");
129
130     /**
131      * Patterns which should be excluded by default.
132      *
133      * <p>Note that you can now add patterns to the list of default
134      * excludes. Added patterns will not become part of this array
135      * that has only been kept around for backwards compatibility
136      * reasons.</p>
137      *
138      * @deprecated since 1.6.x.
139      * Use the {@link #getDefaultExcludes getDefaultExcludes}
140      * method instead.
141      */

142     protected static final String JavaDoc[] DEFAULTEXCLUDES = {
143         // Miscellaneous typical temporary files
144
"**/*~",
145         "**/#*#",
146         "**/.#*",
147         "**/%*%",
148         "**/._*",
149
150         // CVS
151
"**/CVS",
152         "**/CVS/**",
153         "**/.cvsignore",
154
155         // SCCS
156
"**/SCCS",
157         "**/SCCS/**",
158
159         // Visual SourceSafe
160
"**/vssver.scc",
161
162         // Subversion
163
"**/.svn",
164         "**/.svn/**",
165
166         // Mac
167
"**/.DS_Store"
168     };
169
170     /** Helper. */
171     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
172
173     /** iterations for case-sensitive scanning. */
174     private static final boolean[] CS_SCAN_ONLY = new boolean[] {true};
175
176     /** iterations for non-case-sensitive scanning. */
177     private static final boolean[] CS_THEN_NON_CS = new boolean[] {true, false};
178
179     /**
180      * Patterns which should be excluded by default.
181      *
182      * @see #addDefaultExcludes()
183      */

184     private static Vector JavaDoc defaultExcludes = new Vector JavaDoc();
185     static {
186         resetDefaultExcludes();
187     }
188
189     // CheckStyle:VisibilityModifier OFF - bc
190

191     /** The base directory to be scanned. */
192     protected File JavaDoc basedir;
193
194     /** The patterns for the files to be included. */
195     protected String JavaDoc[] includes;
196
197     /** The patterns for the files to be excluded. */
198     protected String JavaDoc[] excludes;
199
200     /** Selectors that will filter which files are in our candidate list. */
201     protected FileSelector[] selectors = null;
202
203     /**
204      * The files which matched at least one include and no excludes
205      * and were selected.
206      */

207     protected Vector JavaDoc filesIncluded;
208
209     /** The files which did not match any includes or selectors. */
210     protected Vector JavaDoc filesNotIncluded;
211
212     /**
213      * The files which matched at least one include and at least
214      * one exclude.
215      */

216     protected Vector JavaDoc filesExcluded;
217
218     /**
219      * The directories which matched at least one include and no excludes
220      * and were selected.
221      */

222     protected Vector JavaDoc dirsIncluded;
223
224     /** The directories which were found and did not match any includes. */
225     protected Vector JavaDoc dirsNotIncluded;
226
227     /**
228      * The directories which matched at least one include and at least one
229      * exclude.
230      */

231     protected Vector JavaDoc dirsExcluded;
232
233     /**
234      * The files which matched at least one include and no excludes and
235      * which a selector discarded.
236      */

237     protected Vector JavaDoc filesDeselected;
238
239     /**
240      * The directories which matched at least one include and no excludes
241      * but which a selector discarded.
242      */

243     protected Vector JavaDoc dirsDeselected;
244
245     /** Whether or not our results were built by a slow scan. */
246     protected boolean haveSlowResults = false;
247
248     /**
249      * Whether or not the file system should be treated as a case sensitive
250      * one.
251      */

252     protected boolean isCaseSensitive = true;
253
254     /**
255      * Whether or not symbolic links should be followed.
256      *
257      * @since Ant 1.5
258      */

259     private boolean followSymlinks = true;
260
261     /** Whether or not everything tested so far has been included. */
262     protected boolean everythingIncluded = true;
263
264     // CheckStyle:VisibilityModifier ON
265

266     /**
267      * Temporary table to speed up the various scanning methods.
268      *
269      * @since Ant 1.6
270      */

271     private Map JavaDoc fileListMap = new HashMap JavaDoc();
272
273     /**
274      * List of all scanned directories.
275      *
276      * @since Ant 1.6
277      */

278     private Set JavaDoc scannedDirs = new HashSet JavaDoc();
279
280     /**
281      * Set of all include patterns that are full file names and don't
282      * contain any wildcards.
283      *
284      * <p>If this instance is not case sensitive, the file names get
285      * turned to lower case.</p>
286      *
287      * <p>Gets lazily initialized on the first invocation of
288      * isIncluded or isExcluded and cleared at the end of the scan
289      * method (cleared in clearCaches, actually).</p>
290      *
291      * @since Ant 1.6.3
292      */

293     private Set JavaDoc includeNonPatterns = new HashSet JavaDoc();
294
295     /**
296      * Set of all include patterns that are full file names and don't
297      * contain any wildcards.
298      *
299      * <p>If this instance is not case sensitive, the file names get
300      * turned to lower case.</p>
301      *
302      * <p>Gets lazily initialized on the first invocation of
303      * isIncluded or isExcluded and cleared at the end of the scan
304      * method (cleared in clearCaches, actually).</p>
305      *
306      * @since Ant 1.6.3
307      */

308     private Set JavaDoc excludeNonPatterns = new HashSet JavaDoc();
309
310     /**
311      * Array of all include patterns that contain wildcards.
312      *
313      * <p>Gets lazily initialized on the first invocation of
314      * isIncluded or isExcluded and cleared at the end of the scan
315      * method (cleared in clearCaches, actually).</p>
316      *
317      * @since Ant 1.6.3
318      */

319     private String JavaDoc[] includePatterns;
320
321     /**
322      * Array of all exclude patterns that contain wildcards.
323      *
324      * <p>Gets lazily initialized on the first invocation of
325      * isIncluded or isExcluded and cleared at the end of the scan
326      * method (cleared in clearCaches, actually).</p>
327      *
328      * @since Ant 1.6.3
329      */

330     private String JavaDoc[] excludePatterns;
331
332     /**
333      * Have the non-pattern sets and pattern arrays for in- and
334      * excludes been initialized?
335      *
336      * @since Ant 1.6.3
337      */

338     private boolean areNonPatternSetsReady = false;
339
340     /**
341      * Scanning flag.
342      *
343      * @since Ant 1.6.3
344      */

345     private boolean scanning = false;
346
347     /**
348      * Scanning lock.
349      *
350      * @since Ant 1.6.3
351      */

352     private Object JavaDoc scanLock = new Object JavaDoc();
353
354     /**
355      * Slow scanning flag.
356      *
357      * @since Ant 1.6.3
358      */

359     private boolean slowScanning = false;
360
361     /**
362      * Slow scanning lock.
363      *
364      * @since Ant 1.6.3
365      */

366     private Object JavaDoc slowScanLock = new Object JavaDoc();
367
368     /**
369      * Exception thrown during scan.
370      *
371      * @since Ant 1.6.3
372      */

373     private IllegalStateException JavaDoc illegal = null;
374
375     /**
376      * Sole constructor.
377      */

378     public DirectoryScanner() {
379     }
380
381     /**
382      * Test whether or not a given path matches the start of a given
383      * pattern up to the first "**".
384      * <p>
385      * This is not a general purpose test and should only be used if you
386      * can live with false positives. For example, <code>pattern=**\a</code>
387      * and <code>str=b</code> will yield <code>true</code>.
388      *
389      * @param pattern The pattern to match against. Must not be
390      * <code>null</code>.
391      * @param str The path to match, as a String. Must not be
392      * <code>null</code>.
393      *
394      * @return whether or not a given path matches the start of a given
395      * pattern up to the first "**".
396      */

397     protected static boolean matchPatternStart(String JavaDoc pattern, String JavaDoc str) {
398         return SelectorUtils.matchPatternStart(pattern, str);
399     }
400
401     /**
402      * Test whether or not a given path matches the start of a given
403      * pattern up to the first "**".
404      * <p>
405      * This is not a general purpose test and should only be used if you
406      * can live with false positives. For example, <code>pattern=**\a</code>
407      * and <code>str=b</code> will yield <code>true</code>.
408      *
409      * @param pattern The pattern to match against. Must not be
410      * <code>null</code>.
411      * @param str The path to match, as a String. Must not be
412      * <code>null</code>.
413      * @param isCaseSensitive Whether or not matching should be performed
414      * case sensitively.
415      *
416      * @return whether or not a given path matches the start of a given
417      * pattern up to the first "**".
418      */

419     protected static boolean matchPatternStart(String JavaDoc pattern, String JavaDoc str,
420                                                boolean isCaseSensitive) {
421         return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
422     }
423
424     /**
425      * Test whether or not a given path matches a given pattern.
426      *
427      * @param pattern The pattern to match against. Must not be
428      * <code>null</code>.
429      * @param str The path to match, as a String. Must not be
430      * <code>null</code>.
431      *
432      * @return <code>true</code> if the pattern matches against the string,
433      * or <code>false</code> otherwise.
434      */

435     protected static boolean matchPath(String JavaDoc pattern, String JavaDoc str) {
436         return SelectorUtils.matchPath(pattern, str);
437     }
438
439     /**
440      * Test whether or not a given path matches a given pattern.
441      *
442      * @param pattern The pattern to match against. Must not be
443      * <code>null</code>.
444      * @param str The path to match, as a String. Must not be
445      * <code>null</code>.
446      * @param isCaseSensitive Whether or not matching should be performed
447      * case sensitively.
448      *
449      * @return <code>true</code> if the pattern matches against the string,
450      * or <code>false</code> otherwise.
451      */

452     protected static boolean matchPath(String JavaDoc pattern, String JavaDoc str,
453                                        boolean isCaseSensitive) {
454         return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
455     }
456
457     /**
458      * Test whether or not a string matches against a pattern.
459      * The pattern may contain two special characters:<br>
460      * '*' means zero or more characters<br>
461      * '?' means one and only one character
462      *
463      * @param pattern The pattern to match against.
464      * Must not be <code>null</code>.
465      * @param str The string which must be matched against the pattern.
466      * Must not be <code>null</code>.
467      *
468      * @return <code>true</code> if the string matches against the pattern,
469      * or <code>false</code> otherwise.
470      */

471     public static boolean match(String JavaDoc pattern, String JavaDoc str) {
472         return SelectorUtils.match(pattern, str);
473     }
474
475     /**
476      * Test whether or not a string matches against a pattern.
477      * The pattern may contain two special characters:<br>
478      * '*' means zero or more characters<br>
479      * '?' means one and only one character
480      *
481      * @param pattern The pattern to match against.
482      * Must not be <code>null</code>.
483      * @param str The string which must be matched against the pattern.
484      * Must not be <code>null</code>.
485      * @param isCaseSensitive Whether or not matching should be performed
486      * case sensitively.
487      *
488      *
489      * @return <code>true</code> if the string matches against the pattern,
490      * or <code>false</code> otherwise.
491      */

492     protected static boolean match(String JavaDoc pattern, String JavaDoc str,
493                                    boolean isCaseSensitive) {
494         return SelectorUtils.match(pattern, str, isCaseSensitive);
495     }
496
497
498     /**
499      * Get the list of patterns that should be excluded by default.
500      *
501      * @return An array of <code>String</code> based on the current
502      * contents of the <code>defaultExcludes</code>
503      * <code>Vector</code>.
504      *
505      * @since Ant 1.6
506      */

507     public static String JavaDoc[] getDefaultExcludes() {
508         return (String JavaDoc[]) defaultExcludes.toArray(new String JavaDoc[defaultExcludes
509                                                              .size()]);
510     }
511
512     /**
513      * Add a pattern to the default excludes unless it is already a
514      * default exclude.
515      *
516      * @param s A string to add as an exclude pattern.
517      * @return <code>true</code> if the string was added;
518      * <code>false</code> if it already existed.
519      *
520      * @since Ant 1.6
521      */

522     public static boolean addDefaultExclude(String JavaDoc s) {
523         if (defaultExcludes.indexOf(s) == -1) {
524             defaultExcludes.add(s);
525             return true;
526         }
527         return false;
528     }
529
530     /**
531      * Remove a string if it is a default exclude.
532      *
533      * @param s The string to attempt to remove.
534      * @return <code>true</code> if <code>s</code> was a default
535      * exclude (and thus was removed);
536      * <code>false</code> if <code>s</code> was not
537      * in the default excludes list to begin with.
538      *
539      * @since Ant 1.6
540      */

541     public static boolean removeDefaultExclude(String JavaDoc s) {
542         return defaultExcludes.remove(s);
543     }
544
545     /**
546      * Go back to the hardwired default exclude patterns.
547      *
548      * @since Ant 1.6
549      */

550     public static void resetDefaultExcludes() {
551         defaultExcludes = new Vector JavaDoc();
552         for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
553             defaultExcludes.add(DEFAULTEXCLUDES[i]);
554         }
555     }
556
557     /**
558      * Set the base directory to be scanned. This is the directory which is
559      * scanned recursively. All '/' and '\' characters are replaced by
560      * <code>File.separatorChar</code>, so the separator used need not match
561      * <code>File.separatorChar</code>.
562      *
563      * @param basedir The base directory to scan.
564      */

565     public void setBasedir(String JavaDoc basedir) {
566         setBasedir(basedir == null ? (File JavaDoc) null
567             : new File JavaDoc(basedir.replace('/', File.separatorChar).replace(
568             '\\', File.separatorChar)));
569     }
570
571     /**
572      * Set the base directory to be scanned. This is the directory which is
573      * scanned recursively.
574      *
575      * @param basedir The base directory for scanning.
576      */

577     public synchronized void setBasedir(File JavaDoc basedir) {
578         this.basedir = basedir;
579     }
580
581     /**
582      * Return the base directory to be scanned.
583      * This is the directory which is scanned recursively.
584      *
585      * @return the base directory to be scanned.
586      */

587     public synchronized File JavaDoc getBasedir() {
588         return basedir;
589     }
590
591     /**
592      * Find out whether include exclude patterns are matched in a
593      * case sensitive way.
594      * @return whether or not the scanning is case sensitive.
595      * @since Ant 1.6
596      */

597     public synchronized boolean isCaseSensitive() {
598         return isCaseSensitive;
599     }
600
601     /**
602      * Set whether or not include and exclude patterns are matched
603      * in a case sensitive way.
604      *
605      * @param isCaseSensitive whether or not the file system should be
606      * regarded as a case sensitive one.
607      */

608     public synchronized void setCaseSensitive(boolean isCaseSensitive) {
609         this.isCaseSensitive = isCaseSensitive;
610     }
611
612     /**
613      * Get whether or not a DirectoryScanner follows symbolic links.
614      *
615      * @return flag indicating whether symbolic links should be followed.
616      *
617      * @since Ant 1.6
618      */

619     public synchronized boolean isFollowSymlinks() {
620         return followSymlinks;
621     }
622
623     /**
624      * Set whether or not symbolic links should be followed.
625      *
626      * @param followSymlinks whether or not symbolic links should be followed.
627      */

628     public synchronized void setFollowSymlinks(boolean followSymlinks) {
629         this.followSymlinks = followSymlinks;
630     }
631
632     /**
633      * Set the list of include patterns to use. All '/' and '\' characters
634      * are replaced by <code>File.separatorChar</code>, so the separator used
635      * need not match <code>File.separatorChar</code>.
636      * <p>
637      * When a pattern ends with a '/' or '\', "**" is appended.
638      *
639      * @param includes A list of include patterns.
640      * May be <code>null</code>, indicating that all files
641      * should be included. If a non-<code>null</code>
642      * list is given, all elements must be
643      * non-<code>null</code>.
644      */

645     public synchronized void setIncludes(String JavaDoc[] includes) {
646         if (includes == null) {
647             this.includes = null;
648         } else {
649             this.includes = new String JavaDoc[includes.length];
650             for (int i = 0; i < includes.length; i++) {
651                 this.includes[i] = normalizePattern(includes[i]);
652             }
653         }
654     }
655
656     /**
657      * Set the list of exclude patterns to use. All '/' and '\' characters
658      * are replaced by <code>File.separatorChar</code>, so the separator used
659      * need not match <code>File.separatorChar</code>.
660      * <p>
661      * When a pattern ends with a '/' or '\', "**" is appended.
662      *
663      * @param excludes A list of exclude patterns.
664      * May be <code>null</code>, indicating that no files
665      * should be excluded. If a non-<code>null</code> list is
666      * given, all elements must be non-<code>null</code>.
667      */

668     public synchronized void setExcludes(String JavaDoc[] excludes) {
669         if (excludes == null) {
670             this.excludes = null;
671         } else {
672             this.excludes = new String JavaDoc[excludes.length];
673             for (int i = 0; i < excludes.length; i++) {
674                 this.excludes[i] = normalizePattern(excludes[i]);
675             }
676         }
677     }
678
679     /**
680      * Add to the list of exclude patterns to use. All '/' and '\'
681      * characters are replaced by <code>File.separatorChar</code>, so
682      * the separator used need not match <code>File.separatorChar</code>.
683      * <p>
684      * When a pattern ends with a '/' or '\', "**" is appended.
685      *
686      * @param excludes A list of exclude patterns.
687      * May be <code>null</code>, in which case the
688      * exclude patterns don't get changed at all.
689      *
690      * @since Ant 1.6.3
691      */

692     public synchronized void addExcludes(String JavaDoc[] excludes) {
693         if (excludes != null && excludes.length > 0) {
694             if (this.excludes != null && this.excludes.length > 0) {
695                 String JavaDoc[] tmp = new String JavaDoc[excludes.length
696                                           + this.excludes.length];
697                 System.arraycopy(this.excludes, 0, tmp, 0,
698                                  this.excludes.length);
699                 for (int i = 0; i < excludes.length; i++) {
700                     tmp[this.excludes.length + i] =
701                         normalizePattern(excludes[i]);
702                 }
703                 this.excludes = tmp;
704             } else {
705                 setExcludes(excludes);
706             }
707         }
708     }
709
710     /**
711      * All '/' and '\' characters are replaced by
712      * <code>File.separatorChar</code>, so the separator used need not
713      * match <code>File.separatorChar</code>.
714      *
715      * <p> When a pattern ends with a '/' or '\', "**" is appended.
716      *
717      * @since Ant 1.6.3
718      */

719     private static String JavaDoc normalizePattern(String JavaDoc p) {
720         String JavaDoc pattern = p.replace('/', File.separatorChar)
721             .replace('\\', File.separatorChar);
722         if (pattern.endsWith(File.separator)) {
723             pattern += "**";
724         }
725         return pattern;
726     }
727
728     /**
729      * Set the selectors that will select the filelist.
730      *
731      * @param selectors specifies the selectors to be invoked on a scan.
732      */

733     public synchronized void setSelectors(FileSelector[] selectors) {
734         this.selectors = selectors;
735     }
736
737     /**
738      * Return whether or not the scanner has included all the files or
739      * directories it has come across so far.
740      *
741      * @return <code>true</code> if all files and directories which have
742      * been found so far have been included.
743      */

744     public synchronized boolean isEverythingIncluded() {
745         return everythingIncluded;
746     }
747
748     /**
749      * Scan for files which match at least one include pattern and don't match
750      * any exclude patterns. If there are selectors then the files must pass
751      * muster there, as well. Scans under basedir, if set; otherwise the
752      * include patterns without leading wildcards specify the absolute paths of
753      * the files that may be included.
754      *
755      * @exception IllegalStateException if the base directory was set
756      * incorrectly (i.e. if it doesn't exist or isn't a directory).
757      */

758     public void scan() throws IllegalStateException JavaDoc {
759         synchronized (scanLock) {
760             if (scanning) {
761                 while (scanning) {
762                     try {
763                         scanLock.wait();
764                     } catch (InterruptedException JavaDoc e) {
765                         continue;
766                     }
767                 }
768                 if (illegal != null) {
769                     throw illegal;
770                 }
771                 return;
772             }
773             scanning = true;
774         }
775         try {
776             synchronized (this) {
777                 illegal = null;
778                 clearResults();
779
780                 // set in/excludes to reasonable defaults if needed:
781
boolean nullIncludes = (includes == null);
782                 includes = nullIncludes ? new String JavaDoc[] {"**"} : includes;
783                 boolean nullExcludes = (excludes == null);
784                 excludes = nullExcludes ? new String JavaDoc[0] : excludes;
785
786                 if (basedir == null) {
787                     // if no basedir and no includes, nothing to do:
788
if (nullIncludes) {
789                         return;
790                     }
791                 } else {
792                     if (!basedir.exists()) {
793                         illegal = new IllegalStateException JavaDoc("basedir " + basedir
794                                                             + " does not exist");
795                     }
796                     if (!basedir.isDirectory()) {
797                         illegal = new IllegalStateException JavaDoc("basedir " + basedir
798                                                             + " is not a directory");
799                     }
800                     if (illegal != null) {
801                         throw illegal;
802                     }
803                 }
804                 if (isIncluded("")) {
805                     if (!isExcluded("")) {
806                         if (isSelected("", basedir)) {
807                             dirsIncluded.addElement("");
808                         } else {
809                             dirsDeselected.addElement("");
810                         }
811                     } else {
812                         dirsExcluded.addElement("");
813                     }
814                 } else {
815                     dirsNotIncluded.addElement("");
816                 }
817                 checkIncludePatterns();
818                 clearCaches();
819                 includes = nullIncludes ? null : includes;
820                 excludes = nullExcludes ? null : excludes;
821             }
822         } finally {
823             synchronized (scanLock) {
824                 scanning = false;
825                 scanLock.notifyAll();
826             }
827         }
828     }
829
830     /**
831      * This routine is actually checking all the include patterns in
832      * order to avoid scanning everything under base dir.
833      * @since Ant 1.6
834      */

835     private void checkIncludePatterns() {
836         Map JavaDoc newroots = new HashMap JavaDoc();
837         // put in the newroots map the include patterns without
838
// wildcard tokens
839
for (int i = 0; i < includes.length; i++) {
840             if (FileUtils.isAbsolutePath(includes[i])) {
841                 //skip abs. paths not under basedir, if set:
842
if (basedir != null
843                     && !SelectorUtils.matchPatternStart(includes[i],
844                     basedir.getAbsolutePath(), isCaseSensitive())) {
845                     continue;
846                 }
847             } else if (basedir == null) {
848                 //skip non-abs. paths if basedir == null:
849
continue;
850             }
851             newroots.put(SelectorUtils.rtrimWildcardTokens(
852                 includes[i]), includes[i]);
853         }
854         if (newroots.containsKey("") && basedir != null) {
855             // we are going to scan everything anyway
856
scandir(basedir, "", true);
857         } else {
858             // only scan directories that can include matched files or
859
// directories
860
Iterator JavaDoc it = newroots.entrySet().iterator();
861
862             File JavaDoc canonBase = null;
863             if (basedir != null) {
864                 try {
865                     canonBase = basedir.getCanonicalFile();
866                 } catch (IOException JavaDoc ex) {
867                     throw new BuildException(ex);
868                 }
869             }
870             while (it.hasNext()) {
871                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
872                 String JavaDoc currentelement = (String JavaDoc) entry.getKey();
873                 if (basedir == null && !FileUtils.isAbsolutePath(currentelement)) {
874                     continue;
875                 }
876                 String JavaDoc originalpattern = (String JavaDoc) entry.getValue();
877                 File JavaDoc myfile = new File JavaDoc(basedir, currentelement);
878
879                 if (myfile.exists()) {
880                     // may be on a case insensitive file system. We want
881
// the results to show what's really on the disk, so
882
// we need to double check.
883
try {
884                         String JavaDoc path = (basedir == null)
885                             ? myfile.getCanonicalPath()
886                             : FILE_UTILS.removeLeadingPath(canonBase,
887                             myfile.getCanonicalFile());
888                         if (!path.equals(currentelement) || ON_VMS) {
889                             myfile = findFile(basedir, currentelement, true);
890                             if (myfile != null && basedir != null) {
891                                 currentelement = FILE_UTILS.removeLeadingPath(
892                                     basedir, myfile);
893                             }
894                         }
895                     } catch (IOException JavaDoc ex) {
896                         throw new BuildException(ex);
897                     }
898                 }
899                 if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
900                     File JavaDoc f = findFile(basedir, currentelement, false);
901                     if (f != null && f.exists()) {
902                         // adapt currentelement to the case we've
903
// actually found
904
currentelement = (basedir == null)
905                             ? f.getAbsolutePath()
906                             : FILE_UTILS.removeLeadingPath(basedir, f);
907                         myfile = f;
908                     }
909                 }
910                 if (myfile != null && myfile.exists()) {
911                     if (!followSymlinks
912                         && isSymlink(basedir, currentelement)) {
913                         continue;
914                     }
915                     if (myfile.isDirectory()) {
916                         if (isIncluded(currentelement)
917                             && currentelement.length() > 0) {
918                             accountForIncludedDir(currentelement, myfile, true);
919                         } else {
920                             if (currentelement.length() > 0) {
921                                 if (currentelement.charAt(currentelement
922                                                           .length() - 1)
923                                     != File.separatorChar) {
924                                     currentelement =
925                                         currentelement + File.separatorChar;
926                                 }
927                             }
928                             scandir(myfile, currentelement, true);
929                         }
930                     } else {
931                         boolean included = isCaseSensitive()
932                             ? originalpattern.equals(currentelement)
933                             : originalpattern.equalsIgnoreCase(currentelement);
934                         if (included) {
935                             accountForIncludedFile(currentelement, myfile);
936                         }
937                     }
938                 }
939             }
940         }
941     }
942
943     /**
944      * Clear the result caches for a scan.
945      */

946     protected synchronized void clearResults() {
947         filesIncluded = new Vector JavaDoc();
948         filesNotIncluded = new Vector JavaDoc();
949         filesExcluded = new Vector JavaDoc();
950         filesDeselected = new Vector JavaDoc();
951         dirsIncluded = new Vector JavaDoc();
952         dirsNotIncluded = new Vector JavaDoc();
953         dirsExcluded = new Vector JavaDoc();
954         dirsDeselected = new Vector JavaDoc();
955         everythingIncluded = (basedir != null);
956         scannedDirs.clear();
957     }
958
959     /**
960      * Top level invocation for a slow scan. A slow scan builds up a full
961      * list of excluded/included files/directories, whereas a fast scan
962      * will only have full results for included files, as it ignores
963      * directories which can't possibly hold any included files/directories.
964      * <p>
965      * Returns immediately if a slow scan has already been completed.
966      */

967     protected void slowScan() {
968         synchronized (slowScanLock) {
969             if (haveSlowResults) {
970                 return;
971             }
972             if (slowScanning) {
973                 while (slowScanning) {
974                     try {
975                         slowScanLock.wait();
976                     } catch (InterruptedException JavaDoc e) {
977                         // Empty
978
}
979                 }
980                 return;
981             }
982             slowScanning = true;
983         }
984         try {
985             synchronized (this) {
986
987                 // set in/excludes to reasonable defaults if needed:
988
boolean nullIncludes = (includes == null);
989                 includes = nullIncludes ? new String JavaDoc[] {"**"} : includes;
990                 boolean nullExcludes = (excludes == null);
991                 excludes = nullExcludes ? new String JavaDoc[0] : excludes;
992
993                 String JavaDoc[] excl = new String JavaDoc[dirsExcluded.size()];
994                 dirsExcluded.copyInto(excl);
995
996                 String JavaDoc[] notIncl = new String JavaDoc[dirsNotIncluded.size()];
997                 dirsNotIncluded.copyInto(notIncl);
998
999                 processSlowScan(excl);
1000                processSlowScan(notIncl);
1001                clearCaches();
1002                includes = nullIncludes ? null : includes;
1003                excludes = nullExcludes ? null : excludes;
1004            }
1005        } finally {
1006            synchronized (slowScanLock) {
1007                haveSlowResults = true;
1008                slowScanning = false;
1009                slowScanLock.notifyAll();
1010            }
1011        }
1012    }
1013
1014    private void processSlowScan(String JavaDoc[] arr) {
1015        for (int i = 0; i < arr.length; i++) {
1016            if (!couldHoldIncluded(arr[i])) {
1017                scandir(new File JavaDoc(basedir, arr[i]),
1018                        arr[i] + File.separator, false);
1019            }
1020        }
1021    }
1022
1023    /**
1024     * Scan the given directory for files and directories. Found files and
1025     * directories are placed in their respective collections, based on the
1026     * matching of includes, excludes, and the selectors. When a directory
1027     * is found, it is scanned recursively.
1028     *
1029     * @param dir The directory to scan. Must not be <code>null</code>.
1030     * @param vpath The path relative to the base directory (needed to
1031     * prevent problems with an absolute path when using
1032     * dir). Must not be <code>null</code>.
1033     * @param fast Whether or not this call is part of a fast scan.
1034     *
1035     * @see #filesIncluded
1036     * @see #filesNotIncluded
1037     * @see #filesExcluded
1038     * @see #dirsIncluded
1039     * @see #dirsNotIncluded
1040     * @see #dirsExcluded
1041     * @see #slowScan
1042     */

1043    protected void scandir(File JavaDoc dir, String JavaDoc vpath, boolean fast) {
1044        if (dir == null) {
1045            throw new BuildException("dir must not be null.");
1046        } else if (!dir.exists()) {
1047            throw new BuildException(dir + " doesn't exist.");
1048        } else if (!dir.isDirectory()) {
1049            throw new BuildException(dir + " is not a directory.");
1050        }
1051        // avoid double scanning of directories, can only happen in fast mode
1052
if (fast && hasBeenScanned(vpath)) {
1053            return;
1054        }
1055        String JavaDoc[] newfiles = dir.list();
1056
1057        if (newfiles == null) {
1058            /*
1059             * two reasons are mentioned in the API docs for File.list
1060             * (1) dir is not a directory. This is impossible as
1061             * we wouldn't get here in this case.
1062             * (2) an IO error occurred (why doesn't it throw an exception
1063             * then???)
1064             */

1065            throw new BuildException("IO error scanning directory '"
1066                                     + dir.getAbsolutePath() + "'");
1067        }
1068        if (!followSymlinks) {
1069            Vector JavaDoc noLinks = new Vector JavaDoc();
1070            for (int i = 0; i < newfiles.length; i++) {
1071                try {
1072                    if (FILE_UTILS.isSymbolicLink(dir, newfiles[i])) {
1073                        String JavaDoc name = vpath + newfiles[i];
1074                        File JavaDoc file = new File JavaDoc(dir, newfiles[i]);
1075                        (file.isDirectory()
1076                            ? dirsExcluded : filesExcluded).addElement(name);
1077                    } else {
1078                        noLinks.addElement(newfiles[i]);
1079                    }
1080                } catch (IOException JavaDoc ioe) {
1081                    String JavaDoc msg = "IOException caught while checking "
1082                        + "for links, couldn't get canonical path!";
1083                    // will be caught and redirected to Ant's logging system
1084
System.err.println(msg);
1085                    noLinks.addElement(newfiles[i]);
1086                }
1087            }
1088            newfiles = (String JavaDoc[]) (noLinks.toArray(new String JavaDoc[noLinks.size()]));
1089        }
1090        for (int i = 0; i < newfiles.length; i++) {
1091            String JavaDoc name = vpath + newfiles[i];
1092            File JavaDoc file = new File JavaDoc(dir, newfiles[i]);
1093            if (file.isDirectory()) {
1094                if (isIncluded(name)) {
1095                    accountForIncludedDir(name, file, fast);
1096                } else {
1097                    everythingIncluded = false;
1098                    dirsNotIncluded.addElement(name);
1099                    if (fast && couldHoldIncluded(name)) {
1100                        scandir(file, name + File.separator, fast);
1101                    }
1102                }
1103                if (!fast) {
1104                    scandir(file, name + File.separator, fast);
1105                }
1106            } else if (file.isFile()) {
1107                if (isIncluded(name)) {
1108                    accountForIncludedFile(name, file);
1109                } else {
1110                    everythingIncluded = false;
1111                    filesNotIncluded.addElement(name);
1112                }
1113            }
1114        }
1115    }
1116
1117    /**
1118     * Process included file.
1119     * @param name path of the file relative to the directory of the FileSet.
1120     * @param file included File.
1121     */

1122    private void accountForIncludedFile(String JavaDoc name, File JavaDoc file) {
1123        processIncluded(name, file, filesIncluded, filesExcluded, filesDeselected);
1124    }
1125
1126    /**
1127     * Process included directory.
1128     * @param name path of the directory relative to the directory of
1129     * the FileSet.
1130     * @param file directory as File.
1131     * @param fast whether to perform fast scans.
1132     */

1133    private void accountForIncludedDir(String JavaDoc name, File JavaDoc file, boolean fast) {
1134        processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
1135        if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
1136            scandir(file, name + File.separator, fast);
1137        }
1138    }
1139
1140    private void processIncluded(String JavaDoc name, File JavaDoc file, Vector JavaDoc inc, Vector JavaDoc exc, Vector JavaDoc des) {
1141
1142        if (inc.contains(name) || exc.contains(name) || des.contains(name)) { return; }
1143
1144        boolean included = false;
1145        if (isExcluded(name)) {
1146            exc.add(name);
1147        } else if (isSelected(name, file)) {
1148            included = true;
1149            inc.add(name);
1150        } else {
1151            des.add(name);
1152        }
1153        everythingIncluded &= included;
1154    }
1155
1156    /**
1157     * Test whether or not a name matches against at least one include
1158     * pattern.
1159     *
1160     * @param name The name to match. Must not be <code>null</code>.
1161     * @return <code>true</code> when the name matches against at least one
1162     * include pattern, or <code>false</code> otherwise.
1163     */

1164    protected boolean isIncluded(String JavaDoc name) {
1165        ensureNonPatternSetsReady();
1166
1167        if (isCaseSensitive()
1168            ? includeNonPatterns.contains(name)
1169            : includeNonPatterns.contains(name.toUpperCase())) {
1170            return true;
1171        }
1172        for (int i = 0; i < includePatterns.length; i++) {
1173            if (matchPath(includePatterns[i], name, isCaseSensitive())) {
1174                return true;
1175            }
1176        }
1177        return false;
1178    }
1179
1180    /**
1181     * Test whether or not a name matches the start of at least one include
1182     * pattern.
1183     *
1184     * @param name The name to match. Must not be <code>null</code>.
1185     * @return <code>true</code> when the name matches against the start of at
1186     * least one include pattern, or <code>false</code> otherwise.
1187     */

1188    protected boolean couldHoldIncluded(String JavaDoc name) {
1189        for (int i = 0; i < includes.length; i++) {
1190            if (matchPatternStart(includes[i], name, isCaseSensitive())
1191                && isMorePowerfulThanExcludes(name, includes[i])
1192                && isDeeper(includes[i], name)) {
1193                return true;
1194            }
1195        }
1196        return false;
1197    }
1198
1199    /**
1200     * Verify that a pattern specifies files deeper
1201     * than the level of the specified file.
1202     * @param pattern the pattern to check.
1203     * @param name the name to check.
1204     * @return whether the pattern is deeper than the name.
1205     * @since Ant 1.6.3
1206     */

1207    private boolean isDeeper(String JavaDoc pattern, String JavaDoc name) {
1208        Vector JavaDoc p = SelectorUtils.tokenizePath(pattern);
1209        Vector JavaDoc n = SelectorUtils.tokenizePath(name);
1210        return p.contains("**") || p.size() > n.size();
1211    }
1212
1213    /**
1214     * Find out whether one particular include pattern is more powerful
1215     * than all the excludes.
1216     * Note: the power comparison is based on the length of the include pattern
1217     * and of the exclude patterns without the wildcards.
1218     * Ideally the comparison should be done based on the depth
1219     * of the match; that is to say how many file separators have been matched
1220     * before the first ** or the end of the pattern.
1221     *
1222     * IMPORTANT : this function should return false "with care".
1223     *
1224     * @param name the relative path to test.
1225     * @param includepattern one include pattern.
1226     * @return true if there is no exclude pattern more powerful than this include pattern.
1227     * @since Ant 1.6
1228     */

1229    private boolean isMorePowerfulThanExcludes(String JavaDoc name, String JavaDoc includepattern) {
1230        String JavaDoc soughtexclude = name + File.separator + "**";
1231        for (int counter = 0; counter < excludes.length; counter++) {
1232            if (excludes[counter].equals(soughtexclude)) {
1233                return false;
1234            }
1235        }
1236        return true;
1237    }
1238
1239    /**
1240     * Test whether all contents of the specified directory must be excluded.
1241     * @param name the directory name to check.
1242     * @return whether all the specified directory's contents are excluded.
1243     */

1244    private boolean contentsExcluded(String JavaDoc name) {
1245        name = (name.endsWith(File.separator)) ? name : name + File.separator;
1246        for (int i = 0; i < excludes.length; i++) {
1247            String JavaDoc e = excludes[i];
1248            if (e.endsWith("**") && SelectorUtils.matchPath(
1249                e.substring(0, e.length() - 2), name, isCaseSensitive())) {
1250                return true;
1251            }
1252        }
1253        return false;
1254    }
1255
1256    /**
1257     * Test whether or not a name matches against at least one exclude
1258     * pattern.
1259     *
1260     * @param name The name to match. Must not be <code>null</code>.
1261     * @return <code>true</code> when the name matches against at least one
1262     * exclude pattern, or <code>false</code> otherwise.
1263     */

1264    protected boolean isExcluded(String JavaDoc name) {
1265        ensureNonPatternSetsReady();
1266
1267        if (isCaseSensitive()
1268            ? excludeNonPatterns.contains(name)
1269            : excludeNonPatterns.contains(name.toUpperCase())) {
1270            return true;
1271        }
1272        for (int i = 0; i < excludePatterns.length; i++) {
1273            if (matchPath(excludePatterns[i], name, isCaseSensitive())) {
1274                return true;
1275            }
1276        }
1277        return false;
1278    }
1279
1280    /**
1281     * Test whether a file should be selected.
1282     *
1283     * @param name the filename to check for selecting.
1284     * @param file the java.io.File object for this filename.
1285     * @return <code>false</code> when the selectors says that the file
1286     * should not be selected, <code>true</code> otherwise.
1287     */

1288    protected boolean isSelected(String JavaDoc name, File JavaDoc file) {
1289        if (selectors != null) {
1290            for (int i = 0; i < selectors.length; i++) {
1291                if (!selectors[i].isSelected(basedir, name, file)) {
1292                    return false;
1293                }
1294            }
1295        }
1296        return true;
1297    }
1298
1299    /**
1300     * Return the names of the files which matched at least one of the
1301     * include patterns and none of the exclude patterns.
1302     * The names are relative to the base directory.
1303     *
1304     * @return the names of the files which matched at least one of the
1305     * include patterns and none of the exclude patterns.
1306     */

1307    public synchronized String JavaDoc[] getIncludedFiles() {
1308        if (filesIncluded == null) {
1309            throw new IllegalStateException JavaDoc("Must call scan() first");
1310        }
1311        String JavaDoc[] files = new String JavaDoc[filesIncluded.size()];
1312        filesIncluded.copyInto(files);
1313        Arrays.sort(files);
1314        return files;
1315    }
1316
1317    /**
1318     * Return the count of included files.
1319     * @return <code>int</code>.
1320     * @since Ant 1.6.3
1321     */

1322    public synchronized int getIncludedFilesCount() {
1323        if (filesIncluded == null) {
1324            throw new IllegalStateException JavaDoc("Must call scan() first");
1325        }
1326        return filesIncluded.size();
1327    }
1328
1329    /**
1330     * Return the names of the files which matched none of the include
1331     * patterns. The names are relative to the base directory. This involves
1332     * performing a slow scan if one has not already been completed.
1333     *
1334     * @return the names of the files which matched none of the include
1335     * patterns.
1336     *
1337     * @see #slowScan
1338     */

1339    public synchronized String JavaDoc[] getNotIncludedFiles() {
1340        slowScan();
1341        String JavaDoc[] files = new String JavaDoc[filesNotIncluded.size()];
1342        filesNotIncluded.copyInto(files);
1343        return files;
1344    }
1345
1346    /**
1347     * Return the names of the files which matched at least one of the
1348     * include patterns and at least one of the exclude patterns.
1349     * The names are relative to the base directory. This involves
1350     * performing a slow scan if one has not already been completed.
1351     *
1352     * @return the names of the files which matched at least one of the
1353     * include patterns and at least one of the exclude patterns.
1354     *
1355     * @see #slowScan
1356     */

1357    public synchronized String JavaDoc[] getExcludedFiles() {
1358        slowScan();
1359        String JavaDoc[] files = new String JavaDoc[filesExcluded.size()];
1360        filesExcluded.copyInto(files);
1361        return files;
1362    }
1363
1364    /**
1365     * <p>Return the names of the files which were selected out and
1366     * therefore not ultimately included.</p>
1367     *
1368     * <p>The names are relative to the base directory. This involves
1369     * performing a slow scan if one has not already been completed.</p>
1370     *
1371     * @return the names of the files which were deselected.
1372     *
1373     * @see #slowScan
1374     */

1375    public synchronized String JavaDoc[] getDeselectedFiles() {
1376        slowScan();
1377        String JavaDoc[] files = new String JavaDoc[filesDeselected.size()];
1378        filesDeselected.copyInto(files);
1379        return files;
1380    }
1381
1382    /**
1383     * Return the names of the directories which matched at least one of the
1384     * include patterns and none of the exclude patterns.
1385     * The names are relative to the base directory.
1386     *
1387     * @return the names of the directories which matched at least one of the
1388     * include patterns and none of the exclude patterns.
1389     */

1390    public synchronized String JavaDoc[] getIncludedDirectories() {
1391        if (dirsIncluded == null) {
1392            throw new IllegalStateException JavaDoc("Must call scan() first");
1393        }
1394        String JavaDoc[] directories = new String JavaDoc[dirsIncluded.size()];
1395        dirsIncluded.copyInto(directories);
1396        Arrays.sort(directories);
1397        return directories;
1398    }
1399
1400    /**
1401     * Return the count of included directories.
1402     * @return <code>int</code>.
1403     * @since Ant 1.6.3
1404     */

1405    public synchronized int getIncludedDirsCount() {
1406        if (dirsIncluded == null) {
1407            throw new IllegalStateException JavaDoc("Must call scan() first");
1408        }
1409        return dirsIncluded.size();
1410    }
1411
1412    /**
1413     * Return the names of the directories which matched none of the include
1414     * patterns. The names are relative to the base directory. This involves
1415     * performing a slow scan if one has not already been completed.
1416     *
1417     * @return the names of the directories which matched none of the include
1418     * patterns.
1419     *
1420     * @see #slowScan
1421     */

1422    public synchronized String JavaDoc[] getNotIncludedDirectories() {
1423        slowScan();
1424        String JavaDoc[] directories = new String JavaDoc[dirsNotIncluded.size()];
1425        dirsNotIncluded.copyInto(directories);
1426        return directories;
1427    }
1428
1429    /**
1430     * Return the names of the directories which matched at least one of the
1431     * include patterns and at least one of the exclude patterns.
1432     * The names are relative to the base directory. This involves
1433     * performing a slow scan if one has not already been completed.
1434     *
1435     * @return the names of the directories which matched at least one of the
1436     * include patterns and at least one of the exclude patterns.
1437     *
1438     * @see #slowScan
1439     */

1440    public synchronized String JavaDoc[] getExcludedDirectories() {
1441        slowScan();
1442        String JavaDoc[] directories = new String JavaDoc[dirsExcluded.size()];
1443        dirsExcluded.copyInto(directories);
1444        return directories;
1445    }
1446
1447    /**
1448     * <p>Return the names of the directories which were selected out and
1449     * therefore not ultimately included.</p>
1450     *
1451     * <p>The names are relative to the base directory. This involves
1452     * performing a slow scan if one has not already been completed.</p>
1453     *
1454     * @return the names of the directories which were deselected.
1455     *
1456     * @see #slowScan
1457     */

1458    public synchronized String JavaDoc[] getDeselectedDirectories() {
1459        slowScan();
1460        String JavaDoc[] directories = new String JavaDoc[dirsDeselected.size()];
1461        dirsDeselected.copyInto(directories);
1462        return directories;
1463    }
1464
1465    /**
1466     * Add default exclusions to the current exclusions set.
1467     */

1468    public synchronized void addDefaultExcludes() {
1469        int excludesLength = excludes == null ? 0 : excludes.length;
1470        String JavaDoc[] newExcludes;
1471        newExcludes = new String JavaDoc[excludesLength + defaultExcludes.size()];
1472        if (excludesLength > 0) {
1473            System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
1474        }
1475        String JavaDoc[] defaultExcludesTemp = getDefaultExcludes();
1476        for (int i = 0; i < defaultExcludesTemp.length; i++) {
1477            newExcludes[i + excludesLength] =
1478                defaultExcludesTemp[i].replace('/', File.separatorChar)
1479                .replace('\\', File.separatorChar);
1480        }
1481        excludes = newExcludes;
1482    }
1483
1484    /**
1485     * Get the named resource.
1486     * @param name path name of the file relative to the dir attribute.
1487     *
1488     * @return the resource with the given name.
1489     * @since Ant 1.5.2
1490     */

1491    public synchronized Resource getResource(String JavaDoc name) {
1492        return new FileResource(basedir, name);
1493    }
1494
1495    /**
1496     * Return a cached result of list performed on file, if
1497     * available. Invokes the method and caches the result otherwise.
1498     *
1499     * @param file File (dir) to list.
1500     * @since Ant 1.6
1501     */

1502    private String JavaDoc[] list(File JavaDoc file) {
1503        String JavaDoc[] files = (String JavaDoc[]) fileListMap.get(file);
1504        if (files == null) {
1505            files = file.list();
1506            if (files != null) {
1507                fileListMap.put(file, files);
1508            }
1509        }
1510        return files;
1511    }
1512
1513    /**
1514     * From <code>base</code> traverse the filesystem in order to find
1515     * a file that matches the given name.
1516     *
1517     * @param base base File (dir).
1518     * @param path file path.
1519     * @param cs whether to scan case-sensitively.
1520     * @return File object that points to the file in question or null.
1521     *
1522     * @since Ant 1.6.3
1523     */

1524    private File JavaDoc findFile(File JavaDoc base, String JavaDoc path, boolean cs) {
1525        if (FileUtils.isAbsolutePath(path)) {
1526            if (base == null) {
1527                String JavaDoc[] s = FILE_UTILS.dissect(path);
1528                base = new File JavaDoc(s[0]);
1529                path = s[1];
1530            } else {
1531                File JavaDoc f = FILE_UTILS.normalize(path);
1532                String JavaDoc s = FILE_UTILS.removeLeadingPath(base, f);
1533                if (s.equals(f.getAbsolutePath())) {
1534                    //removing base from path yields no change; path not child of base
1535
return null;
1536                }
1537                path = s;
1538            }
1539        }
1540        return findFile(base, SelectorUtils.tokenizePath(path), cs);
1541    }
1542
1543    /**
1544     * From <code>base</code> traverse the filesystem in order to find
1545     * a file that matches the given stack of names.
1546     *
1547     * @param base base File (dir).
1548     * @param pathElements Vector of path elements (dirs...file).
1549     * @param cs whether to scan case-sensitively.
1550     * @return File object that points to the file in question or null.
1551     *
1552     * @since Ant 1.6.3
1553     */

1554    private File JavaDoc findFile(File JavaDoc base, Vector JavaDoc pathElements, boolean cs) {
1555        if (pathElements.size() == 0) {
1556            return base;
1557        }
1558        String JavaDoc current = (String JavaDoc) pathElements.remove(0);
1559        if (base == null) {
1560            return findFile(new File JavaDoc(current), pathElements, cs);
1561        }
1562        if (!base.isDirectory()) {
1563            return null;
1564        }
1565        String JavaDoc[] files = list(base);
1566        if (files == null) {
1567            throw new BuildException("IO error scanning directory "
1568                                     + base.getAbsolutePath());
1569        }
1570        boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS;
1571        for (int i = 0; i < matchCase.length; i++) {
1572            for (int j = 0; j < files.length; j++) {
1573                if (matchCase[i] ? files[j].equals(current)
1574                                 : files[j].equalsIgnoreCase(current)) {
1575                    return findFile(new File JavaDoc(base, files[j]), pathElements, cs);
1576                }
1577            }
1578        }
1579        return null;
1580    }
1581
1582    /**
1583     * Do we have to traverse a symlink when trying to reach path from
1584     * basedir?
1585     * @param base base File (dir).
1586     * @param path file path.
1587     * @since Ant 1.6
1588     */

1589    private boolean isSymlink(File JavaDoc base, String JavaDoc path) {
1590        return isSymlink(base, SelectorUtils.tokenizePath(path));
1591    }
1592
1593    /**
1594     * Do we have to traverse a symlink when trying to reach path from
1595     * basedir?
1596     * @param base base File (dir).
1597     * @param pathElements Vector of path elements (dirs...file).
1598     * @since Ant 1.6
1599     */

1600    private boolean isSymlink(File JavaDoc base, Vector JavaDoc pathElements) {
1601        if (pathElements.size() > 0) {
1602            String JavaDoc current = (String JavaDoc) pathElements.remove(0);
1603            try {
1604                return FILE_UTILS.isSymbolicLink(base, current)
1605                    || isSymlink(new File JavaDoc(base, current), pathElements);
1606            } catch (IOException JavaDoc ioe) {
1607                String JavaDoc msg = "IOException caught while checking "
1608                    + "for links, couldn't get canonical path!";
1609                // will be caught and redirected to Ant's logging system
1610
System.err.println(msg);
1611            }
1612        }
1613        return false;
1614    }
1615
1616    /**
1617     * Has the directory with the given path relative to the base
1618     * directory already been scanned?
1619     *
1620     * <p>Registers the given directory as scanned as a side effect.</p>
1621     *
1622     * @since Ant 1.6
1623     */

1624    private boolean hasBeenScanned(String JavaDoc vpath) {
1625        return !scannedDirs.add(vpath);
1626    }
1627
1628    /**
1629     * This method is of interest for testing purposes. The returned
1630     * Set is live and should not be modified.
1631     * @return the Set of relative directory names that have been scanned.
1632     */

1633    /* package-private */ Set JavaDoc getScannedDirs() {
1634        return scannedDirs;
1635    }
1636
1637    /**
1638     * Clear internal caches.
1639     *
1640     * @since Ant 1.6
1641     */

1642    private synchronized void clearCaches() {
1643        fileListMap.clear();
1644        includeNonPatterns.clear();
1645        excludeNonPatterns.clear();
1646        includePatterns = null;
1647        excludePatterns = null;
1648        areNonPatternSetsReady = false;
1649    }
1650
1651    /**
1652     * Ensure that the in|exclude &quot;patterns&quot;
1653     * have been properly divided up.
1654     *
1655     * @since Ant 1.6.3
1656     */

1657    private synchronized void ensureNonPatternSetsReady() {
1658        if (!areNonPatternSetsReady) {
1659            includePatterns = fillNonPatternSet(includeNonPatterns, includes);
1660            excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
1661            areNonPatternSetsReady = true;
1662        }
1663    }
1664
1665    /**
1666     * Add all patterns that are not real patterns (do not contain
1667     * wildcards) to the set and returns the real patterns.
1668     *
1669     * @param set Set to populate.
1670     * @param patterns String[] of patterns.
1671     * @since Ant 1.6.3
1672     */

1673    private String JavaDoc[] fillNonPatternSet(Set JavaDoc set, String JavaDoc[] patterns) {
1674        ArrayList JavaDoc al = new ArrayList JavaDoc(patterns.length);
1675        for (int i = 0; i < patterns.length; i++) {
1676            if (!SelectorUtils.hasWildcards(patterns[i])) {
1677                set.add(isCaseSensitive() ? patterns[i]
1678                    : patterns[i].toUpperCase());
1679            } else {
1680                al.add(patterns[i]);
1681            }
1682        }
1683        return set.size() == 0 ? patterns
1684            : (String JavaDoc[]) al.toArray(new String JavaDoc[al.size()]);
1685    }
1686
1687}
1688
Popular Tags