Coverage Report - org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner
 
Classes in this File Line Coverage Branch Coverage Complexity
DirectoryScanner
43%
216/497
29%
101/346
3.833
 
 1  
 /*
 2  
  *  Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  *  contributor license agreements.  See the NOTICE file distributed with
 4  
  *  this work for additional information regarding copyright ownership.
 5  
  *  The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  *  (the "License"); you may not use this file except in compliance with
 7  
  *  the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  *  Unless required by applicable law or agreed to in writing, software
 12  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 13  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  *  See the License for the specific language governing permissions and
 15  
  *  limitations under the License.
 16  
  *
 17  
  */
 18  
 package org.owasp.dependencycheck.org.apache.tools.ant;
 19  
 
 20  
 import java.io.File;
 21  
 import java.io.IOException;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Arrays;
 24  
 import java.util.HashMap;
 25  
 import java.util.HashSet;
 26  
 import java.util.Iterator;
 27  
 import java.util.LinkedList;
 28  
 import java.util.Map;
 29  
 import java.util.Set;
 30  
 import java.util.Vector;
 31  
 import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os;
 32  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource;
 33  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.ResourceFactory;
 34  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileResource;
 35  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.FileSelector;
 36  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorScanner;
 37  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorUtils;
 38  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPath;
 39  
 import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPattern;
 40  
 import org.owasp.dependencycheck.org.apache.tools.ant.util.CollectionUtils;
 41  
 import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils;
 42  
 import org.owasp.dependencycheck.org.apache.tools.ant.util.SymbolicLinkUtils;
 43  
 import org.owasp.dependencycheck.org.apache.tools.ant.util.VectorSet;
 44  
 
 45  
 /**
 46  
  * Class for scanning a directory for files/directories which match certain criteria.
 47  
  * <p>
 48  
  * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which
 49  
  * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude
 50  
  * files based on their filename.
 51  
  * <p>
 52  
  * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is
 53  
  * matched against a set of selectors, including special support for matching against filenames with include and and
 54  
  * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file
 55  
  * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will
 56  
  * be placed in the list of files/directories found.
 57  
  * <p>
 58  
  * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no
 59  
  * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors
 60  
  * are supplied, none are applied.
 61  
  * <p>
 62  
  * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment
 63  
  * is the name of a directory or file, which is bounded by <code>File.separator</code> ('/' under UNIX, '\' under
 64  
  * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same
 65  
  * is done for the pattern against which should be matched.
 66  
  * <p>
 67  
  * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in
 68  
  * the pattern, it matches zero or more path segments of the name.
 69  
  * <p>
 70  
  * There is a special case regarding the use of <code>File.separator</code>s at the beginning of the pattern and the
 71  
  * string to match:<br>
 72  
  * When a pattern starts with a <code>File.separator</code>, the string to match must also start with a
 73  
  * <code>File.separator</code>. When a pattern does not start with a <code>File.separator</code>, the string to match
 74  
  * may not start with a <code>File.separator</code>. When one of these rules is not obeyed, the string will not match.
 75  
  * <p>
 76  
  * When a name path segment is matched against a pattern path segment, the following special characters can be used:<br>
 77  
  * '*' matches zero or more characters<br>
 78  
  * '?' matches one character.
 79  
  * <p>
 80  
  * Examples:
 81  
  * <p>
 82  
  * "**\*.class" matches all .class files/dirs in a directory tree.
 83  
  * <p>
 84  
  * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a
 85  
  * directory called test.
 86  
  * <p>
 87  
  * "**" matches everything in a directory tree.
 88  
  * <p>
 89  
  * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test
 90  
  * (e.g. "abc\test\def\ghi\XYZ123").
 91  
  * <p>
 92  
  * Case sensitivity may be turned off if necessary. By default, it is turned on.
 93  
  * <p>
 94  
  * Example of usage:
 95  
  * <pre>
 96  
  *   String[] includes = {"**\\*.class"};
 97  
  *   String[] excludes = {"modules\\*\\**"};
 98  
  *   ds.setIncludes(includes);
 99  
  *   ds.setExcludes(excludes);
 100  
  *   ds.setBasedir(new File("test"));
 101  
  *   ds.setCaseSensitive(true);
 102  
  *   ds.scan();
 103  
  *
 104  
  *   System.out.println("FILES:");
 105  
  *   String[] files = ds.getIncludedFiles();
 106  
  *   for (int i = 0; i &lt; files.length; i++) {
 107  
  *     System.out.println(files[i]);
 108  
  *   }
 109  
  * </pre> This will scan a directory called test for .class files, but excludes all files in all proper subdirectories
 110  
  * of a directory called "modules".
 111  
  *
 112  
  */
 113  
 public class DirectoryScanner
 114  
         implements FileScanner, SelectorScanner, ResourceFactory {
 115  
 
 116  
     /**
 117  
      * Is OpenVMS the operating system we're running on?
 118  
      */
 119  8
     private static final boolean ON_VMS = Os.isFamily("openvms");
 120  
 
 121  
     /**
 122  
      * Patterns which should be excluded by default.
 123  
      *
 124  
      * <p>
 125  
      * Note that you can now add patterns to the list of default excludes. Added patterns will not become part of this
 126  
      * array that has only been kept around for backwards compatibility reasons.</p>
 127  
      *
 128  
      * @deprecated since 1.6.x. Use the {@link #getDefaultExcludes getDefaultExcludes} method instead.
 129  
      */
 130  8
     protected static final String[] DEFAULTEXCLUDES = {
 131  
         // Miscellaneous typical temporary files
 132  
         SelectorUtils.DEEP_TREE_MATCH + "/*~",
 133  
         SelectorUtils.DEEP_TREE_MATCH + "/#*#",
 134  
         SelectorUtils.DEEP_TREE_MATCH + "/.#*",
 135  
         SelectorUtils.DEEP_TREE_MATCH + "/%*%",
 136  
         SelectorUtils.DEEP_TREE_MATCH + "/._*",
 137  
         // CVS
 138  
         SelectorUtils.DEEP_TREE_MATCH + "/CVS",
 139  
         SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH,
 140  
         SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore",
 141  
         // SCCS
 142  
         SelectorUtils.DEEP_TREE_MATCH + "/SCCS",
 143  
         SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH,
 144  
         // Visual SourceSafe
 145  
         SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc",
 146  
         // Subversion
 147  
         SelectorUtils.DEEP_TREE_MATCH + "/.svn",
 148  
         SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH,
 149  
         // Git
 150  
         SelectorUtils.DEEP_TREE_MATCH + "/.git",
 151  
         SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH,
 152  
         SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes",
 153  
         SelectorUtils.DEEP_TREE_MATCH + "/.gitignore",
 154  
         SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules",
 155  
         // Mercurial
 156  
         SelectorUtils.DEEP_TREE_MATCH + "/.hg",
 157  
         SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH,
 158  
         SelectorUtils.DEEP_TREE_MATCH + "/.hgignore",
 159  
         SelectorUtils.DEEP_TREE_MATCH + "/.hgsub",
 160  
         SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate",
 161  
         SelectorUtils.DEEP_TREE_MATCH + "/.hgtags",
 162  
         // Bazaar
 163  
         SelectorUtils.DEEP_TREE_MATCH + "/.bzr",
 164  
         SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH,
 165  
         SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore",
 166  
         // Mac
 167  
         SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store"
 168  
     };
 169  
 
 170  
     /**
 171  
      * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks}
 172  
      *
 173  
      * @since Ant 1.8.0
 174  
      */
 175  
     public static final int MAX_LEVELS_OF_SYMLINKS = 5;
 176  
     /**
 177  
      * The end of the exception message if something that should be there doesn't exist.
 178  
      */
 179  
     public static final String DOES_NOT_EXIST_POSTFIX = " does not exist.";
 180  
 
 181  
     /**
 182  
      * Helper.
 183  
      */
 184  8
     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
 185  
 
 186  
     /**
 187  
      * Helper.
 188  
      */
 189  8
     private static final SymbolicLinkUtils SYMLINK_UTILS
 190  
             = SymbolicLinkUtils.getSymbolicLinkUtils();
 191  
 
 192  
     /**
 193  
      * Patterns which should be excluded by default.
 194  
      *
 195  
      * @see #addDefaultExcludes()
 196  
      */
 197  8
     private static final Set<String> defaultExcludes = new HashSet<String>();
 198  
 
 199  
     static {
 200  8
         resetDefaultExcludes();
 201  8
     }
 202  
 
 203  
     // CheckStyle:VisibilityModifier OFF - bc
 204  
     /**
 205  
      * The base directory to be scanned.
 206  
      */
 207  
     protected File basedir;
 208  
 
 209  
     /**
 210  
      * The patterns for the files to be included.
 211  
      */
 212  
     protected String[] includes;
 213  
 
 214  
     /**
 215  
      * The patterns for the files to be excluded.
 216  
      */
 217  
     protected String[] excludes;
 218  
 
 219  
     /**
 220  
      * Selectors that will filter which files are in our candidate list.
 221  
      */
 222  8
     protected FileSelector[] selectors = null;
 223  
 
 224  
     /**
 225  
      * The files which matched at least one include and no excludes and were selected.
 226  
      */
 227  
     protected Vector<String> filesIncluded;
 228  
 
 229  
     /**
 230  
      * The files which did not match any includes or selectors.
 231  
      */
 232  
     protected Vector<String> filesNotIncluded;
 233  
 
 234  
     /**
 235  
      * The files which matched at least one include and at least one exclude.
 236  
      */
 237  
     protected Vector<String> filesExcluded;
 238  
 
 239  
     /**
 240  
      * The directories which matched at least one include and no excludes and were selected.
 241  
      */
 242  
     protected Vector<String> dirsIncluded;
 243  
 
 244  
     /**
 245  
      * The directories which were found and did not match any includes.
 246  
      */
 247  
     protected Vector<String> dirsNotIncluded;
 248  
 
 249  
     /**
 250  
      * The directories which matched at least one include and at least one exclude.
 251  
      */
 252  
     protected Vector<String> dirsExcluded;
 253  
 
 254  
     /**
 255  
      * The files which matched at least one include and no excludes and which a selector discarded.
 256  
      */
 257  
     protected Vector<String> filesDeselected;
 258  
 
 259  
     /**
 260  
      * The directories which matched at least one include and no excludes but which a selector discarded.
 261  
      */
 262  
     protected Vector<String> dirsDeselected;
 263  
 
 264  
     /**
 265  
      * Whether or not our results were built by a slow scan.
 266  
      */
 267  8
     protected boolean haveSlowResults = false;
 268  
 
 269  
     /**
 270  
      * Whether or not the file system should be treated as a case sensitive one.
 271  
      */
 272  8
     protected boolean isCaseSensitive = true;
 273  
 
 274  
     /**
 275  
      * Whether a missing base directory is an error.
 276  
      *
 277  
      * @since Ant 1.7.1
 278  
      */
 279  8
     protected boolean errorOnMissingDir = true;
 280  
 
 281  
     /**
 282  
      * Whether or not symbolic links should be followed.
 283  
      *
 284  
      * @since Ant 1.5
 285  
      */
 286  8
     private boolean followSymlinks = true;
 287  
 
 288  
     /**
 289  
      * Whether or not everything tested so far has been included.
 290  
      */
 291  8
     protected boolean everythingIncluded = true;
 292  
 
 293  
     // CheckStyle:VisibilityModifier ON
 294  
     /**
 295  
      * List of all scanned directories.
 296  
      *
 297  
      * @since Ant 1.6
 298  
      */
 299  8
     private Set<String> scannedDirs = new HashSet<String>();
 300  
 
 301  
     /**
 302  
      * Map of all include patterns that are full file names and don't contain any wildcards.
 303  
      *
 304  
      * <p>
 305  
      * Maps pattern string to TokenizedPath.</p>
 306  
      *
 307  
      * <p>
 308  
      * If this instance is not case sensitive, the file names get turned to upper case.</p>
 309  
      *
 310  
      * <p>
 311  
      * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
 312  
      * method (cleared in clearCaches, actually).</p>
 313  
      *
 314  
      * @since Ant 1.8.0
 315  
      */
 316  8
     private Map<String, TokenizedPath> includeNonPatterns = new HashMap<String, TokenizedPath>();
 317  
 
 318  
     /**
 319  
      * Map of all exclude patterns that are full file names and don't contain any wildcards.
 320  
      *
 321  
      * <p>
 322  
      * Maps pattern string to TokenizedPath.</p>
 323  
      *
 324  
      * <p>
 325  
      * If this instance is not case sensitive, the file names get turned to upper case.</p>
 326  
      *
 327  
      * <p>
 328  
      * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
 329  
      * method (cleared in clearCaches, actually).</p>
 330  
      *
 331  
      * @since Ant 1.8.0
 332  
      */
 333  8
     private Map<String, TokenizedPath> excludeNonPatterns = new HashMap<String, TokenizedPath>();
 334  
 
 335  
     /**
 336  
      * Array of all include patterns that contain wildcards.
 337  
      *
 338  
      * <p>
 339  
      * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
 340  
      * method (cleared in clearCaches, actually).</p>
 341  
      */
 342  
     private TokenizedPattern[] includePatterns;
 343  
 
 344  
     /**
 345  
      * Array of all exclude patterns that contain wildcards.
 346  
      *
 347  
      * <p>
 348  
      * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan
 349  
      * method (cleared in clearCaches, actually).</p>
 350  
      */
 351  
     private TokenizedPattern[] excludePatterns;
 352  
 
 353  
     /**
 354  
      * Have the non-pattern sets and pattern arrays for in- and excludes been initialized?
 355  
      *
 356  
      * @since Ant 1.6.3
 357  
      */
 358  8
     private boolean areNonPatternSetsReady = false;
 359  
 
 360  
     /**
 361  
      * Scanning flag.
 362  
      *
 363  
      * @since Ant 1.6.3
 364  
      */
 365  8
     private boolean scanning = false;
 366  
 
 367  
     /**
 368  
      * Scanning lock.
 369  
      *
 370  
      * @since Ant 1.6.3
 371  
      */
 372  8
     private Object scanLock = new Object();
 373  
 
 374  
     /**
 375  
      * Slow scanning flag.
 376  
      *
 377  
      * @since Ant 1.6.3
 378  
      */
 379  8
     private boolean slowScanning = false;
 380  
 
 381  
     /**
 382  
      * Slow scanning lock.
 383  
      *
 384  
      * @since Ant 1.6.3
 385  
      */
 386  8
     private Object slowScanLock = new Object();
 387  
 
 388  
     /**
 389  
      * Exception thrown during scan.
 390  
      *
 391  
      * @since Ant 1.6.3
 392  
      */
 393  8
     private IllegalStateException illegal = null;
 394  
 
 395  
     /**
 396  
      * The maximum number of times a symbolic link may be followed during a scan.
 397  
      *
 398  
      * @since Ant 1.8.0
 399  
      */
 400  8
     private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS;
 401  
 
 402  
     /**
 403  
      * Absolute paths of all symlinks that haven't been followed but would have been if followsymlinks had been true or
 404  
      * maxLevelsOfSymlinks had been higher.
 405  
      *
 406  
      * @since Ant 1.8.0
 407  
      */
 408  8
     private Set<String> notFollowedSymlinks = new HashSet<String>();
 409  
 
 410  
     /**
 411  
      * Sole constructor.
 412  
      */
 413  8
     public DirectoryScanner() {
 414  8
     }
 415  
 
 416  
     /**
 417  
      * Test whether or not a given path matches the start of a given pattern up to the first "**".
 418  
      * <p>
 419  
      * This is not a general purpose test and should only be used if you can live with false positives. For example,
 420  
      * <code>pattern=**\a</code> and <code>str=b</code> will yield <code>true</code>.
 421  
      *
 422  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 423  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 424  
      *
 425  
      * @return whether or not a given path matches the start of a given pattern up to the first "**".
 426  
      */
 427  
     protected static boolean matchPatternStart(String pattern, String str) {
 428  8
         return SelectorUtils.matchPatternStart(pattern, str);
 429  
     }
 430  
 
 431  
     /**
 432  
      * Test whether or not a given path matches the start of a given pattern up to the first "**".
 433  
      * <p>
 434  
      * This is not a general purpose test and should only be used if you can live with false positives. For example,
 435  
      * <code>pattern=**\a</code> and <code>str=b</code> will yield <code>true</code>.
 436  
      *
 437  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 438  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 439  
      * @param isCaseSensitive Whether or not matching should be performed case sensitively.
 440  
      *
 441  
      * @return whether or not a given path matches the start of a given pattern up to the first "**".
 442  
      */
 443  
     protected static boolean matchPatternStart(String pattern, String str,
 444  
             boolean isCaseSensitive) {
 445  16
         return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
 446  
     }
 447  
 
 448  
     /**
 449  
      * Test whether or not a given path matches a given pattern.
 450  
      *
 451  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 452  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 453  
      *
 454  
      * @return <code>true</code> if the pattern matches against the string, or <code>false</code> otherwise.
 455  
      */
 456  
     protected static boolean matchPath(String pattern, String str) {
 457  8
         return SelectorUtils.matchPath(pattern, str);
 458  
     }
 459  
 
 460  
     /**
 461  
      * Test whether or not a given path matches a given pattern.
 462  
      *
 463  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 464  
      * @param str The path to match, as a String. Must not be <code>null</code>.
 465  
      * @param isCaseSensitive Whether or not matching should be performed case sensitively.
 466  
      *
 467  
      * @return <code>true</code> if the pattern matches against the string, or <code>false</code> otherwise.
 468  
      */
 469  
     protected static boolean matchPath(String pattern, String str,
 470  
             boolean isCaseSensitive) {
 471  0
         return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
 472  
     }
 473  
 
 474  
     /**
 475  
      * Test whether or not a string matches against a pattern. The pattern may contain two special characters:<br>
 476  
      * '*' means zero or more characters<br>
 477  
      * '?' means one and only one character
 478  
      *
 479  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 480  
      * @param str The string which must be matched against the pattern. Must not be <code>null</code>.
 481  
      *
 482  
      * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
 483  
      */
 484  
     public static boolean match(String pattern, String str) {
 485  0
         return SelectorUtils.match(pattern, str);
 486  
     }
 487  
 
 488  
     /**
 489  
      * Test whether or not a string matches against a pattern. The pattern may contain two special characters:<br>
 490  
      * '*' means zero or more characters<br>
 491  
      * '?' means one and only one character
 492  
      *
 493  
      * @param pattern The pattern to match against. Must not be <code>null</code>.
 494  
      * @param str The string which must be matched against the pattern. Must not be <code>null</code>.
 495  
      * @param isCaseSensitive Whether or not matching should be performed case sensitively.
 496  
      *
 497  
      *
 498  
      * @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise.
 499  
      */
 500  
     protected static boolean match(String pattern, String str,
 501  
             boolean isCaseSensitive) {
 502  0
         return SelectorUtils.match(pattern, str, isCaseSensitive);
 503  
     }
 504  
 
 505  
     /**
 506  
      * Get the list of patterns that should be excluded by default.
 507  
      *
 508  
      * @return An array of <code>String</code> based on the current contents of the <code>defaultExcludes</code>
 509  
      * <code>Set</code>.
 510  
      *
 511  
      * @since Ant 1.6
 512  
      */
 513  
     public static String[] getDefaultExcludes() {
 514  0
         synchronized (defaultExcludes) {
 515  0
             return (String[]) defaultExcludes.toArray(new String[defaultExcludes
 516  
                     .size()]);
 517  0
         }
 518  
     }
 519  
 
 520  
     /**
 521  
      * Add a pattern to the default excludes unless it is already a default exclude.
 522  
      *
 523  
      * @param s A string to add as an exclude pattern.
 524  
      * @return    <code>true</code> if the string was added; <code>false</code> if it already existed.
 525  
      *
 526  
      * @since Ant 1.6
 527  
      */
 528  
     public static boolean addDefaultExclude(String s) {
 529  0
         synchronized (defaultExcludes) {
 530  0
             return defaultExcludes.add(s);
 531  0
         }
 532  
     }
 533  
 
 534  
     /**
 535  
      * Remove a string if it is a default exclude.
 536  
      *
 537  
      * @param s The string to attempt to remove.
 538  
      * @return    <code>true</code> if <code>s</code> was a default exclude (and thus was removed); <code>false</code> if
 539  
      * <code>s</code> was not in the default excludes list to begin with.
 540  
      *
 541  
      * @since Ant 1.6
 542  
      */
 543  
     public static boolean removeDefaultExclude(String s) {
 544  0
         synchronized (defaultExcludes) {
 545  0
             return defaultExcludes.remove(s);
 546  0
         }
 547  
     }
 548  
 
 549  
     /**
 550  
      * Go back to the hardwired default exclude patterns.
 551  
      *
 552  
      * @since Ant 1.6
 553  
      */
 554  
     public static void resetDefaultExcludes() {
 555  8
         synchronized (defaultExcludes) {
 556  8
             defaultExcludes.clear();
 557  232
             for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
 558  224
                 defaultExcludes.add(DEFAULTEXCLUDES[i]);
 559  
             }
 560  8
         }
 561  8
     }
 562  
 
 563  
     /**
 564  
      * Set the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\'
 565  
      * characters are replaced by <code>File.separatorChar</code>, so the separator used need not match
 566  
      * <code>File.separatorChar</code>.
 567  
      *
 568  
      * @param basedir The base directory to scan.
 569  
      */
 570  
     public void setBasedir(String basedir) {
 571  8
         setBasedir(basedir == null ? (File) null
 572  
                 : new File(basedir.replace('/', File.separatorChar).replace(
 573  
                                 '\\', File.separatorChar)));
 574  8
     }
 575  
 
 576  
     /**
 577  
      * Set the base directory to be scanned. This is the directory which is scanned recursively.
 578  
      *
 579  
      * @param basedir The base directory for scanning.
 580  
      */
 581  
     public synchronized void setBasedir(File basedir) {
 582  8
         this.basedir = basedir;
 583  8
     }
 584  
 
 585  
     /**
 586  
      * Return the base directory to be scanned. This is the directory which is scanned recursively.
 587  
      *
 588  
      * @return the base directory to be scanned.
 589  
      */
 590  
     public synchronized File getBasedir() {
 591  0
         return basedir;
 592  
     }
 593  
 
 594  
     /**
 595  
      * Find out whether include exclude patterns are matched in a case sensitive way.
 596  
      *
 597  
      * @return whether or not the scanning is case sensitive.
 598  
      * @since Ant 1.6
 599  
      */
 600  
     public synchronized boolean isCaseSensitive() {
 601  568
         return isCaseSensitive;
 602  
     }
 603  
 
 604  
     /**
 605  
      * Set whether or not include and exclude patterns are matched in a case sensitive way.
 606  
      *
 607  
      * @param isCaseSensitive whether or not the file system should be regarded as a case sensitive one.
 608  
      */
 609  
     public synchronized void setCaseSensitive(boolean isCaseSensitive) {
 610  0
         this.isCaseSensitive = isCaseSensitive;
 611  0
     }
 612  
 
 613  
     /**
 614  
      * Sets whether or not a missing base directory is an error
 615  
      *
 616  
      * @param errorOnMissingDir whether or not a missing base directory is an error
 617  
      * @since Ant 1.7.1
 618  
      */
 619  
     public void setErrorOnMissingDir(boolean errorOnMissingDir) {
 620  0
         this.errorOnMissingDir = errorOnMissingDir;
 621  0
     }
 622  
 
 623  
     /**
 624  
      * Get whether or not a DirectoryScanner follows symbolic links.
 625  
      *
 626  
      * @return flag indicating whether symbolic links should be followed.
 627  
      *
 628  
      * @since Ant 1.6
 629  
      */
 630  
     public synchronized boolean isFollowSymlinks() {
 631  0
         return followSymlinks;
 632  
     }
 633  
 
 634  
     /**
 635  
      * Set whether or not symbolic links should be followed.
 636  
      *
 637  
      * @param followSymlinks whether or not symbolic links should be followed.
 638  
      */
 639  
     public synchronized void setFollowSymlinks(boolean followSymlinks) {
 640  0
         this.followSymlinks = followSymlinks;
 641  0
     }
 642  
 
 643  
     /**
 644  
      * The maximum number of times a symbolic link may be followed during a scan.
 645  
      *
 646  
      * @since Ant 1.8.0
 647  
      */
 648  
     public void setMaxLevelsOfSymlinks(int max) {
 649  0
         maxLevelsOfSymlinks = max;
 650  0
     }
 651  
 
 652  
     /**
 653  
      * Set the list of include patterns to use. All '/' and '\' characters are replaced by
 654  
      * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>.
 655  
      * <p>
 656  
      * When a pattern ends with a '/' or '\', "**" is appended.
 657  
      *
 658  
      * @param includes A list of include patterns. May be <code>null</code>, indicating that all files should be
 659  
      * included. If a non-<code>null</code> list is given, all elements must be non-<code>null</code>.
 660  
      */
 661  
     public synchronized void setIncludes(String[] includes) {
 662  0
         if (includes == null) {
 663  0
             this.includes = null;
 664  
         } else {
 665  0
             this.includes = new String[includes.length];
 666  0
             for (int i = 0; i < includes.length; i++) {
 667  0
                 this.includes[i] = normalizePattern(includes[i]);
 668  
             }
 669  
         }
 670  0
     }
 671  
 
 672  
     public synchronized void setIncludes(String include) {
 673  8
         if (include == null) {
 674  0
             this.includes = null;
 675  
         } else {
 676  8
             this.includes = new String[1];
 677  8
             this.includes[0] = normalizePattern(include);
 678  
         }
 679  8
     }
 680  
 
 681  
     /**
 682  
      * Set the list of exclude patterns to use. All '/' and '\' characters are replaced by
 683  
      * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>.
 684  
      * <p>
 685  
      * When a pattern ends with a '/' or '\', "**" is appended.
 686  
      *
 687  
      * @param excludes A list of exclude patterns. May be <code>null</code>, indicating that no files should be
 688  
      * excluded. If a non-<code>null</code> list is given, all elements must be non-<code>null</code>.
 689  
      */
 690  
     public synchronized void setExcludes(String[] excludes) {
 691  0
         if (excludes == null) {
 692  0
             this.excludes = null;
 693  
         } else {
 694  0
             this.excludes = new String[excludes.length];
 695  0
             for (int i = 0; i < excludes.length; i++) {
 696  0
                 this.excludes[i] = normalizePattern(excludes[i]);
 697  
             }
 698  
         }
 699  0
     }
 700  
 
 701  
     /**
 702  
      * Add to the list of exclude patterns to use. All '/' and '\' characters are replaced by
 703  
      * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>.
 704  
      * <p>
 705  
      * When a pattern ends with a '/' or '\', "**" is appended.
 706  
      *
 707  
      * @param excludes A list of exclude patterns. May be <code>null</code>, in which case the exclude patterns don't
 708  
      * get changed at all.
 709  
      *
 710  
      * @since Ant 1.6.3
 711  
      */
 712  
     public synchronized void addExcludes(String[] excludes) {
 713  0
         if (excludes != null && excludes.length > 0) {
 714  0
             if (this.excludes != null && this.excludes.length > 0) {
 715  0
                 String[] tmp = new String[excludes.length
 716  
                         + this.excludes.length];
 717  0
                 System.arraycopy(this.excludes, 0, tmp, 0,
 718  
                         this.excludes.length);
 719  0
                 for (int i = 0; i < excludes.length; i++) {
 720  0
                     tmp[this.excludes.length + i]
 721  
                             = normalizePattern(excludes[i]);
 722  
                 }
 723  0
                 this.excludes = tmp;
 724  0
             } else {
 725  0
                 setExcludes(excludes);
 726  
             }
 727  
         }
 728  0
     }
 729  
 
 730  
     /**
 731  
      * All '/' and '\' characters are replaced by <code>File.separatorChar</code>, so the separator used need not match
 732  
      * <code>File.separatorChar</code>.
 733  
      *
 734  
      * <p>
 735  
      * When a pattern ends with a '/' or '\', "**" is appended.
 736  
      *
 737  
      * @since Ant 1.6.3
 738  
      */
 739  
     private static String normalizePattern(String p) {
 740  8
         String pattern = p.replace('/', File.separatorChar)
 741  
                 .replace('\\', File.separatorChar);
 742  8
         if (pattern.endsWith(File.separator)) {
 743  0
             pattern += SelectorUtils.DEEP_TREE_MATCH;
 744  
         }
 745  8
         return pattern;
 746  
     }
 747  
 
 748  
     /**
 749  
      * Set the selectors that will select the filelist.
 750  
      *
 751  
      * @param selectors specifies the selectors to be invoked on a scan.
 752  
      */
 753  
     public synchronized void setSelectors(FileSelector[] selectors) {
 754  0
         this.selectors = selectors;
 755  0
     }
 756  
 
 757  
     /**
 758  
      * Return whether or not the scanner has included all the files or directories it has come across so far.
 759  
      *
 760  
      * @return <code>true</code> if all files and directories which have been found so far have been included.
 761  
      */
 762  
     public synchronized boolean isEverythingIncluded() {
 763  0
         return everythingIncluded;
 764  
     }
 765  
 
 766  
     /**
 767  
      * Scan for files which match at least one include pattern and don't match any exclude patterns. If there are
 768  
      * selectors then the files must pass muster there, as well. Scans under basedir, if set; otherwise the include
 769  
      * patterns without leading wildcards specify the absolute paths of the files that may be included.
 770  
      *
 771  
      * @exception IllegalStateException if the base directory was set incorrectly (i.e. if it doesn't exist or isn't a
 772  
      * directory).
 773  
      */
 774  
     public void scan() throws IllegalStateException {
 775  8
         synchronized (scanLock) {
 776  8
             if (scanning) {
 777  0
                 while (scanning) {
 778  
                     try {
 779  0
                         scanLock.wait();
 780  0
                     } catch (InterruptedException e) {
 781  0
                         continue;
 782  0
                     }
 783  
                 }
 784  0
                 if (illegal != null) {
 785  0
                     throw illegal;
 786  
                 }
 787  0
                 return;
 788  
             }
 789  8
             scanning = true;
 790  8
         }
 791  8
         File savedBase = basedir;
 792  
         try {
 793  8
             synchronized (this) {
 794  8
                 illegal = null;
 795  8
                 clearResults();
 796  
 
 797  
                 // set in/excludes to reasonable defaults if needed:
 798  8
                 boolean nullIncludes = (includes == null);
 799  8
                 includes = nullIncludes
 800  
                         ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes;
 801  8
                 boolean nullExcludes = (excludes == null);
 802  8
                 excludes = nullExcludes ? new String[0] : excludes;
 803  
 
 804  8
                 if (basedir != null && !followSymlinks
 805  
                         && SYMLINK_UTILS.isSymbolicLink(basedir)) {
 806  0
                     notFollowedSymlinks.add(basedir.getAbsolutePath());
 807  0
                     basedir = null;
 808  
                 }
 809  
 
 810  8
                 if (basedir == null) {
 811  
                     // if no basedir and no includes, nothing to do:
 812  0
                     if (nullIncludes) {
 813  0
                         return;
 814  
                     }
 815  
                 } else {
 816  8
                     if (!basedir.exists()) {
 817  0
                         if (errorOnMissingDir) {
 818  0
                             illegal = new IllegalStateException("basedir "
 819  
                                     + basedir
 820  
                                     + DOES_NOT_EXIST_POSTFIX);
 821  
                         } else {
 822  
                             // Nothing to do - basedir does not exist
 823  0
                             return;
 824  
                         }
 825  8
                     } else if (!basedir.isDirectory()) {
 826  0
                         illegal = new IllegalStateException("basedir "
 827  
                                 + basedir
 828  
                                 + " is not a"
 829  
                                 + " directory.");
 830  
                     }
 831  8
                     if (illegal != null) {
 832  0
                         throw illegal;
 833  
                     }
 834  
                 }
 835  8
                 if (isIncluded(TokenizedPath.EMPTY_PATH)) {
 836  0
                     if (!isExcluded(TokenizedPath.EMPTY_PATH)) {
 837  0
                         if (isSelected("", basedir)) {
 838  0
                             dirsIncluded.addElement("");
 839  
                         } else {
 840  0
                             dirsDeselected.addElement("");
 841  
                         }
 842  
                     } else {
 843  0
                         dirsExcluded.addElement("");
 844  
                     }
 845  
                 } else {
 846  8
                     dirsNotIncluded.addElement("");
 847  
                 }
 848  8
                 checkIncludePatterns();
 849  8
                 clearCaches();
 850  8
                 includes = nullIncludes ? null : includes;
 851  8
                 excludes = nullExcludes ? null : excludes;
 852  8
             }
 853  0
         } catch (IOException ex) {
 854  0
             throw new BuildException(ex);
 855  
         } finally {
 856  8
             basedir = savedBase;
 857  8
             synchronized (scanLock) {
 858  8
                 scanning = false;
 859  8
                 scanLock.notifyAll();
 860  8
             }
 861  8
         }
 862  8
     }
 863  
 
 864  
     /**
 865  
      * This routine is actually checking all the include patterns in order to avoid scanning everything under base dir.
 866  
      *
 867  
      * @since Ant 1.6
 868  
      */
 869  
     private void checkIncludePatterns() {
 870  8
         ensureNonPatternSetsReady();
 871  8
         Map<TokenizedPath, String> newroots = new HashMap<TokenizedPath, String>();
 872  
 
 873  
         // put in the newroots map the include patterns without
 874  
         // wildcard tokens
 875  16
         for (int i = 0; i < includePatterns.length; i++) {
 876  8
             String pattern = includePatterns[i].toString();
 877  8
             if (!shouldSkipPattern(pattern)) {
 878  8
                 newroots.put(includePatterns[i].rtrimWildcardTokens(),
 879  
                         pattern);
 880  
             }
 881  
         }
 882  8
         for (Map.Entry<String, TokenizedPath> entry : includeNonPatterns.entrySet()) {
 883  0
             String pattern = entry.getKey();
 884  0
             if (!shouldSkipPattern(pattern)) {
 885  0
                 newroots.put(entry.getValue(), pattern);
 886  
             }
 887  0
         }
 888  
 
 889  8
         if (newroots.containsKey(TokenizedPath.EMPTY_PATH)
 890  
                 && basedir != null) {
 891  
             // we are going to scan everything anyway
 892  0
             scandir(basedir, "", true);
 893  
         } else {
 894  8
             File canonBase = null;
 895  8
             if (basedir != null) {
 896  
                 try {
 897  8
                     canonBase = basedir.getCanonicalFile();
 898  0
                 } catch (IOException ex) {
 899  0
                     throw new BuildException(ex);
 900  8
                 }
 901  
             }
 902  
             // only scan directories that can include matched files or
 903  
             // directories
 904  8
             for (Map.Entry<TokenizedPath, String> entry : newroots.entrySet()) {
 905  8
                 TokenizedPath currentPath = entry.getKey();
 906  8
                 String currentelement = currentPath.toString();
 907  8
                 if (basedir == null
 908  
                         && !FileUtils.isAbsolutePath(currentelement)) {
 909  0
                     continue;
 910  
                 }
 911  8
                 File myfile = new File(basedir, currentelement);
 912  
 
 913  8
                 if (myfile.exists()) {
 914  
                     // may be on a case insensitive file system.  We want
 915  
                     // the results to show what's really on the disk, so
 916  
                     // we need to double check.
 917  
                     try {
 918  8
                         String path = (basedir == null)
 919  
                                 ? myfile.getCanonicalPath()
 920  
                                 : FILE_UTILS.removeLeadingPath(canonBase,
 921  
                                         myfile.getCanonicalFile());
 922  8
                         if (!path.equals(currentelement) || ON_VMS) {
 923  0
                             myfile = currentPath.findFile(basedir, true);
 924  0
                             if (myfile != null && basedir != null) {
 925  0
                                 currentelement = FILE_UTILS.removeLeadingPath(
 926  
                                         basedir, myfile);
 927  0
                                 if (!currentPath.toString()
 928  
                                         .equals(currentelement)) {
 929  0
                                     currentPath
 930  
                                             = new TokenizedPath(currentelement);
 931  
                                 }
 932  
                             }
 933  
                         }
 934  0
                     } catch (IOException ex) {
 935  0
                         throw new BuildException(ex);
 936  8
                     }
 937  
                 }
 938  
 
 939  8
                 if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
 940  0
                     File f = currentPath.findFile(basedir, false);
 941  0
                     if (f != null && f.exists()) {
 942  
                         // adapt currentelement to the case we've
 943  
                         // actually found
 944  0
                         currentelement = (basedir == null)
 945  
                                 ? f.getAbsolutePath()
 946  
                                 : FILE_UTILS.removeLeadingPath(basedir, f);
 947  0
                         myfile = f;
 948  0
                         currentPath = new TokenizedPath(currentelement);
 949  
                     }
 950  
                 }
 951  
 
 952  8
                 if (myfile != null && myfile.exists()) {
 953  8
                     if (!followSymlinks && currentPath.isSymlink(basedir)) {
 954  0
                         accountForNotFollowedSymlink(currentPath, myfile);
 955  0
                         continue;
 956  
                     }
 957  8
                     if (myfile.isDirectory()) {
 958  8
                         if (isIncluded(currentPath)
 959  
                                 && currentelement.length() > 0) {
 960  8
                             accountForIncludedDir(currentPath, myfile, true);
 961  
                         } else {
 962  0
                             scandir(myfile, currentPath, true);
 963  
                         }
 964  0
                     } else if (myfile.isFile()) {
 965  0
                         String originalpattern = (String) entry.getValue();
 966  0
                         boolean included = isCaseSensitive()
 967  
                                 ? originalpattern.equals(currentelement)
 968  
                                 : originalpattern.equalsIgnoreCase(currentelement);
 969  0
                         if (included) {
 970  0
                             accountForIncludedFile(currentPath, myfile);
 971  
                         }
 972  
                     }
 973  
                 }
 974  8
             }
 975  
         }
 976  8
     }
 977  
 
 978  
     /**
 979  
      * true if the pattern specifies a relative path without basedir or an absolute path not inside basedir.
 980  
      *
 981  
      * @since Ant 1.8.0
 982  
      */
 983  
     private boolean shouldSkipPattern(String pattern) {
 984  8
         if (FileUtils.isAbsolutePath(pattern)) {
 985  
             //skip abs. paths not under basedir, if set:
 986  0
             if (basedir != null
 987  
                     && !SelectorUtils.matchPatternStart(pattern,
 988  
                             basedir.getAbsolutePath(),
 989  
                             isCaseSensitive())) {
 990  0
                 return true;
 991  
             }
 992  8
         } else if (basedir == null) {
 993  
             //skip non-abs. paths if basedir == null:
 994  0
             return true;
 995  
         }
 996  8
         return false;
 997  
     }
 998  
 
 999  
     /**
 1000  
      * Clear the result caches for a scan.
 1001  
      */
 1002  
     protected synchronized void clearResults() {
 1003  8
         filesIncluded = new VectorSet<String>();
 1004  8
         filesNotIncluded = new VectorSet<String>();
 1005  8
         filesExcluded = new VectorSet<String>();
 1006  8
         filesDeselected = new VectorSet<String>();
 1007  8
         dirsIncluded = new VectorSet<String>();
 1008  8
         dirsNotIncluded = new VectorSet<String>();
 1009  8
         dirsExcluded = new VectorSet<String>();
 1010  8
         dirsDeselected = new VectorSet<String>();
 1011  8
         everythingIncluded = (basedir != null);
 1012  8
         scannedDirs.clear();
 1013  8
         notFollowedSymlinks.clear();
 1014  8
     }
 1015  
 
 1016  
     /**
 1017  
      * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories,
 1018  
      * whereas a fast scan will only have full results for included files, as it ignores directories which can't
 1019  
      * possibly hold any included files/directories.
 1020  
      * <p>
 1021  
      * Returns immediately if a slow scan has already been completed.
 1022  
      */
 1023  
     protected void slowScan() {
 1024  0
         synchronized (slowScanLock) {
 1025  0
             if (haveSlowResults) {
 1026  0
                 return;
 1027  
             }
 1028  0
             if (slowScanning) {
 1029  0
                 while (slowScanning) {
 1030  
                     try {
 1031  0
                         slowScanLock.wait();
 1032  0
                     } catch (InterruptedException e) {
 1033  
                         // Empty
 1034  0
                     }
 1035  
                 }
 1036  0
                 return;
 1037  
             }
 1038  0
             slowScanning = true;
 1039  0
         }
 1040  
         try {
 1041  0
             synchronized (this) {
 1042  
 
 1043  
                 // set in/excludes to reasonable defaults if needed:
 1044  0
                 boolean nullIncludes = (includes == null);
 1045  0
                 includes = nullIncludes
 1046  
                         ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes;
 1047  0
                 boolean nullExcludes = (excludes == null);
 1048  0
                 excludes = nullExcludes ? new String[0] : excludes;
 1049  
 
 1050  0
                 String[] excl = new String[dirsExcluded.size()];
 1051  0
                 dirsExcluded.copyInto(excl);
 1052  
 
 1053  0
                 String[] notIncl = new String[dirsNotIncluded.size()];
 1054  0
                 dirsNotIncluded.copyInto(notIncl);
 1055  
 
 1056  0
                 ensureNonPatternSetsReady();
 1057  
 
 1058  0
                 processSlowScan(excl);
 1059  0
                 processSlowScan(notIncl);
 1060  0
                 clearCaches();
 1061  0
                 includes = nullIncludes ? null : includes;
 1062  0
                 excludes = nullExcludes ? null : excludes;
 1063  0
             }
 1064  
         } finally {
 1065  0
             synchronized (slowScanLock) {
 1066  0
                 haveSlowResults = true;
 1067  0
                 slowScanning = false;
 1068  0
                 slowScanLock.notifyAll();
 1069  0
             }
 1070  0
         }
 1071  0
     }
 1072  
 
 1073  
     private void processSlowScan(String[] arr) {
 1074  0
         for (int i = 0; i < arr.length; i++) {
 1075  0
             TokenizedPath path = new TokenizedPath(arr[i]);
 1076  0
             if (!couldHoldIncluded(path) || contentsExcluded(path)) {
 1077  0
                 scandir(new File(basedir, arr[i]), path, false);
 1078  
             }
 1079  
         }
 1080  0
     }
 1081  
 
 1082  
     /**
 1083  
      * Scan the given directory for files and directories. Found files and directories are placed in their respective
 1084  
      * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is
 1085  
      * scanned recursively.
 1086  
      *
 1087  
      * @param dir The directory to scan. Must not be <code>null</code>.
 1088  
      * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using
 1089  
      * dir). Must not be <code>null</code>.
 1090  
      * @param fast Whether or not this call is part of a fast scan.
 1091  
      *
 1092  
      * @see #filesIncluded
 1093  
      * @see #filesNotIncluded
 1094  
      * @see #filesExcluded
 1095  
      * @see #dirsIncluded
 1096  
      * @see #dirsNotIncluded
 1097  
      * @see #dirsExcluded
 1098  
      * @see #slowScan
 1099  
      */
 1100  
     protected void scandir(File dir, String vpath, boolean fast) {
 1101  0
         scandir(dir, new TokenizedPath(vpath), fast);
 1102  0
     }
 1103  
 
 1104  
     /**
 1105  
      * Scan the given directory for files and directories. Found files and directories are placed in their respective
 1106  
      * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is
 1107  
      * scanned recursively.
 1108  
      *
 1109  
      * @param dir The directory to scan. Must not be <code>null</code>.
 1110  
      * @param path The path relative to the base directory (needed to prevent problems with an absolute path when using
 1111  
      * dir). Must not be <code>null</code>.
 1112  
      * @param fast Whether or not this call is part of a fast scan.
 1113  
      *
 1114  
      * @see #filesIncluded
 1115  
      * @see #filesNotIncluded
 1116  
      * @see #filesExcluded
 1117  
      * @see #dirsIncluded
 1118  
      * @see #dirsNotIncluded
 1119  
      * @see #dirsExcluded
 1120  
      * @see #slowScan
 1121  
      */
 1122  
     private void scandir(File dir, TokenizedPath path, boolean fast) {
 1123  8
         if (dir == null) {
 1124  0
             throw new BuildException("dir must not be null.");
 1125  
         }
 1126  8
         String[] newfiles = dir.list();
 1127  8
         if (newfiles == null) {
 1128  0
             if (!dir.exists()) {
 1129  0
                 throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX);
 1130  0
             } else if (!dir.isDirectory()) {
 1131  0
                 throw new BuildException(dir + " is not a directory.");
 1132  
             } else {
 1133  0
                 throw new BuildException("IO error scanning directory '"
 1134  
                         + dir.getAbsolutePath() + "'");
 1135  
             }
 1136  
         }
 1137  8
         scandir(dir, path, fast, newfiles, new LinkedList<String>());
 1138  8
     }
 1139  
 
 1140  
     private void scandir(File dir, TokenizedPath path, boolean fast,
 1141  
             String[] newfiles, LinkedList<String> directoryNamesFollowed) {
 1142  72
         String vpath = path.toString();
 1143  72
         if (vpath.length() > 0 && !vpath.endsWith(File.separator)) {
 1144  72
             vpath += File.separator;
 1145  
         }
 1146  
 
 1147  
         // avoid double scanning of directories, can only happen in fast mode
 1148  72
         if (fast && hasBeenScanned(vpath)) {
 1149  0
             return;
 1150  
         }
 1151  72
         if (!followSymlinks) {
 1152  0
             ArrayList<String> noLinks = new ArrayList<String>();
 1153  0
             for (int i = 0; i < newfiles.length; i++) {
 1154  
                 try {
 1155  0
                     if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) {
 1156  0
                         String name = vpath + newfiles[i];
 1157  0
                         File file = new File(dir, newfiles[i]);
 1158  0
                         if (file.isDirectory()) {
 1159  0
                             dirsExcluded.addElement(name);
 1160  0
                         } else if (file.isFile()) {
 1161  0
                             filesExcluded.addElement(name);
 1162  
                         }
 1163  0
                         accountForNotFollowedSymlink(name, file);
 1164  0
                     } else {
 1165  0
                         noLinks.add(newfiles[i]);
 1166  
                     }
 1167  0
                 } catch (IOException ioe) {
 1168  0
                     String msg = "IOException caught while checking "
 1169  
                             + "for links, couldn't get canonical path!";
 1170  
                     // will be caught and redirected to Ant's logging system
 1171  0
                     System.err.println(msg);
 1172  0
                     noLinks.add(newfiles[i]);
 1173  0
                 }
 1174  
             }
 1175  0
             newfiles = (String[]) (noLinks.toArray(new String[noLinks.size()]));
 1176  0
         } else {
 1177  72
             directoryNamesFollowed.addFirst(dir.getName());
 1178  
         }
 1179  
 
 1180  224
         for (int i = 0; i < newfiles.length; i++) {
 1181  152
             String name = vpath + newfiles[i];
 1182  152
             TokenizedPath newPath = new TokenizedPath(path, newfiles[i]);
 1183  152
             File file = new File(dir, newfiles[i]);
 1184  152
             String[] children = file.list();
 1185  152
             if (children == null || (children.length == 0 && file.isFile())) {
 1186  88
                 if (isIncluded(newPath)) {
 1187  88
                     accountForIncludedFile(newPath, file);
 1188  
                 } else {
 1189  0
                     everythingIncluded = false;
 1190  0
                     filesNotIncluded.addElement(name);
 1191  
                 }
 1192  64
             } else if (file.isDirectory()) { // dir
 1193  
 
 1194  64
                 if (followSymlinks
 1195  
                         && causesIllegalSymlinkLoop(newfiles[i], dir,
 1196  
                                 directoryNamesFollowed)) {
 1197  
                     // will be caught and redirected to Ant's logging system
 1198  0
                     System.err.println("skipping symbolic link "
 1199  
                             + file.getAbsolutePath()
 1200  
                             + " -- too many levels of symbolic"
 1201  
                             + " links.");
 1202  0
                     notFollowedSymlinks.add(file.getAbsolutePath());
 1203  0
                     continue;
 1204  
                 }
 1205  
 
 1206  64
                 if (isIncluded(newPath)) {
 1207  64
                     accountForIncludedDir(newPath, file, fast, children,
 1208  
                             directoryNamesFollowed);
 1209  
                 } else {
 1210  0
                     everythingIncluded = false;
 1211  0
                     dirsNotIncluded.addElement(name);
 1212  0
                     if (fast && couldHoldIncluded(newPath)
 1213  
                             && !contentsExcluded(newPath)) {
 1214  0
                         scandir(file, newPath, fast, children,
 1215  
                                 directoryNamesFollowed);
 1216  
                     }
 1217  
                 }
 1218  64
                 if (!fast) {
 1219  0
                     scandir(file, newPath, fast, children, directoryNamesFollowed);
 1220  
                 }
 1221  
             }
 1222  
         }
 1223  
 
 1224  72
         if (followSymlinks) {
 1225  72
             directoryNamesFollowed.removeFirst();
 1226  
         }
 1227  72
     }
 1228  
 
 1229  
     /**
 1230  
      * Process included file.
 1231  
      *
 1232  
      * @param name path of the file relative to the directory of the FileSet.
 1233  
      * @param file included File.
 1234  
      */
 1235  
     private void accountForIncludedFile(TokenizedPath name, File file) {
 1236  88
         processIncluded(name, file, filesIncluded, filesExcluded,
 1237  
                 filesDeselected);
 1238  88
     }
 1239  
 
 1240  
     /**
 1241  
      * Process included directory.
 1242  
      *
 1243  
      * @param name path of the directory relative to the directory of the FileSet.
 1244  
      * @param file directory as File.
 1245  
      * @param fast whether to perform fast scans.
 1246  
      */
 1247  
     private void accountForIncludedDir(TokenizedPath name, File file,
 1248  
             boolean fast) {
 1249  8
         processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
 1250  8
         if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
 1251  8
             scandir(file, name, fast);
 1252  
         }
 1253  8
     }
 1254  
 
 1255  
     private void accountForIncludedDir(TokenizedPath name,
 1256  
             File file, boolean fast,
 1257  
             String[] children,
 1258  
             LinkedList<String> directoryNamesFollowed) {
 1259  64
         processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
 1260  64
         if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
 1261  64
             scandir(file, name, fast, children, directoryNamesFollowed);
 1262  
         }
 1263  64
     }
 1264  
 
 1265  
     private void accountForNotFollowedSymlink(String name, File file) {
 1266  0
         accountForNotFollowedSymlink(new TokenizedPath(name), file);
 1267  0
     }
 1268  
 
 1269  
     private void accountForNotFollowedSymlink(TokenizedPath name, File file) {
 1270  0
         if (!isExcluded(name)
 1271  
                 && (isIncluded(name)
 1272  
                 || (file.isDirectory() && couldHoldIncluded(name)
 1273  
                 && !contentsExcluded(name)))) {
 1274  0
             notFollowedSymlinks.add(file.getAbsolutePath());
 1275  
         }
 1276  0
     }
 1277  
 
 1278  
     private void processIncluded(TokenizedPath path,
 1279  
             File file, Vector<String> inc, Vector<String> exc,
 1280  
             Vector<String> des) {
 1281  160
         String name = path.toString();
 1282  160
         if (inc.contains(name) || exc.contains(name) || des.contains(name)) {
 1283  0
             return;
 1284  
         }
 1285  
 
 1286  160
         boolean included = false;
 1287  160
         if (isExcluded(path)) {
 1288  0
             exc.add(name);
 1289  160
         } else if (isSelected(name, file)) {
 1290  160
             included = true;
 1291  160
             inc.add(name);
 1292  
         } else {
 1293  0
             des.add(name);
 1294  
         }
 1295  160
         everythingIncluded &= included;
 1296  160
     }
 1297  
 
 1298  
     /**
 1299  
      * Test whether or not a name matches against at least one include pattern.
 1300  
      *
 1301  
      * @param name The name to match. Must not be <code>null</code>.
 1302  
      * @return <code>true</code> when the name matches against at least one include pattern, or <code>false</code>
 1303  
      * otherwise.
 1304  
      */
 1305  
     protected boolean isIncluded(String name) {
 1306  0
         return isIncluded(new TokenizedPath(name));
 1307  
     }
 1308  
 
 1309  
     /**
 1310  
      * Test whether or not a name matches against at least one include pattern.
 1311  
      *
 1312  
      * @param name The name to match. Must not be <code>null</code>.
 1313  
      * @return <code>true</code> when the name matches against at least one include pattern, or <code>false</code>
 1314  
      * otherwise.
 1315  
      */
 1316  
     private boolean isIncluded(TokenizedPath path) {
 1317  168
         ensureNonPatternSetsReady();
 1318  
 
 1319  168
         if (isCaseSensitive()
 1320  
                 ? includeNonPatterns.containsKey(path.toString())
 1321  
                 : includeNonPatterns.containsKey(path.toString().toUpperCase())) {
 1322  0
             return true;
 1323  
         }
 1324  176
         for (int i = 0; i < includePatterns.length; i++) {
 1325  168
             if (includePatterns[i].matchPath(path, isCaseSensitive())) {
 1326  160
                 return true;
 1327  
             }
 1328  
         }
 1329  8
         return false;
 1330  
     }
 1331  
 
 1332  
     /**
 1333  
      * Test whether or not a name matches the start of at least one include pattern.
 1334  
      *
 1335  
      * @param name The name to match. Must not be <code>null</code>.
 1336  
      * @return <code>true</code> when the name matches against the start of at least one include pattern, or
 1337  
      * <code>false</code> otherwise.
 1338  
      */
 1339  
     protected boolean couldHoldIncluded(String name) {
 1340  0
         return couldHoldIncluded(new TokenizedPath(name));
 1341  
     }
 1342  
 
 1343  
     /**
 1344  
      * Test whether or not a name matches the start of at least one include pattern.
 1345  
      *
 1346  
      * @param tokenizedName The name to match. Must not be <code>null</code>.
 1347  
      * @return <code>true</code> when the name matches against the start of at least one include pattern, or
 1348  
      * <code>false</code> otherwise.
 1349  
      */
 1350  
     private boolean couldHoldIncluded(TokenizedPath tokenizedName) {
 1351  72
         for (int i = 0; i < includePatterns.length; i++) {
 1352  72
             if (couldHoldIncluded(tokenizedName, includePatterns[i])) {
 1353  72
                 return true;
 1354  
             }
 1355  
         }
 1356  0
         for (Iterator<TokenizedPath> iter = includeNonPatterns.values().iterator();
 1357  0
                 iter.hasNext();) {
 1358  0
             if (couldHoldIncluded(tokenizedName,
 1359  
                     iter.next().toPattern())) {
 1360  0
                 return true;
 1361  
             }
 1362  
         }
 1363  0
         return false;
 1364  
     }
 1365  
 
 1366  
     /**
 1367  
      * Test whether or not a name matches the start of the given include pattern.
 1368  
      *
 1369  
      * @param tokenizedName The name to match. Must not be <code>null</code>.
 1370  
      * @return <code>true</code> when the name matches against the start of the include pattern, or <code>false</code>
 1371  
      * otherwise.
 1372  
      */
 1373  
     private boolean couldHoldIncluded(TokenizedPath tokenizedName,
 1374  
             TokenizedPattern tokenizedInclude) {
 1375  72
         return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive())
 1376  
                 && isMorePowerfulThanExcludes(tokenizedName.toString())
 1377  
                 && isDeeper(tokenizedInclude, tokenizedName);
 1378  
     }
 1379  
 
 1380  
     /**
 1381  
      * Verify that a pattern specifies files deeper than the level of the specified file.
 1382  
      *
 1383  
      * @param pattern the pattern to check.
 1384  
      * @param name the name to check.
 1385  
      * @return whether the pattern is deeper than the name.
 1386  
      * @since Ant 1.6.3
 1387  
      */
 1388  
     private boolean isDeeper(TokenizedPattern pattern, TokenizedPath name) {
 1389  72
         return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH)
 1390  
                 || pattern.depth() > name.depth();
 1391  
     }
 1392  
 
 1393  
     /**
 1394  
      * Find out whether one particular include pattern is more powerful than all the excludes. Note: the power
 1395  
      * comparison is based on the length of the include pattern and of the exclude patterns without the wildcards.
 1396  
      * Ideally the comparison should be done based on the depth of the match; that is to say how many file separators
 1397  
      * have been matched before the first ** or the end of the pattern.
 1398  
      *
 1399  
      * IMPORTANT : this function should return false "with care".
 1400  
      *
 1401  
      * @param name the relative path to test.
 1402  
      * @return true if there is no exclude pattern more powerful than this include pattern.
 1403  
      * @since Ant 1.6
 1404  
      */
 1405  
     private boolean isMorePowerfulThanExcludes(String name) {
 1406  72
         final String soughtexclude
 1407  
                 = name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH;
 1408  72
         for (int counter = 0; counter < excludePatterns.length; counter++) {
 1409  0
             if (excludePatterns[counter].toString().equals(soughtexclude)) {
 1410  0
                 return false;
 1411  
             }
 1412  
         }
 1413  72
         return true;
 1414  
     }
 1415  
 
 1416  
     /**
 1417  
      * Test whether all contents of the specified directory must be excluded.
 1418  
      *
 1419  
      * @param path the path to check.
 1420  
      * @return whether all the specified directory's contents are excluded.
 1421  
      */
 1422  
     /* package */ boolean contentsExcluded(TokenizedPath path) {
 1423  72
         for (int i = 0; i < excludePatterns.length; i++) {
 1424  0
             if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH)
 1425  
                     && excludePatterns[i].withoutLastToken()
 1426  
                     .matchPath(path, isCaseSensitive())) {
 1427  0
                 return true;
 1428  
             }
 1429  
         }
 1430  72
         return false;
 1431  
     }
 1432  
 
 1433  
     /**
 1434  
      * Test whether or not a name matches against at least one exclude pattern.
 1435  
      *
 1436  
      * @param name The name to match. Must not be <code>null</code>.
 1437  
      * @return <code>true</code> when the name matches against at least one exclude pattern, or <code>false</code>
 1438  
      * otherwise.
 1439  
      */
 1440  
     protected boolean isExcluded(String name) {
 1441  0
         return isExcluded(new TokenizedPath(name));
 1442  
     }
 1443  
 
 1444  
     /**
 1445  
      * Test whether or not a name matches against at least one exclude pattern.
 1446  
      *
 1447  
      * @param name The name to match. Must not be <code>null</code>.
 1448  
      * @return <code>true</code> when the name matches against at least one exclude pattern, or <code>false</code>
 1449  
      * otherwise.
 1450  
      */
 1451  
     private boolean isExcluded(TokenizedPath name) {
 1452  160
         ensureNonPatternSetsReady();
 1453  
 
 1454  160
         if (isCaseSensitive()
 1455  
                 ? excludeNonPatterns.containsKey(name.toString())
 1456  
                 : excludeNonPatterns.containsKey(name.toString().toUpperCase())) {
 1457  0
             return true;
 1458  
         }
 1459  160
         for (int i = 0; i < excludePatterns.length; i++) {
 1460  0
             if (excludePatterns[i].matchPath(name, isCaseSensitive())) {
 1461  0
                 return true;
 1462  
             }
 1463  
         }
 1464  160
         return false;
 1465  
     }
 1466  
 
 1467  
     /**
 1468  
      * Test whether a file should be selected.
 1469  
      *
 1470  
      * @param name the filename to check for selecting.
 1471  
      * @param file the java.io.File object for this filename.
 1472  
      * @return <code>false</code> when the selectors says that the file should not be selected, <code>true</code>
 1473  
      * otherwise.
 1474  
      */
 1475  
     protected boolean isSelected(String name, File file) {
 1476  160
         if (selectors != null) {
 1477  0
             for (int i = 0; i < selectors.length; i++) {
 1478  0
                 if (!selectors[i].isSelected(basedir, name, file)) {
 1479  0
                     return false;
 1480  
                 }
 1481  
             }
 1482  
         }
 1483  160
         return true;
 1484  
     }
 1485  
 
 1486  
     /**
 1487  
      * Return the names of the files which matched at least one of the include patterns and none of the exclude
 1488  
      * patterns. The names are relative to the base directory.
 1489  
      *
 1490  
      * @return the names of the files which matched at least one of the include patterns and none of the exclude
 1491  
      * patterns.
 1492  
      */
 1493  
     public String[] getIncludedFiles() {
 1494  
         String[] files;
 1495  8
         synchronized (this) {
 1496  8
             if (filesIncluded == null) {
 1497  0
                 throw new IllegalStateException("Must call scan() first");
 1498  
             }
 1499  8
             files = new String[filesIncluded.size()];
 1500  8
             filesIncluded.copyInto(files);
 1501  8
         }
 1502  8
         Arrays.sort(files);
 1503  8
         return files;
 1504  
     }
 1505  
 
 1506  
     /**
 1507  
      * Return the count of included files.
 1508  
      *
 1509  
      * @return <code>int</code>.
 1510  
      * @since Ant 1.6.3
 1511  
      */
 1512  
     public synchronized int getIncludedFilesCount() {
 1513  0
         if (filesIncluded == null) {
 1514  0
             throw new IllegalStateException("Must call scan() first");
 1515  
         }
 1516  0
         return filesIncluded.size();
 1517  
     }
 1518  
 
 1519  
     /**
 1520  
      * Return the names of the files which matched none of the include patterns. The names are relative to the base
 1521  
      * directory. This involves performing a slow scan if one has not already been completed.
 1522  
      *
 1523  
      * @return the names of the files which matched none of the include patterns.
 1524  
      *
 1525  
      * @see #slowScan
 1526  
      */
 1527  
     public synchronized String[] getNotIncludedFiles() {
 1528  0
         slowScan();
 1529  0
         String[] files = new String[filesNotIncluded.size()];
 1530  0
         filesNotIncluded.copyInto(files);
 1531  0
         return files;
 1532  
     }
 1533  
 
 1534  
     /**
 1535  
      * Return the names of the files which matched at least one of the include patterns and at least one of the exclude
 1536  
      * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not
 1537  
      * already been completed.
 1538  
      *
 1539  
      * @return the names of the files which matched at least one of the include patterns and at least one of the exclude
 1540  
      * patterns.
 1541  
      *
 1542  
      * @see #slowScan
 1543  
      */
 1544  
     public synchronized String[] getExcludedFiles() {
 1545  0
         slowScan();
 1546  0
         String[] files = new String[filesExcluded.size()];
 1547  0
         filesExcluded.copyInto(files);
 1548  0
         return files;
 1549  
     }
 1550  
 
 1551  
     /**
 1552  
      * <p>
 1553  
      * Return the names of the files which were selected out and therefore not ultimately included.</p>
 1554  
      *
 1555  
      * <p>
 1556  
      * The names are relative to the base directory. This involves performing a slow scan if one has not already been
 1557  
      * completed.</p>
 1558  
      *
 1559  
      * @return the names of the files which were deselected.
 1560  
      *
 1561  
      * @see #slowScan
 1562  
      */
 1563  
     public synchronized String[] getDeselectedFiles() {
 1564  0
         slowScan();
 1565  0
         String[] files = new String[filesDeselected.size()];
 1566  0
         filesDeselected.copyInto(files);
 1567  0
         return files;
 1568  
     }
 1569  
 
 1570  
     /**
 1571  
      * Return the names of the directories which matched at least one of the include patterns and none of the exclude
 1572  
      * patterns. The names are relative to the base directory.
 1573  
      *
 1574  
      * @return the names of the directories which matched at least one of the include patterns and none of the exclude
 1575  
      * patterns.
 1576  
      */
 1577  
     public String[] getIncludedDirectories() {
 1578  
         String[] directories;
 1579  0
         synchronized (this) {
 1580  0
             if (dirsIncluded == null) {
 1581  0
                 throw new IllegalStateException("Must call scan() first");
 1582  
             }
 1583  0
             directories = new String[dirsIncluded.size()];
 1584  0
             dirsIncluded.copyInto(directories);
 1585  0
         }
 1586  0
         Arrays.sort(directories);
 1587  0
         return directories;
 1588  
     }
 1589  
 
 1590  
     /**
 1591  
      * Return the count of included directories.
 1592  
      *
 1593  
      * @return <code>int</code>.
 1594  
      * @since Ant 1.6.3
 1595  
      */
 1596  
     public synchronized int getIncludedDirsCount() {
 1597  0
         if (dirsIncluded == null) {
 1598  0
             throw new IllegalStateException("Must call scan() first");
 1599  
         }
 1600  0
         return dirsIncluded.size();
 1601  
     }
 1602  
 
 1603  
     /**
 1604  
      * Return the names of the directories which matched none of the include patterns. The names are relative to the
 1605  
      * base directory. This involves performing a slow scan if one has not already been completed.
 1606  
      *
 1607  
      * @return the names of the directories which matched none of the include patterns.
 1608  
      *
 1609  
      * @see #slowScan
 1610  
      */
 1611  
     public synchronized String[] getNotIncludedDirectories() {
 1612  0
         slowScan();
 1613  0
         String[] directories = new String[dirsNotIncluded.size()];
 1614  0
         dirsNotIncluded.copyInto(directories);
 1615  0
         return directories;
 1616  
     }
 1617  
 
 1618  
     /**
 1619  
      * Return the names of the directories which matched at least one of the include patterns and at least one of the
 1620  
      * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has
 1621  
      * not already been completed.
 1622  
      *
 1623  
      * @return the names of the directories which matched at least one of the include patterns and at least one of the
 1624  
      * exclude patterns.
 1625  
      *
 1626  
      * @see #slowScan
 1627  
      */
 1628  
     public synchronized String[] getExcludedDirectories() {
 1629  0
         slowScan();
 1630  0
         String[] directories = new String[dirsExcluded.size()];
 1631  0
         dirsExcluded.copyInto(directories);
 1632  0
         return directories;
 1633  
     }
 1634  
 
 1635  
     /**
 1636  
      * <p>
 1637  
      * Return the names of the directories which were selected out and therefore not ultimately included.</p>
 1638  
      *
 1639  
      * <p>
 1640  
      * The names are relative to the base directory. This involves performing a slow scan if one has not already been
 1641  
      * completed.</p>
 1642  
      *
 1643  
      * @return the names of the directories which were deselected.
 1644  
      *
 1645  
      * @see #slowScan
 1646  
      */
 1647  
     public synchronized String[] getDeselectedDirectories() {
 1648  0
         slowScan();
 1649  0
         String[] directories = new String[dirsDeselected.size()];
 1650  0
         dirsDeselected.copyInto(directories);
 1651  0
         return directories;
 1652  
     }
 1653  
 
 1654  
     /**
 1655  
      * Absolute paths of all symbolic links that haven't been followed but would have been followed had followsymlinks
 1656  
      * been true or maxLevelsOfSymlinks been bigger.
 1657  
      *
 1658  
      * @return sorted array of not followed symlinks
 1659  
      * @since Ant 1.8.0
 1660  
      * @see #notFollowedSymlinks
 1661  
      */
 1662  
     public synchronized String[] getNotFollowedSymlinks() {
 1663  
         String[] links;
 1664  0
         synchronized (this) {
 1665  0
             links = (String[]) notFollowedSymlinks
 1666  
                     .toArray(new String[notFollowedSymlinks.size()]);
 1667  0
         }
 1668  0
         Arrays.sort(links);
 1669  0
         return links;
 1670  
     }
 1671  
 
 1672  
     /**
 1673  
      * Add default exclusions to the current exclusions set.
 1674  
      */
 1675  
     public synchronized void addDefaultExcludes() {
 1676  0
         int excludesLength = excludes == null ? 0 : excludes.length;
 1677  
         String[] newExcludes;
 1678  0
         String[] defaultExcludesTemp = getDefaultExcludes();
 1679  0
         newExcludes = new String[excludesLength + defaultExcludesTemp.length];
 1680  0
         if (excludesLength > 0) {
 1681  0
             System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
 1682  
         }
 1683  0
         for (int i = 0; i < defaultExcludesTemp.length; i++) {
 1684  0
             newExcludes[i + excludesLength]
 1685  
                     = defaultExcludesTemp[i].replace('/', File.separatorChar)
 1686  
                     .replace('\\', File.separatorChar);
 1687  
         }
 1688  0
         excludes = newExcludes;
 1689  0
     }
 1690  
 
 1691  
     /**
 1692  
      * Get the named resource.
 1693  
      *
 1694  
      * @param name path name of the file relative to the dir attribute.
 1695  
      *
 1696  
      * @return the resource with the given name.
 1697  
      * @since Ant 1.5.2
 1698  
      */
 1699  
     public synchronized Resource getResource(String name) {
 1700  0
         return new FileResource(basedir, name);
 1701  
     }
 1702  
 
 1703  
     /**
 1704  
      * Has the directory with the given path relative to the base directory already been scanned?
 1705  
      *
 1706  
      * <p>
 1707  
      * Registers the given directory as scanned as a side effect.</p>
 1708  
      *
 1709  
      * @since Ant 1.6
 1710  
      */
 1711  
     private boolean hasBeenScanned(String vpath) {
 1712  72
         return !scannedDirs.add(vpath);
 1713  
     }
 1714  
 
 1715  
     /**
 1716  
      * This method is of interest for testing purposes. The returned Set is live and should not be modified.
 1717  
      *
 1718  
      * @return the Set of relative directory names that have been scanned.
 1719  
      */
 1720  
     /* package-private */ Set<String> getScannedDirs() {
 1721  0
         return scannedDirs;
 1722  
     }
 1723  
 
 1724  
     /**
 1725  
      * Clear internal caches.
 1726  
      *
 1727  
      * @since Ant 1.6
 1728  
      */
 1729  
     private synchronized void clearCaches() {
 1730  8
         includeNonPatterns.clear();
 1731  8
         excludeNonPatterns.clear();
 1732  8
         includePatterns = null;
 1733  8
         excludePatterns = null;
 1734  8
         areNonPatternSetsReady = false;
 1735  8
     }
 1736  
 
 1737  
     /**
 1738  
      * Ensure that the in|exclude &quot;patterns&quot; have been properly divided up.
 1739  
      *
 1740  
      * @since Ant 1.6.3
 1741  
      */
 1742  
     /* package */ synchronized void ensureNonPatternSetsReady() {
 1743  336
         if (!areNonPatternSetsReady) {
 1744  8
             includePatterns = fillNonPatternSet(includeNonPatterns, includes);
 1745  8
             excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
 1746  8
             areNonPatternSetsReady = true;
 1747  
         }
 1748  336
     }
 1749  
 
 1750  
     /**
 1751  
      * Add all patterns that are not real patterns (do not contain wildcards) to the set and returns the real patterns.
 1752  
      *
 1753  
      * @param map Map to populate.
 1754  
      * @param patterns String[] of patterns.
 1755  
      * @since Ant 1.8.0
 1756  
      */
 1757  
     private TokenizedPattern[] fillNonPatternSet(Map<String, TokenizedPath> map, String[] patterns) {
 1758  16
         ArrayList<TokenizedPattern> al = new ArrayList<TokenizedPattern>(patterns.length);
 1759  24
         for (int i = 0; i < patterns.length; i++) {
 1760  8
             if (!SelectorUtils.hasWildcards(patterns[i])) {
 1761  0
                 String s = isCaseSensitive()
 1762  
                         ? patterns[i] : patterns[i].toUpperCase();
 1763  0
                 map.put(s, new TokenizedPath(s));
 1764  0
             } else {
 1765  8
                 al.add(new TokenizedPattern(patterns[i]));
 1766  
             }
 1767  
         }
 1768  16
         return (TokenizedPattern[]) al.toArray(new TokenizedPattern[al.size()]);
 1769  
     }
 1770  
 
 1771  
     /**
 1772  
      * Would following the given directory cause a loop of symbolic links deeper than allowed?
 1773  
      *
 1774  
      * <p>
 1775  
      * Can only happen if the given directory has been seen at least more often than allowed during the current scan and
 1776  
      * it is a symbolic link and enough other occurrences of the same name higher up are symbolic links that point to
 1777  
      * the same place.</p>
 1778  
      *
 1779  
      * @since Ant 1.8.0
 1780  
      */
 1781  
     private boolean causesIllegalSymlinkLoop(String dirName, File parent,
 1782  
             LinkedList<String> directoryNamesFollowed) {
 1783  
         try {
 1784  64
             if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks
 1785  
                     && CollectionUtils.frequency(directoryNamesFollowed, dirName)
 1786  
                     >= maxLevelsOfSymlinks
 1787  
                     && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) {
 1788  
 
 1789  0
                 ArrayList<String> files = new ArrayList<String>();
 1790  0
                 File f = FILE_UTILS.resolveFile(parent, dirName);
 1791  0
                 String target = f.getCanonicalPath();
 1792  0
                 files.add(target);
 1793  
 
 1794  0
                 String relPath = "";
 1795  0
                 for (String dir : directoryNamesFollowed) {
 1796  0
                     relPath += "../";
 1797  0
                     if (dirName.equals(dir)) {
 1798  0
                         f = FILE_UTILS.resolveFile(parent, relPath + dir);
 1799  0
                         files.add(f.getCanonicalPath());
 1800  0
                         if (files.size() > maxLevelsOfSymlinks
 1801  
                                 && CollectionUtils.frequency(files, target)
 1802  
                                 > maxLevelsOfSymlinks) {
 1803  0
                             return true;
 1804  
                         }
 1805  
                     }
 1806  0
                 }
 1807  
 
 1808  
             }
 1809  64
             return false;
 1810  0
         } catch (IOException ex) {
 1811  0
             throw new BuildException("Caught error while checking for"
 1812  
                     + " symbolic links", ex);
 1813  
         }
 1814  
     }
 1815  
 
 1816  
 }