1 package org.apache.maven.shared.utils.io; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 import java.io.File; 23 import java.io.IOException; 24 import java.nio.file.Files; 25 import java.util.ArrayList; 26 import java.util.Collections; 27 import java.util.HashSet; 28 import java.util.List; 29 import java.util.Set; 30 31 import javax.annotation.Nonnull; 32 import javax.annotation.Nullable; 33 34 /** 35 * Class for scanning a directory for files/directories which match certain criteria. 36 * <p/> 37 * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which 38 * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude 39 * files based on their filename. 40 * <p/> 41 * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is 42 * matched against a set of selectors, including special support for matching against filenames with include and and 43 * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file 44 * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will 45 * be placed in the list of files/directories found. 46 * <p/> 47 * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no 48 * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors 49 * are supplied, none are applied. 50 * <p/> 51 * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment 52 * is the name of a directory or file, which is bounded by <code>File.separator</code> ('/' under UNIX, '\' under 53 * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same 54 * is done for the pattern against which should be matched. 55 * <p/> 56 * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in 57 * the pattern, it matches zero or more path segments of the name. 58 * <p/> 59 * There is a special case regarding the use of <code>File.separator</code>s at the beginning of the pattern and the 60 * string to match:<br> 61 * When a pattern starts with a <code>File.separator</code>, the string to match must also start with a 62 * <code>File.separator</code>. When a pattern does not start with a <code>File.separator</code>, the string to match 63 * may not start with a <code>File.separator</code>. When one of these rules is not obeyed, the string will not match. 64 * <p/> 65 * When a name path segment is matched against a pattern path segment, the following special characters can be used:<br> 66 * '*' matches zero or more characters<br> 67 * '?' matches one character. 68 * <p/> 69 * Examples: 70 * <p/> 71 * "**\*.class" matches all .class files/dirs in a directory tree. 72 * <p/> 73 * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a 74 * directory called test. 75 * <p/> 76 * "**" matches everything in a directory tree. 77 * <p/> 78 * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test 79 * (e.g. "abc\test\def\ghi\XYZ123"). 80 * <p/> 81 * Case sensitivity may be turned off if necessary. By default, it is turned on. 82 * <p/> 83 * Example of usage: 84 * <p/> 85 * <pre> 86 * String[] includes = { "**\\*.class" }; 87 * String[] excludes = { "modules\\*\\**" }; 88 * ds.setIncludes( includes ); 89 * ds.setExcludes( excludes ); 90 * ds.setBasedir( new File( "test" ) ); 91 * ds.setCaseSensitive( true ); 92 * ds.scan(); 93 * 94 * System.out.println( "FILES:" ); 95 * String[] files = ds.getIncludedFiles(); 96 * for ( int i = 0; i < files.length; i++ ) 97 * { 98 * System.out.println( files[i] ); 99 * } 100 * </pre> 101 * <p/> 102 * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a 103 * directory called "modules" 104 * <p/> 105 * This class must not be used from multiple Threads concurrently! 106 * 107 * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a> 108 * @author Magesh Umasankar 109 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a> 110 * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a> 111 * @deprecated use {@code java.nio.file.DirectoryStream} and related classes 112 */ 113 @Deprecated 114 public class DirectoryScanner 115 { 116 /** 117 * Patterns which should be excluded by default. 118 * 119 * @see #addDefaultExcludes() 120 */ 121 public static final String[] DEFAULTEXCLUDES = { 122 // Miscellaneous typical temporary files 123 "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", 124 125 // CVS 126 "**/CVS", "**/CVS/**", "**/.cvsignore", 127 128 // Subversion 129 "**/.svn", "**/.svn/**", 130 131 // Arch 132 "**/.arch-ids", "**/.arch-ids/**", 133 134 // Bazaar 135 "**/.bzr", "**/.bzr/**", 136 137 // SurroundSCM 138 "**/.MySCMServerInfo", 139 140 // Mac 141 "**/.DS_Store", 142 143 // Serena Dimensions Version 10 144 "**/.metadata", "**/.metadata/**", 145 146 // Mercurial 147 "**/.hg", "**/.hg/**", 148 149 // git 150 "**/.git", "**/.git/**", 151 152 // BitKeeper 153 "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", 154 155 // darcs 156 "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; 157 158 /** 159 * The base directory to be scanned. 160 */ 161 private File basedir; 162 163 /** 164 * The patterns for the files to be included. 165 */ 166 private String[] includes; 167 168 /** 169 * The patterns for the files to be excluded. 170 */ 171 private String[] excludes; 172 173 private MatchPatterns excludesPatterns; 174 175 private MatchPatterns includesPatterns; 176 177 178 /** 179 * The files which matched at least one include and no excludes and were selected. 180 */ 181 private List<String> filesIncluded; 182 183 /** 184 * The files which did not match any includes or selectors. 185 */ 186 private List<String> filesNotIncluded; 187 188 /** 189 * The files which matched at least one include and at least one exclude. 190 */ 191 private List<String> filesExcluded; 192 193 /** 194 * The directories which matched at least one include and no excludes and were selected. 195 */ 196 private List<String> dirsIncluded; 197 198 /** 199 * The directories which were found and did not match any includes. 200 */ 201 private List<String> dirsNotIncluded; 202 203 /** 204 * The directories which matched at least one include and at least one exclude. 205 */ 206 private List<String> dirsExcluded; 207 208 /** 209 * Whether or not our results were built by a slow scan. 210 */ 211 private boolean haveSlowResults = false; 212 213 /** 214 * Whether or not the file system should be treated as a case sensitive one. 215 */ 216 private boolean isCaseSensitive = true; 217 218 /** 219 * Whether or not symbolic links should be followed. 220 * 221 * 222 */ 223 private boolean followSymlinks = true; 224 225 226 /** 227 * A {@link ScanConductor} an control the scanning process. 228 */ 229 private ScanConductor scanConductor = null; 230 231 /** 232 * The last ScanAction. We need to store this in the instance as the scan() method doesn't return 233 */ 234 private ScanConductor.ScanAction scanAction = null; 235 236 /** 237 * Sole constructor. 238 */ 239 public DirectoryScanner() 240 { 241 } 242 243 /** 244 * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' 245 * characters are replaced by <code>File.separatorChar</code>, so the separator used need not match 246 * <code>File.separatorChar</code>. 247 * 248 * @param basedir The base directory to scan. Must not be <code>null</code>. 249 */ 250 public void setBasedir( final String basedir ) 251 { 252 setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) ); 253 } 254 255 /** 256 * Sets the base directory to be scanned. This is the directory which is scanned recursively. 257 * 258 * @param basedir The base directory for scanning. Should not be <code>null</code>. 259 */ 260 public void setBasedir( @Nonnull final File basedir ) 261 { 262 this.basedir = basedir; 263 } 264 265 /** 266 * Returns the base directory to be scanned. This is the directory which is scanned recursively. 267 * 268 * @return the base directory to be scanned 269 */ 270 public File getBasedir() 271 { 272 return basedir; 273 } 274 275 /** 276 * Sets whether or not the file system should be regarded as case sensitive. 277 * 278 * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one 279 */ 280 public void setCaseSensitive( final boolean isCaseSensitiveParameter ) 281 { 282 this.isCaseSensitive = isCaseSensitiveParameter; 283 } 284 285 /** 286 * Sets whether or not symbolic links should be followed. 287 * 288 * @param followSymlinks whether or not symbolic links should be followed 289 */ 290 public void setFollowSymlinks( final boolean followSymlinks ) 291 { 292 this.followSymlinks = followSymlinks; 293 } 294 295 /** 296 * Sets the list of include patterns to use. All '/' and '\' characters are replaced by 297 * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>. 298 * <p/> 299 * When a pattern ends with a '/' or '\', "**" is appended. 300 * 301 * @param includes A list of include patterns. May be <code>null</code>, indicating that all files should be 302 * included. If a non-<code>null</code> list is given, all elements must be non-<code>null</code>. 303 */ 304 public void setIncludes( final String... includes ) 305 { 306 if ( includes == null ) 307 { 308 this.includes = null; 309 } 310 else 311 { 312 this.includes = new String[includes.length]; 313 for ( int i = 0; i < includes.length; i++ ) 314 { 315 String pattern; 316 pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); 317 if ( pattern.endsWith( File.separator ) ) 318 { 319 pattern += "**"; 320 } 321 this.includes[i] = pattern; 322 } 323 } 324 } 325 326 /** 327 * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by 328 * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>. 329 * <p/> 330 * When a pattern ends with a '/' or '\', "**" is appended. 331 * 332 * @param excludes A list of exclude patterns. May be <code>null</code>, indicating that no files should be 333 * excluded. If a non-<code>null</code> list is given, all elements must be non-<code>null</code>. 334 */ 335 public void setExcludes( final String... excludes ) 336 { 337 if ( excludes == null ) 338 { 339 this.excludes = null; 340 } 341 else 342 { 343 this.excludes = new String[excludes.length]; 344 for ( int i = 0; i < excludes.length; i++ ) 345 { 346 String pattern; 347 pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); 348 if ( pattern.endsWith( File.separator ) ) 349 { 350 pattern += "**"; 351 } 352 this.excludes[i] = pattern; 353 } 354 } 355 } 356 357 /** 358 * @param scanConductor {@link #scanConductor} 359 */ 360 public void setScanConductor( final ScanConductor scanConductor ) 361 { 362 this.scanConductor = scanConductor; 363 } 364 365 /** 366 * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns. 367 * If there are selectors then the files must pass muster there, as well. 368 * 369 * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is <code>null</code>, 370 * doesn't exist, or isn't a directory). 371 */ 372 public void scan() 373 throws IllegalStateException 374 { 375 if ( basedir == null ) 376 { 377 throw new IllegalStateException( "No basedir set" ); 378 } 379 if ( !basedir.exists() ) 380 { 381 throw new IllegalStateException( "basedir " + basedir + " does not exist" ); 382 } 383 if ( !basedir.isDirectory() ) 384 { 385 throw new IllegalStateException( "basedir " + basedir + " is not a directory" ); 386 } 387 388 setupDefaultFilters(); 389 setupMatchPatterns(); 390 391 filesIncluded = new ArrayList<String>(); 392 filesNotIncluded = new ArrayList<String>(); 393 filesExcluded = new ArrayList<String>(); 394 dirsIncluded = new ArrayList<String>(); 395 dirsNotIncluded = new ArrayList<String>(); 396 dirsExcluded = new ArrayList<String>(); 397 scanAction = ScanConductor.ScanAction.CONTINUE; 398 399 if ( isIncluded( "" ) ) 400 { 401 if ( !isExcluded( "" ) ) 402 { 403 if ( scanConductor != null ) 404 { 405 scanAction = scanConductor.visitDirectory( "", basedir ); 406 407 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) 408 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) 409 || ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) 410 { 411 return; 412 } 413 } 414 415 dirsIncluded.add( "" ); 416 } 417 else 418 { 419 dirsExcluded.add( "" ); 420 } 421 } 422 else 423 { 424 dirsNotIncluded.add( "" ); 425 } 426 scandir( basedir, "", true ); 427 } 428 429 /** 430 * Determine the file differences between the currently included files and 431 * a previously captured list of files. 432 * This method will not look for a changed in content but sole in the 433 * list of files given. 434 * <p/> 435 * The method will compare the given array of file Strings with the result 436 * of the last directory scan. It will execute a {@link #scan()} if no 437 * result of a previous scan could be found. 438 * <p/> 439 * The result of the diff can be queried by the methods 440 * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()} 441 * 442 * @param oldFiles the list of previously captured files names. 443 * @return the result of the directory scan. 444 */ 445 public DirectoryScanResult diffIncludedFiles( String... oldFiles ) 446 { 447 if ( filesIncluded == null ) 448 { 449 // perform a scan if the directory didn't got scanned yet 450 scan(); 451 } 452 453 return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) ); 454 } 455 456 /** 457 * @param oldFiles array of old files 458 * @param newFiles array of new files 459 * @return calculated difference 460 */ 461 public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles ) 462 { 463 Set<String> oldFileSet = arrayAsHashSet( oldFiles ); 464 Set<String> newFileSet = arrayAsHashSet( newFiles ); 465 466 List<String> added = new ArrayList<String>(); 467 List<String> removed = new ArrayList<String>(); 468 469 for ( String oldFile : oldFileSet ) 470 { 471 if ( !newFileSet.contains( oldFile ) ) 472 { 473 removed.add( oldFile ); 474 } 475 } 476 477 for ( String newFile : newFileSet ) 478 { 479 if ( !oldFileSet.contains( newFile ) ) 480 { 481 added.add( newFile ); 482 } 483 } 484 485 String[] filesAdded = added.toArray( new String[added.size()] ); 486 String[] filesRemoved = removed.toArray( new String[removed.size()] ); 487 488 return new DirectoryScanResult( filesAdded, filesRemoved ); 489 } 490 491 492 /** 493 * Take an array of type T and convert it into a HashSet of type T. 494 * If <code>null</code> or an empty array gets passed, an empty Set will be returned. 495 * 496 * @param array The array 497 * @return the filled HashSet of type T 498 */ 499 private static <T> Set<T> arrayAsHashSet( @Nullable T[] array ) 500 { 501 if ( array == null || array.length == 0 ) 502 { 503 return Collections.emptySet(); 504 } 505 506 Set<T> set = new HashSet<T>( array.length ); 507 Collections.addAll( set, array ); 508 509 return set; 510 } 511 512 /** 513 * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, 514 * whereas a fast scan will only have full results for included files, as it ignores directories which can't 515 * possibly hold any included files/directories. 516 * <p/> 517 * Returns immediately if a slow scan has already been completed. 518 */ 519 void slowScan() 520 { 521 if ( haveSlowResults ) 522 { 523 return; 524 } 525 526 final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] ); 527 528 final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); 529 530 for ( String anExcl : excl ) 531 { 532 if ( !couldHoldIncluded( anExcl ) ) 533 { 534 scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); 535 } 536 } 537 538 for ( String aNotIncl : notIncl ) 539 { 540 if ( !couldHoldIncluded( aNotIncl ) ) 541 { 542 scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); 543 } 544 } 545 546 haveSlowResults = true; 547 } 548 549 /** 550 * Scans the given directory for files and directories. Found files and directories are placed in their respective 551 * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is 552 * scanned recursively. 553 * 554 * @param dir The directory to scan. Must not be <code>null</code>. 555 * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using 556 * dir). Must not be <code>null</code>. 557 * @param fast Whether or not this call is part of a fast scan. 558 * @see #filesIncluded 559 * @see #filesNotIncluded 560 * @see #filesExcluded 561 * @see #dirsIncluded 562 * @see #dirsNotIncluded 563 * @see #dirsExcluded 564 * @see #slowScan 565 */ 566 void scandir( @Nonnull final File dir, @Nonnull final String vpath, final boolean fast ) 567 { 568 String[] newfiles = dir.list(); 569 570 if ( newfiles == null ) 571 { 572 /* 573 * two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as 574 * we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???) 575 */ 576 577 /* 578 * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the 579 * assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find 580 * the problematic code, as it appears to come from a native method in UnixFileSystem... 581 */ 582 newfiles = new String[0]; 583 584 // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() ); 585 } 586 587 if ( !followSymlinks ) 588 { 589 newfiles = doNotFollowSymbolicLinks( dir, vpath, newfiles ); 590 } 591 592 for ( final String newfile : newfiles ) 593 { 594 final String name = vpath + newfile; 595 final File file = new File( dir, newfile ); 596 if ( file.isDirectory() ) 597 { 598 if ( isIncluded( name ) ) 599 { 600 if ( !isExcluded( name ) ) 601 { 602 if ( scanConductor != null ) 603 { 604 scanAction = scanConductor.visitDirectory( name, file ); 605 606 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) 607 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) 608 { 609 return; 610 } 611 } 612 613 if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) 614 { 615 dirsIncluded.add( name ); 616 if ( fast ) 617 { 618 scandir( file, name + File.separator, fast ); 619 620 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) 621 { 622 return; 623 } 624 } 625 } 626 scanAction = null; 627 628 } 629 else 630 { 631 dirsExcluded.add( name ); 632 if ( fast && couldHoldIncluded( name ) ) 633 { 634 scandir( file, name + File.separator, fast ); 635 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) 636 { 637 return; 638 } 639 scanAction = null; 640 } 641 } 642 } 643 else 644 { 645 if ( fast && couldHoldIncluded( name ) ) 646 { 647 if ( scanConductor != null ) 648 { 649 scanAction = scanConductor.visitDirectory( name, file ); 650 651 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) 652 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) 653 { 654 return; 655 } 656 } 657 if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) ) 658 { 659 dirsNotIncluded.add( name ); 660 661 scandir( file, name + File.separator, fast ); 662 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) 663 { 664 return; 665 } 666 } 667 scanAction = null; 668 } 669 } 670 if ( !fast ) 671 { 672 scandir( file, name + File.separator, fast ); 673 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) ) 674 { 675 return; 676 } 677 scanAction = null; 678 } 679 } 680 else if ( file.isFile() ) 681 { 682 if ( isIncluded( name ) ) 683 { 684 if ( !isExcluded( name ) ) 685 { 686 if ( scanConductor != null ) 687 { 688 scanAction = scanConductor.visitFile( name, file ); 689 } 690 691 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) 692 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) ) 693 { 694 return; 695 } 696 697 filesIncluded.add( name ); 698 } 699 else 700 { 701 filesExcluded.add( name ); 702 } 703 } 704 else 705 { 706 filesNotIncluded.add( name ); 707 } 708 } 709 } 710 } 711 712 private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles ) 713 { 714 final List<String> noLinks = new ArrayList<String>(); 715 for ( final String newfile : newfiles ) 716 { 717 try 718 { 719 if ( isSymbolicLink( dir, newfile ) ) 720 { 721 final String name = vpath + newfile; 722 final File file = new File( dir, newfile ); 723 if ( file.isDirectory() ) 724 { 725 dirsExcluded.add( name ); 726 } 727 else 728 { 729 filesExcluded.add( name ); 730 } 731 } 732 else 733 { 734 noLinks.add( newfile ); 735 } 736 } 737 catch ( final IOException ioe ) 738 { 739 final String msg = 740 "IOException caught while checking " + "for links, couldn't get cannonical path!"; 741 // will be caught and redirected to Ant's logging system 742 System.err.println( msg ); 743 noLinks.add( newfile ); 744 } 745 } 746 newfiles = noLinks.toArray( new String[noLinks.size()] ); 747 return newfiles; 748 } 749 750 /** 751 * Tests whether or not a name matches against at least one include pattern. 752 * 753 * @param name The name to match. Must not be <code>null</code>. 754 * @return <code>true</code> when the name matches against at least one include pattern, or <code>false</code> 755 * otherwise. 756 */ 757 boolean isIncluded( final String name ) 758 { 759 return includesPatterns.matches( name, isCaseSensitive ); 760 } 761 762 /** 763 * Tests whether or not a name matches the start of at least one include pattern. 764 * 765 * @param name The name to match. Must not be <code>null</code>. 766 * @return <code>true</code> when the name matches against the start of at least one include pattern, or 767 * <code>false</code> otherwise. 768 */ 769 boolean couldHoldIncluded( @Nonnull final String name ) 770 { 771 return includesPatterns.matchesPatternStart( name, isCaseSensitive ); 772 } 773 774 /** 775 * Tests whether or not a name matches against at least one exclude pattern. 776 * 777 * @param name The name to match. Must not be <code>null</code>. 778 * @return <code>true</code> when the name matches against at least one exclude pattern, or <code>false</code> 779 * otherwise. 780 */ 781 boolean isExcluded( @Nonnull final String name ) 782 { 783 return excludesPatterns.matches( name, isCaseSensitive ); 784 } 785 786 /** 787 * Returns the names of the files which matched at least one of the include patterns and none of the exclude 788 * patterns. The names are relative to the base directory. 789 * 790 * @deprecated this method does not work correctly on Windows. 791 * @return the names of the files which matched at least one of the include patterns and none of the exclude 792 * patterns. May also contain symbolic links to files. 793 */ 794 @Deprecated 795 public String[] getIncludedFiles() 796 { 797 if ( filesIncluded == null ) 798 { 799 return new String[0]; 800 } 801 return filesIncluded.toArray( new String[filesIncluded.size()] ); 802 } 803 804 /** 805 * Returns the names of the files which matched none of the include patterns. The names are relative to the base 806 * directory. This involves performing a slow scan if one has not already been completed. 807 * 808 * @return the names of the files which matched none of the include patterns. 809 * @see #slowScan 810 */ 811 public String[] getNotIncludedFiles() 812 { 813 slowScan(); 814 return filesNotIncluded.toArray( new String[filesNotIncluded.size()] ); 815 } 816 817 /** 818 * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude 819 * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not 820 * already been completed. 821 * 822 * @return the names of the files which matched at least one of the include patterns and at at least one of the 823 * exclude patterns. 824 * @see #slowScan 825 */ 826 public String[] getExcludedFiles() 827 { 828 slowScan(); 829 return filesExcluded.toArray( new String[filesExcluded.size()] ); 830 } 831 832 /** 833 * Returns the names of the directories which matched at least one of the include patterns and none of the exclude 834 * patterns. The names are relative to the base directory. 835 * 836 * @deprecated this method is buggy. Do not depend on it. 837 * @return the names of the directories which matched at least one of the include patterns and none of the exclude 838 * patterns. May also contain symbolic links to directories. 839 */ 840 @Deprecated 841 public String[] getIncludedDirectories() 842 { 843 return dirsIncluded.toArray( new String[dirsIncluded.size()] ); 844 } 845 846 /** 847 * Returns the names of the directories which matched none of the include patterns. The names are relative to the 848 * base directory. This involves performing a slow scan if one has not already been completed. 849 * 850 * @return the names of the directories which matched none of the include patterns. 851 * @see #slowScan 852 */ 853 public String[] getNotIncludedDirectories() 854 { 855 slowScan(); 856 return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] ); 857 } 858 859 /** 860 * Returns the names of the directories which matched at least one of the include patterns and at least one of the 861 * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has 862 * not already been completed. 863 * 864 * @return the names of the directories which matched at least one of the include patterns and at least one of the 865 * exclude patterns. 866 * @see #slowScan 867 */ 868 public String[] getExcludedDirectories() 869 { 870 slowScan(); 871 return dirsExcluded.toArray( new String[dirsExcluded.size()] ); 872 } 873 874 /** 875 * Adds default exclusions to the current exclusions set. 876 */ 877 public void addDefaultExcludes() 878 { 879 final int excludesLength = excludes == null ? 0 : excludes.length; 880 String[] newExcludes; 881 newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length]; 882 if ( excludesLength > 0 ) 883 { 884 System.arraycopy( excludes, 0, newExcludes, 0, excludesLength ); 885 } 886 for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ ) 887 { 888 newExcludes[i + excludesLength] = 889 DEFAULTEXCLUDES[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ); 890 } 891 excludes = newExcludes; 892 } 893 894 /** 895 * Checks whether a given file is a symbolic link. 896 * <p> 897 * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical 898 * - this may lead to false positives on some platforms. 899 * </p> 900 * 901 * @param parent the parent directory of the file to test 902 * @param name the name of the file to test. 903 * 904 */ 905 boolean isSymbolicLink( final File parent, final String name ) 906 throws IOException 907 { 908 return Files.isSymbolicLink( parent.toPath() ); 909 } 910 911 private void setupDefaultFilters() 912 { 913 if ( includes == null ) 914 { 915 // No includes supplied, so set it to 'matches all' 916 includes = new String[1]; 917 includes[0] = "**"; 918 } 919 if ( excludes == null ) 920 { 921 excludes = new String[0]; 922 } 923 } 924 925 926 private void setupMatchPatterns() 927 { 928 includesPatterns = MatchPatterns.from( includes ); 929 excludesPatterns = MatchPatterns.from( excludes ); 930 } 931 932 }