ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Bruce Atherton <br...@callenish.com>
Subject [SUBMIT] Selectors
Date Mon, 29 Apr 2002 22:18:28 GMT
In the time-honored tradition of getting things in at the last minute, here 
is the selectors proposal. I'll be creating proper documentation and test 
cases in the next week or two. Since it has already had one review, 
hopefully there won't be too much that needs correcting for it to go in to 
1.5. My fingers are crossed, anyway.

I've inlined the patch for DirectoryScanner and AbstractFileSet, though it 
can also be found in the tarfile. All the other files are new.



Index: src/main/org/apache/tools/ant/DirectoryScanner.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/DirectoryScanner.java,v
retrieving revision 1.24
diff -u -r1.24 DirectoryScanner.java
--- src/main/org/apache/tools/ant/DirectoryScanner.java	9 Apr 2002 15:27:07 
-0000	1.24
+++ src/main/org/apache/tools/ant/DirectoryScanner.java	29 Apr 2002 
22:07:56 -0000
@@ -58,29 +58,37 @@
  import java.util.Vector;
  import java.util.StringTokenizer;

+import org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
  /**
   * Class for scanning a directory for files/directories which match certain
   * criteria.
   * <p>
- * These criteria consist of a set of include and exclude patterns. With these
- * patterns, you can select which files you want to have included, and which
- * files you want to have excluded.
+ * These criteria consist of selectors and patterns which have been specified.
+ * With the selectors you can select which files you want to have included.
+ * Files which are not selected are excluded. With patterns you can include
+ * or exclude files based on their filename.
   * <p>
   * The idea is simple. A given directory is recursively scanned for all files
- * and directories. Each file/directory is matched against a set of include
+ * and directories. Each file/directory is matched against a set of selectors,
+ * including special support for matching against filenames with include and
   * and exclude patterns. Only files/directories which match at least one
- * pattern of the include pattern list, and don't match any pattern of the
- * exclude pattern list will be placed in the list of files/directories found.
+ * pattern of the include pattern list or other file selector, and don't match
+ * any pattern of the exclude pattern list or fail to match against a required
+ * selector will be placed in the list of files/directories found.
   * <p>
   * When no list of include patterns is supplied, "**" will be used, which
   * means that everything will be matched. When no list of exclude patterns is
- * supplied, an empty list is used, such that nothing will be excluded.
+ * supplied, an empty list is used, such that nothing will be excluded. When
+ * no selectors are supplied, none are applied.
   * <p>
- * The pattern matching is done as follows:
+ * The filename pattern matching is done as follows:
   * The name to be matched is split up in path segments. A path segment is the
   * name of a directory or file, which is bounded by
   * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
- * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
+ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
   * "def","ghi" and "xyz.java".
   * The same is done for the pattern against which should be matched.
   * <p>
@@ -136,11 +144,12 @@
   * This will scan a directory called test for .class files, but excludes all
   * files in all proper subdirectories of a directory called "modules"
   *
- * @author Arnout J. Kuiper
+ * @author Arnout J. Kuiper
   * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
   * @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
+ * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
   */
-public class DirectoryScanner implements FileScanner {
+public class DirectoryScanner implements FileScanner, SelectorScanner {

      /**
       * Patterns which should be excluded by default.
@@ -170,36 +179,53 @@
      /** The patterns for the files to be excluded. */
      protected String[] excludes;

-    /** The files which matched at least one include and no excludes. */
+    /** Selectors that will filter which files are in our candidate list. */
+    protected FileSelector[] selectors = null;
+
+    /** The files which matched at least one include and no excludes
+     *  and were selected.
+     */
      protected Vector filesIncluded;

-    /** The files which did not match any includes. */
+    /** The files which did not match any includes or selectors. */
      protected Vector filesNotIncluded;

-    /**
-     * The files which matched at least one include and at least
+    /**
+     * The files which matched at least one include and at least
       * one exclude.
       */
      protected Vector filesExcluded;

-    /** The directories which matched at least one include and no excludes. */
+    /** The directories which matched at least one include and no excludes
+     *  and were selected.
+     */
      protected Vector dirsIncluded;

      /** The directories which were found and did not match any includes. */
      protected Vector dirsNotIncluded;

-    /**
-     * The directories which matched at least one include and at least one
+    /**
+     * The directories which matched at least one include and at least one
       * exclude.
       */
      protected Vector dirsExcluded;

+    /** The files which matched at least one include and no excludes and
+     *  which a selector discarded.
+     */
+    protected Vector filesDeselected;
+
+    /** The directories which matched at least one include and no excludes
+     *  but which a selector discarded.
+     */
+    protected Vector dirsDeselected;
+
      /** Whether or not our results were built by a slow scan. */
      protected boolean haveSlowResults = false;

-    /**
-     * Whether or not the file system should be treated as a case sensitive
-     * one.
+    /**
+     * Whether or not the file system should be treated as a case sensitive
+     * one.
       */
      protected boolean isCaseSensitive = true;

@@ -213,241 +239,87 @@
      }

      /**
-     * Tests whether or not a given path matches the start of a given
+     * Tests whether or not a given path matches the start of a given
       * pattern up to the first "**".
       * <p>
       * This is not a general purpose test and should only be used if you
-     * can live with false positives. For example, <code>pattern=**\a</code>
+     * can live with false positives. For example, <code>pattern=**\a</code>
       * and <code>str=b</code> will yield <code>true</code>.
       *
-     * @param pattern The pattern to match against. Must not be
+     * @param pattern The pattern to match against. Must not be
       *                <code>null</code>.
-     * @param str     The path to match, as a String. Must not be
+     * @param str     The path to match, as a String. Must not be
       *                <code>null</code>.
-     *
-     * @return whether or not a given path matches the start of a given
+     *
+     * @return whether or not a given path matches the start of a given
       * pattern up to the first "**".
       */
      protected static boolean matchPatternStart(String pattern, String str) {
-        return matchPatternStart(pattern, str, true);
+        return SelectorUtils.matchPatternStart(pattern, str);
      }
      /**
-     * Tests whether or not a given path matches the start of a given
+     * Tests whether or not a given path matches the start of a given
       * pattern up to the first "**".
       * <p>
       * This is not a general purpose test and should only be used if you
-     * can live with false positives. For example, <code>pattern=**\a</code>
+     * can live with false positives. For example, <code>pattern=**\a</code>
       * and <code>str=b</code> will yield <code>true</code>.
       *
-     * @param pattern The pattern to match against. Must not be
+     * @param pattern The pattern to match against. Must not be
       *                <code>null</code>.
-     * @param str     The path to match, as a String. Must not be
+     * @param str     The path to match, as a String. Must not be
       *                <code>null</code>.
-     * @param isCaseSensitive Whether or not matching should be performed
+     * @param isCaseSensitive Whether or not matching should be performed
       *                        case sensitively.
-     *
-     * @return whether or not a given path matches the start of a given
+     *
+     * @return whether or not a given path matches the start of a given
       * pattern up to the first "**".
       */
      protected static boolean matchPatternStart(String pattern, String str,
                                                 boolean isCaseSensitive) {
-        // 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 = tokenizePath (pattern);
-        Vector strDirs = tokenizePath (str);
-
-        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), 
isCaseSensitive)) {
-                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;
-        }
+        return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
      }

      /**
       * Tests whether or not a given path matches a given pattern.
       *
-     * @param pattern The pattern to match against. Must not be
+     * @param pattern The pattern to match against. Must not be
       *                <code>null</code>.
-     * @param str     The path to match, as a String. Must not be
+     * @param str     The path to match, as a String. Must not be
       *                <code>null</code>.
       *
-     * @return <code>true</code> if the pattern matches against the string,
+     * @return <code>true</code> if the pattern matches against the string,
       *         or <code>false</code> otherwise.
       */
      protected static boolean matchPath(String pattern, String str) {
-        return matchPath(pattern, str, true);
+        return SelectorUtils.matchPath(pattern, str);
      }

      /**
       * Tests whether or not a given path matches a given pattern.
       *
-     * @param pattern The pattern to match against. Must not be
+     * @param pattern The pattern to match against. Must not be
       *                <code>null</code>.
-     * @param str     The path to match, as a String. Must not be
+     * @param str     The path to match, as a String. Must not be
       *                <code>null</code>.
-     * @param isCaseSensitive Whether or not matching should be performed
+     * @param isCaseSensitive Whether or not matching should be performed
       *                        case sensitively.
       *
-     * @return <code>true</code> if the pattern matches against the string,
+     * @return <code>true</code> if the pattern matches against the string,
       *         or <code>false</code> otherwise.
       */
-    protected static boolean matchPath(String pattern, String str,
+    protected static boolean matchPath(String pattern, String str,
                                         boolean isCaseSensitive) {
-        // 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 = tokenizePath (pattern);
-        Vector strDirs = tokenizePath (str);
-
-        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), 
isCaseSensitive)) {
-                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), 
isCaseSensitive)) {
-                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, isCaseSensitive)) {
-                        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;
+        return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
      }

      /**
-     * Tests whether or not a string matches against a pattern.
+     * Tests whether or not a string matches against a pattern.
       * The pattern may contain two special characters:<br>
       * '*' means zero or more characters<br>
       * '?' means one and only one character
-     *
-     * @param pattern The pattern to match against.
+     *
+     * @param pattern The pattern to match against.
       *                Must not be <code>null</code>.
       * @param str     The string which must be matched against the pattern.
       *                Must not be <code>null</code>.
@@ -456,211 +328,62 @@
       *         or <code>false</code> otherwise.
       */
      public static boolean match(String pattern, String str) {
-        return match(pattern, str, true);
+        return SelectorUtils.match(pattern, str);
      }

      /**
-     * Tests whether or not a string matches against a pattern.
+     * Tests whether or not a string matches against a pattern.
       * The pattern may contain two special characters:<br>
       * '*' means zero or more characters<br>
       * '?' means one and only one character
-     *
-     * @param pattern The pattern to match against.
+     *
+     * @param pattern The pattern to match against.
       *                Must not be <code>null</code>.
       * @param str     The string which must be matched against the pattern.
       *                Must not be <code>null</code>.
-     * @param isCaseSensitive Whether or not matching should be performed
+     * @param isCaseSensitive Whether or not matching should be performed
       *                        case sensitively.
       *
       *
-     * @return <code>true</code> if the string matches against the pattern,
+     * @return <code>true</code> if the string matches against the pattern,
       *         or <code>false</code> otherwise.
       */
-    protected static boolean match(String pattern, String str,
+    protected static boolean match(String pattern, String str,
                                     boolean isCaseSensitive) {
-        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 != '?') {
-                    if (isCaseSensitive && ch != strArr[i]) {
-                        return false;// Character mismatch
-                    }
-                    if (!isCaseSensitive && Character.toUpperCase(ch) !=
-                        Character.toUpperCase(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 != '?') {
-                if (isCaseSensitive && ch != strArr[strIdxStart]) {
-                    return false;// Character mismatch
-                }
-                if (!isCaseSensitive && Character.toUpperCase(ch) !=
-                    Character.toUpperCase(strArr[strIdxStart])) {
-                    return false;// Character mismatch
-                }
-            }
-            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 != '?') {
-                if (isCaseSensitive && ch != strArr[strIdxEnd]) {
-                    return false;// Character mismatch
-                }
-                if (!isCaseSensitive && Character.toUpperCase(ch) !=
-                    Character.toUpperCase(strArr[strIdxEnd])) {
-                    return false;// Character mismatch
-                }
-            }
-            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 != '?') {
-                        if (isCaseSensitive
-                             && ch != strArr[strIdxStart + i + j]) {
-                            continue strLoop;
-                        }
-                        if (!isCaseSensitive && Character.toUpperCase(ch) !=
-                            Character.toUpperCase(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;
+        return SelectorUtils.match(pattern, str, isCaseSensitive);
      }

      /**
       * Breaks a path up into a Vector of path elements, tokenizing on
       * <code>File.separator</code>.
-     *
+     *
       * @param path Path to tokenize. Must not be <code>null</code>.
-     *
+     *
       * @return a Vector of path elements from the tokenized path
       */
      private static Vector tokenizePath (String path) {
-        Vector ret = new Vector();
-        StringTokenizer st = new StringTokenizer(path, File.separator);
-        while (st.hasMoreTokens()) {
-            ret.addElement(st.nextToken());
-        }
-        return ret;
+        return SelectorUtils.tokenizePath(path);
      }
-
+
      /**
       * Sets the base directory to be scanned. This is the directory which is
       * scanned recursively. All '/' and '\' characters are replaced by
       * <code>File.separatorChar</code>, so the separator used need not match
       * <code>File.separatorChar</code>.
       *
-     * @param basedir The base directory to scan.
+     * @param basedir The base directory to scan.
       *                Must not be <code>null</code>.
       */
      public void setBasedir(String basedir) {
-        setBasedir(new File(basedir.replace('/', 
File.separatorChar).replace('\\', File.separatorChar)));
+        setBasedir(new File(basedir.replace('/', File.separatorChar).replace(
+                '\\', File.separatorChar)));
      }

      /**
-     * Sets the base directory to be scanned. This is the directory which is
+     * Sets the base directory to be scanned. This is the directory which is
       * scanned recursively.
       *
-     * @param basedir The base directory for scanning.
+     * @param basedir The base directory for scanning.
       *                Should not be <code>null</code>.
       */
      public void setBasedir(File basedir) {
@@ -668,7 +391,7 @@
      }

      /**
-     * Returns the base directory to be scanned.
+     * Returns the base directory to be scanned.
       * This is the directory which is scanned recursively.
       *
       * @return the base directory to be scanned
@@ -680,7 +403,7 @@
      /**
       * Sets whether or not the file system should be regarded as case 
sensitive.
       *
-     * @param isCaseSensitive whether or not the file system should be
+     * @param isCaseSensitive whether or not the file system should be
       *                        regarded as a case sensitive one
       */
      public void setCaseSensitive(boolean isCaseSensitive) {
@@ -688,16 +411,16 @@
      }

      /**
-     * Sets the list of include patterns to use. All '/' and '\' characters
-     * are replaced by <code>File.separatorChar</code>, so the separator used
+     * Sets the list of include patterns to use. All '/' and '\' characters
+     * are replaced by <code>File.separatorChar</code>, so the separator used
       * need not match <code>File.separatorChar</code>.
       * <p>
       * When a pattern ends with a '/' or '\', "**" is appended.
       *
       * @param includes A list of include patterns.
-     *                 May be <code>null</code>, indicating that all files
+     *                 May be <code>null</code>, indicating that all files
       *                 should be included. If a non-<code>null</code>
-     *                 list is given, all elements must be
+     *                 list is given, all elements must be
       * non-<code>null</code>.
       */
      public void setIncludes(String[] includes) {
@@ -707,7 +430,8 @@
              this.includes = new String[includes.length];
              for (int i = 0; i < includes.length; i++) {
                  String pattern;
-                pattern = includes[i].replace('/', 
File.separatorChar).replace('\\', File.separatorChar);
+                pattern = includes[i].replace('/', 
File.separatorChar).replace(
+                        '\\', File.separatorChar);
                  if (pattern.endsWith(File.separator)) {
                      pattern += "**";
                  }
@@ -716,16 +440,17 @@
          }
      }

+
      /**
-     * Sets the list of exclude patterns to use. All '/' and '\' characters
-     * are replaced by <code>File.separatorChar</code>, so the separator used
+     * Sets the list of exclude patterns to use. All '/' and '\' characters
+     * are replaced by <code>File.separatorChar</code>, so the separator used
       * need not match <code>File.separatorChar</code>.
       * <p>
       * When a pattern ends with a '/' or '\', "**" is appended.
       *
-     * @param excludes A list of exclude patterns.
-     *                 May be <code>null</code>, indicating that no files
-     *                 should be excluded. If a non-<code>null</code> list is
+     * @param excludes A list of exclude patterns.
+     *                 May be <code>null</code>, indicating that no files
+     *                 should be excluded. If a non-<code>null</code> list is
       *                 given, all elements must be non-<code>null</code>.
       */
      public void setExcludes(String[] excludes) {
@@ -735,7 +460,8 @@
              this.excludes = new String[excludes.length];
              for (int i = 0; i < excludes.length; i++) {
                  String pattern;
-                pattern = excludes[i].replace('/', 
File.separatorChar).replace('\\', File.separatorChar);
+                pattern = excludes[i].replace('/', 
File.separatorChar).replace(
+                        '\\', File.separatorChar);
                  if (pattern.endsWith(File.separator)) {
                      pattern += "**";
                  }
@@ -744,11 +470,22 @@
          }
      }

+
+    /**
+     * Sets the selectors that will select the filelist.
+     *
+     * @param selectors specifies the selectors to be invoked on a scan
+     */
+    public void setSelectors(FileSelector[] selectors) {
+        this.selectors = selectors;
+    }
+
+
      /**
       * Returns whether or not the scanner has included all the files or
       * directories it has come across so far.
       *
-     * @return <code>true</code> if all files and directories which have
+     * @return <code>true</code> if all files and directories which have
       *         been found so far have been included.
       */
      public boolean isEverythingIncluded() {
@@ -757,9 +494,10 @@

      /**
       * Scans the base directory for files which match at least one include
-     * pattern and don't match any exclude patterns.
+     * pattern and don't match any exclude patterns. If there are selectors
+     * then the files must pass muster there, as well.
       *
-     * @exception IllegalStateException if the base directory was set
+     * @exception IllegalStateException if the base directory was set
       *            incorrectly (i.e. if it is <code>null</code>, doesn't 
exist,
       *            or isn't a directory).
       */
@@ -788,13 +526,19 @@
          filesIncluded    = new Vector();
          filesNotIncluded = new Vector();
          filesExcluded    = new Vector();
+        filesDeselected  = new Vector();
          dirsIncluded     = new Vector();
          dirsNotIncluded  = new Vector();
          dirsExcluded     = new Vector();
+        dirsDeselected   = new Vector();

          if (isIncluded("")) {
              if (!isExcluded("")) {
-                dirsIncluded.addElement("");
+                if (isSelected("",basedir)) {
+                    dirsIncluded.addElement("");
+                } else {
+                    dirsDeselected.addElement("");
+                }
              } else {
                  dirsExcluded.addElement("");
              }
@@ -806,8 +550,8 @@

      /**
       * Top level invocation for a slow scan. A slow scan builds up a full
-     * list of excluded/included files/directories, whereas a fast scan
-     * will only have full results for included files, as it ignores
+     * list of excluded/included files/directories, whereas a fast scan
+     * will only have full results for included files, as it ignores
       * directories which can't possibly hold any included files/directories.
       * <p>
       * Returns immediately if a slow scan has already been completed.
@@ -843,12 +587,12 @@
      /**
       * Scans the given directory for files and directories. Found files and
       * directories are placed in their respective collections, based on the
-     * matching of includes and excludes. When a directory is found, it is
-     * scanned recursively.
+     * matching of includes, excludes, and the selectors.  When a directory
+     * is found, it is scanned recursively.
       *
       * @param dir   The directory to scan. Must not be <code>null</code>.
-     * @param vpath The path relative to the base directory (needed to
-     *              prevent problems with an absolute path when using
+     * @param vpath The path relative to the base directory (needed to
+     *              prevent problems with an absolute path when using
       *              dir). Must not be <code>null</code>.
       * @param fast  Whether or not this call is part of a fast scan.
       *
@@ -881,10 +625,19 @@
              if (file.isDirectory()) {
                  if (isIncluded(name)) {
                      if (!isExcluded(name)) {
-                        dirsIncluded.addElement(name);
-                        if (fast) {
-                            scandir(file, name + File.separator, fast);
+                        if (isSelected(name,file)) {
+                            dirsIncluded.addElement(name);
+                            if (fast) {
+                                scandir(file, name + File.separator, fast);
+                            }
+                        } else {
+                            everythingIncluded = false;
+                            dirsDeselected.addElement(name);
+                            if (fast && couldHoldIncluded(name)) {
+                                scandir(file, name + File.separator, fast);
+                            }
                          }
+
                      } else {
                          everythingIncluded = false;
                          dirsExcluded.addElement(name);
@@ -905,7 +658,12 @@
              } else if (file.isFile()) {
                  if (isIncluded(name)) {
                      if (!isExcluded(name)) {
-                        filesIncluded.addElement(name);
+                        if (isSelected(name,file)) {
+                            filesIncluded.addElement(name);
+                        } else {
+                            everythingIncluded = false;
+                            filesDeselected.addElement(name);
+                        }
                      } else {
                          everythingIncluded = false;
                          filesExcluded.addElement(name);
@@ -919,7 +677,7 @@
      }

      /**
-     * Tests whether or not a name matches against at least one include
+     * Tests whether or not a name matches against at least one include
       * pattern.
       *
       * @param name The name to match. Must not be <code>null</code>.
@@ -936,11 +694,11 @@
      }

      /**
-     * Tests whether or not a name matches the start of at least one include
+     * Tests whether or not a name matches the start of at least one include
       * pattern.
       *
       * @param name The name to match. Must not be <code>null</code>.
-     * @return <code>true</code> when the name matches against the start 
of at
+     * @return <code>true</code> when the name matches against the start of at
       *         least one include pattern, or <code>false</code> otherwise.
       */
      protected boolean couldHoldIncluded(String name) {
@@ -953,7 +711,7 @@
      }

      /**
-     * Tests whether or not a name matches against at least one exclude
+     * Tests whether or not a name matches against at least one exclude
       * pattern.
       *
       * @param name The name to match. Must not be <code>null</code>.
@@ -970,7 +728,26 @@
      }

      /**
-     * Returns the names of the files which matched at least one of the
+     * Tests whether a name should be selected.
+     *
+     * @param name the filename to check for selecting
+     * @param file the java.io.File object for this filename
+     * @return <code>false</code> when the selectors says that the file
+     *         should not be selected, <code>true</code> otherwise.
+     */
+    protected boolean isSelected(String name, File file) {
+        if (selectors != null) {
+            for (int i = 0; i < selectors.length; i++) {
+                if ((selectors[i].isSelected(basedir, name, file)) == false) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the names of the files which matched at least one of the
       * include patterns and none of the exclude patterns.
       * The names are relative to the base directory.
       *
@@ -981,19 +758,19 @@
          int count = filesIncluded.size();
          String[] files = new String[count];
          for (int i = 0; i < count; i++) {
-            files[i] = (String) filesIncluded.elementAt(i);
+            files[i] = (String)filesIncluded.elementAt(i);
          }
          return files;
      }

      /**
-     * Returns the names of the files which matched none of the include
+     * Returns the names of the files which matched none of the include
       * patterns. The names are relative to the base directory. This involves
       * performing a slow scan if one has not already been completed.
       *
-     * @return the names of the files which matched none of the include
+     * @return the names of the files which matched none of the include
       *         patterns.
-     *
+     *
       * @see #slowScan
       */
      public String[] getNotIncludedFiles() {
@@ -1001,20 +778,20 @@
          int count = filesNotIncluded.size();
          String[] files = new String[count];
          for (int i = 0; i < count; i++) {
-            files[i] = (String) filesNotIncluded.elementAt(i);
+            files[i] = (String)filesNotIncluded.elementAt(i);
          }
          return files;
      }

      /**
-     * Returns the names of the files which matched at least one of the
+     * Returns the names of the files which matched at least one of the
       * include patterns and at least one of the exclude patterns.
       * The names are relative to the base directory. This involves
       * performing a slow scan if one has not already been completed.
       *
-     * @return the names of the files which matched at least one of the
+     * @return the names of the files which matched at least one of the
       *         include patterns and at at least one of the exclude patterns.
-     *
+     *
       * @see #slowScan
       */
      public String[] getExcludedFiles() {
@@ -1022,13 +799,32 @@
          int count = filesExcluded.size();
          String[] files = new String[count];
          for (int i = 0; i < count; i++) {
-            files[i] = (String) filesExcluded.elementAt(i);
+            files[i] = (String)filesExcluded.elementAt(i);
+        }
+        return files;
+    }
+
+    /**
+     * Returns the names of the files which were selected. The names
+     * are relative to the base directory. This involves performing
+     * a slow scan if one has not already been completed.
+     *
+     * @return the names of the files which were selected.
+     *
+     * @see #slowScan
+     */
+    public String[] getDeselectedFiles() {
+        slowScan();
+        int count = filesDeselected.size();
+        String[] files = new String[count];
+        for (int i = 0; i < count; i++) {
+            files[i] = (String)filesDeselected.elementAt(i);
          }
          return files;
      }

      /**
-     * Returns the names of the directories which matched at least one of the
+     * Returns the names of the directories which matched at least one of the
       * include patterns and none of the exclude patterns.
       * The names are relative to the base directory.
       *
@@ -1039,7 +835,7 @@
          int count = dirsIncluded.size();
          String[] directories = new String[count];
          for (int i = 0; i < count; i++) {
-            directories[i] = (String) dirsIncluded.elementAt(i);
+            directories[i] = (String)dirsIncluded.elementAt(i);
          }
          return directories;
      }
@@ -1051,7 +847,7 @@
       *
       * @return the names of the directories which matched none of the include
       * patterns.
-     *
+     *
       * @see #slowScan
       */
      public String[] getNotIncludedDirectories() {
@@ -1059,20 +855,20 @@
          int count = dirsNotIncluded.size();
          String[] directories = new String[count];
          for (int i = 0; i < count; i++) {
-            directories[i] = (String) dirsNotIncluded.elementAt(i);
+            directories[i] = (String)dirsNotIncluded.elementAt(i);
          }
          return directories;
      }

      /**
-     * Returns the names of the directories which matched at least one of the
+     * Returns the names of the directories which matched at least one of the
       * include patterns and at least one of the exclude patterns.
       * The names are relative to the base directory. This involves
       * performing a slow scan if one has not already been completed.
-     *
-     * @return the names of the directories which matched at least one of the
+     *
+     * @return the names of the directories which matched at least one of the
       * include patterns and at least one of the exclude patterns.
-     *
+     *
       * @see #slowScan
       */
      public String[] getExcludedDirectories() {
@@ -1080,7 +876,26 @@
          int count = dirsExcluded.size();
          String[] directories = new String[count];
          for (int i = 0; i < count; i++) {
-            directories[i] = (String) dirsExcluded.elementAt(i);
+            directories[i] = (String)dirsExcluded.elementAt(i);
+        }
+        return directories;
+    }
+
+    /**
+     * Returns the names of the directories which were selected. The names
+     * are relative to the base directory. This involves performing a
+     * slow scan if one has not already been completed.
+     *
+     * @return the names of the directories which were selected.
+     *
+     * @see #slowScan
+     */
+    public String[] getDeselectedDirectories() {
+        slowScan();
+        int count = dirsDeselected.size();
+        String[] directories = new String[count];
+        for (int i = 0; i < count; i++) {
+            directories[i] = (String)dirsDeselected.elementAt(i);
          }
          return directories;
      }
@@ -1096,7 +911,8 @@
              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] = DEFAULTEXCLUDES[i].replace('/',
+                    File.separatorChar).replace('\\', File.separatorChar);
          }
          excludes = newExcludes;
      }
Index: src/main/org/apache/tools/ant/types/AbstractFileSet.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/types/AbstractFileSet.java,v
retrieving revision 1.4
diff -u -r1.4 AbstractFileSet.java
--- src/main/org/apache/tools/ant/types/AbstractFileSet.java	15 Apr 2002 
12:11:50 -0000	1.4
+++ src/main/org/apache/tools/ant/types/AbstractFileSet.java	29 Apr 2002 
22:07:56 -0000
@@ -57,6 +57,7 @@
  import org.apache.tools.ant.FileScanner;
  import org.apache.tools.ant.DirectoryScanner;
  import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.*;

  import java.io.File;
  import java.util.Stack;
@@ -70,23 +71,26 @@
   *
   * <p>Common base class for DirSet and FileSet.</p>
   *
- * @author <a href="mailto:ajkuiper@wxs.nl">Arnout J. Kuiper</a>
+ * @author <a href="mailto:ajkuiper@wxs.nl">Arnout J. Kuiper</a>
   * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
   * @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
   * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
   * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
   * @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
+ * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
   */
-public abstract class AbstractFileSet extends DataType implements Cloneable {
-
+public abstract class AbstractFileSet extends DataType implements Cloneable,
+        SelectorContainer {
+
      private PatternSet defaultPatterns = new PatternSet();
      private Vector additionalPatterns = new Vector();
+    private Vector selectors = new Vector();

      private File dir;
      private boolean useDefaultExcludes = true;
      private boolean isCaseSensitive = true;

-
+
      public AbstractFileSet() {
          super();
      }
@@ -104,7 +108,7 @@
       * Makes this instance in effect a reference to another instance.
       *
       * <p>You must not set another attribute or nest elements inside
-     * this element if you make it a reference.</p>
+     * this element if you make it a reference.</p>
       */
      public void setRefid(Reference r) throws BuildException {
          if (dir != null || defaultPatterns.hasPatterns()) {
@@ -158,7 +162,7 @@
          }
          return defaultPatterns.createInclude();
      }
-
+
      /**
       * add a name entry on the include files list
       */
@@ -168,7 +172,7 @@
          }
          return defaultPatterns.createIncludesFile();
      }
-
+
      /**
       * add a name entry on the exclude list
       */
@@ -188,7 +192,7 @@
          }
          return defaultPatterns.createExcludesFile();
      }
-
+
      /**
       * Appends <code>includes</code> to the current list of include
       * patterns.
@@ -224,7 +228,7 @@
      /**
       * Sets the name of the file containing the includes patterns.
       *
-     * @param incl The file to fetch the include patterns from.
+     * @param incl The file to fetch the include patterns from.
       */
       public void setIncludesfile(File incl) throws BuildException {
           if (isReference()) {
@@ -237,7 +241,7 @@
      /**
       * Sets the name of the file containing the includes patterns.
       *
-     * @param excl The file to fetch the exclude patterns from.
+     * @param excl The file to fetch the exclude patterns from.
       */
       public void setExcludesfile(File excl) throws BuildException {
           if (isReference()) {
@@ -250,7 +254,7 @@
      /**
       * Sets whether default exclusions should be used or not.
       *
-     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
       *                           should be used, "false"|"off"|"no" when they
       *                           shouldn't be used.
       */
@@ -272,8 +276,8 @@
          this.isCaseSensitive = isCaseSensitive;
      }

-
-
+
+
      /**
       * sets the name used for this datatype instance.
       */
@@ -290,14 +294,14 @@
                  }
              }
          }
-
+
          String classname = getClass().getName();
-
+
          int dotIndex = classname.lastIndexOf(".");
          if (dotIndex == -1) {
              return classname;
          }
-        return classname.substring(dotIndex + 1);
+        return classname.substring(dotIndex + 1);
      }

      /**
@@ -326,12 +330,12 @@
          ds.scan();
          return ds;
      }
-
+
      public void setupDirectoryScanner(FileScanner ds, Project p) {
          if (ds == null) {
              throw new IllegalArgumentException("ds cannot be null");
          }
-
+
          ds.setBasedir(dir);

          final int count = additionalPatterns.size();
@@ -340,11 +344,16 @@
              defaultPatterns.append((PatternSet) o, p);
          }

-        p.log(getDataTypeName() + ": Setup scanner in dir " + dir +
+        p.log(getDataTypeName() + ": Setup scanner in dir " + dir +
              " with " + defaultPatterns, Project.MSG_DEBUG);
-
+
          ds.setIncludes(defaultPatterns.getIncludePatterns(p));
          ds.setExcludes(defaultPatterns.getExcludePatterns(p));
+        if (ds instanceof SelectorScanner) {
+            SelectorScanner ss = (SelectorScanner)ds;
+            ss.setSelectors(getSelectors(p));
+        }
+
          if (useDefaultExcludes) {
              ds.addDefaultExcludes();
          }
@@ -353,7 +362,7 @@

      /**
       * Performs the check for circular references and returns the
-     * referenced FileSet.
+     * referenced FileSet.
       */
      protected AbstractFileSet getRef(Project p) {
          if (!checked) {
@@ -361,15 +370,164 @@
              stk.push(this);
              dieOnCircularReference(stk, p);
          }
-
+
          Object o = ref.getReferencedObject(p);
          if (!getClass().isAssignableFrom(o.getClass())) {
-            String msg = ref.getRefId() + " doesn\'t denote a "
+            String msg = ref.getRefId() + " doesn\'t denote a "
                  + getDataTypeName();
              throw new BuildException(msg);
          } else {
              return (AbstractFileSet) o;
          }
+    }
+
+    // SelectorContainer methods
+
+    /**
+     * Indicates whether there are any selectors here.
+     *
+     * @return whether any selectors are in this container
+     */
+    public boolean hasSelectors() {
+        return !(selectors.isEmpty());
+    }
+
+    /**
+     * Gives the count of the number of selectors in this container
+     *
+     * @return the number of selectors in this container
+     */
+    public int selectorCount() {
+        return selectors.size();
+    }
+
+    /**
+     * Returns the set of selectors as an array.
+     *
+     * @return an array of selectors in this container
+     */
+    public FileSelector[] getSelectors(Project p) {
+        if (isReference()) {
+            return getRef(p).getSelectors(p);
+        } else {
+            FileSelector[] result = new FileSelector[selectors.size()];
+            selectors.copyInto(result);
+            return result;
+        }
+    }
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     *
+     * @return an enumerator that goes through each of the selectors
+     */
+    public Enumeration selectorElements() {
+        return selectors.elements();
+    }
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new selector to add
+     */
+    public void appendSelector(FileSelector selector) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        selectors.addElement(selector);
+    }
+
+    /* Methods below all implement the static selectors */
+
+    /**
+     * add an "And" selector entry on the selector list
+     */
+    public void addAnd(AndSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an "Or" selector entry on the selector list
+     */
+    public void addOr(OrSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a "Not" selector entry on the selector list
+     */
+    public void addNot(NotSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a "None" selector entry on the selector list
+     */
+    public void addNone(NoneSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a majority selector entry on the selector list
+     */
+    public void addMajority(MajoritySelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector date entry on the selector list
+     */
+    public void addDateselect(DateSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector size entry on the selector list
+     */
+    public void addSizeselect(SizeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector filename entry on the selector list
+     */
+    public void addFilenameselect(FilenameSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an extended selector entry on the selector list
+     */
+    public void addExtendSelect(ExtendSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a contains selector entry on the selector list
+     */
+    public void addContainsSelect(ContainsSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a present selector entry on the selector list
+     */
+    public void addPresentSelect(PresentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a depth selector entry on the selector list
+     */
+    public void addDepthSelect(DepthSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a depends selector entry on the selector list
+     */
+    public void addDependSelect(DependSelector selector) {
+        appendSelector(selector);
      }

  }
Mime
View raw message