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 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 protected boolean haveSlowResults = false;
244
245 /**
246 * Whether or not the file system should be treated as a case sensitive
247 * one.
248 */
249 protected boolean isCaseSensitive = true;
250
251 /**
252 * Whether or not symbolic links should be followed.
253 *
254 * @since Ant 1.5
255 */
256 private boolean followSymlinks = true;
257
258 /** Whether or not everything tested so far has been included. */
259 protected boolean everythingIncluded = true;
260
261 /**
262 * Sole constructor.
263 */
264 public DirectoryScanner()
265 {
266 }
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 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 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 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 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 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 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 setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace(
402 '\\', File.separatorChar ) ) );
403 }
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 this.basedir = basedir;
415 }
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 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 this.isCaseSensitive = isCaseSensitive;
437 }
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 this.followSymlinks = followSymlinks;
447 }
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 if ( includes == null )
465 {
466 this.includes = null;
467 }
468 else
469 {
470 this.includes = new String[includes.length];
471 for ( int i = 0; i < includes.length; i++ )
472 {
473 String pattern;
474 pattern = includes[i].trim().replace( '/', File.separatorChar ).replace(
475 '\\', File.separatorChar );
476 if ( pattern.endsWith( File.separator ) )
477 {
478 pattern += "**";
479 }
480 this.includes[i] = pattern;
481 }
482 }
483 }
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 if ( excludes == null )
501 {
502 this.excludes = null;
503 }
504 else
505 {
506 this.excludes = new String[excludes.length];
507 for ( int i = 0; i < excludes.length; i++ )
508 {
509 String pattern;
510 pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace(
511 '\\', File.separatorChar );
512 if ( pattern.endsWith( File.separator ) )
513 {
514 pattern += "**";
515 }
516 this.excludes[i] = pattern;
517 }
518 }
519 }
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 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 if ( basedir == null )
545 {
546 throw new IllegalStateException( "No basedir set" );
547 }
548 if ( !basedir.exists() )
549 {
550 throw new IllegalStateException( "basedir " + basedir
551 + " does not exist" );
552 }
553 if ( !basedir.isDirectory() )
554 {
555 throw new IllegalStateException( "basedir " + basedir
556 + " is not a directory" );
557 }
558
559 if ( includes == null )
560 {
561 // No includes supplied, so set it to 'matches all'
562 includes = new String[1];
563 includes[0] = "**";
564 }
565 if ( excludes == null )
566 {
567 excludes = new String[0];
568 }
569
570 filesIncluded = new Vector();
571 filesNotIncluded = new Vector();
572 filesExcluded = new Vector();
573 filesDeselected = new Vector();
574 dirsIncluded = new Vector();
575 dirsNotIncluded = new Vector();
576 dirsExcluded = new Vector();
577 dirsDeselected = new Vector();
578
579 if ( isIncluded( "" ) )
580 {
581 if ( !isExcluded( "" ) )
582 {
583 if ( isSelected( "", basedir ) )
584 {
585 dirsIncluded.addElement( "" );
586 }
587 else
588 {
589 dirsDeselected.addElement( "" );
590 }
591 }
592 else
593 {
594 dirsExcluded.addElement( "" );
595 }
596 }
597 else
598 {
599 dirsNotIncluded.addElement( "" );
600 }
601 scandir( basedir, "", true );
602 }
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 if ( haveSlowResults )
615 {
616 return;
617 }
618
619 String[] excl = new String[dirsExcluded.size()];
620 dirsExcluded.copyInto( excl );
621
622 String[] notIncl = new String[dirsNotIncluded.size()];
623 dirsNotIncluded.copyInto( notIncl );
624
625 for ( int i = 0; i < excl.length; i++ )
626 {
627 if ( !couldHoldIncluded( excl[i] ) )
628 {
629 scandir( new File( basedir, excl[i] ),
630 excl[i] + File.separator, false );
631 }
632 }
633
634 for ( int i = 0; i < notIncl.length; i++ )
635 {
636 if ( !couldHoldIncluded( notIncl[i] ) )
637 {
638 scandir( new File( basedir, notIncl[i] ),
639 notIncl[i] + File.separator, false );
640 }
641 }
642
643 haveSlowResults = true;
644 }
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 String[] newfiles = dir.list();
670
671 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 newfiles = new String[0];
691
692 // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() );
693 }
694
695 if ( !followSymlinks )
696 {
697 Vector noLinks = new Vector();
698 for ( int i = 0; i < newfiles.length; i++ )
699 {
700 try
701 {
702 if ( isSymbolicLink( dir, newfiles[i] ) )
703 {
704 String name = vpath + newfiles[i];
705 File file = new File( dir, newfiles[i] );
706 if ( file.isDirectory() )
707 {
708 dirsExcluded.addElement( name );
709 }
710 else
711 {
712 filesExcluded.addElement( name );
713 }
714 }
715 else
716 {
717 noLinks.addElement( newfiles[i] );
718 }
719 }
720 catch ( IOException ioe )
721 {
722 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 System.err.println( msg );
726 noLinks.addElement( newfiles[i] );
727 }
728 }
729 newfiles = new String[noLinks.size()];
730 noLinks.copyInto( newfiles );
731 }
732
733 for ( int i = 0; i < newfiles.length; i++ )
734 {
735 String name = vpath + newfiles[i];
736 File file = new File( dir, newfiles[i] );
737 if ( file.isDirectory() )
738 {
739 if ( isIncluded( name ) )
740 {
741 if ( !isExcluded( name ) )
742 {
743 if ( isSelected( name, file ) )
744 {
745 dirsIncluded.addElement( name );
746 if ( fast )
747 {
748 scandir( file, name + File.separator, fast );
749 }
750 }
751 else
752 {
753 everythingIncluded = false;
754 dirsDeselected.addElement( name );
755 if ( fast && couldHoldIncluded( name ) )
756 {
757 scandir( file, name + File.separator, fast );
758 }
759 }
760
761 }
762 else
763 {
764 everythingIncluded = false;
765 dirsExcluded.addElement( name );
766 if ( fast && couldHoldIncluded( name ) )
767 {
768 scandir( file, name + File.separator, fast );
769 }
770 }
771 }
772 else
773 {
774 everythingIncluded = false;
775 dirsNotIncluded.addElement( name );
776 if ( fast && couldHoldIncluded( name ) )
777 {
778 scandir( file, name + File.separator, fast );
779 }
780 }
781 if ( !fast )
782 {
783 scandir( file, name + File.separator, fast );
784 }
785 }
786 else if ( file.isFile() )
787 {
788 if ( isIncluded( name ) )
789 {
790 if ( !isExcluded( name ) )
791 {
792 if ( isSelected( name, file ) )
793 {
794 filesIncluded.addElement( name );
795 }
796 else
797 {
798 everythingIncluded = false;
799 filesDeselected.addElement( name );
800 }
801 }
802 else
803 {
804 everythingIncluded = false;
805 filesExcluded.addElement( name );
806 }
807 }
808 else
809 {
810 everythingIncluded = false;
811 filesNotIncluded.addElement( name );
812 }
813 }
814 }
815 }
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 for ( int i = 0; i < includes.length; i++ )
828 {
829 if ( matchPath( includes[i], name, isCaseSensitive ) )
830 {
831 return true;
832 }
833 }
834 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 for ( int i = 0; i < includes.length; i++ )
848 {
849 if ( matchPatternStart( includes[i], name, isCaseSensitive ) )
850 {
851 return true;
852 }
853 }
854 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 for ( int i = 0; i < excludes.length; i++ )
868 {
869 if ( matchPath( excludes[i], name, isCaseSensitive ) )
870 {
871 return true;
872 }
873 }
874 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 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 String[] files = new String[filesIncluded.size()];
901 filesIncluded.copyInto( files );
902 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 slowScan();
918 String[] files = new String[filesNotIncluded.size()];
919 filesNotIncluded.copyInto( files );
920 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 slowScan();
937 String[] files = new String[filesExcluded.size()];
938 filesExcluded.copyInto( files );
939 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 slowScan();
956 String[] files = new String[filesDeselected.size()];
957 filesDeselected.copyInto( files );
958 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 String[] directories = new String[dirsIncluded.size()];
972 dirsIncluded.copyInto( directories );
973 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 slowScan();
989 String[] directories = new String[dirsNotIncluded.size()];
990 dirsNotIncluded.copyInto( directories );
991 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 slowScan();
1008 String[] directories = new String[dirsExcluded.size()];
1009 dirsExcluded.copyInto( directories );
1010 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 slowScan();
1027 String[] directories = new String[dirsDeselected.size()];
1028 dirsDeselected.copyInto( directories );
1029 return directories;
1030 }
1031
1032 /**
1033 * Adds default exclusions to the current exclusions set.
1034 */
1035 public void addDefaultExcludes()
1036 {
1037 int excludesLength = excludes == null ? 0 : excludes.length;
1038 String[] newExcludes;
1039 newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
1040 if ( excludesLength > 0 )
1041 {
1042 System.arraycopy( excludes, 0, newExcludes, 0, excludesLength );
1043 }
1044 for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ )
1045 {
1046 newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace( '/',
1047 File.separatorChar ).replace( '\\', File.separatorChar );
1048 }
1049 excludes = newExcludes;
1050 }
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 File resolvedParent = new File( parent.getCanonicalPath() );
1068 File toTest = new File( resolvedParent, name );
1069 return !toTest.getAbsolutePath().equals( toTest.getCanonicalPath() );
1070 }
1071
1072 }