You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by bo...@apache.org on 2004/12/03 09:04:44 UTC

cvs commit: ant/src/main/org/apache/tools/ant DirectoryScanner.java

bodewig     2004/12/03 00:04:43

  Modified:    src/main/org/apache/tools/ant DirectoryScanner.java
  Log:
  Try to speed up DirectoryScanner by using hash lookups instead of
  linear searches and pattern matching on non-wildcard patterns.
  
  Suggested by: Dominique Devienne
  
  Revision  Changes    Path
  1.75      +123 -8    ant/src/main/org/apache/tools/ant/DirectoryScanner.java
  
  Index: DirectoryScanner.java
  ===================================================================
  RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/DirectoryScanner.java,v
  retrieving revision 1.74
  retrieving revision 1.75
  diff -u -r1.74 -r1.75
  --- DirectoryScanner.java	22 Nov 2004 09:23:26 -0000	1.74
  +++ DirectoryScanner.java	3 Dec 2004 08:04:43 -0000	1.75
  @@ -19,6 +19,7 @@
   
   import java.io.File;
   import java.io.IOException;
  +import java.util.ArrayList;
   import java.util.Arrays;
   import java.util.Enumeration;
   import java.util.HashMap;
  @@ -247,6 +248,66 @@
       protected boolean everythingIncluded = true;
   
       /**
  +     * Set of all include patterns that are full file names and don't
  +     * contain any wildcards.
  +     *
  +     * <p>If this instance is not case sensitive, the file names get
  +     * turned to lower case.</p>
  +     *
  +     * <p>Gets lazily initialized on the first invocation of
  +     * isIncluded or isExcluded and cleared at the end of the scan
  +     * method (cleared in clearCaches, actually).</p>
  +     *
  +     * @since Ant 1.7
  +     */
  +    private Set includeNonPatterns = new HashSet();
  +
  +    /**
  +     * Set of all include patterns that are full file names and don't
  +     * contain any wildcards.
  +     *
  +     * <p>If this instance is not case sensitive, the file names get
  +     * turned to lower case.</p>
  +     *
  +     * <p>Gets lazily initialized on the first invocation of
  +     * isIncluded or isExcluded and cleared at the end of the scan
  +     * method (cleared in clearCaches, actually).</p>
  +     *
  +     * @since Ant 1.7
  +     */
  +    private Set excludeNonPatterns = new HashSet();
  +
  +    /**
  +     * Array of all include patterns that contain wildcards.
  +     *
  +     * <p>Gets lazily initialized on the first invocation of
  +     * isIncluded or isExcluded and cleared at the end of the scan
  +     * method (cleared in clearCaches, actually).</p>
  +     *
  +     * @since Ant 1.7
  +     */
  +    private String[] includePatterns;
  +
  +    /**
  +     * Array of all exclude patterns that contain wildcards.
  +     *
  +     * <p>Gets lazily initialized on the first invocation of
  +     * isIncluded or isExcluded and cleared at the end of the scan
  +     * method (cleared in clearCaches, actually).</p>
  +     *
  +     * @since Ant 1.7
  +     */
  +    private String[] excludePatterns;
  +
  +    /**
  +     * Have the non-pattern sets and pattern arrays for in- and
  +     * excludes been initialized?
  +     *
  +     * @since Ant 1.7
  +     */
  +    private boolean areNonPatternSetsReady = false;
  +
  +    /**
        * Sole constructor.
        */
       public DirectoryScanner() {
  @@ -699,7 +760,7 @@
                       }
                   }
   
  -                if ((myfile == null || !myfile.exists()) && !isCaseSensitive) {
  +                if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
                       File f = findFileCaseInsensitive(basedir, currentelement);
                       if (f.exists()) {
                           // adapt currentelement to the case we've
  @@ -732,10 +793,10 @@
                               scandir(myfile, currentelement, true);
                           }
                       } else {
  -                        if (isCaseSensitive
  +                        if (isCaseSensitive()
                               && originalpattern.equals(currentelement)) {
                               accountForIncludedFile(currentelement, myfile);
  -                        } else if (!isCaseSensitive
  +                        } else if (!isCaseSensitive()
                                      && originalpattern
                                      .equalsIgnoreCase(currentelement)) {
                               accountForIncludedFile(currentelement, myfile);
  @@ -950,8 +1011,21 @@
        *         include pattern, or <code>false</code> otherwise.
        */
       protected boolean isIncluded(String name) {
  -        for (int i = 0; i < includes.length; i++) {
  -            if (matchPath(includes[i], name, isCaseSensitive)) {
  +        if (!areNonPatternSetsReady) {
  +            includePatterns = fillNonPatternSet(includeNonPatterns, includes);
  +            excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
  +            areNonPatternSetsReady = true;
  +        }
  +
  +        if ((isCaseSensitive() && includeNonPatterns.contains(name))
  +            ||
  +            (!isCaseSensitive() 
  +             && includeNonPatterns.contains(name.toUpperCase()))) {
  +                return true;
  +        }
  +
  +        for (int i = 0; i < includePatterns.length; i++) {
  +            if (matchPath(includePatterns[i], name, isCaseSensitive())) {
                   return true;
               }
           }
  @@ -968,7 +1042,7 @@
        */
       protected boolean couldHoldIncluded(String name) {
           for (int i = 0; i < includes.length; i++) {
  -            if (matchPatternStart(includes[i], name, isCaseSensitive)) {
  +            if (matchPatternStart(includes[i], name, isCaseSensitive())) {
                   if (isMorePowerfulThanExcludes(name, includes[i])) {
                       return true;
                   }
  @@ -1011,8 +1085,21 @@
        *         exclude pattern, or <code>false</code> otherwise.
        */
       protected boolean isExcluded(String name) {
  -        for (int i = 0; i < excludes.length; i++) {
  -            if (matchPath(excludes[i], name, isCaseSensitive)) {
  +        if (!areNonPatternSetsReady) {
  +            includePatterns = fillNonPatternSet(includeNonPatterns, includes);
  +            excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
  +            areNonPatternSetsReady = true;
  +        }
  +        
  +        if ((isCaseSensitive() && excludeNonPatterns.contains(name))
  +            ||
  +            (!isCaseSensitive() 
  +             && excludeNonPatterns.contains(name.toUpperCase()))) {
  +                return true;
  +        }
  +
  +        for (int i = 0; i < excludePatterns.length; i++) {
  +            if (matchPath(excludePatterns[i], name, isCaseSensitive())) {
                   return true;
               }
           }
  @@ -1414,5 +1501,33 @@
       private void clearCaches() {
           fileListMap.clear();
           scannedDirs.clear();
  +        includeNonPatterns.clear();
  +        excludeNonPatterns.clear();
  +        includePatterns = excludePatterns = null;
  +        areNonPatternSetsReady = false;
       }
  +
  +    /**
  +     * Adds all patterns that are no real patterns (doesn't contain
  +     * wildcards) to the set and returns the real patterns.
  +     *
  +     * @since Ant 1.7
  +     */
  +    private String[] fillNonPatternSet(Set set, String[] patterns) {
  +        ArrayList al = new ArrayList(patterns.length);
  +        for (int i = 0; i < patterns.length; i++) {
  +            if (!SelectorUtils.hasWildcards(patterns[i])) {
  +                if (isCaseSensitive()) {
  +                    set.add(patterns[i]);
  +                } else {
  +                    set.add(patterns[i].toUpperCase());
  +                }
  +            } else {
  +                al.add(patterns[i]);
  +            }
  +        }
  +        return set.size() == 0 ? patterns 
  +            : (String[]) al.toArray(new String[al.size()]);
  +    }
  +
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


Re: cvs commit: ant/src/main/org/apache/tools/ant DirectoryScanner.java

Posted by Stefan Bodewig <bo...@apache.org>.
On Fri, 03 Dec 2004, Stefan Bodewig <bo...@apache.org> wrote:

> I'll watch the next Gump run closely, both for breaking builds as
> well as performance impact.

No obvious breaks.

The run took 179 minutes, the previous one 184, but I don't think it
is related to my change (but rather to CVS and SVN updates being
faster this time).

Stefan

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


Re: cvs commit: ant/src/main/org/apache/tools/ant DirectoryScanner.java

Posted by Stefan Bodewig <bo...@apache.org>.
On 3 Dec 2004, <bo...@apache.org> wrote:

>   Try to speed up DirectoryScanner by using hash lookups instead of
>   linear searches and pattern matching on non-wildcard patterns.

I threatened to do it.  I'll watch the next Gump run closely, both for
breaking builds as well as performance impact.  I don't think things
have gotten much worse in cases where all patterns contain wildcards,
but it really should improve things if no wildcards are there at all.

>   - if ((myfile == null || !myfile.exists()) && !isCaseSensitive) {
>   +                if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {

I also changed these in a few places since isCaseSensitive() is
non-final and protected.  If subclasses override this method, it
should have some effect IMHO.

>   +     * Set of all include patterns that are full file names and don't
>   +     * contain any wildcards.
>   +     *
>   +     * <p>Gets lazily initialized on the first invocation of
>   +     * isIncluded or isExcluded and cleared at the end of the scan
>   +     * method (cleared in clearCaches, actually).</p>

My first attempt initialized them in scan(), but that doesn't mix with
subclasses like ZipScanner that invoke isIncluded and isExcluded but
never invoke scan at all.

Stefan

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org