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 }