Coverage Report - org.apache.maven.it.util.DirectoryScanner
 
Classes in this File Line Coverage Branch Coverage Complexity
DirectoryScanner
0%
0/194
0%
0/102
2.688
 
 1  
 /*
 2  
  * The Apache Software License, Version 1.1
 3  
  *
 4  
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5  
  * reserved.
 6  
  *
 7  
  * Redistribution and use in source and binary forms, with or without
 8  
  * modification, are permitted provided that the following conditions
 9  
  * are met:
 10  
  *
 11  
  * 1. Redistributions of source code must retain the above copyright
 12  
  *    notice, this list of conditions and the following disclaimer.
 13  
  *
 14  
  * 2. Redistributions in binary form must reproduce the above copyright
 15  
  *    notice, this list of conditions and the following disclaimer in
 16  
  *    the documentation and/or other materials provided with the
 17  
  *    distribution.
 18  
  *
 19  
  * 3. The end-user documentation included with the redistribution, if
 20  
  *    any, must include the following acknowlegement:
 21  
  *       "This product includes software developed by the
 22  
  *        Apache Software Foundation (http://www.apache.org/)."
 23  
  *    Alternately, this acknowlegement may appear in the software itself,
 24  
  *    if and wherever such third-party acknowlegements normally appear.
 25  
  *
 26  
  * 4. The names "Ant" and "Apache Software
 27  
  *    Foundation" must not be used to endorse or promote products derived
 28  
  *    from this software without prior written permission. For written
 29  
  *    permission, please contact apache@apache.org.
 30  
  *
 31  
  * 5. Products derived from this software may not be called "Apache"
 32  
  *    nor may "Apache" appear in their names without prior written
 33  
  *    permission of the Apache Group.
 34  
  *
 35  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38  
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39  
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40  
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42  
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44  
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46  
  * SUCH DAMAGE.
 47  
  * ====================================================================
 48  
  *
 49  
  * This software consists of voluntary contributions made by many
 50  
  * individuals on behalf of the Apache Software Foundation.  For more
 51  
  * information on the Apache Software Foundation, please see
 52  
  * <http://www.apache.org/>.
 53  
  */
 54  
 
 55  
 package org.apache.maven.it.util;
 56  
 
 57  
 import java.io.File;
 58  
 import java.io.IOException;
 59  
 import java.util.Vector;
 60  
 
 61  
 /**
 62  
  * Class for scanning a directory for files/directories which match certain
 63  
  * criteria.
 64  
  * <p>
 65  
  * These criteria consist of selectors and patterns which have been specified.
 66  
  * With the selectors you can select which files you want to have included.
 67  
  * Files which are not selected are excluded. With patterns you can include
 68  
  * or exclude files based on their filename.
 69  
  * <p>
 70  
  * The idea is simple. A given directory is recursively scanned for all files
 71  
  * and directories. Each file/directory is matched against a set of selectors,
 72  
  * including special support for matching against filenames with include and
 73  
  * and exclude patterns. Only files/directories which match at least one
 74  
  * pattern of the include pattern list or other file selector, and don't match
 75  
  * any pattern of the exclude pattern list or fail to match against a required
 76  
  * selector will be placed in the list of files/directories found.
 77  
  * <p>
 78  
  * When no list of include patterns is supplied, "**" will be used, which
 79  
  * means that everything will be matched. When no list of exclude patterns is
 80  
  * supplied, an empty list is used, such that nothing will be excluded. When
 81  
  * no selectors are supplied, none are applied.
 82  
  * <p>
 83  
  * The filename pattern matching is done as follows:
 84  
  * The name to be matched is split up in path segments. A path segment is the
 85  
  * name of a directory or file, which is bounded by
 86  
  * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
 87  
  * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
 88  
  * "def","ghi" and "xyz.java".
 89  
  * The same is done for the pattern against which should be matched.
 90  
  * <p>
 91  
  * The segments of the name and the pattern are then matched against each
 92  
  * other. When '**' is used for a path segment in the pattern, it matches
 93  
  * zero or more path segments of the name.
 94  
  * <p>
 95  
  * There is a special case regarding the use of <code>File.separator</code>s
 96  
  * at the beginning of the pattern and the string to match:<br>
 97  
  * When a pattern starts with a <code>File.separator</code>, the string
 98  
  * to match must also start with a <code>File.separator</code>.
 99  
  * When a pattern does not start with a <code>File.separator</code>, the
 100  
  * string to match may not start with a <code>File.separator</code>.
 101  
  * When one of these rules is not obeyed, the string will not
 102  
  * match.
 103  
  * <p>
 104  
  * When a name path segment is matched against a pattern path segment, the
 105  
  * following special characters can be used:<br>
 106  
  * '*' matches zero or more characters<br>
 107  
  * '?' matches one character.
 108  
  * <p>
 109  
  * Examples:
 110  
  * <p>
 111  
  * "**\*.class" matches all .class files/dirs in a directory tree.
 112  
  * <p>
 113  
  * "test\a??.java" matches all files/dirs which start with an 'a', then two
 114  
  * more characters and then ".java", in a directory called test.
 115  
  * <p>
 116  
  * "**" matches everything in a directory tree.
 117  
  * <p>
 118  
  * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
 119  
  * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
 120  
  * <p>
 121  
  * Case sensitivity may be turned off if necessary. By default, it is
 122  
  * turned on.
 123  
  * <p>
 124  
  * Example of usage:
 125  
  * <pre>
 126  
  *   String[] includes = {"**\\*.class"};
 127  
  *   String[] excludes = {"modules\\*\\**"};
 128  
  *   ds.setIncludes(includes);
 129  
  *   ds.setExcludes(excludes);
 130  
  *   ds.setBasedir(new File("test"));
 131  
  *   ds.setCaseSensitive(true);
 132  
  *   ds.scan();
 133  
  *
 134  
  *   System.out.println("FILES:");
 135  
  *   String[] files = ds.getIncludedFiles();
 136  
  *   for (int i = 0; i < files.length; i++) {
 137  
  *     System.out.println(files[i]);
 138  
  *   }
 139  
  * </pre>
 140  
  * This will scan a directory called test for .class files, but excludes all
 141  
  * files in all proper subdirectories of a directory called "modules"
 142  
  *
 143  
  * @author Arnout J. Kuiper
 144  
  * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
 145  
  * @author Magesh Umasankar
 146  
  * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
 147  
  * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a>
 148  
  */
 149  
 public class DirectoryScanner
 150  
 {
 151  
     /**
 152  
      * Patterns which should be excluded by default.
 153  
      *
 154  
      * @see #addDefaultExcludes()
 155  
      */
 156  0
     public static final String[] DEFAULTEXCLUDES = {
 157  
         // Miscellaneous typical temporary files
 158  
         "**/*~",
 159  
         "**/#*#",
 160  
         "**/.#*",
 161  
         "**/%*%",
 162  
         "**/._*",
 163  
 
 164  
         // CVS
 165  
         "**/CVS",
 166  
         "**/CVS/**",
 167  
         "**/.cvsignore",
 168  
 
 169  
         // SCCS
 170  
         "**/SCCS",
 171  
         "**/SCCS/**",
 172  
 
 173  
         // Visual SourceSafe
 174  
         "**/vssver.scc",
 175  
 
 176  
         // Subversion
 177  
         "**/.svn",
 178  
         "**/.svn/**",
 179  
 
 180  
         // Arch
 181  
         "**/.arch-ids",
 182  
         "**/.arch-ids/**",
 183  
 
 184  
         //Bazaar
 185  
         "**/.bzr",
 186  
         "**/.bzr/**",
 187  
 
 188  
         //SurroundSCM
 189  
         "**/.MySCMServerInfo",
 190  
 
 191  
         // Mac
 192  
         "**/.DS_Store"
 193  
     };
 194  
 
 195  
     /** The base directory to be scanned. */
 196  
     protected File basedir;
 197  
 
 198  
     /** The patterns for the files to be included. */
 199  
     protected String[] includes;
 200  
 
 201  
     /** The patterns for the files to be excluded. */
 202  
     protected String[] excludes;
 203  
 
 204  
     /** The files which matched at least one include and no excludes
 205  
      *  and were selected.
 206  
      */
 207  
     protected Vector filesIncluded;
 208  
 
 209  
     /** The files which did not match any includes or selectors. */
 210  
     protected Vector filesNotIncluded;
 211  
 
 212  
     /**
 213  
      * The files which matched at least one include and at least
 214  
      * one exclude.
 215  
      */
 216  
     protected Vector filesExcluded;
 217  
 
 218  
     /** The directories which matched at least one include and no excludes
 219  
      *  and were selected.
 220  
      */
 221  
     protected Vector dirsIncluded;
 222  
 
 223  
     /** The directories which were found and did not match any includes. */
 224  
     protected Vector dirsNotIncluded;
 225  
 
 226  
     /**
 227  
      * The directories which matched at least one include and at least one
 228  
      * exclude.
 229  
      */
 230  
     protected Vector dirsExcluded;
 231  
 
 232  
     /** The files which matched at least one include and no excludes and
 233  
      *  which a selector discarded.
 234  
      */
 235  
     protected Vector filesDeselected;
 236  
 
 237  
     /** The directories which matched at least one include and no excludes
 238  
      *  but which a selector discarded.
 239  
      */
 240  
     protected Vector dirsDeselected;
 241  
 
 242  
     /** Whether or not our results were built by a slow scan. */
 243  0
     protected boolean haveSlowResults = false;
 244  
 
 245  
     /**
 246  
      * Whether or not the file system should be treated as a case sensitive
 247  
      * one.
 248  
      */
 249  0
     protected boolean isCaseSensitive = true;
 250  
 
 251  
     /**
 252  
      * Whether or not symbolic links should be followed.
 253  
      *
 254  
      * @since Ant 1.5
 255  
      */
 256  0
     private boolean followSymlinks = true;
 257  
 
 258  
     /** Whether or not everything tested so far has been included. */
 259  0
     protected boolean everythingIncluded = true;
 260  
 
 261  
     /**
 262  
      * Sole constructor.
 263  
      */
 264  
     public DirectoryScanner()
 265  0
     {
 266  0
     }
 267  
 
 268  
     /**
 269  
      * Tests whether or not a given path matches the start of a given
 270  
      * pattern up to the first "**".
 271  
      * <p>
 272  
      * This is not a general purpose test and should only be used if you
 273  
      * can live with false positives. For example, <code>pattern=**\a</code>
 274  
      * and <code>str=b</code> will yield <code>true</code>.
 275  
      *
 276  
      * @param pattern The pattern to match against. Must not be
 277  
      *                <code>null</code>.
 278  
      * @param str     The path to match, as a String. Must not be
 279  
      *                <code>null</code>.
 280  
      *
 281  
      * @return whether or not a given path matches the start of a given
 282  
      * pattern up to the first "**".
 283  
      */
 284  
     protected static boolean matchPatternStart( String pattern, String str )
 285  
     {
 286  0
         return SelectorUtils.matchPatternStart( pattern, str );
 287  
     }
 288  
 
 289  
     /**
 290  
      * Tests whether or not a given path matches the start of a given
 291  
      * pattern up to the first "**".
 292  
      * <p>
 293  
      * This is not a general purpose test and should only be used if you
 294  
      * can live with false positives. For example, <code>pattern=**\a</code>
 295  
      * and <code>str=b</code> will yield <code>true</code>.
 296  
      *
 297  
      * @param pattern The pattern to match against. Must not be
 298  
      *                <code>null</code>.
 299  
      * @param str     The path to match, as a String. Must not be
 300  
      *                <code>null</code>.
 301  
      * @param isCaseSensitive Whether or not matching should be performed
 302  
      *                        case sensitively.
 303  
      *
 304  
      * @return whether or not a given path matches the start of a given
 305  
      * pattern up to the first "**".
 306  
      */
 307  
     protected static boolean matchPatternStart( String pattern, String str,
 308  
                                                 boolean isCaseSensitive )
 309  
     {
 310  0
         return SelectorUtils.matchPatternStart( pattern, str, isCaseSensitive );
 311  
     }
 312  
 
 313  
     /**
 314  
      * Tests whether or not a given path matches a given pattern.
 315  
      *
 316  
      * @param pattern The pattern to match against. Must not be
 317  
      *                <code>null</code>.
 318  
      * @param str     The path to match, as a String. Must not be
 319  
      *                <code>null</code>.
 320  
      *
 321  
      * @return <code>true</code> if the pattern matches against the string,
 322  
      *         or <code>false</code> otherwise.
 323  
      */
 324  
     protected static boolean matchPath( String pattern, String str )
 325  
     {
 326  0
         return SelectorUtils.matchPath( pattern, str );
 327  
     }
 328  
 
 329  
     /**
 330  
      * Tests whether or not a given path matches a given pattern.
 331  
      *
 332  
      * @param pattern The pattern to match against. Must not be
 333  
      *                <code>null</code>.
 334  
      * @param str     The path to match, as a String. Must not be
 335  
      *                <code>null</code>.
 336  
      * @param isCaseSensitive Whether or not matching should be performed
 337  
      *                        case sensitively.
 338  
      *
 339  
      * @return <code>true</code> if the pattern matches against the string,
 340  
      *         or <code>false</code> otherwise.
 341  
      */
 342  
     protected static boolean matchPath( String pattern, String str,
 343  
                                         boolean isCaseSensitive )
 344  
     {
 345  0
         return SelectorUtils.matchPath( pattern, str, isCaseSensitive );
 346  
     }
 347  
 
 348  
     /**
 349  
      * Tests whether or not a string matches against a pattern.
 350  
      * The pattern may contain two special characters:<br>
 351  
      * '*' means zero or more characters<br>
 352  
      * '?' means one and only one character
 353  
      *
 354  
      * @param pattern The pattern to match against.
 355  
      *                Must not be <code>null</code>.
 356  
      * @param str     The string which must be matched against the pattern.
 357  
      *                Must not be <code>null</code>.
 358  
      *
 359  
      * @return <code>true</code> if the string matches against the pattern,
 360  
      *         or <code>false</code> otherwise.
 361  
      */
 362  
     public static boolean match( String pattern, String str )
 363  
     {
 364  0
         return SelectorUtils.match( pattern, str );
 365  
     }
 366  
 
 367  
     /**
 368  
      * Tests whether or not a string matches against a pattern.
 369  
      * The pattern may contain two special characters:<br>
 370  
      * '*' means zero or more characters<br>
 371  
      * '?' means one and only one character
 372  
      *
 373  
      * @param pattern The pattern to match against.
 374  
      *                Must not be <code>null</code>.
 375  
      * @param str     The string which must be matched against the pattern.
 376  
      *                Must not be <code>null</code>.
 377  
      * @param isCaseSensitive Whether or not matching should be performed
 378  
      *                        case sensitively.
 379  
      *
 380  
      *
 381  
      * @return <code>true</code> if the string matches against the pattern,
 382  
      *         or <code>false</code> otherwise.
 383  
      */
 384  
     protected static boolean match( String pattern, String str,
 385  
                                     boolean isCaseSensitive )
 386  
     {
 387  0
         return SelectorUtils.match( pattern, str, isCaseSensitive );
 388  
     }
 389  
 
 390  
     /**
 391  
      * Sets the base directory to be scanned. This is the directory which is
 392  
      * scanned recursively. All '/' and '\' characters are replaced by
 393  
      * <code>File.separatorChar</code>, so the separator used need not match
 394  
      * <code>File.separatorChar</code>.
 395  
      *
 396  
      * @param basedir The base directory to scan.
 397  
      *                Must not be <code>null</code>.
 398  
      */
 399  
     public void setBasedir( String basedir )
 400  
     {
 401  0
         setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace(
 402  
             '\\', File.separatorChar ) ) );
 403  0
     }
 404  
 
 405  
     /**
 406  
      * Sets the base directory to be scanned. This is the directory which is
 407  
      * scanned recursively.
 408  
      *
 409  
      * @param basedir The base directory for scanning.
 410  
      *                Should not be <code>null</code>.
 411  
      */
 412  
     public void setBasedir( File basedir )
 413  
     {
 414  0
         this.basedir = basedir;
 415  0
     }
 416  
 
 417  
     /**
 418  
      * Returns the base directory to be scanned.
 419  
      * This is the directory which is scanned recursively.
 420  
      *
 421  
      * @return the base directory to be scanned
 422  
      */
 423  
     public File getBasedir()
 424  
     {
 425  0
         return basedir;
 426  
     }
 427  
 
 428  
     /**
 429  
      * Sets whether or not the file system should be regarded as case sensitive.
 430  
      *
 431  
      * @param isCaseSensitive whether or not the file system should be
 432  
      *                        regarded as a case sensitive one
 433  
      */
 434  
     public void setCaseSensitive( boolean isCaseSensitive )
 435  
     {
 436  0
         this.isCaseSensitive = isCaseSensitive;
 437  0
     }
 438  
 
 439  
     /**
 440  
      * Sets whether or not symbolic links should be followed.
 441  
      *
 442  
      * @param followSymlinks whether or not symbolic links should be followed
 443  
      */
 444  
     public void setFollowSymlinks( boolean followSymlinks )
 445  
     {
 446  0
         this.followSymlinks = followSymlinks;
 447  0
     }
 448  
 
 449  
     /**
 450  
      * Sets the list of include patterns to use. All '/' and '\' characters
 451  
      * are replaced by <code>File.separatorChar</code>, so the separator used
 452  
      * need not match <code>File.separatorChar</code>.
 453  
      * <p>
 454  
      * When a pattern ends with a '/' or '\', "**" is appended.
 455  
      *
 456  
      * @param includes A list of include patterns.
 457  
      *                 May be <code>null</code>, indicating that all files
 458  
      *                 should be included. If a non-<code>null</code>
 459  
      *                 list is given, all elements must be
 460  
      * non-<code>null</code>.
 461  
      */
 462  
     public void setIncludes( String[] includes )
 463  
     {
 464  0
         if ( includes == null )
 465  
         {
 466  0
             this.includes = null;
 467  
         }
 468  
         else
 469  
         {
 470  0
             this.includes = new String[includes.length];
 471  0
             for ( int i = 0; i < includes.length; i++ )
 472  
             {
 473  
                 String pattern;
 474  0
                 pattern = includes[i].trim().replace( '/', File.separatorChar ).replace(
 475  
                     '\\', File.separatorChar );
 476  0
                 if ( pattern.endsWith( File.separator ) )
 477  
                 {
 478  0
                     pattern += "**";
 479  
                 }
 480  0
                 this.includes[i] = pattern;
 481  
             }
 482  
         }
 483  0
     }
 484  
 
 485  
 
 486  
     /**
 487  
      * Sets the list of exclude patterns to use. All '/' and '\' characters
 488  
      * are replaced by <code>File.separatorChar</code>, so the separator used
 489  
      * need not match <code>File.separatorChar</code>.
 490  
      * <p>
 491  
      * When a pattern ends with a '/' or '\', "**" is appended.
 492  
      *
 493  
      * @param excludes A list of exclude patterns.
 494  
      *                 May be <code>null</code>, indicating that no files
 495  
      *                 should be excluded. If a non-<code>null</code> list is
 496  
      *                 given, all elements must be non-<code>null</code>.
 497  
      */
 498  
     public void setExcludes( String[] excludes )
 499  
     {
 500  0
         if ( excludes == null )
 501  
         {
 502  0
             this.excludes = null;
 503  
         }
 504  
         else
 505  
         {
 506  0
             this.excludes = new String[excludes.length];
 507  0
             for ( int i = 0; i < excludes.length; i++ )
 508  
             {
 509  
                 String pattern;
 510  0
                 pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace(
 511  
                     '\\', File.separatorChar );
 512  0
                 if ( pattern.endsWith( File.separator ) )
 513  
                 {
 514  0
                     pattern += "**";
 515  
                 }
 516  0
                 this.excludes[i] = pattern;
 517  
             }
 518  
         }
 519  0
     }
 520  
 
 521  
     /**
 522  
      * Returns whether or not the scanner has included all the files or
 523  
      * directories it has come across so far.
 524  
      *
 525  
      * @return <code>true</code> if all files and directories which have
 526  
      *         been found so far have been included.
 527  
      */
 528  
     public boolean isEverythingIncluded()
 529  
     {
 530  0
         return everythingIncluded;
 531  
     }
 532  
 
 533  
     /**
 534  
      * Scans the base directory for files which match at least one include
 535  
      * pattern and don't match any exclude patterns. If there are selectors
 536  
      * then the files must pass muster there, as well.
 537  
      *
 538  
      * @exception IllegalStateException if the base directory was set
 539  
      *            incorrectly (i.e. if it is <code>null</code>, doesn't exist,
 540  
      *            or isn't a directory).
 541  
      */
 542  
     public void scan() throws IllegalStateException
 543  
     {
 544  0
         if ( basedir == null )
 545  
         {
 546  0
             throw new IllegalStateException( "No basedir set" );
 547  
         }
 548  0
         if ( !basedir.exists() )
 549  
         {
 550  0
             throw new IllegalStateException( "basedir " + basedir
 551  
                                              + " does not exist" );
 552  
         }
 553  0
         if ( !basedir.isDirectory() )
 554  
         {
 555  0
             throw new IllegalStateException( "basedir " + basedir
 556  
                                              + " is not a directory" );
 557  
         }
 558  
 
 559  0
         if ( includes == null )
 560  
         {
 561  
             // No includes supplied, so set it to 'matches all'
 562  0
             includes = new String[1];
 563  0
             includes[0] = "**";
 564  
         }
 565  0
         if ( excludes == null )
 566  
         {
 567  0
             excludes = new String[0];
 568  
         }
 569  
 
 570  0
         filesIncluded = new Vector();
 571  0
         filesNotIncluded = new Vector();
 572  0
         filesExcluded = new Vector();
 573  0
         filesDeselected = new Vector();
 574  0
         dirsIncluded = new Vector();
 575  0
         dirsNotIncluded = new Vector();
 576  0
         dirsExcluded = new Vector();
 577  0
         dirsDeselected = new Vector();
 578  
 
 579  0
         if ( isIncluded( "" ) )
 580  
         {
 581  0
             if ( !isExcluded( "" ) )
 582  
             {
 583  0
                 if ( isSelected( "", basedir ) )
 584  
                 {
 585  0
                     dirsIncluded.addElement( "" );
 586  
                 }
 587  
                 else
 588  
                 {
 589  0
                     dirsDeselected.addElement( "" );
 590  
                 }
 591  
             }
 592  
             else
 593  
             {
 594  0
                 dirsExcluded.addElement( "" );
 595  
             }
 596  
         }
 597  
         else
 598  
         {
 599  0
             dirsNotIncluded.addElement( "" );
 600  
         }
 601  0
         scandir( basedir, "", true );
 602  0
     }
 603  
 
 604  
     /**
 605  
      * Top level invocation for a slow scan. A slow scan builds up a full
 606  
      * list of excluded/included files/directories, whereas a fast scan
 607  
      * will only have full results for included files, as it ignores
 608  
      * directories which can't possibly hold any included files/directories.
 609  
      * <p>
 610  
      * Returns immediately if a slow scan has already been completed.
 611  
      */
 612  
     protected void slowScan()
 613  
     {
 614  0
         if ( haveSlowResults )
 615  
         {
 616  0
             return;
 617  
         }
 618  
 
 619  0
         String[] excl = new String[dirsExcluded.size()];
 620  0
         dirsExcluded.copyInto( excl );
 621  
 
 622  0
         String[] notIncl = new String[dirsNotIncluded.size()];
 623  0
         dirsNotIncluded.copyInto( notIncl );
 624  
 
 625  0
         for ( int i = 0; i < excl.length; i++ )
 626  
         {
 627  0
             if ( !couldHoldIncluded( excl[i] ) )
 628  
             {
 629  0
                 scandir( new File( basedir, excl[i] ),
 630  
                          excl[i] + File.separator, false );
 631  
             }
 632  
         }
 633  
 
 634  0
         for ( int i = 0; i < notIncl.length; i++ )
 635  
         {
 636  0
             if ( !couldHoldIncluded( notIncl[i] ) )
 637  
             {
 638  0
                 scandir( new File( basedir, notIncl[i] ),
 639  
                          notIncl[i] + File.separator, false );
 640  
             }
 641  
         }
 642  
 
 643  0
         haveSlowResults = true;
 644  0
     }
 645  
 
 646  
     /**
 647  
      * Scans the given directory for files and directories. Found files and
 648  
      * directories are placed in their respective collections, based on the
 649  
      * matching of includes, excludes, and the selectors.  When a directory
 650  
      * is found, it is scanned recursively.
 651  
      *
 652  
      * @param dir   The directory to scan. Must not be <code>null</code>.
 653  
      * @param vpath The path relative to the base directory (needed to
 654  
      *              prevent problems with an absolute path when using
 655  
      *              dir). Must not be <code>null</code>.
 656  
      * @param fast  Whether or not this call is part of a fast scan.
 657  
      * @throws IOException 
 658  
      *
 659  
      * @see #filesIncluded
 660  
      * @see #filesNotIncluded
 661  
      * @see #filesExcluded
 662  
      * @see #dirsIncluded
 663  
      * @see #dirsNotIncluded
 664  
      * @see #dirsExcluded
 665  
      * @see #slowScan
 666  
      */
 667  
     protected void scandir( File dir, String vpath, boolean fast )
 668  
     {
 669  0
         String[] newfiles = dir.list();
 670  
 
 671  0
         if ( newfiles == null )
 672  
         {
 673  
             /*
 674  
              * two reasons are mentioned in the API docs for File.list
 675  
              * (1) dir is not a directory. This is impossible as
 676  
              *     we wouldn't get here in this case.
 677  
              * (2) an IO error occurred (why doesn't it throw an exception
 678  
              *     then???)
 679  
              */
 680  
             
 681  
 
 682  
             /*
 683  
              * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... 
 684  
              * this is affecting the assembly plugin, fwiw. I will initialize the newfiles array as 
 685  
              * zero-length for now.
 686  
              * 
 687  
              * NOTE: I can't find the problematic code, as it appears to come from a native method 
 688  
              * in UnixFileSystem...
 689  
              */
 690  0
             newfiles = new String[0];
 691  
             
 692  
             // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() );
 693  
         }
 694  
 
 695  0
         if ( !followSymlinks )
 696  
         {
 697  0
             Vector noLinks = new Vector();
 698  0
             for ( int i = 0; i < newfiles.length; i++ )
 699  
             {
 700  
                 try
 701  
                 {
 702  0
                     if ( isSymbolicLink( dir, newfiles[i] ) )
 703  
                     {
 704  0
                         String name = vpath + newfiles[i];
 705  0
                         File file = new File( dir, newfiles[i] );
 706  0
                         if ( file.isDirectory() )
 707  
                         {
 708  0
                             dirsExcluded.addElement( name );
 709  
                         }
 710  
                         else
 711  
                         {
 712  0
                             filesExcluded.addElement( name );
 713  
                         }
 714  0
                     }
 715  
                     else
 716  
                     {
 717  0
                         noLinks.addElement( newfiles[i] );
 718  
                     }
 719  
                 }
 720  0
                 catch ( IOException ioe )
 721  
                 {
 722  0
                     String msg = "IOException caught while checking "
 723  
                         + "for links, couldn't get cannonical path!";
 724  
                     // will be caught and redirected to Ant's logging system
 725  0
                     System.err.println( msg );
 726  0
                     noLinks.addElement( newfiles[i] );
 727  0
                 }
 728  
             }
 729  0
             newfiles = new String[noLinks.size()];
 730  0
             noLinks.copyInto( newfiles );
 731  
         }
 732  
 
 733  0
         for ( int i = 0; i < newfiles.length; i++ )
 734  
         {
 735  0
             String name = vpath + newfiles[i];
 736  0
             File file = new File( dir, newfiles[i] );
 737  0
             if ( file.isDirectory() )
 738  
             {
 739  0
                 if ( isIncluded( name ) )
 740  
                 {
 741  0
                     if ( !isExcluded( name ) )
 742  
                     {
 743  0
                         if ( isSelected( name, file ) )
 744  
                         {
 745  0
                             dirsIncluded.addElement( name );
 746  0
                             if ( fast )
 747  
                             {
 748  0
                                 scandir( file, name + File.separator, fast );
 749  
                             }
 750  
                         }
 751  
                         else
 752  
                         {
 753  0
                             everythingIncluded = false;
 754  0
                             dirsDeselected.addElement( name );
 755  0
                             if ( fast && couldHoldIncluded( name ) )
 756  
                             {
 757  0
                                 scandir( file, name + File.separator, fast );
 758  
                             }
 759  
                         }
 760  
 
 761  
                     }
 762  
                     else
 763  
                     {
 764  0
                         everythingIncluded = false;
 765  0
                         dirsExcluded.addElement( name );
 766  0
                         if ( fast && couldHoldIncluded( name ) )
 767  
                         {
 768  0
                             scandir( file, name + File.separator, fast );
 769  
                         }
 770  
                     }
 771  
                 }
 772  
                 else
 773  
                 {
 774  0
                     everythingIncluded = false;
 775  0
                     dirsNotIncluded.addElement( name );
 776  0
                     if ( fast && couldHoldIncluded( name ) )
 777  
                     {
 778  0
                         scandir( file, name + File.separator, fast );
 779  
                     }
 780  
                 }
 781  0
                 if ( !fast )
 782  
                 {
 783  0
                     scandir( file, name + File.separator, fast );
 784  
                 }
 785  
             }
 786  0
             else if ( file.isFile() )
 787  
             {
 788  0
                 if ( isIncluded( name ) )
 789  
                 {
 790  0
                     if ( !isExcluded( name ) )
 791  
                     {
 792  0
                         if ( isSelected( name, file ) )
 793  
                         {
 794  0
                             filesIncluded.addElement( name );
 795  
                         }
 796  
                         else
 797  
                         {
 798  0
                             everythingIncluded = false;
 799  0
                             filesDeselected.addElement( name );
 800  
                         }
 801  
                     }
 802  
                     else
 803  
                     {
 804  0
                         everythingIncluded = false;
 805  0
                         filesExcluded.addElement( name );
 806  
                     }
 807  
                 }
 808  
                 else
 809  
                 {
 810  0
                     everythingIncluded = false;
 811  0
                     filesNotIncluded.addElement( name );
 812  
                 }
 813  
             }
 814  
         }
 815  0
     }
 816  
 
 817  
     /**
 818  
      * Tests whether or not a name matches against at least one include
 819  
      * pattern.
 820  
      *
 821  
      * @param name The name to match. Must not be <code>null</code>.
 822  
      * @return <code>true</code> when the name matches against at least one
 823  
      *         include pattern, or <code>false</code> otherwise.
 824  
      */
 825  
     protected boolean isIncluded( String name )
 826  
     {
 827  0
         for ( int i = 0; i < includes.length; i++ )
 828  
         {
 829  0
             if ( matchPath( includes[i], name, isCaseSensitive ) )
 830  
             {
 831  0
                 return true;
 832  
             }
 833  
         }
 834  0
         return false;
 835  
     }
 836  
 
 837  
     /**
 838  
      * Tests whether or not a name matches the start of at least one include
 839  
      * pattern.
 840  
      *
 841  
      * @param name The name to match. Must not be <code>null</code>.
 842  
      * @return <code>true</code> when the name matches against the start of at
 843  
      *         least one include pattern, or <code>false</code> otherwise.
 844  
      */
 845  
     protected boolean couldHoldIncluded( String name )
 846  
     {
 847  0
         for ( int i = 0; i < includes.length; i++ )
 848  
         {
 849  0
             if ( matchPatternStart( includes[i], name, isCaseSensitive ) )
 850  
             {
 851  0
                 return true;
 852  
             }
 853  
         }
 854  0
         return false;
 855  
     }
 856  
 
 857  
     /**
 858  
      * Tests whether or not a name matches against at least one exclude
 859  
      * pattern.
 860  
      *
 861  
      * @param name The name to match. Must not be <code>null</code>.
 862  
      * @return <code>true</code> when the name matches against at least one
 863  
      *         exclude pattern, or <code>false</code> otherwise.
 864  
      */
 865  
     protected boolean isExcluded( String name )
 866  
     {
 867  0
         for ( int i = 0; i < excludes.length; i++ )
 868  
         {
 869  0
             if ( matchPath( excludes[i], name, isCaseSensitive ) )
 870  
             {
 871  0
                 return true;
 872  
             }
 873  
         }
 874  0
         return false;
 875  
     }
 876  
 
 877  
     /**
 878  
      * Tests whether a name should be selected.
 879  
      *
 880  
      * @param name the filename to check for selecting
 881  
      * @param file the java.io.File object for this filename
 882  
      * @return <code>false</code> when the selectors says that the file
 883  
      *         should not be selected, <code>true</code> otherwise.
 884  
      */
 885  
     protected boolean isSelected( String name, File file )
 886  
     {
 887  0
         return true;
 888  
     }
 889  
 
 890  
     /**
 891  
      * Returns the names of the files which matched at least one of the
 892  
      * include patterns and none of the exclude patterns.
 893  
      * The names are relative to the base directory.
 894  
      *
 895  
      * @return the names of the files which matched at least one of the
 896  
      *         include patterns and none of the exclude patterns.
 897  
      */
 898  
     public String[] getIncludedFiles()
 899  
     {
 900  0
         String[] files = new String[filesIncluded.size()];
 901  0
         filesIncluded.copyInto( files );
 902  0
         return files;
 903  
     }
 904  
 
 905  
     /**
 906  
      * Returns the names of the files which matched none of the include
 907  
      * patterns. The names are relative to the base directory. This involves
 908  
      * performing a slow scan if one has not already been completed.
 909  
      *
 910  
      * @return the names of the files which matched none of the include
 911  
      *         patterns.
 912  
      *
 913  
      * @see #slowScan
 914  
      */
 915  
     public String[] getNotIncludedFiles()
 916  
     {
 917  0
         slowScan();
 918  0
         String[] files = new String[filesNotIncluded.size()];
 919  0
         filesNotIncluded.copyInto( files );
 920  0
         return files;
 921  
     }
 922  
 
 923  
     /**
 924  
      * Returns the names of the files which matched at least one of the
 925  
      * include patterns and at least one of the exclude patterns.
 926  
      * The names are relative to the base directory. This involves
 927  
      * performing a slow scan if one has not already been completed.
 928  
      *
 929  
      * @return the names of the files which matched at least one of the
 930  
      *         include patterns and at at least one of the exclude patterns.
 931  
      *
 932  
      * @see #slowScan
 933  
      */
 934  
     public String[] getExcludedFiles()
 935  
     {
 936  0
         slowScan();
 937  0
         String[] files = new String[filesExcluded.size()];
 938  0
         filesExcluded.copyInto( files );
 939  0
         return files;
 940  
     }
 941  
 
 942  
     /**
 943  
      * <p>Returns the names of the files which were selected out and
 944  
      * therefore not ultimately included.</p>
 945  
      *
 946  
      * <p>The names are relative to the base directory. This involves
 947  
      * performing a slow scan if one has not already been completed.</p>
 948  
      *
 949  
      * @return the names of the files which were deselected.
 950  
      *
 951  
      * @see #slowScan
 952  
      */
 953  
     public String[] getDeselectedFiles()
 954  
     {
 955  0
         slowScan();
 956  0
         String[] files = new String[filesDeselected.size()];
 957  0
         filesDeselected.copyInto( files );
 958  0
         return files;
 959  
     }
 960  
 
 961  
     /**
 962  
      * Returns the names of the directories which matched at least one of the
 963  
      * include patterns and none of the exclude patterns.
 964  
      * The names are relative to the base directory.
 965  
      *
 966  
      * @return the names of the directories which matched at least one of the
 967  
      * include patterns and none of the exclude patterns.
 968  
      */
 969  
     public String[] getIncludedDirectories()
 970  
     {
 971  0
         String[] directories = new String[dirsIncluded.size()];
 972  0
         dirsIncluded.copyInto( directories );
 973  0
         return directories;
 974  
     }
 975  
 
 976  
     /**
 977  
      * Returns the names of the directories which matched none of the include
 978  
      * patterns. The names are relative to the base directory. This involves
 979  
      * performing a slow scan if one has not already been completed.
 980  
      *
 981  
      * @return the names of the directories which matched none of the include
 982  
      * patterns.
 983  
      *
 984  
      * @see #slowScan
 985  
      */
 986  
     public String[] getNotIncludedDirectories()
 987  
     {
 988  0
         slowScan();
 989  0
         String[] directories = new String[dirsNotIncluded.size()];
 990  0
         dirsNotIncluded.copyInto( directories );
 991  0
         return directories;
 992  
     }
 993  
 
 994  
     /**
 995  
      * Returns the names of the directories which matched at least one of the
 996  
      * include patterns and at least one of the exclude patterns.
 997  
      * The names are relative to the base directory. This involves
 998  
      * performing a slow scan if one has not already been completed.
 999  
      *
 1000  
      * @return the names of the directories which matched at least one of the
 1001  
      * include patterns and at least one of the exclude patterns.
 1002  
      *
 1003  
      * @see #slowScan
 1004  
      */
 1005  
     public String[] getExcludedDirectories()
 1006  
     {
 1007  0
         slowScan();
 1008  0
         String[] directories = new String[dirsExcluded.size()];
 1009  0
         dirsExcluded.copyInto( directories );
 1010  0
         return directories;
 1011  
     }
 1012  
 
 1013  
     /**
 1014  
      * <p>Returns the names of the directories which were selected out and
 1015  
      * therefore not ultimately included.</p>
 1016  
      *
 1017  
      * <p>The names are relative to the base directory. This involves
 1018  
      * performing a slow scan if one has not already been completed.</p>
 1019  
      *
 1020  
      * @return the names of the directories which were deselected.
 1021  
      *
 1022  
      * @see #slowScan
 1023  
      */
 1024  
     public String[] getDeselectedDirectories()
 1025  
     {
 1026  0
         slowScan();
 1027  0
         String[] directories = new String[dirsDeselected.size()];
 1028  0
         dirsDeselected.copyInto( directories );
 1029  0
         return directories;
 1030  
     }
 1031  
 
 1032  
     /**
 1033  
      * Adds default exclusions to the current exclusions set.
 1034  
      */
 1035  
     public void addDefaultExcludes()
 1036  
     {
 1037  0
         int excludesLength = excludes == null ? 0 : excludes.length;
 1038  
         String[] newExcludes;
 1039  0
         newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
 1040  0
         if ( excludesLength > 0 )
 1041  
         {
 1042  0
             System.arraycopy( excludes, 0, newExcludes, 0, excludesLength );
 1043  
         }
 1044  0
         for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ )
 1045  
         {
 1046  0
             newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace( '/',
 1047  
                                                                           File.separatorChar ).replace( '\\', File.separatorChar );
 1048  
         }
 1049  0
         excludes = newExcludes;
 1050  0
     }
 1051  
 
 1052  
     /**
 1053  
      * Checks whether a given file is a symbolic link.
 1054  
      *
 1055  
      * <p>It doesn't really test for symbolic links but whether the
 1056  
      * canonical and absolute paths of the file are identical - this
 1057  
      * may lead to false positives on some platforms.</p>
 1058  
      *
 1059  
      * @param parent the parent directory of the file to test
 1060  
      * @param name the name of the file to test.
 1061  
      *
 1062  
      * @since Ant 1.5
 1063  
      */
 1064  
     public boolean isSymbolicLink( File parent, String name )
 1065  
         throws IOException
 1066  
     {
 1067  0
         File resolvedParent = new File( parent.getCanonicalPath() );
 1068  0
         File toTest = new File( resolvedParent, name );
 1069  0
         return !toTest.getAbsolutePath().equals( toTest.getCanonicalPath() );
 1070  
     }
 1071  
 
 1072  
 }