Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DirectoryScanner |
|
| 2.6875;2.688 |
1 | /* | |
2 | * The Apache Software License, Version 1.1 | |
3 | * | |
4 | * Copyright (c) 2000-2003 The Apache Software Foundation. All rights | |
5 | * reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in | |
16 | * the documentation and/or other materials provided with the | |
17 | * distribution. | |
18 | * | |
19 | * 3. The end-user documentation included with the redistribution, if | |
20 | * any, must include the following acknowlegement: | |
21 | * "This product includes software developed by the | |
22 | * Apache Software Foundation (http://www.apache.org/)." | |
23 | * Alternately, this acknowlegement may appear in the software itself, | |
24 | * if and wherever such third-party acknowlegements normally appear. | |
25 | * | |
26 | * 4. The names "Ant" and "Apache Software | |
27 | * Foundation" must not be used to endorse or promote products derived | |
28 | * from this software without prior written permission. For written | |
29 | * permission, please contact apache@apache.org. | |
30 | * | |
31 | * 5. Products derived from this software may not be called "Apache" | |
32 | * nor may "Apache" appear in their names without prior written | |
33 | * permission of the Apache Group. | |
34 | * | |
35 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |
36 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
37 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
38 | * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |
39 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
40 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
41 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
42 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
43 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
44 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
45 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
46 | * SUCH DAMAGE. | |
47 | * ==================================================================== | |
48 | * | |
49 | * This software consists of voluntary contributions made by many | |
50 | * individuals on behalf of the Apache Software Foundation. For more | |
51 | * information on the Apache Software Foundation, please see | |
52 | * <http://www.apache.org/>. | |
53 | */ | |
54 | ||
55 | package org.apache.maven.it.util; | |
56 | ||
57 | import java.io.File; | |
58 | import java.io.IOException; | |
59 | import java.util.Vector; | |
60 | ||
61 | /** | |
62 | * Class for scanning a directory for files/directories which match certain | |
63 | * criteria. | |
64 | * <p> | |
65 | * These criteria consist of selectors and patterns which have been specified. | |
66 | * With the selectors you can select which files you want to have included. | |
67 | * Files which are not selected are excluded. With patterns you can include | |
68 | * or exclude files based on their filename. | |
69 | * <p> | |
70 | * The idea is simple. A given directory is recursively scanned for all files | |
71 | * and directories. Each file/directory is matched against a set of selectors, | |
72 | * including special support for matching against filenames with include and | |
73 | * and exclude patterns. Only files/directories which match at least one | |
74 | * pattern of the include pattern list or other file selector, and don't match | |
75 | * any pattern of the exclude pattern list or fail to match against a required | |
76 | * selector will be placed in the list of files/directories found. | |
77 | * <p> | |
78 | * When no list of include patterns is supplied, "**" will be used, which | |
79 | * means that everything will be matched. When no list of exclude patterns is | |
80 | * supplied, an empty list is used, such that nothing will be excluded. When | |
81 | * no selectors are supplied, none are applied. | |
82 | * <p> | |
83 | * The filename pattern matching is done as follows: | |
84 | * The name to be matched is split up in path segments. A path segment is the | |
85 | * name of a directory or file, which is bounded by | |
86 | * <code>File.separator</code> ('/' under UNIX, '\' under Windows). | |
87 | * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", | |
88 | * "def","ghi" and "xyz.java". | |
89 | * The same is done for the pattern against which should be matched. | |
90 | * <p> | |
91 | * The segments of the name and the pattern are then matched against each | |
92 | * other. When '**' is used for a path segment in the pattern, it matches | |
93 | * zero or more path segments of the name. | |
94 | * <p> | |
95 | * There is a special case regarding the use of <code>File.separator</code>s | |
96 | * at the beginning of the pattern and the string to match:<br> | |
97 | * When a pattern starts with a <code>File.separator</code>, the string | |
98 | * to match must also start with a <code>File.separator</code>. | |
99 | * When a pattern does not start with a <code>File.separator</code>, the | |
100 | * string to match may not start with a <code>File.separator</code>. | |
101 | * When one of these rules is not obeyed, the string will not | |
102 | * match. | |
103 | * <p> | |
104 | * When a name path segment is matched against a pattern path segment, the | |
105 | * following special characters can be used:<br> | |
106 | * '*' matches zero or more characters<br> | |
107 | * '?' matches one character. | |
108 | * <p> | |
109 | * Examples: | |
110 | * <p> | |
111 | * "**\*.class" matches all .class files/dirs in a directory tree. | |
112 | * <p> | |
113 | * "test\a??.java" matches all files/dirs which start with an 'a', then two | |
114 | * more characters and then ".java", in a directory called test. | |
115 | * <p> | |
116 | * "**" matches everything in a directory tree. | |
117 | * <p> | |
118 | * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where | |
119 | * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). | |
120 | * <p> | |
121 | * Case sensitivity may be turned off if necessary. By default, it is | |
122 | * turned on. | |
123 | * <p> | |
124 | * Example of usage: | |
125 | * <pre> | |
126 | * String[] includes = {"**\\*.class"}; | |
127 | * String[] excludes = {"modules\\*\\**"}; | |
128 | * ds.setIncludes(includes); | |
129 | * ds.setExcludes(excludes); | |
130 | * ds.setBasedir(new File("test")); | |
131 | * ds.setCaseSensitive(true); | |
132 | * ds.scan(); | |
133 | * | |
134 | * System.out.println("FILES:"); | |
135 | * String[] files = ds.getIncludedFiles(); | |
136 | * for (int i = 0; i < files.length; i++) { | |
137 | * System.out.println(files[i]); | |
138 | * } | |
139 | * </pre> | |
140 | * This will scan a directory called test for .class files, but excludes all | |
141 | * files in all proper subdirectories of a directory called "modules" | |
142 | * | |
143 | * @author Arnout J. Kuiper | |
144 | * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a> | |
145 | * @author Magesh Umasankar | |
146 | * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> | |
147 | * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a> | |
148 | */ | |
149 | public class DirectoryScanner | |
150 | { | |
151 | /** | |
152 | * Patterns which should be excluded by default. | |
153 | * | |
154 | * @see #addDefaultExcludes() | |
155 | */ | |
156 | 0 | public static final String[] DEFAULTEXCLUDES = { |
157 | // Miscellaneous typical temporary files | |
158 | "**/*~", | |
159 | "**/#*#", | |
160 | "**/.#*", | |
161 | "**/%*%", | |
162 | "**/._*", | |
163 | ||
164 | // CVS | |
165 | "**/CVS", | |
166 | "**/CVS/**", | |
167 | "**/.cvsignore", | |
168 | ||
169 | // SCCS | |
170 | "**/SCCS", | |
171 | "**/SCCS/**", | |
172 | ||
173 | // Visual SourceSafe | |
174 | "**/vssver.scc", | |
175 | ||
176 | // Subversion | |
177 | "**/.svn", | |
178 | "**/.svn/**", | |
179 | ||
180 | // Arch | |
181 | "**/.arch-ids", | |
182 | "**/.arch-ids/**", | |
183 | ||
184 | //Bazaar | |
185 | "**/.bzr", | |
186 | "**/.bzr/**", | |
187 | ||
188 | //SurroundSCM | |
189 | "**/.MySCMServerInfo", | |
190 | ||
191 | // Mac | |
192 | "**/.DS_Store" | |
193 | }; | |
194 | ||
195 | /** The base directory to be scanned. */ | |
196 | protected File basedir; | |
197 | ||
198 | /** The patterns for the files to be included. */ | |
199 | protected String[] includes; | |
200 | ||
201 | /** The patterns for the files to be excluded. */ | |
202 | protected String[] excludes; | |
203 | ||
204 | /** The files which matched at least one include and no excludes | |
205 | * and were selected. | |
206 | */ | |
207 | protected Vector filesIncluded; | |
208 | ||
209 | /** The files which did not match any includes or selectors. */ | |
210 | protected Vector filesNotIncluded; | |
211 | ||
212 | /** | |
213 | * The files which matched at least one include and at least | |
214 | * one exclude. | |
215 | */ | |
216 | protected Vector filesExcluded; | |
217 | ||
218 | /** The directories which matched at least one include and no excludes | |
219 | * and were selected. | |
220 | */ | |
221 | protected Vector dirsIncluded; | |
222 | ||
223 | /** The directories which were found and did not match any includes. */ | |
224 | protected Vector dirsNotIncluded; | |
225 | ||
226 | /** | |
227 | * The directories which matched at least one include and at least one | |
228 | * exclude. | |
229 | */ | |
230 | protected Vector dirsExcluded; | |
231 | ||
232 | /** The files which matched at least one include and no excludes and | |
233 | * which a selector discarded. | |
234 | */ | |
235 | protected Vector filesDeselected; | |
236 | ||
237 | /** The directories which matched at least one include and no excludes | |
238 | * but which a selector discarded. | |
239 | */ | |
240 | protected Vector dirsDeselected; | |
241 | ||
242 | /** Whether or not our results were built by a slow scan. */ | |
243 | 0 | protected boolean haveSlowResults = false; |
244 | ||
245 | /** | |
246 | * Whether or not the file system should be treated as a case sensitive | |
247 | * one. | |
248 | */ | |
249 | 0 | protected boolean isCaseSensitive = true; |
250 | ||
251 | /** | |
252 | * Whether or not symbolic links should be followed. | |
253 | * | |
254 | * @since Ant 1.5 | |
255 | */ | |
256 | 0 | private boolean followSymlinks = true; |
257 | ||
258 | /** Whether or not everything tested so far has been included. */ | |
259 | 0 | protected boolean everythingIncluded = true; |
260 | ||
261 | /** | |
262 | * Sole constructor. | |
263 | */ | |
264 | public DirectoryScanner() | |
265 | 0 | { |
266 | 0 | } |
267 | ||
268 | /** | |
269 | * Tests whether or not a given path matches the start of a given | |
270 | * pattern up to the first "**". | |
271 | * <p> | |
272 | * This is not a general purpose test and should only be used if you | |
273 | * can live with false positives. For example, <code>pattern=**\a</code> | |
274 | * and <code>str=b</code> will yield <code>true</code>. | |
275 | * | |
276 | * @param pattern The pattern to match against. Must not be | |
277 | * <code>null</code>. | |
278 | * @param str The path to match, as a String. Must not be | |
279 | * <code>null</code>. | |
280 | * | |
281 | * @return whether or not a given path matches the start of a given | |
282 | * pattern up to the first "**". | |
283 | */ | |
284 | protected static boolean matchPatternStart( String pattern, String str ) | |
285 | { | |
286 | 0 | return SelectorUtils.matchPatternStart( pattern, str ); |
287 | } | |
288 | ||
289 | /** | |
290 | * Tests whether or not a given path matches the start of a given | |
291 | * pattern up to the first "**". | |
292 | * <p> | |
293 | * This is not a general purpose test and should only be used if you | |
294 | * can live with false positives. For example, <code>pattern=**\a</code> | |
295 | * and <code>str=b</code> will yield <code>true</code>. | |
296 | * | |
297 | * @param pattern The pattern to match against. Must not be | |
298 | * <code>null</code>. | |
299 | * @param str The path to match, as a String. Must not be | |
300 | * <code>null</code>. | |
301 | * @param isCaseSensitive Whether or not matching should be performed | |
302 | * case sensitively. | |
303 | * | |
304 | * @return whether or not a given path matches the start of a given | |
305 | * pattern up to the first "**". | |
306 | */ | |
307 | protected static boolean matchPatternStart( String pattern, String str, | |
308 | boolean isCaseSensitive ) | |
309 | { | |
310 | 0 | return SelectorUtils.matchPatternStart( pattern, str, isCaseSensitive ); |
311 | } | |
312 | ||
313 | /** | |
314 | * Tests whether or not a given path matches a given pattern. | |
315 | * | |
316 | * @param pattern The pattern to match against. Must not be | |
317 | * <code>null</code>. | |
318 | * @param str The path to match, as a String. Must not be | |
319 | * <code>null</code>. | |
320 | * | |
321 | * @return <code>true</code> if the pattern matches against the string, | |
322 | * or <code>false</code> otherwise. | |
323 | */ | |
324 | protected static boolean matchPath( String pattern, String str ) | |
325 | { | |
326 | 0 | return SelectorUtils.matchPath( pattern, str ); |
327 | } | |
328 | ||
329 | /** | |
330 | * Tests whether or not a given path matches a given pattern. | |
331 | * | |
332 | * @param pattern The pattern to match against. Must not be | |
333 | * <code>null</code>. | |
334 | * @param str The path to match, as a String. Must not be | |
335 | * <code>null</code>. | |
336 | * @param isCaseSensitive Whether or not matching should be performed | |
337 | * case sensitively. | |
338 | * | |
339 | * @return <code>true</code> if the pattern matches against the string, | |
340 | * or <code>false</code> otherwise. | |
341 | */ | |
342 | protected static boolean matchPath( String pattern, String str, | |
343 | boolean isCaseSensitive ) | |
344 | { | |
345 | 0 | return SelectorUtils.matchPath( pattern, str, isCaseSensitive ); |
346 | } | |
347 | ||
348 | /** | |
349 | * Tests whether or not a string matches against a pattern. | |
350 | * The pattern may contain two special characters:<br> | |
351 | * '*' means zero or more characters<br> | |
352 | * '?' means one and only one character | |
353 | * | |
354 | * @param pattern The pattern to match against. | |
355 | * Must not be <code>null</code>. | |
356 | * @param str The string which must be matched against the pattern. | |
357 | * Must not be <code>null</code>. | |
358 | * | |
359 | * @return <code>true</code> if the string matches against the pattern, | |
360 | * or <code>false</code> otherwise. | |
361 | */ | |
362 | public static boolean match( String pattern, String str ) | |
363 | { | |
364 | 0 | return SelectorUtils.match( pattern, str ); |
365 | } | |
366 | ||
367 | /** | |
368 | * Tests whether or not a string matches against a pattern. | |
369 | * The pattern may contain two special characters:<br> | |
370 | * '*' means zero or more characters<br> | |
371 | * '?' means one and only one character | |
372 | * | |
373 | * @param pattern The pattern to match against. | |
374 | * Must not be <code>null</code>. | |
375 | * @param str The string which must be matched against the pattern. | |
376 | * Must not be <code>null</code>. | |
377 | * @param isCaseSensitive Whether or not matching should be performed | |
378 | * case sensitively. | |
379 | * | |
380 | * | |
381 | * @return <code>true</code> if the string matches against the pattern, | |
382 | * or <code>false</code> otherwise. | |
383 | */ | |
384 | protected static boolean match( String pattern, String str, | |
385 | boolean isCaseSensitive ) | |
386 | { | |
387 | 0 | return SelectorUtils.match( pattern, str, isCaseSensitive ); |
388 | } | |
389 | ||
390 | /** | |
391 | * Sets the base directory to be scanned. This is the directory which is | |
392 | * scanned recursively. All '/' and '\' characters are replaced by | |
393 | * <code>File.separatorChar</code>, so the separator used need not match | |
394 | * <code>File.separatorChar</code>. | |
395 | * | |
396 | * @param basedir The base directory to scan. | |
397 | * Must not be <code>null</code>. | |
398 | */ | |
399 | public void setBasedir( String basedir ) | |
400 | { | |
401 | 0 | setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( |
402 | '\\', File.separatorChar ) ) ); | |
403 | 0 | } |
404 | ||
405 | /** | |
406 | * Sets the base directory to be scanned. This is the directory which is | |
407 | * scanned recursively. | |
408 | * | |
409 | * @param basedir The base directory for scanning. | |
410 | * Should not be <code>null</code>. | |
411 | */ | |
412 | public void setBasedir( File basedir ) | |
413 | { | |
414 | 0 | this.basedir = basedir; |
415 | 0 | } |
416 | ||
417 | /** | |
418 | * Returns the base directory to be scanned. | |
419 | * This is the directory which is scanned recursively. | |
420 | * | |
421 | * @return the base directory to be scanned | |
422 | */ | |
423 | public File getBasedir() | |
424 | { | |
425 | 0 | return basedir; |
426 | } | |
427 | ||
428 | /** | |
429 | * Sets whether or not the file system should be regarded as case sensitive. | |
430 | * | |
431 | * @param isCaseSensitive whether or not the file system should be | |
432 | * regarded as a case sensitive one | |
433 | */ | |
434 | public void setCaseSensitive( boolean isCaseSensitive ) | |
435 | { | |
436 | 0 | this.isCaseSensitive = isCaseSensitive; |
437 | 0 | } |
438 | ||
439 | /** | |
440 | * Sets whether or not symbolic links should be followed. | |
441 | * | |
442 | * @param followSymlinks whether or not symbolic links should be followed | |
443 | */ | |
444 | public void setFollowSymlinks( boolean followSymlinks ) | |
445 | { | |
446 | 0 | this.followSymlinks = followSymlinks; |
447 | 0 | } |
448 | ||
449 | /** | |
450 | * Sets the list of include patterns to use. All '/' and '\' characters | |
451 | * are replaced by <code>File.separatorChar</code>, so the separator used | |
452 | * need not match <code>File.separatorChar</code>. | |
453 | * <p> | |
454 | * When a pattern ends with a '/' or '\', "**" is appended. | |
455 | * | |
456 | * @param includes A list of include patterns. | |
457 | * May be <code>null</code>, indicating that all files | |
458 | * should be included. If a non-<code>null</code> | |
459 | * list is given, all elements must be | |
460 | * non-<code>null</code>. | |
461 | */ | |
462 | public void setIncludes( String[] includes ) | |
463 | { | |
464 | 0 | if ( includes == null ) |
465 | { | |
466 | 0 | this.includes = null; |
467 | } | |
468 | else | |
469 | { | |
470 | 0 | this.includes = new String[includes.length]; |
471 | 0 | for ( int i = 0; i < includes.length; i++ ) |
472 | { | |
473 | String pattern; | |
474 | 0 | pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( |
475 | '\\', File.separatorChar ); | |
476 | 0 | if ( pattern.endsWith( File.separator ) ) |
477 | { | |
478 | 0 | pattern += "**"; |
479 | } | |
480 | 0 | this.includes[i] = pattern; |
481 | } | |
482 | } | |
483 | 0 | } |
484 | ||
485 | ||
486 | /** | |
487 | * Sets the list of exclude patterns to use. All '/' and '\' characters | |
488 | * are replaced by <code>File.separatorChar</code>, so the separator used | |
489 | * need not match <code>File.separatorChar</code>. | |
490 | * <p> | |
491 | * When a pattern ends with a '/' or '\', "**" is appended. | |
492 | * | |
493 | * @param excludes A list of exclude patterns. | |
494 | * May be <code>null</code>, indicating that no files | |
495 | * should be excluded. If a non-<code>null</code> list is | |
496 | * given, all elements must be non-<code>null</code>. | |
497 | */ | |
498 | public void setExcludes( String[] excludes ) | |
499 | { | |
500 | 0 | if ( excludes == null ) |
501 | { | |
502 | 0 | this.excludes = null; |
503 | } | |
504 | else | |
505 | { | |
506 | 0 | this.excludes = new String[excludes.length]; |
507 | 0 | for ( int i = 0; i < excludes.length; i++ ) |
508 | { | |
509 | String pattern; | |
510 | 0 | pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( |
511 | '\\', File.separatorChar ); | |
512 | 0 | if ( pattern.endsWith( File.separator ) ) |
513 | { | |
514 | 0 | pattern += "**"; |
515 | } | |
516 | 0 | this.excludes[i] = pattern; |
517 | } | |
518 | } | |
519 | 0 | } |
520 | ||
521 | /** | |
522 | * Returns whether or not the scanner has included all the files or | |
523 | * directories it has come across so far. | |
524 | * | |
525 | * @return <code>true</code> if all files and directories which have | |
526 | * been found so far have been included. | |
527 | */ | |
528 | public boolean isEverythingIncluded() | |
529 | { | |
530 | 0 | return everythingIncluded; |
531 | } | |
532 | ||
533 | /** | |
534 | * Scans the base directory for files which match at least one include | |
535 | * pattern and don't match any exclude patterns. If there are selectors | |
536 | * then the files must pass muster there, as well. | |
537 | * | |
538 | * @exception IllegalStateException if the base directory was set | |
539 | * incorrectly (i.e. if it is <code>null</code>, doesn't exist, | |
540 | * or isn't a directory). | |
541 | */ | |
542 | public void scan() throws IllegalStateException | |
543 | { | |
544 | 0 | if ( basedir == null ) |
545 | { | |
546 | 0 | throw new IllegalStateException( "No basedir set" ); |
547 | } | |
548 | 0 | if ( !basedir.exists() ) |
549 | { | |
550 | 0 | throw new IllegalStateException( "basedir " + basedir |
551 | + " does not exist" ); | |
552 | } | |
553 | 0 | if ( !basedir.isDirectory() ) |
554 | { | |
555 | 0 | throw new IllegalStateException( "basedir " + basedir |
556 | + " is not a directory" ); | |
557 | } | |
558 | ||
559 | 0 | if ( includes == null ) |
560 | { | |
561 | // No includes supplied, so set it to 'matches all' | |
562 | 0 | includes = new String[1]; |
563 | 0 | includes[0] = "**"; |
564 | } | |
565 | 0 | if ( excludes == null ) |
566 | { | |
567 | 0 | excludes = new String[0]; |
568 | } | |
569 | ||
570 | 0 | filesIncluded = new Vector(); |
571 | 0 | filesNotIncluded = new Vector(); |
572 | 0 | filesExcluded = new Vector(); |
573 | 0 | filesDeselected = new Vector(); |
574 | 0 | dirsIncluded = new Vector(); |
575 | 0 | dirsNotIncluded = new Vector(); |
576 | 0 | dirsExcluded = new Vector(); |
577 | 0 | dirsDeselected = new Vector(); |
578 | ||
579 | 0 | if ( isIncluded( "" ) ) |
580 | { | |
581 | 0 | if ( !isExcluded( "" ) ) |
582 | { | |
583 | 0 | if ( isSelected( "", basedir ) ) |
584 | { | |
585 | 0 | dirsIncluded.addElement( "" ); |
586 | } | |
587 | else | |
588 | { | |
589 | 0 | dirsDeselected.addElement( "" ); |
590 | } | |
591 | } | |
592 | else | |
593 | { | |
594 | 0 | dirsExcluded.addElement( "" ); |
595 | } | |
596 | } | |
597 | else | |
598 | { | |
599 | 0 | dirsNotIncluded.addElement( "" ); |
600 | } | |
601 | 0 | scandir( basedir, "", true ); |
602 | 0 | } |
603 | ||
604 | /** | |
605 | * Top level invocation for a slow scan. A slow scan builds up a full | |
606 | * list of excluded/included files/directories, whereas a fast scan | |
607 | * will only have full results for included files, as it ignores | |
608 | * directories which can't possibly hold any included files/directories. | |
609 | * <p> | |
610 | * Returns immediately if a slow scan has already been completed. | |
611 | */ | |
612 | protected void slowScan() | |
613 | { | |
614 | 0 | if ( haveSlowResults ) |
615 | { | |
616 | 0 | return; |
617 | } | |
618 | ||
619 | 0 | String[] excl = new String[dirsExcluded.size()]; |
620 | 0 | dirsExcluded.copyInto( excl ); |
621 | ||
622 | 0 | String[] notIncl = new String[dirsNotIncluded.size()]; |
623 | 0 | dirsNotIncluded.copyInto( notIncl ); |
624 | ||
625 | 0 | for ( int i = 0; i < excl.length; i++ ) |
626 | { | |
627 | 0 | if ( !couldHoldIncluded( excl[i] ) ) |
628 | { | |
629 | 0 | scandir( new File( basedir, excl[i] ), |
630 | excl[i] + File.separator, false ); | |
631 | } | |
632 | } | |
633 | ||
634 | 0 | for ( int i = 0; i < notIncl.length; i++ ) |
635 | { | |
636 | 0 | if ( !couldHoldIncluded( notIncl[i] ) ) |
637 | { | |
638 | 0 | scandir( new File( basedir, notIncl[i] ), |
639 | notIncl[i] + File.separator, false ); | |
640 | } | |
641 | } | |
642 | ||
643 | 0 | haveSlowResults = true; |
644 | 0 | } |
645 | ||
646 | /** | |
647 | * Scans the given directory for files and directories. Found files and | |
648 | * directories are placed in their respective collections, based on the | |
649 | * matching of includes, excludes, and the selectors. When a directory | |
650 | * is found, it is scanned recursively. | |
651 | * | |
652 | * @param dir The directory to scan. Must not be <code>null</code>. | |
653 | * @param vpath The path relative to the base directory (needed to | |
654 | * prevent problems with an absolute path when using | |
655 | * dir). Must not be <code>null</code>. | |
656 | * @param fast Whether or not this call is part of a fast scan. | |
657 | * @throws IOException | |
658 | * | |
659 | * @see #filesIncluded | |
660 | * @see #filesNotIncluded | |
661 | * @see #filesExcluded | |
662 | * @see #dirsIncluded | |
663 | * @see #dirsNotIncluded | |
664 | * @see #dirsExcluded | |
665 | * @see #slowScan | |
666 | */ | |
667 | protected void scandir( File dir, String vpath, boolean fast ) | |
668 | { | |
669 | 0 | String[] newfiles = dir.list(); |
670 | ||
671 | 0 | if ( newfiles == null ) |
672 | { | |
673 | /* | |
674 | * two reasons are mentioned in the API docs for File.list | |
675 | * (1) dir is not a directory. This is impossible as | |
676 | * we wouldn't get here in this case. | |
677 | * (2) an IO error occurred (why doesn't it throw an exception | |
678 | * then???) | |
679 | */ | |
680 | ||
681 | ||
682 | /* | |
683 | * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... | |
684 | * this is affecting the assembly plugin, fwiw. I will initialize the newfiles array as | |
685 | * zero-length for now. | |
686 | * | |
687 | * NOTE: I can't find the problematic code, as it appears to come from a native method | |
688 | * in UnixFileSystem... | |
689 | */ | |
690 | 0 | newfiles = new String[0]; |
691 | ||
692 | // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); | |
693 | } | |
694 | ||
695 | 0 | if ( !followSymlinks ) |
696 | { | |
697 | 0 | Vector noLinks = new Vector(); |
698 | 0 | for ( int i = 0; i < newfiles.length; i++ ) |
699 | { | |
700 | try | |
701 | { | |
702 | 0 | if ( isSymbolicLink( dir, newfiles[i] ) ) |
703 | { | |
704 | 0 | String name = vpath + newfiles[i]; |
705 | 0 | File file = new File( dir, newfiles[i] ); |
706 | 0 | if ( file.isDirectory() ) |
707 | { | |
708 | 0 | dirsExcluded.addElement( name ); |
709 | } | |
710 | else | |
711 | { | |
712 | 0 | filesExcluded.addElement( name ); |
713 | } | |
714 | 0 | } |
715 | else | |
716 | { | |
717 | 0 | noLinks.addElement( newfiles[i] ); |
718 | } | |
719 | } | |
720 | 0 | catch ( IOException ioe ) |
721 | { | |
722 | 0 | String msg = "IOException caught while checking " |
723 | + "for links, couldn't get cannonical path!"; | |
724 | // will be caught and redirected to Ant's logging system | |
725 | 0 | System.err.println( msg ); |
726 | 0 | noLinks.addElement( newfiles[i] ); |
727 | 0 | } |
728 | } | |
729 | 0 | newfiles = new String[noLinks.size()]; |
730 | 0 | noLinks.copyInto( newfiles ); |
731 | } | |
732 | ||
733 | 0 | for ( int i = 0; i < newfiles.length; i++ ) |
734 | { | |
735 | 0 | String name = vpath + newfiles[i]; |
736 | 0 | File file = new File( dir, newfiles[i] ); |
737 | 0 | if ( file.isDirectory() ) |
738 | { | |
739 | 0 | if ( isIncluded( name ) ) |
740 | { | |
741 | 0 | if ( !isExcluded( name ) ) |
742 | { | |
743 | 0 | if ( isSelected( name, file ) ) |
744 | { | |
745 | 0 | dirsIncluded.addElement( name ); |
746 | 0 | if ( fast ) |
747 | { | |
748 | 0 | scandir( file, name + File.separator, fast ); |
749 | } | |
750 | } | |
751 | else | |
752 | { | |
753 | 0 | everythingIncluded = false; |
754 | 0 | dirsDeselected.addElement( name ); |
755 | 0 | if ( fast && couldHoldIncluded( name ) ) |
756 | { | |
757 | 0 | scandir( file, name + File.separator, fast ); |
758 | } | |
759 | } | |
760 | ||
761 | } | |
762 | else | |
763 | { | |
764 | 0 | everythingIncluded = false; |
765 | 0 | dirsExcluded.addElement( name ); |
766 | 0 | if ( fast && couldHoldIncluded( name ) ) |
767 | { | |
768 | 0 | scandir( file, name + File.separator, fast ); |
769 | } | |
770 | } | |
771 | } | |
772 | else | |
773 | { | |
774 | 0 | everythingIncluded = false; |
775 | 0 | dirsNotIncluded.addElement( name ); |
776 | 0 | if ( fast && couldHoldIncluded( name ) ) |
777 | { | |
778 | 0 | scandir( file, name + File.separator, fast ); |
779 | } | |
780 | } | |
781 | 0 | if ( !fast ) |
782 | { | |
783 | 0 | scandir( file, name + File.separator, fast ); |
784 | } | |
785 | } | |
786 | 0 | else if ( file.isFile() ) |
787 | { | |
788 | 0 | if ( isIncluded( name ) ) |
789 | { | |
790 | 0 | if ( !isExcluded( name ) ) |
791 | { | |
792 | 0 | if ( isSelected( name, file ) ) |
793 | { | |
794 | 0 | filesIncluded.addElement( name ); |
795 | } | |
796 | else | |
797 | { | |
798 | 0 | everythingIncluded = false; |
799 | 0 | filesDeselected.addElement( name ); |
800 | } | |
801 | } | |
802 | else | |
803 | { | |
804 | 0 | everythingIncluded = false; |
805 | 0 | filesExcluded.addElement( name ); |
806 | } | |
807 | } | |
808 | else | |
809 | { | |
810 | 0 | everythingIncluded = false; |
811 | 0 | filesNotIncluded.addElement( name ); |
812 | } | |
813 | } | |
814 | } | |
815 | 0 | } |
816 | ||
817 | /** | |
818 | * Tests whether or not a name matches against at least one include | |
819 | * pattern. | |
820 | * | |
821 | * @param name The name to match. Must not be <code>null</code>. | |
822 | * @return <code>true</code> when the name matches against at least one | |
823 | * include pattern, or <code>false</code> otherwise. | |
824 | */ | |
825 | protected boolean isIncluded( String name ) | |
826 | { | |
827 | 0 | for ( int i = 0; i < includes.length; i++ ) |
828 | { | |
829 | 0 | if ( matchPath( includes[i], name, isCaseSensitive ) ) |
830 | { | |
831 | 0 | return true; |
832 | } | |
833 | } | |
834 | 0 | return false; |
835 | } | |
836 | ||
837 | /** | |
838 | * Tests whether or not a name matches the start of at least one include | |
839 | * pattern. | |
840 | * | |
841 | * @param name The name to match. Must not be <code>null</code>. | |
842 | * @return <code>true</code> when the name matches against the start of at | |
843 | * least one include pattern, or <code>false</code> otherwise. | |
844 | */ | |
845 | protected boolean couldHoldIncluded( String name ) | |
846 | { | |
847 | 0 | for ( int i = 0; i < includes.length; i++ ) |
848 | { | |
849 | 0 | if ( matchPatternStart( includes[i], name, isCaseSensitive ) ) |
850 | { | |
851 | 0 | return true; |
852 | } | |
853 | } | |
854 | 0 | return false; |
855 | } | |
856 | ||
857 | /** | |
858 | * Tests whether or not a name matches against at least one exclude | |
859 | * pattern. | |
860 | * | |
861 | * @param name The name to match. Must not be <code>null</code>. | |
862 | * @return <code>true</code> when the name matches against at least one | |
863 | * exclude pattern, or <code>false</code> otherwise. | |
864 | */ | |
865 | protected boolean isExcluded( String name ) | |
866 | { | |
867 | 0 | for ( int i = 0; i < excludes.length; i++ ) |
868 | { | |
869 | 0 | if ( matchPath( excludes[i], name, isCaseSensitive ) ) |
870 | { | |
871 | 0 | return true; |
872 | } | |
873 | } | |
874 | 0 | return false; |
875 | } | |
876 | ||
877 | /** | |
878 | * Tests whether a name should be selected. | |
879 | * | |
880 | * @param name the filename to check for selecting | |
881 | * @param file the java.io.File object for this filename | |
882 | * @return <code>false</code> when the selectors says that the file | |
883 | * should not be selected, <code>true</code> otherwise. | |
884 | */ | |
885 | protected boolean isSelected( String name, File file ) | |
886 | { | |
887 | 0 | return true; |
888 | } | |
889 | ||
890 | /** | |
891 | * Returns the names of the files which matched at least one of the | |
892 | * include patterns and none of the exclude patterns. | |
893 | * The names are relative to the base directory. | |
894 | * | |
895 | * @return the names of the files which matched at least one of the | |
896 | * include patterns and none of the exclude patterns. | |
897 | */ | |
898 | public String[] getIncludedFiles() | |
899 | { | |
900 | 0 | String[] files = new String[filesIncluded.size()]; |
901 | 0 | filesIncluded.copyInto( files ); |
902 | 0 | return files; |
903 | } | |
904 | ||
905 | /** | |
906 | * Returns the names of the files which matched none of the include | |
907 | * patterns. The names are relative to the base directory. This involves | |
908 | * performing a slow scan if one has not already been completed. | |
909 | * | |
910 | * @return the names of the files which matched none of the include | |
911 | * patterns. | |
912 | * | |
913 | * @see #slowScan | |
914 | */ | |
915 | public String[] getNotIncludedFiles() | |
916 | { | |
917 | 0 | slowScan(); |
918 | 0 | String[] files = new String[filesNotIncluded.size()]; |
919 | 0 | filesNotIncluded.copyInto( files ); |
920 | 0 | return files; |
921 | } | |
922 | ||
923 | /** | |
924 | * Returns the names of the files which matched at least one of the | |
925 | * include patterns and at least one of the exclude patterns. | |
926 | * The names are relative to the base directory. This involves | |
927 | * performing a slow scan if one has not already been completed. | |
928 | * | |
929 | * @return the names of the files which matched at least one of the | |
930 | * include patterns and at at least one of the exclude patterns. | |
931 | * | |
932 | * @see #slowScan | |
933 | */ | |
934 | public String[] getExcludedFiles() | |
935 | { | |
936 | 0 | slowScan(); |
937 | 0 | String[] files = new String[filesExcluded.size()]; |
938 | 0 | filesExcluded.copyInto( files ); |
939 | 0 | return files; |
940 | } | |
941 | ||
942 | /** | |
943 | * <p>Returns the names of the files which were selected out and | |
944 | * therefore not ultimately included.</p> | |
945 | * | |
946 | * <p>The names are relative to the base directory. This involves | |
947 | * performing a slow scan if one has not already been completed.</p> | |
948 | * | |
949 | * @return the names of the files which were deselected. | |
950 | * | |
951 | * @see #slowScan | |
952 | */ | |
953 | public String[] getDeselectedFiles() | |
954 | { | |
955 | 0 | slowScan(); |
956 | 0 | String[] files = new String[filesDeselected.size()]; |
957 | 0 | filesDeselected.copyInto( files ); |
958 | 0 | return files; |
959 | } | |
960 | ||
961 | /** | |
962 | * Returns the names of the directories which matched at least one of the | |
963 | * include patterns and none of the exclude patterns. | |
964 | * The names are relative to the base directory. | |
965 | * | |
966 | * @return the names of the directories which matched at least one of the | |
967 | * include patterns and none of the exclude patterns. | |
968 | */ | |
969 | public String[] getIncludedDirectories() | |
970 | { | |
971 | 0 | String[] directories = new String[dirsIncluded.size()]; |
972 | 0 | dirsIncluded.copyInto( directories ); |
973 | 0 | return directories; |
974 | } | |
975 | ||
976 | /** | |
977 | * Returns the names of the directories which matched none of the include | |
978 | * patterns. The names are relative to the base directory. This involves | |
979 | * performing a slow scan if one has not already been completed. | |
980 | * | |
981 | * @return the names of the directories which matched none of the include | |
982 | * patterns. | |
983 | * | |
984 | * @see #slowScan | |
985 | */ | |
986 | public String[] getNotIncludedDirectories() | |
987 | { | |
988 | 0 | slowScan(); |
989 | 0 | String[] directories = new String[dirsNotIncluded.size()]; |
990 | 0 | dirsNotIncluded.copyInto( directories ); |
991 | 0 | return directories; |
992 | } | |
993 | ||
994 | /** | |
995 | * Returns the names of the directories which matched at least one of the | |
996 | * include patterns and at least one of the exclude patterns. | |
997 | * The names are relative to the base directory. This involves | |
998 | * performing a slow scan if one has not already been completed. | |
999 | * | |
1000 | * @return the names of the directories which matched at least one of the | |
1001 | * include patterns and at least one of the exclude patterns. | |
1002 | * | |
1003 | * @see #slowScan | |
1004 | */ | |
1005 | public String[] getExcludedDirectories() | |
1006 | { | |
1007 | 0 | slowScan(); |
1008 | 0 | String[] directories = new String[dirsExcluded.size()]; |
1009 | 0 | dirsExcluded.copyInto( directories ); |
1010 | 0 | return directories; |
1011 | } | |
1012 | ||
1013 | /** | |
1014 | * <p>Returns the names of the directories which were selected out and | |
1015 | * therefore not ultimately included.</p> | |
1016 | * | |
1017 | * <p>The names are relative to the base directory. This involves | |
1018 | * performing a slow scan if one has not already been completed.</p> | |
1019 | * | |
1020 | * @return the names of the directories which were deselected. | |
1021 | * | |
1022 | * @see #slowScan | |
1023 | */ | |
1024 | public String[] getDeselectedDirectories() | |
1025 | { | |
1026 | 0 | slowScan(); |
1027 | 0 | String[] directories = new String[dirsDeselected.size()]; |
1028 | 0 | dirsDeselected.copyInto( directories ); |
1029 | 0 | return directories; |
1030 | } | |
1031 | ||
1032 | /** | |
1033 | * Adds default exclusions to the current exclusions set. | |
1034 | */ | |
1035 | public void addDefaultExcludes() | |
1036 | { | |
1037 | 0 | int excludesLength = excludes == null ? 0 : excludes.length; |
1038 | String[] newExcludes; | |
1039 | 0 | newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; |
1040 | 0 | if ( excludesLength > 0 ) |
1041 | { | |
1042 | 0 | System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); |
1043 | } | |
1044 | 0 | for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) |
1045 | { | |
1046 | 0 | newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace( '/', |
1047 | File.separatorChar ).replace( '\\', File.separatorChar ); | |
1048 | } | |
1049 | 0 | excludes = newExcludes; |
1050 | 0 | } |
1051 | ||
1052 | /** | |
1053 | * Checks whether a given file is a symbolic link. | |
1054 | * | |
1055 | * <p>It doesn't really test for symbolic links but whether the | |
1056 | * canonical and absolute paths of the file are identical - this | |
1057 | * may lead to false positives on some platforms.</p> | |
1058 | * | |
1059 | * @param parent the parent directory of the file to test | |
1060 | * @param name the name of the file to test. | |
1061 | * | |
1062 | * @since Ant 1.5 | |
1063 | */ | |
1064 | public boolean isSymbolicLink( File parent, String name ) | |
1065 | throws IOException | |
1066 | { | |
1067 | 0 | File resolvedParent = new File( parent.getCanonicalPath() ); |
1068 | 0 | File toTest = new File( resolvedParent, name ); |
1069 | 0 | return !toTest.getAbsolutePath().equals( toTest.getCanonicalPath() ); |
1070 | } | |
1071 | ||
1072 | } |