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