For our project, we want to explicitly specify which java files
are compiled, rather than trust ant's usual catchall
"src/**/*.java" approach.  So I made a manifest file listing all
the desired files (250 or so), and referred to it in:
        <javac includesfile=.../>

... only to discover that ant took agonizingly long to figure out
which files to recompile.  This turned out to be because of the
expensive matchPath() calls on all of those 250 non-patterns.

Taken together, the two attached patches solve this problem, by
doing a simple string-compare when the pattern is a simple
string, ie. when it contains no pattern metacharacters.

I've split this into two patches for readability.  Patch 1 is a
refactoring; it extracts the pattern-matching code out of
DirectoryScanner into a new Pattern class.  Patch 2 adds the
simple-string-compare optimization to Pattern.  There should be
no user-visible change (except speed, of course).

The savings are dramatic.  Here are some timings (taken with no
compilations needed, so that most of the work was ant overhead).
The "ant 1.3" test was a default build, ie. including the "jars"
and "dist-lite" targets.  The "our project" test includes a
<depend> task that takes several seconds.

                            Build
                        ant       our
Use                     1.3     project
---                     ---     -------
Vanilla ant 1.3         25"       71"
Patched ant 1.3         15"        8"
Savings                 40%       89%

Even on ant's build.xml itself, which doesn't use simple-filename
"patterns" nearly as heavily as our project, the optimization
saves 40%.

I've included the two patches (against the Release 1.3 sources),
and a new PatternTest.java with a bezillion test cases.

PROBLEMS: The code seems to run fine, but the JUnit tests have
two issues:

 1. The PatternTest class runs fine from the command line (except
    for a few failed tests; see below), but if I run it using
    "ant run-tests", *every* test case throws the following
    exception:

        Testcase: test000 took 0.134 sec
            Caused an ERROR
        try to access class org/apache/tools/ant/Pattern from class 
org/apache/tools/ant/PatternTest
        java.lang.IllegalAccessError: try to access class 
org/apache/tools/ant/Pattern from class org/apache/tools/ant/PatternTe

            at org.apache.tools.ant.PatternTest.tstPath(PatternTest.java:92)
            at org.apache.tools.ant.PatternTest.test000(PatternTest.java:114)
            at java.lang.reflect.Method.invoke(Native Method)
            at junit.framework.TestCase.runTest(TestCase.java:156)
            at junit.framework.TestCase.runBare(TestCase.java:130)
            at junit.framework.TestResult$1.protect(TestResult.java:106)
            at junit.framework.TestResult.runProtected(TestResult.java:124)
            at junit.framework.TestResult.run(TestResult.java:109)
            at junit.framework.TestCase.run(TestCase.java:121)
            at junit.framework.TestSuite.runTest(TestSuite.java:157)
            at junit.framework.TestSuite.run(TestSuite.java:152)
            at 
org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:209)
            at 
org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.executeInVM(JUnitTask.java:409)
            at 
org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.execute(JUnitTask.java:283)
            at 
org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.execute(JUnitTask.java:263)
            at org.apache.tools.ant.Target.execute(Target.java:153)
            at org.apache.tools.ant.Project.runTarget(Project.java:898)
            at org.apache.tools.ant.Project.executeTarget(Project.java:536)
            at org.apache.tools.ant.Project.executeTargets(Project.java:510)
            at org.apache.tools.ant.Main.runBuild(Main.java:421)
            at org.apache.tools.ant.Main.main(Main.java:149)

    I haven't a clue why this could be.  It may well be a simple
    error -- I've never used JUnit before.  (I saw a similar
    report in another context on the ant-dev archive, with a
    suggested fix of instantiating AntClassLoader objects with a
    systemFirst argument of "false" -- but <junit> already does
    that!)

    Help!


 2. Several tests fail.  The obvious approach of checking how
    they worked before my change is problematic because:
      - The way I wrote the test cases, I can't do this -- they
        depend on the Pattern class's having been factored out.
      - It wouldn't be helpful anyway.  Quite possibly, the bugs
        (if any) were already there in the preexisting code.

    I need some feedback from people who've been working with ant
    longer than I have (which wouldn't be hard; I've only been
    here a week).  My question is, what are the *expected*
    results from the tests in question?  Before I go "fixing" the
    code, I want to make sure it's broken...

    Here are the cases, and what I *think* the expected results
    should be.  The "method" column shows the method being tested
    (in ant 1.3, these are in DirectoryScanner, but my Patch 1
    moves them to a new Pattern class).

                                             Candidate       Expected
    test #   Method              Pattern     Filename        Result
    ======   =================   =========   ==========      ========
    #
    # Empty(ish)-filename tests
    #
    003      matchPath           "/*"        "/"             true
    004      matchPath           "*"         ""              true
    2000     matchPatternStart   "/aa/bb"    "/"             true
    2001     matchPatternStart   "aa/bb"     ""              true


    #
    # Filename ending in "/".  In all of these cases, the
    # trailing "/" has no effect; my expected "false" assumes it
    # should have one.  Should it?
    #
    1041     matchPath           "/aa/*/cc"  "/aa/bb/cc/"    false
    2041     matchPatternStart   "/aa/*/cc"  "/aa/bb/cc/"    false
    2043     matchPatternStart   "/aa/*/cc"  "/aa/"          false
    2063     matchPatternStart   "/aa/?/cc"  "/aa/b/cc/"     false

    #
    # Pattern ending in "/".  I think I recall reading that "/aa/" is
    # supposed to be equivalent to "/aa/**"; is this correct?
    #
    1160     matchPath           "/aa/"      "/aa/cc"        true
    2160     matchPatternStart   "/aa/"      "/aa/cc"        true

Thanks much!

--

|  | /\
|-_|/  >   Eric Siegerman, Toronto, Ont.        [EMAIL PROTECTED]
|  |  /
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea.
        - RFC 1925 (quoting an unnamed source)
diff -ru ant-orig/src/main/org/apache/tools/ant/DirectoryScanner.java 
ant/src/main/org/apache/tools/ant/DirectoryScanner.java
--- ant-orig/src/main/org/apache/tools/ant/DirectoryScanner.java        Thu Mar 
22 14:05:21 2001
+++ ant/src/main/org/apache/tools/ant/DirectoryScanner.java     Thu Mar 22 
13:54:41 2001
@@ -157,12 +157,12 @@
     /**
      * The patterns for the files that should be included.
      */
-    protected String[] includes;
+    protected Pattern[] includes;
 
     /**
      * The patterns for the files that should be excluded.
      */
-    protected String[] excludes;
+    protected Pattern[] excludes;
 
     /**
      * The files that where found and matched at least one includes, and 
matched
@@ -221,57 +221,8 @@
      * @param pattern the (non-null) pattern to match against
      * @param str     the (non-null) string (path) to match
      */
-    protected static boolean matchPatternStart(String pattern, String str) {
-        // When str starts with a File.separator, pattern has to start with a
-        // File.separator.
-        // When pattern starts with a File.separator, str has to start with a
-        // File.separator.
-        if (str.startsWith(File.separator) !=
-            pattern.startsWith(File.separator)) {
-            return false;
-        }
-
-        Vector patDirs = new Vector();
-        StringTokenizer st = new StringTokenizer(pattern,File.separator);
-        while (st.hasMoreTokens()) {
-            patDirs.addElement(st.nextToken());
-        }
-
-        Vector strDirs = new Vector();
-        st = new StringTokenizer(str,File.separator);
-        while (st.hasMoreTokens()) {
-            strDirs.addElement(st.nextToken());
-        }
-
-        int patIdxStart = 0;
-        int patIdxEnd   = patDirs.size()-1;
-        int strIdxStart = 0;
-        int strIdxEnd   = strDirs.size()-1;
-
-        // up to first '**'
-        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
-            String patDir = (String)patDirs.elementAt(patIdxStart);
-            if (patDir.equals("**")) {
-                break;
-            }
-            if (!match(patDir,(String)strDirs.elementAt(strIdxStart))) {
-                return false;
-            }
-            patIdxStart++;
-            strIdxStart++;
-        }
-
-        if (strIdxStart > strIdxEnd) {
-            // String is exhausted
-            return true;
-        } else if (patIdxStart > patIdxEnd) {
-            // String not exhausted, but pattern is. Failure.
-            return false;
-        } else {
-            // pattern now holds ** while string is not exhausted
-            // this will generate false positives but we can live with that.
-            return true;
-        }
+    protected static boolean matchPatternStart(Pattern pattern, String str) {
+       return pattern.matchPatternStart(str);
     }
 
     /**
@@ -283,268 +234,8 @@
      * @return <code>true</code> when the pattern matches against the string.
      *         <code>false</code> otherwise.
      */
-    protected static boolean matchPath(String pattern, String str) {
-        // When str starts with a File.separator, pattern has to start with a
-        // File.separator.
-        // When pattern starts with a File.separator, str has to start with a
-        // File.separator.
-        if (str.startsWith(File.separator) !=
-            pattern.startsWith(File.separator)) {
-            return false;
-        }
-
-        Vector patDirs = new Vector();
-        StringTokenizer st = new StringTokenizer(pattern,File.separator);
-        while (st.hasMoreTokens()) {
-            patDirs.addElement(st.nextToken());
-        }
-
-        Vector strDirs = new Vector();
-        st = new StringTokenizer(str,File.separator);
-        while (st.hasMoreTokens()) {
-            strDirs.addElement(st.nextToken());
-        }
-
-        int patIdxStart = 0;
-        int patIdxEnd   = patDirs.size()-1;
-        int strIdxStart = 0;
-        int strIdxEnd   = strDirs.size()-1;
-
-        // up to first '**'
-        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
-            String patDir = (String)patDirs.elementAt(patIdxStart);
-            if (patDir.equals("**")) {
-                break;
-            }
-            if (!match(patDir,(String)strDirs.elementAt(strIdxStart))) {
-                return false;
-            }
-            patIdxStart++;
-            strIdxStart++;
-        }
-        if (strIdxStart > strIdxEnd) {
-            // String is exhausted
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (!patDirs.elementAt(i).equals("**")) {
-                    return false;
-                }
-            }
-            return true;
-        } else {
-            if (patIdxStart > patIdxEnd) {
-                // String not exhausted, but pattern is. Failure.
-                return false;
-            }
-        }
-
-        // up to last '**'
-        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
-            String patDir = (String)patDirs.elementAt(patIdxEnd);
-            if (patDir.equals("**")) {
-                break;
-            }
-            if (!match(patDir,(String)strDirs.elementAt(strIdxEnd))) {
-                return false;
-            }
-            patIdxEnd--;
-            strIdxEnd--;
-        }
-        if (strIdxStart > strIdxEnd) {
-            // String is exhausted
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (!patDirs.elementAt(i).equals("**")) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
-            int patIdxTmp = -1;
-            for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
-                if (patDirs.elementAt(i).equals("**")) {
-                    patIdxTmp = i;
-                    break;
-                }
-            }
-            if (patIdxTmp == patIdxStart+1) {
-                // '**/**' situation, so skip one
-                patIdxStart++;
-                continue;
-            }
-            // Find the pattern between padIdxStart & padIdxTmp in str between
-            // strIdxStart & strIdxEnd
-            int patLength = (patIdxTmp-patIdxStart-1);
-            int strLength = (strIdxEnd-strIdxStart+1);
-            int foundIdx  = -1;
-strLoop:
-            for (int i = 0; i <= strLength - patLength; i++) {
-                for (int j = 0; j < patLength; j++) {
-                    String subPat = (String)patDirs.elementAt(patIdxStart+j+1);
-                    String subStr = (String)strDirs.elementAt(strIdxStart+i+j);
-                    if (!match(subPat,subStr)) {
-                        continue strLoop;
-                    }
-                }
-
-                foundIdx = strIdxStart+i;
-                break;
-            }
-
-            if (foundIdx == -1) {
-                return false;
-            }
-
-            patIdxStart = patIdxTmp;
-            strIdxStart = foundIdx+patLength;
-        }
-
-        for (int i = patIdxStart; i <= patIdxEnd; i++) {
-            if (!patDirs.elementAt(i).equals("**")) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-
-
-    /**
-     * Matches a string against a pattern. The pattern contains two special
-     * characters:
-     * '*' which means zero or more characters,
-     * '?' which means one and only one character.
-     *
-     * @param pattern the (non-null) pattern to match against
-     * @param str     the (non-null) string that must be matched against the
-     *                pattern
-     *
-     * @return <code>true</code> when the string matches against the pattern,
-     *         <code>false</code> otherwise.
-     */
-    protected static boolean match(String pattern, String str) {
-        char[] patArr = pattern.toCharArray();
-        char[] strArr = str.toCharArray();
-        int patIdxStart = 0;
-        int patIdxEnd   = patArr.length-1;
-        int strIdxStart = 0;
-        int strIdxEnd   = strArr.length-1;
-        char ch;
-
-        boolean containsStar = false;
-        for (int i = 0; i < patArr.length; i++) {
-            if (patArr[i] == '*') {
-                containsStar = true;
-                break;
-            }
-        }
-
-        if (!containsStar) {
-            // No '*'s, so we make a shortcut
-            if (patIdxEnd != strIdxEnd) {
-                return false; // Pattern and string do not have the same size
-            }
-            for (int i = 0; i <= patIdxEnd; i++) {
-                ch = patArr[i];
-                if (ch != '?' && ch != strArr[i]) {
-                    return false; // Character mismatch
-                }
-            }
-            return true; // String matches against pattern
-        }
-
-        if (patIdxEnd == 0) {
-            return true; // Pattern contains only '*', which matches anything
-        }
-
-        // Process characters before first star
-        while((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
-            if (ch != '?' && ch != strArr[strIdxStart]) {
-                return false;
-            }
-            patIdxStart++;
-            strIdxStart++;
-        }
-        if (strIdxStart > strIdxEnd) {
-            // All characters in the string are used. Check if only '*'s are
-            // left in the pattern. If so, we succeeded. Otherwise failure.
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (patArr[i] != '*') {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        // Process characters after last star
-        while((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
-            if (ch != '?' && ch != strArr[strIdxEnd]) {
-                return false;
-            }
-            patIdxEnd--;
-            strIdxEnd--;
-        }
-        if (strIdxStart > strIdxEnd) {
-            // All characters in the string are used. Check if only '*'s are
-            // left in the pattern. If so, we succeeded. Otherwise failure.
-            for (int i = patIdxStart; i <= patIdxEnd; i++) {
-                if (patArr[i] != '*') {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        // process pattern between stars. padIdxStart and patIdxEnd point
-        // always to a '*'.
-        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
-            int patIdxTmp = -1;
-            for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
-                if (patArr[i] == '*') {
-                    patIdxTmp = i;
-                    break;
-                }
-            }
-            if (patIdxTmp == patIdxStart+1) {
-                // Two stars next to each other, skip the first one.
-                patIdxStart++;
-                continue;
-            }
-            // Find the pattern between padIdxStart & padIdxTmp in str between
-            // strIdxStart & strIdxEnd
-            int patLength = (patIdxTmp-patIdxStart-1);
-            int strLength = (strIdxEnd-strIdxStart+1);
-            int foundIdx  = -1;
-strLoop:
-            for (int i = 0; i <= strLength - patLength; i++) {
-                for (int j = 0; j < patLength; j++) {
-                    ch = patArr[patIdxStart+j+1];
-                    if (ch != '?' && ch != strArr[strIdxStart+i+j]) {
-                        continue strLoop;
-                    }
-                }
-
-                foundIdx = strIdxStart+i;
-                break;
-            }
-
-            if (foundIdx == -1) {
-                return false;
-            }
-
-            patIdxStart = patIdxTmp;
-            strIdxStart = foundIdx+patLength;
-        }
-
-        // All characters in the string are used. Check if only '*'s are left
-        // in the pattern. If so, we succeeded. Otherwise failure.
-        for (int i = patIdxStart; i <= patIdxEnd; i++) {
-            if (patArr[i] != '*') {
-                return false;
-            }
-        }
-        return true;
+    protected static boolean matchPath(Pattern pattern, String str) {
+       return pattern.matchPath(str);
     }
 
 
@@ -600,14 +291,14 @@
         if (includes == null) {
             this.includes = null;
         } else {
-            this.includes = new String[includes.length];
+            this.includes = new Pattern[includes.length];
             for (int i = 0; i < includes.length; i++) {
                 String pattern;
                 pattern = 
includes[i].replace('/',File.separatorChar).replace('\\',File.separatorChar);
                 if (pattern.endsWith(File.separator)) {
                     pattern += "**";
                 }
-                this.includes[i] = pattern;
+                this.includes[i] = new Pattern(pattern);
             }
         }
     }
@@ -627,14 +318,14 @@
         if (excludes == null) {
             this.excludes = null;
         } else {
-            this.excludes = new String[excludes.length];
+            this.excludes = new Pattern[excludes.length];
             for (int i = 0; i < excludes.length; i++) {
                 String pattern;
                 pattern = 
excludes[i].replace('/',File.separatorChar).replace('\\',File.separatorChar);
                 if (pattern.endsWith(File.separator)) {
                     pattern += "**";
                 }
-                this.excludes[i] = pattern;
+                this.excludes[i] = new Pattern(pattern);
             }
         }
     }
@@ -660,11 +351,11 @@
 
         if (includes == null) {
             // No includes supplied, so set it to 'matches all'
-            includes = new String[1];
-            includes[0] = "**";
+            includes = new Pattern[1];
+            includes[0] = new Pattern("**");
         }
         if (excludes == null) {
-            excludes = new String[0];
+            excludes = new Pattern[0];
         }
 
         filesIncluded    = new Vector();
@@ -945,13 +636,13 @@
      */
     public void addDefaultExcludes() {
         int excludesLength = excludes == null ? 0 : excludes.length;
-        String[] newExcludes;
-        newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
+        Pattern[] newExcludes;
+        newExcludes = new Pattern[excludesLength + DEFAULTEXCLUDES.length];
         if (excludesLength > 0) {
             System.arraycopy(excludes,0,newExcludes,0,excludesLength);
         }
         for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
-            newExcludes[i+excludesLength] = 
DEFAULTEXCLUDES[i].replace('/',File.separatorChar).replace('\\',File.separatorChar);
+            newExcludes[i+excludesLength] = new 
Pattern(DEFAULTEXCLUDES[i].replace('/',File.separatorChar).replace('\\',File.separatorChar));
         }
         excludes = newExcludes;
     }
diff -ru ant-orig/src/main/org/apache/tools/ant/types/ZipScanner.java 
ant/src/main/org/apache/tools/ant/types/ZipScanner.java
--- ant-orig/src/main/org/apache/tools/ant/types/ZipScanner.java        Thu Mar 
22 14:05:22 2001
+++ ant/src/main/org/apache/tools/ant/types/ZipScanner.java     Wed Mar 21 
17:59:41 2001
@@ -111,12 +111,14 @@
      */
     public void init() {
         if (includes == null) {
+           String[] temp;
             // No includes supplied, so set it to 'matches all'
-            includes = new String[1];
-            includes[0] = "**";
+            temp = new String[1];
+            temp[0] = "**";
+           setIncludes(temp);
         }
         if (excludes == null) {
-            excludes = new String[0];
+            setExcludes(new String[0]);
         }
     }
 
diff -ru /dev/null ant/src/main/org/apache/tools/ant/Pattern.java
--- /dev/null   Wed Mar 29 00:25:18 2000
+++ ant/src/main/org/apache/tools/ant/Pattern.java      Thu Mar 22 13:51:35 2001
@@ -0,0 +1,433 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact [EMAIL PROTECTED]
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.tools.ant;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This inner class holds a pattern, and contains the methods
+ * to match that pattern against a pathname.
+ */
+class Pattern {
+    private String pattern;
+
+    protected Pattern(String pattern) {
+       this.pattern = pattern;
+    }
+
+    /**
+     * Does the path match the start of this Pattern object up to the first 
"**".
+     +
+     * <p>This is not a general purpose test and should only be used if you
+     * can live with false positives.</p>
+     *
+     * <p><code>pattern=**\\a</code> and <code>str=b</code> will yield true.
+     *
+     * @param str     the (non-null) string (path) to match
+     */
+    protected boolean matchPatternStart(String str) {
+       return matchPatternStart(this.pattern, str);
+    }
+
+    /**
+     * Matches a path against this Pattern object.
+     *
+     * @param str     the (non-null) string (path) to match
+     *
+     * @return <code>true</code> when this pattern matches against the string.
+     *         <code>false</code> otherwise.
+     */
+    protected boolean matchPath(String str) {
+       return matchPath(this.pattern, str);
+    }
+
+    /**
+     * Does the path match the start of a pattern string to the first "**".
+     +
+     * <p>This is not a general purpose test and should only be used if you
+     * can live with false positives.</p>
+     *
+     * <p><code>pattern=**\\a</code> and <code>str=b</code> will yield true.
+     *
+     * @param pattern the (non-null) pattern to match against
+     * @param str     the (non-null) string (path) to match
+     */
+    protected static boolean matchPatternStart(String pattern, String str) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(File.separator) !=
+            pattern.startsWith(File.separator)) {
+            return false;
+        }
+
+        Vector patDirs = new Vector();
+        StringTokenizer st = new StringTokenizer(pattern,File.separator);
+        while (st.hasMoreTokens()) {
+            patDirs.addElement(st.nextToken());
+        }
+
+        Vector strDirs = new Vector();
+        st = new StringTokenizer(str,File.separator);
+        while (st.hasMoreTokens()) {
+            strDirs.addElement(st.nextToken());
+        }
+
+        int patIdxStart = 0;
+        int patIdxEnd   = patDirs.size()-1;
+        int strIdxStart = 0;
+        int strIdxEnd   = strDirs.size()-1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = (String)patDirs.elementAt(patIdxStart);
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir,(String)strDirs.elementAt(strIdxStart))) {
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            return true;
+        } else if (patIdxStart > patIdxEnd) {
+            // String not exhausted, but pattern is. Failure.
+            return false;
+        } else {
+            // pattern now holds ** while string is not exhausted
+            // this will generate false positives but we can live with that.
+            return true;
+        }
+    }
+
+    /**
+     * Matches a path against a pattern string.
+     *
+     * @param pattern the (non-null) pattern to match against
+     * @param str     the (non-null) string (path) to match
+     *
+     * @return <code>true</code> when the pattern matches against the string.
+     *         <code>false</code> otherwise.
+     */
+    protected static boolean matchPath(String pattern, String str) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(File.separator) !=
+            pattern.startsWith(File.separator)) {
+            return false;
+        }
+
+        Vector patDirs = new Vector();
+        StringTokenizer st = new StringTokenizer(pattern,File.separator);
+        while (st.hasMoreTokens()) {
+            patDirs.addElement(st.nextToken());
+        }
+
+        Vector strDirs = new Vector();
+        st = new StringTokenizer(str,File.separator);
+        while (st.hasMoreTokens()) {
+            strDirs.addElement(st.nextToken());
+        }
+
+        int patIdxStart = 0;
+        int patIdxEnd   = patDirs.size()-1;
+        int strIdxStart = 0;
+        int strIdxEnd   = strDirs.size()-1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = (String)patDirs.elementAt(patIdxStart);
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir,(String)strDirs.elementAt(strIdxStart))) {
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs.elementAt(i).equals("**")) {
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            if (patIdxStart > patIdxEnd) {
+                // String not exhausted, but pattern is. Failure.
+                return false;
+            }
+        }
+
+        // up to last '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = (String)patDirs.elementAt(patIdxEnd);
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir,(String)strDirs.elementAt(strIdxEnd))) {
+                return false;
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs.elementAt(i).equals("**")) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
+                if (patDirs.elementAt(i).equals("**")) {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart+1) {
+                // '**/**' situation, so skip one
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp-patIdxStart-1);
+            int strLength = (strIdxEnd-strIdxStart+1);
+            int foundIdx  = -1;
+strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    String subPat = (String)patDirs.elementAt(patIdxStart+j+1);
+                    String subStr = (String)strDirs.elementAt(strIdxStart+i+j);
+                    if (!match(subPat,subStr)) {
+                        continue strLoop;
+                    }
+                }
+
+                foundIdx = strIdxStart+i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx+patLength;
+        }
+
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (!patDirs.elementAt(i).equals("**")) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+
+    /**
+     * Matches a candidate string against a pattern string. The pattern string 
contains two special
+     * characters:
+     * '*' which means zero or more characters,
+     * '?' which means one and only one character.
+     *
+     * @param pattern the (non-null) pattern to match against
+     * @param str     the (non-null) string that must be matched against the
+     *                pattern
+     *
+     * @return <code>true</code> when the string matches against the pattern,
+     *         <code>false</code> otherwise.
+     */
+    protected static boolean match(String pattern, String str) {
+        char[] patArr = pattern.toCharArray();
+        char[] strArr = str.toCharArray();
+        int patIdxStart = 0;
+        int patIdxEnd   = patArr.length-1;
+        int strIdxStart = 0;
+        int strIdxEnd   = strArr.length-1;
+        char ch;
+
+        boolean containsStar = false;
+        for (int i = 0; i < patArr.length; i++) {
+            if (patArr[i] == '*') {
+                containsStar = true;
+                break;
+            }
+        }
+
+        if (!containsStar) {
+            // No '*'s, so we make a shortcut
+            if (patIdxEnd != strIdxEnd) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patIdxEnd; i++) {
+                ch = patArr[i];
+                if (ch != '?' && ch != strArr[i]) {
+                    return false; // Character mismatch
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+        if (patIdxEnd == 0) {
+            return true; // Pattern contains only '*', which matches anything
+        }
+
+        // Process characters before first star
+        while((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
+            if (ch != '?' && ch != strArr[strIdxStart]) {
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // Process characters after last star
+        while((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
+            if (ch != '?' && ch != strArr[strIdxEnd]) {
+                return false;
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // process pattern between stars. padIdxStart and patIdxEnd point
+        // always to a '*'.
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
+                if (patArr[i] == '*') {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart+1) {
+                // Two stars next to each other, skip the first one.
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp-patIdxStart-1);
+            int strLength = (strIdxEnd-strIdxStart+1);
+            int foundIdx  = -1;
+strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    ch = patArr[patIdxStart+j+1];
+                    if (ch != '?' && ch != strArr[strIdxStart+i+j]) {
+                        continue strLoop;
+                    }
+                }
+
+                foundIdx = strIdxStart+i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx+patLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (patArr[i] != '*') {
+                return false;
+            }
+        }
+        return true;
+    }
+}
Index: src/main/org/apache/tools/ant/Pattern.java
===================================================================
RCS file: 
/home/erics/Repos/freeware/ant/src/main/org/apache/tools/ant/Pattern.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- src/main/org/apache/tools/ant/Pattern.java  2001/03/22 19:02:02     1.1
+++ src/main/org/apache/tools/ant/Pattern.java  2001/03/22 19:32:45     1.2
@@ -63,9 +63,12 @@
  */
 class Pattern {
     private String pattern;
+    private boolean isSimpleFilename;  // ie. no pattern-matching characters
 
     protected Pattern(String pattern) {
        this.pattern = pattern;
+       isSimpleFilename = (pattern.indexOf('*') == -1) &&
+                          (pattern.indexOf('?') == -1);
     }
 
     /**
@@ -79,7 +82,13 @@
      * @param str     the (non-null) string (path) to match
      */
     protected boolean matchPatternStart(String str) {
-       return matchPatternStart(this.pattern, str);
+       if (isSimpleFilename) {
+           return pattern.startsWith(str) && (
+                       pattern.length() == str.length() ||
+                       pattern.charAt(str.length()) == File.separatorChar);
+       } else {
+           return matchPatternStart(this.pattern, str);
+       }
     }
 
     /**
@@ -91,7 +100,11 @@
      *         <code>false</code> otherwise.
      */
     protected boolean matchPath(String str) {
-       return matchPath(this.pattern, str);
+       if (isSimpleFilename) {
+           return pattern.equals(str);
+       } else {
+           return matchPath(pattern, str);
+       }
     }
 
     /**
/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.tools.ant;

import org.apache.tools.ant.types.*;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * Test class for Pattern.
 *
 * XXX Some of the actual results seem wrong to me, but I don't
 *     know for sure.  I've left the expected results the way I
 *     think they should be, and marked these tests with XXX --
 *     the more X's, the more sure I am of the expected result
 *     I've coded.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Eric Siegerman</a>
 */
public class PatternTest extends TestCase {

    /*
     * Scaffolding
     */
    public PatternTest(String name) {
        super(name);
    }

    public static Test suite() {
        return (new TestSuite(org.apache.tools.ant.PatternTest.class));
    }

    public static void main(String[] args) {
        junit.textui.TestRunner.run(suite());
    }

    private static void tstPath(String pattern, String candidate, boolean 
expected) {
        String msg = "Testing matchPath(\"" + pattern + "\", \"" + candidate + 
"\"";
        Pattern p = new Pattern(pattern);
        boolean actual = p.matchPath(candidate);
        assert( expected ? actual : !actual);
    }

    private static void tstStart(String pattern, String candidate, boolean 
expected) {
        String msg = "Testing matchPatternStart(\"" + pattern + "\", \"" + 
candidate + "\"";
        Pattern p = new Pattern(pattern);
        boolean actual = p.matchPatternStart(candidate);
        assert( expected ? actual : !actual);
    }


    /*
     *********** Test match() **********
     * These tests call matchPath(), because match() isn't exposed in a
     * useful way.  But they're intended to bypass matchPath's
     * processing, and be translated into direct calls to match().
     *
     * "*"
     */
    public void test000() { tstPath("/*",               "/a",           true); }
    public void test001() { tstPath("/*",               "/ab",          true); }
    public void test002() { tstPath("/*",               "/abcdefgh",    true); }
    public void test003() { tstPath("/*",               "/",            true); 
} // XXXXX
    public void test004() { tstPath("*",                "",             true); 
} // XXXXX
    public void test005() { tstPath("/*",               "/a/a",         false); 
}

    public void test020() { tstPath("/a*",              "/a",           true); }
    public void test021() { tstPath("/a*",              "/ab",          true); }
    public void test022() { tstPath("/a*",              "/abcdefgh",    true); }
    public void test023() { tstPath("/a*",              "/aa",          true); }
    public void test024() { tstPath("/a*",              "/aaaaaaaa",    true); }
    public void test025() { tstPath("/a*",              "/",            false); 
}
    public void test026() { tstPath("/a*",              "/b",           false); 
}
    public void test027() { tstPath("/a*",              "/ba",          false); 
}
    public void test028() { tstPath("/a*",              "/bacdefgh",    false); 
}
    public void test029() { tstPath("/a*",              "/a/a",         false); 
}

    public void test040() { tstPath("/*a",              "/a",           true); }
    public void test041() { tstPath("/*a",              "/ba",          true); }
    public void test042() { tstPath("/*a",              "/bcdefgha",    true); }
    public void test043() { tstPath("/*a",              "/aa",          true); }
    public void test044() { tstPath("/*a",              "/aaaaaaaa",    true); }
    public void test045() { tstPath("/*a",              "/",            false); 
}
    public void test046() { tstPath("/*a",              "/b",           false); 
}
    public void test047() { tstPath("/*a",              "/ab",          false); 
}
    public void test048() { tstPath("/*a",              "/bcdefgah",    false); 
}
    public void test049() { tstPath("/*a",              "/a/a",         false); 
}

    public void test060() { tstPath("/*a*",             "/a",           true); }
    public void test061() { tstPath("/*a*",             "/aa",          true); }
    public void test062() { tstPath("/*a*",             "/aaaaaaaa",    true); }
    public void test063() { tstPath("/*a*",             "/ab",          true); }
    public void test064() { tstPath("/*a*",             "/ba",          true); }
    public void test065() { tstPath("/*a*",             "/bac",         true); }
    public void test066() { tstPath("/*a*",             "/bcdafgh",     true); }
    public void test067() { tstPath("/*a*",             "/",            false); 
}
    public void test068() { tstPath("/*a*",             "/b",           false); 
}
    public void test069() { tstPath("/*a*",             "/bc",          false); 
}
    public void test070() { tstPath("/*a*",             "/bcdefghi",    false); 
}
    public void test071() { tstPath("/*a*",             "/a/a",         false); 
}

    public void test080() { tstPath("/a*a",             "/aa",          true); }
    public void test081() { tstPath("/a*a",             "/aba",         true); }
    public void test082() { tstPath("/a*a",             "/abcdefga",    true); }
    public void test083() { tstPath("/a*a",             "/abababa",     true); }
    public void test084() { tstPath("/a*a",             "/aaaaaaaa",    true); }
    public void test085() { tstPath("/a*a",             "/",            false); 
}
    public void test086() { tstPath("/a*a",             "/a",           false); 
}
    public void test087() { tstPath("/a*a",             "/ab",          false); 
}
    public void test088() { tstPath("/a*a",             "/b",           false); 
}
    public void test089() { tstPath("/a*a",             "/ba",          false); 
}
    public void test090() { tstPath("/a*a",             "/bac",         false); 
}
    public void test091() { tstPath("/a*a",             "/ababab",      false); 
}
    public void test092() { tstPath("/a*a",             "/bababa",      false); 
}
    public void test093() { tstPath("/a*a",             "/a/a",         false); 
}

    public void test100() { tstPath("/a**",             "/a",           true); }
    public void test101() { tstPath("/a**",             "/aa",          true); }
    public void test102() { tstPath("/a**",             "/ababab",      true); }
    public void test103() { tstPath("/a**",             "/",            false); 
}
    public void test104() { tstPath("/a**",             "/bababa",      false); 
}
    public void test105() { tstPath("/a**",             "/a/a",         false); 
}

    public void test120() { tstPath("/**a",             "/a",           true); }
    public void test121() { tstPath("/**a",             "/aa",          true); }
    public void test122() { tstPath("/**a",             "/bababa",      true); }
    public void test123() { tstPath("/**a",             "/",            false); 
}
    public void test124() { tstPath("/**a",             "/ababab",      false); 
}
    public void test125() { tstPath("/**a",             "/a/a",         false); 
}


    /*
     * "?"
     */
    public void test200() { tstPath("/?",               "/a",           true); }
    public void test201() { tstPath("/?",               "/",            false); 
}
    public void test202() { tstPath("/?",               "/ab",          false); 
}
    public void test203() { tstPath("/?",               "/abcdefgh",    false); 
}

    public void test220() { tstPath("/a?",              "/ab",          true); }
    public void test221() { tstPath("/a?",              "/aa",          true); }
    public void test222() { tstPath("/a?",              "/a",           false); 
}
    public void test223() { tstPath("/a?",              "/abcdefgh",    false); 
}
    public void test224() { tstPath("/a?",              "/aaaaaaaa",    false); 
}
    public void test225() { tstPath("/a?",              "/",            false); 
}
    public void test226() { tstPath("/a?",              "/b",           false); 
}
    public void test227() { tstPath("/a?",              "/ba",          false); 
}
    public void test228() { tstPath("/a?",              "/bacdefgh",    false); 
}

    public void test240() { tstPath("/?a",              "/aa",          true); }
    public void test241() { tstPath("/?a",              "/ba",          true); }
    public void test242() { tstPath("/?a",              "/a",           false); 
}
    public void test243() { tstPath("/?a",              "/bcdefgha",    false); 
}
    public void test244() { tstPath("/?a",              "/aaaaaaaa",    false); 
}
    public void test245() { tstPath("/?a",              "/",            false); 
}
    public void test246() { tstPath("/?a",              "/b",           false); 
}
    public void test247() { tstPath("/?a",              "/ab",          false); 
}
    public void test248() { tstPath("/?a",              "/bcdefgah",    false); 
}

    public void test260() { tstPath("/?a?",             "/aaa",         true); }
    public void test261() { tstPath("/?a?",             "/bac",         true); }
    public void test262() { tstPath("/?a?",             "/a",           false); 
}
    public void test263() { tstPath("/?a?",             "/aa",          false); 
}
    public void test264() { tstPath("/?a?",             "/aaaaaaaa",    false); 
}
    public void test265() { tstPath("/?a?",             "/ab",          false); 
}
    public void test266() { tstPath("/?a?",             "/ba",          false); 
}
    public void test267() { tstPath("/?a?",             "/bcdafgh",     false); 
}
    public void test268() { tstPath("/?a?",             "/",            false); 
}
    public void test269() { tstPath("/?a?",             "/b",           false); 
}
    public void test270() { tstPath("/?a?",             "/bc",          false); 
}
    public void test271() { tstPath("/?a?",             "/bcdefghi",    false); 
}

    public void test280() { tstPath("/a?a",             "/aaa",         true); }
    public void test281() { tstPath("/a?a",             "/aba",         true); }
    public void test282() { tstPath("/a?a",             "/aa",          false); 
}
    public void test283() { tstPath("/a?a",             "/abcdefga",    false); 
}
    public void test284() { tstPath("/a?a",             "/",            false); 
}
    public void test285() { tstPath("/a?a",             "/a",           false); 
}
    public void test286() { tstPath("/a?a",             "/aa",          false); 
}
    public void test287() { tstPath("/a?a",             "/aaaa",        false); 
}
    public void test288() { tstPath("/a?a",             "/ab",          false); 
}
    public void test289() { tstPath("/a?a",             "/b",           false); 
}
    public void test290() { tstPath("/a?a",             "/ba",          false); 
}
    public void test291() { tstPath("/a?a",             "/bac",         false); 
}
    public void test292() { tstPath("/a?a",             "/abba",        false); 
}

    public void test300() { tstPath("/??",              "/ab",          true); }
    public void test301() { tstPath("/??",              "/",            false); 
}
    public void test302() { tstPath("/??",              "/a",           false); 
}
    public void test303() { tstPath("/??",              "/abcdefgh",    false); 
}

    public void test320() { tstPath("/a??",             "/aaa",         true); }
    public void test321() { tstPath("/a??",             "/abc",         true); }
    public void test322() { tstPath("/a??",             "/bac",         false); 
}
    public void test323() { tstPath("/a??",             "/ab",          false); 
}
    public void test324() { tstPath("/a??",             "/aa",          false); 
}
    public void test325() { tstPath("/a??",             "/a",           false); 
}
    public void test326() { tstPath("/a??",             "/abcdefgh",    false); 
}
    public void test327() { tstPath("/a??",             "/aaaaaaaa",    false); 
}
    public void test328() { tstPath("/a??",             "/",            false); 
}
    public void test329() { tstPath("/a??",             "/b",           false); 
}
    public void test330() { tstPath("/a??",             "/ba",          false); 
}
    public void test331() { tstPath("/a??",             "/bacdefgh",    false); 
}

    public void test340() { tstPath("/??a",             "/aaa",         true); }
    public void test341() { tstPath("/??a",             "/bca",         true); }
    public void test342() { tstPath("/??a",             "/bac",         false); 
}
    public void test343() { tstPath("/??a",             "/ba",          false); 
}
    public void test344() { tstPath("/??a",             "/a",           false); 
}
    public void test345() { tstPath("/??a",             "/bcdefgha",    false); 
}
    public void test346() { tstPath("/??a",             "/aaaaaaaa",    false); 
}
    public void test347() { tstPath("/??a",             "/",            false); 
}
    public void test348() { tstPath("/??a",             "/b",           false); 
}
    public void test349() { tstPath("/??a",             "/ab",          false); 
}
    public void test350() { tstPath("/??a",             "/bcdefgah",    false); 
}

    public void test360() { tstPath("/?a?",             "/aaa",         true); }
    public void test361() { tstPath("/?a?",             "/bac",         true); }
    public void test362() { tstPath("/?a?",             "/a",           false); 
}
    public void test363() { tstPath("/?a?",             "/aa",          false); 
}
    public void test364() { tstPath("/?a?",             "/aaaaaaaa",    false); 
}
    public void test365() { tstPath("/?a?",             "/ab",          false); 
}
    public void test366() { tstPath("/?a?",             "/ba",          false); 
}
    public void test367() { tstPath("/?a?",             "/bcdafgh",     false); 
}
    public void test368() { tstPath("/?a?",             "/",            false); 
}
    public void test369() { tstPath("/?a?",             "/b",           false); 
}
    public void test370() { tstPath("/?a?",             "/bc",          false); 
}
    public void test371() { tstPath("/?a?",             "/bcdefghi",    false); 
}

    public void test380() { tstPath("/a??????b",        "/acdefghb",    true); }
    public void test381() { tstPath("/a??????b",        "/ab",          false); 
}
    public void test382() { tstPath("/a??????b",        "/acb",         false); 
}
    public void test383() { tstPath("/a??????b",        "/acdb",        false); 
}
    public void test384() { tstPath("/a??????b",        "/acdeb",       false); 
}
    public void test385() { tstPath("/a??????b",        "/acdefb",      false); 
}
    public void test386() { tstPath("/a??????b",        "/acdefgb",     false); 
}
    public void test387() { tstPath("/a??????b",        "/acdefghib",   false); 
}
    public void test388() { tstPath("/a??????b",        "/acdefghijb",  false); 
}
    public void test389() { tstPath("/a??????b",        "/acdefghijkb", false); 
}


    /*
     * "*" and "?" together
     */
    public void test400() { tstPath("/*?",              "/a",           true); }
    public void test401() { tstPath("/*?",              "/ab",          true); }
    public void test402() { tstPath("/*?",              "/abcdefgh",    true); }
    public void test403() { tstPath("/*?",              "/",            false); 
}

    public void test420() { tstPath("/?*",              "/a",           true); }
    public void test421() { tstPath("/?*",              "/ab",          true); }
    public void test422() { tstPath("/?*",              "/abcdefgh",    true); }
    public void test423() { tstPath("/?*",              "/",            false); 
}

    public void test440() { tstPath("/a?b*c",           "/a1bc",        true); }
    public void test441() { tstPath("/a?b*c",           "/a1b3c",       true); }
    public void test442() { tstPath("/a?b*c",           "/a1b34c",      true); }
    public void test443() { tstPath("/a?b*c",           "/a1b345c",     true); }
    public void test444() { tstPath("/a?b*c",           "/ab",          false); 
}
    public void test445() { tstPath("/a?b*c",           "/abc",         false); 
}
    public void test446() { tstPath("/a?b*c",           "/a1b",         false); 
}
    public void test447() { tstPath("/a?b*c",           "/a1bcd",       false); 
}
    public void test448() { tstPath("/a?b*c",           "/ab2c",        false); 
}
    public void test449() { tstPath("/a?b*c",           "/a12bc",       false); 
}
    public void test450() { tstPath("/a?b*c",           "/a12b3c",      false); 
}
    public void test451() { tstPath("/a?b*c",           "/a12b34c",     false); 
}
    public void test452() { tstPath("/a?b*c",           "/a12b345c",    false); 
}

    public void test460() { tstPath("/a*b?c",           "/ab2c",        true); }
    public void test461() { tstPath("/a*b?c",           "/a1b3c",       true); }
    public void test462() { tstPath("/a*b?c",           "/a12b3c",      true); }
    public void test463() { tstPath("/a*b?c",           "/a123b3c",     true); }
    public void test464() { tstPath("/a*b?c",           "/a1bc",        false); 
}
    public void test465() { tstPath("/a*b?c",           "/a1b34c",      false); 
}
    public void test466() { tstPath("/a*b?c",           "/a1b345c",     false); 
}
    public void test467() { tstPath("/a*b?c",           "/ab",          false); 
}
    public void test468() { tstPath("/a*b?c",           "/abc",         false); 
}
    public void test469() { tstPath("/a*b?c",           "/a1b",         false); 
}
    public void test470() { tstPath("/a*b?c",           "/a1bcd",       false); 
}
    public void test471() { tstPath("/a*b?c",           "/a12bc",       false); 
}
    public void test472() { tstPath("/a*b?c",           "/a12b34c",     false); 
}
    public void test473() { tstPath("/a*b?c",           "/a12b345c",    false); 
}



    /********** Test matchPath() **********
     *
     * Simple (non-pattern) multicomponent paths
     */
    public void test1000() { tstPath("/aa/bb",          "/aa",          false); 
}
    public void test1001() { tstPath("/aa/bb",          "/aa/bb",       true); }
    public void test1002() { tstPath("/aa/bb",          "/aa/bb/cc",    false); 
}
    public void test1003() { tstPath("/aa/bb",          "/aa/bb/b",     false); 
}

    /*
     * Leading '/', or not
     */
    public void test1020() { tstPath("/aa",             "/aa",          true); }
    public void test1021() { tstPath("aa",              "aa",           true); }
    public void test1022() { tstPath("/aa",             "aa",           false); 
}
    public void test1023() { tstPath("aa",              "/aa",          false); 
}

    /*
     * Patterns, without "**"
     */
    public void test1040() { tstPath("/aa/*/cc",        "/aa/bb/cc",    true); }
    public void test1041() { tstPath("/aa/*/cc",        "/aa/bb/cc/",   false); 
} //XXX
    public void test1042() { tstPath("/aa/*/cc",        "/aa",          false); 
}
    public void test1043() { tstPath("/aa/*/cc",        "/aa/",         false); 
}
    public void test1044() { tstPath("/aa/*/cc",        "/aa/bb",       false); 
}
    public void test1045() { tstPath("/aa/*/cc",        "/aa/cc",       false); 
}
    public void test1046() { tstPath("/aa/*/cc",        "/aa/bb/cc/dd", false); 
}

    public void test1060() { tstPath("/aa/?/cc",        "/aa/b/cc",     true); }
    public void test1061() { tstPath("/aa/?/cc",        "/aa/bb/cc",    false); 
}
    public void test1062() { tstPath("/aa/?/cc",        "/aa/bb/cc/",   false); 
}
    public void test1063() { tstPath("/aa/?/cc",        "/aa",          false); 
}
    public void test1064() { tstPath("/aa/?/cc",        "/aa/",         false); 
}
    public void test1065() { tstPath("/aa/?/cc",        "/aa/bb",       false); 
}
    public void test1066() { tstPath("/aa/?/cc",        "/aa/cc",       false); 
}
    public void test1067() { tstPath("/aa/?/cc",        "/aa/bb/cc/dd", false); 
}

    /*
     * "**"
     */
    public void test1080() { tstPath("/aa/**/cc",       "/aa/cc",       true); }
    public void test1081() { tstPath("/aa/**/cc",       "/aa/b/cc",     true); }
    public void test1082() { tstPath("/aa/**/cc",       "/aa/b/c/cc",   true); }
    public void test1083() { tstPath("/aa/**/cc",       "/aa/b/c/d/cc", true); }
    public void test1084() { tstPath("/aa/**/cc",       "/aa",          false); 
}
    public void test1085() { tstPath("/aa/**/cc",       "/aa/b/c/d/e",  false); 
}
    public void test1086() { tstPath("/aa/**/cc",       "/",            false); 
}
    public void test1087() { tstPath("/aa/**/cc",       "/bb",          false); 
}
    public void test1088() { tstPath("/aa/**/cc",       "/bb/cc",       false); 
}
    public void test1089() { tstPath("/aa/**/cc",       "/bb/b/cc",     false); 
}
    public void test1090() { tstPath("/aa/**/cc",       "/bb/b/c/cc",   false); 
}

    public void test1100() { tstPath("/aa/**",          "/aa/cc",       true); }
    public void test1101() { tstPath("/aa/**",          "/aa/b/cc",     true); }
    public void test1102() { tstPath("/aa/**",          "/aa/b/c/cc",   true); }
    public void test1103() { tstPath("/aa/**",          "/aa/b/c/d/cc", true); }
    public void test1104() { tstPath("/aa/**",          "/aa",          true); }
    public void test1105() { tstPath("/aa/**",          "/aa/b/c/d/e",  true); }
    public void test1106() { tstPath("/aa/**",          "/",            false); 
}
    public void test1107() { tstPath("/aa/**",          "/bb",          false); 
}
    public void test1108() { tstPath("/aa/**",          "/bb/cc",       false); 
}
    public void test1109() { tstPath("/aa/**",          "/bb/b/cc",     false); 
}
    public void test1110() { tstPath("/aa/**",          "/bb/b/c/cc",   false); 
}

    public void test1120() { tstPath("/**/cc",          "/aa/cc",       true); }
    public void test1121() { tstPath("/**/cc",          "/aa/b/cc",     true); }
    public void test1122() { tstPath("/**/cc",          "/aa/b/c/cc",   true); }
    public void test1123() { tstPath("/**/cc",          "/aa/b/c/d/cc", true); }
    public void test1124() { tstPath("/**/cc",          "/cc",          true); }
    public void test1125() { tstPath("/**/cc",          "/a/b/c/d/cc",  true); }
    public void test1126() { tstPath("/**/cc",          "/",            false); 
}
    public void test1127() { tstPath("/**/cc",          "/bb",          false); 
}
    public void test1128() { tstPath("/**/cc",          "/aa/bb",       false); 
}
    public void test1129() { tstPath("/**/cc",          "/aa/bb/b",     false); 
}
    public void test1130() { tstPath("/**/cc",          "/aa/b/cc/d",   false); 
}

    public void test1140() { tstPath("/**",             "/",            true); }
    public void test1141() { tstPath("/**",             "aa",           false); 
}
    public void test1142() { tstPath("/**",             "/aa",          true); }
    public void test1143() { tstPath("/**",             "/aa/bb",       true); }
    public void test1144() { tstPath("/**",             "/aa/bb/cc",    true); }

    public void test1160() { tstPath("/aa/",            "/aa/cc",       true); 
} // XXXXX
    public void test1161() { tstPath("/aa",             "/aa/cc",       false); 
}


    /********** Test matchPatternStart() **********
     *
     * Simple (non-pattern) multicomponent paths
     */
    public void test2000() { tstStart("/aa/bb",         "/",            true); 
} // XXXXX
    public void test2001() { tstStart("aa/bb",          "",             true); 
} // XXX
    public void test2002() { tstStart("/aa/bb",         "/aa",          true); }
    public void test2003() { tstStart("/aa/bb",         "/aa/bb",       true); }
    public void test2004() { tstStart("/aa/bb",         "/aa/bb/cc",    false); 
}

    /*
     * Leading '/', or not
     */
    public void test2020() { tstStart("/aa",            "/aa",          true); }
    public void test2021() { tstStart("aa",             "aa",           true); }
    public void test2022() { tstStart("/aa",            "aa",           false); 
}
    public void test2023() { tstStart("aa",             "/aa",          false); 
}

    /*
     * Patterns, without "**"
     */
    public void test2040() { tstStart("/aa/*/cc",       "/aa/bb/cc",    true); }
    public void test2041() { tstStart("/aa/*/cc",       "/aa/bb/cc/",   false); 
} // XXX
    public void test2042() { tstStart("/aa/*/cc",       "/aa",          true); }
    public void test2043() { tstStart("/aa/*/cc",       "/aa/",         false); 
} // XXX
    public void test2044() { tstStart("/aa/*/cc",       "/aa/bb",       true); }
    public void test2045() { tstStart("/aa/*/cc",       "/aa/cc",       true); }
    public void test2046() { tstStart("/aa/*/cc",       "/aa/bb/cc/dd", false); 
}

    public void test2060() { tstStart("/aa/?/cc",       "/aa",          true); }
    public void test2061() { tstStart("/aa/?/cc",       "/aa/b",        true); }
    public void test2062() { tstStart("/aa/?/cc",       "/aa/b/cc",     true); }
    public void test2063() { tstStart("/aa/?/cc",       "/aa/b/cc/",    false); 
} // XXX
    public void test2064() { tstStart("/aa/?/cc",       "/aa/bb",       false); 
}
    public void test2065() { tstStart("/aa/?/cc",       "/aa/bb/cc",    false); 
}
    public void test2066() { tstStart("/aa/?/cc",       "/aa/bb/cc/dd", false); 
}

    /*
     * "**"
     */
    public void test2080() { tstStart("/aa/**/cc",      "/aa",          true); }
    public void test2081() { tstStart("/aa/**/cc",      "/aa/bb",       true); }
    public void test2082() { tstStart("/aa/**/cc",      "/aa/cc",       true); }
    public void test2083() { tstStart("/aa/**/cc",      "/aa/bb/dd",    true); }
    public void test2084() { tstStart("/aa/**/cc",      "/aa/cc/dd",    true); }
    public void test2085() { tstStart("/aa/**/cc",      "/aa/b/c/d/e",  true); }
    public void test2086() { tstStart("/aa/**/cc",      "/",            true); }
    public void test2087() { tstStart("/aa/**/cc",      "/bb",          false); 
}
    public void test2088() { tstStart("/aa/**/cc",      "/bb/cc",       false); 
}
    public void test2089() { tstStart("/aa/**/cc",      "/bb/b/cc",     false); 
}
    public void test2090() { tstStart("/aa/**/cc",      "/bb/b/c/cc",   false); 
}
    public void test2091() { tstStart("/aa/**/cc",      "/bb/b/c/dd",   false); 
}

    public void test2100() { tstStart("/aa/**",         "/aa",          true); }
    public void test2101() { tstStart("/aa/**",         "/aa/cc",       true); }
    public void test2102() { tstStart("/aa/**",         "/aa/b/cc",     true); }
    public void test2103() { tstStart("/aa/**",         "/aa/b/c/cc",   true); }
    public void test2104() { tstStart("/aa/**",         "/",            true); }
    public void test2105() { tstStart("/aa/**",         "/bb",          false); 
}
    public void test2106() { tstStart("/aa/**",         "/bb/cc",       false); 
}
    public void test2107() { tstStart("/aa/**",         "/bb/b/cc",     false); 
}
    public void test2108() { tstStart("/aa/**",         "/bb/b/c/cc",   false); 
}

    public void test2120() { tstStart("/**/cc",         "/cc",          true); }
    public void test2121() { tstStart("/**/cc",         "/aa/cc",       true); }
    public void test2122() { tstStart("/**/cc",         "/aa/b/cc",     true); }
    public void test2123() { tstStart("/**/cc",         "/aa/b/c/cc",   true); }
    public void test2124() { tstStart("/**/cc",         "/a/b/c/d/cc",  true); }
    public void test2125() { tstStart("/**/cc",         "/",            true); }
    public void test2126() { tstStart("/**/cc",         "/bb",          true); }
    public void test2127() { tstStart("/**/cc",         "/aa/bb",       true); }
    public void test2128() { tstStart("/**/cc",         "/aa/bb/b",     true); }
    public void test2129() { tstStart("/**/cc",         "/aa/b/cc/d",   true); }

    public void test2140() { tstStart("/**",            "/",            true); }
    public void test2141() { tstStart("/**",            "/aa",          true); }
    public void test2142() { tstStart("/**",            "/aa/bb",       true); }
    public void test2143() { tstStart("/**",            "/aa/bb/cc",    true); }
    public void test2144() { tstStart("/**",            "aa",           false); 
}

    public void test2160() { tstStart("/aa/",           "/aa/cc",       true); 
} // XXXXX
    public void test2161() { tstStart("/aa",            "/aa/cc",       false); 
}
}

Reply via email to