flex-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aha...@apache.org
Subject [06/14] port DirectoryScanner from Ant code base and hook up FileSet to it
Date Mon, 09 Dec 2013 23:30:08 GMT
http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/3a58a60e/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/DirectoryScanner.as
----------------------------------------------------------------------
diff --git a/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/DirectoryScanner.as b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/DirectoryScanner.as
new file mode 100644
index 0000000..e6f1927
--- /dev/null
+++ b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/DirectoryScanner.as
@@ -0,0 +1,1764 @@
+/*
+*  Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  See the NOTICE file distributed with
+*  this work for additional information regarding copyright ownership.
+*  The ASF licenses this file to You under the Apache License, Version 2.0
+*  (the "License"); you may not use this file except in compliance with
+*  the License.  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*/
+
+package org.apache.flex.ant.tags.filesetClasses
+{
+    import flash.filesystem.File;
+    import flash.utils.Dictionary;
+    
+    import org.apache.flex.ant.Ant;
+    import org.apache.flex.ant.tags.filesetClasses.Resource;
+    import org.apache.flex.ant.tags.filesetClasses.SelectorUtils;
+    import org.apache.flex.ant.tags.filesetClasses.exceptions.BuildException;
+    import org.apache.flex.ant.tags.filesetClasses.exceptions.IOException;
+    import org.apache.flex.ant.tags.filesetClasses.exceptions.IllegalStateException;
+    
+    /**
+     * Ported from org.apache.tools.ant.DirectoryScanner.java on 12/3/13.
+     * Class for scanning a directory for files/directories which match certain
+     * criteria.
+     * <p>
+     * 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 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 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. When
+     * no selectors are supplied, none are applied.
+     * <p>
+     * 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",
+     * "def","ghi" and "xyz.java".
+     * The same is done for the pattern against which should be matched.
+     * <p>
+     * The segments of the name and the pattern are then matched against each
+     * other. When '**' is used for a path segment in the pattern, it matches
+     * zero or more path segments of the name.
+     * <p>
+     * There is a special case regarding the use of <code>File.separator</code>s
+     * at the beginning of the pattern and the string to match:<br>
+     * When a pattern starts with a <code>File.separator</code>, the string
+     * to match must also start with a <code>File.separator</code>.
+     * When a pattern does not start with a <code>File.separator</code>, the
+     * string to match may not start with a <code>File.separator</code>.
+     * When one of these rules is not obeyed, the string will not
+     * match.
+     * <p>
+     * When a name path segment is matched against a pattern path segment, the
+     * following special characters can be used:<br>
+     * '*' matches zero or more characters<br>
+     * '?' matches one character.
+     * <p>
+     * Examples:
+     * <p>
+     * "**\*.class" matches all .class files/dirs in a directory tree.
+     * <p>
+     * "test\a??.java" matches all files/dirs which start with an 'a', then two
+     * more characters and then ".java", in a directory called test.
+     * <p>
+     * "**" matches everything in a directory tree.
+     * <p>
+     * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
+     * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
+     * <p>
+     * Case sensitivity may be turned off if necessary. By default, it is
+     * turned on.
+     * <p>
+     * Example of usage:
+     * <pre>
+     *   String[] includes = {"**\\*.class"};
+     *   String[] excludes = {"modules\\*\\**"};
+     *   ds.setIncludes(includes);
+     *   ds.setExcludes(excludes);
+     *   ds.setBasedir(new File("test"));
+     *   ds.setCaseSensitive(true);
+     *   ds.scan();
+     *
+     *   System.out.println("FILES:");
+     *   String[] files = ds.getIncludedFiles();
+     *   for (int i = 0; i < files.length; i++) {
+     *     System.out.println(files[i]);
+     *   }
+     * </pre>
+     * This will scan a directory called test for .class files, but excludes all
+     * files in all proper subdirectories of a directory called "modules"
+     *
+     */
+    public class DirectoryScanner
+        /* implements FileScanner, SelectorScanner, ResourceFactory*/ {
+        
+        /**
+         * Patterns which should be excluded by default.
+         *
+         * <p>Note that you can now add patterns to the list of default
+         * excludes.  Added patterns will not become part of this array
+         * that has only been kept around for backwards compatibility
+         * reasons.</p>
+         *
+         * @deprecated since 1.6.x.
+         *             Use the {@link #getDefaultExcludes getDefaultExcludes}
+         *             method instead.
+         */
+        protected static const DEFAULTEXCLUDES:Vector.<String> = Vector.<String>([
+            // Miscellaneous typical temporary files
+            SelectorUtils.DEEP_TREE_MATCH + "/*~",
+                SelectorUtils.DEEP_TREE_MATCH + "/#*#",
+                SelectorUtils.DEEP_TREE_MATCH + "/.#*",
+                SelectorUtils.DEEP_TREE_MATCH + "/%*%",
+                SelectorUtils.DEEP_TREE_MATCH + "/._*",
+                
+                // CVS
+                SelectorUtils.DEEP_TREE_MATCH + "/CVS",
+                SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH,
+                SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore",
+                
+                // SCCS
+                SelectorUtils.DEEP_TREE_MATCH + "/SCCS",
+                SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH,
+                
+                // Visual SourceSafe
+                SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc",
+                
+                // Subversion
+                SelectorUtils.DEEP_TREE_MATCH + "/.svn",
+                SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH,
+                
+                // Git
+                SelectorUtils.DEEP_TREE_MATCH + "/.git",
+                SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH,
+                SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes",
+                SelectorUtils.DEEP_TREE_MATCH + "/.gitignore",
+                SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules",
+                
+                // Mercurial
+                SelectorUtils.DEEP_TREE_MATCH + "/.hg",
+                SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH,
+                SelectorUtils.DEEP_TREE_MATCH + "/.hgignore",
+                SelectorUtils.DEEP_TREE_MATCH + "/.hgsub",
+                SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate",
+                SelectorUtils.DEEP_TREE_MATCH + "/.hgtags",
+                
+                // Bazaar
+                SelectorUtils.DEEP_TREE_MATCH + "/.bzr",
+                SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH,
+                SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore",
+                
+                // Mac
+                SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store"
+        ]);
+        
+        /**
+         * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks}
+         * @since Ant 1.8.0
+         */
+        public static const MAX_LEVELS_OF_SYMLINKS:int = 5;
+        /**
+         * The end of the exception message if something that should be
+         * there doesn't exist.
+         */
+        public static const DOES_NOT_EXIST_POSTFIX:String = " does not exist.";
+        
+        /** Helper. */
+        private static const FILE_UTILS:FileUtils = FileUtils.getFileUtils();
+        
+        /**
+         * Patterns which should be excluded by default.
+         *
+         * @see #addDefaultExcludes()
+         */
+        private static const defaultExcludes:Vector.<String> = resetDefaultExcludes();
+        
+        // CheckStyle:VisibilityModifier OFF - bc
+        
+        /** The base directory to be scanned. */
+        protected var basedir:File;
+        
+        /** The patterns for the files to be included. */
+        protected var includes:Vector.<String>;
+        
+        /** The patterns for the files to be excluded. */
+        protected var excludes:Vector.<String>;
+        
+        /** Selectors that will filter which files are in our candidate list. */
+        protected var selectors:Vector.<FileSelector> = null;
+        
+        /**
+         * The files which matched at least one include and no excludes
+         * and were selected.
+         */
+        protected var filesIncluded:Vector.<String>;
+        
+        /** The files which did not match any includes or selectors. */
+        protected var filesNotIncluded:Vector.<String>;
+        
+        /**
+         * The files which matched at least one include and at least
+         * one exclude.
+         */
+        protected var filesExcluded:Vector.<String>;
+        
+        /**
+         * The directories which matched at least one include and no excludes
+         * and were selected.
+         */
+        protected var dirsIncluded:Vector.<String>;
+        
+        /** The directories which were found and did not match any includes. */
+        protected var dirsNotIncluded:Vector.<String>;
+        
+        /**
+         * The directories which matched at least one include and at least one
+         * exclude.
+         */
+        protected var dirsExcluded:Vector.<String>;
+        
+        /**
+         * The files which matched at least one include and no excludes and
+         * which a selector discarded.
+         */
+        protected var filesDeselected:Vector.<String>;
+        
+        /**
+         * The directories which matched at least one include and no excludes
+         * but which a selector discarded.
+         */
+        protected var dirsDeselected:Vector.<String>;
+        
+        /** Whether or not our results were built by a slow scan. */
+        protected var haveSlowResults:Boolean = false;
+        
+        /**
+         * Whether or not the file system should be treated as a case sensitive
+         * one.
+         */
+        protected var _isCaseSensitive:Boolean = true;
+        
+        /**
+         * Whether a missing base directory is an error.
+         * @since Ant 1.7.1
+         */
+        protected var errorOnMissingDir:Boolean = true;
+        
+        /**
+         * Whether or not symbolic links should be followed.
+         *
+         * @since Ant 1.5
+         */
+        private var followSymlinks:Boolean = true;
+        
+        /** Whether or not everything tested so far has been included. */
+        protected var everythingIncluded:Boolean = true;
+        
+        // CheckStyle:VisibilityModifier ON
+        
+        /**
+         * List of all scanned directories.
+         *
+         * @since Ant 1.6
+         */
+        private var scannedDirs:Vector.<String> = new Vector.<String>();
+        
+        /**
+         * Map of all include patterns that are full file names and don't
+         * contain any wildcards.
+         *
+         * <p>Maps pattern string to TokenizedPath.</p>
+         *
+         * <p>If this instance is not case sensitive, the file names get
+         * turned to upper case.</p>
+         *
+         * <p>Gets lazily initialized on the first invocation of
+         * isIncluded or isExcluded and cleared at the end of the scan
+         * method (cleared in clearCaches, actually).</p>
+         *
+         * @since Ant 1.8.0
+         */
+        private var includeNonPatterns:Object = {}; //new HashMap<String, TokenizedPath>();
+        
+        /**
+         * Map of all exclude patterns that are full file names and don't
+         * contain any wildcards.
+         *
+         * <p>Maps pattern string to TokenizedPath.</p>
+         *
+         * <p>If this instance is not case sensitive, the file names get
+         * turned to upper case.</p>
+         *
+         * <p>Gets lazily initialized on the first invocation of
+         * isIncluded or isExcluded and cleared at the end of the scan
+         * method (cleared in clearCaches, actually).</p>
+         *
+         * @since Ant 1.8.0
+         */
+        private var excludeNonPatterns:Object = {}; //new HashMap<String, TokenizedPath>();
+        
+        /**
+         * Array of all include patterns that contain wildcards.
+         *
+         * <p>Gets lazily initialized on the first invocation of
+         * isIncluded or isExcluded and cleared at the end of the scan
+         * method (cleared in clearCaches, actually).</p>
+         */
+        private var includePatterns:Vector.<TokenizedPattern>;
+        
+        /**
+         * Array of all exclude patterns that contain wildcards.
+         *
+         * <p>Gets lazily initialized on the first invocation of
+         * isIncluded or isExcluded and cleared at the end of the scan
+         * method (cleared in clearCaches, actually).</p>
+         */
+        private var excludePatterns:Vector.<TokenizedPattern>;
+        
+        /**
+         * Have the non-pattern sets and pattern arrays for in- and
+         * excludes been initialized?
+         *
+         * @since Ant 1.6.3
+         */
+        private var areNonPatternSetsReady:Boolean = false;
+        
+        /**
+         * Scanning flag.
+         *
+         * @since Ant 1.6.3
+         */
+        private var scanning:Boolean = false;
+        
+        /**
+         * Scanning lock.
+         *
+         * @since Ant 1.6.3
+         */
+        private var scanLock:Object = new Object();
+        
+        /**
+         * Slow scanning flag.
+         *
+         * @since Ant 1.6.3
+         */
+        private var slowScanning:Boolean = false;
+        
+        /**
+         * Slow scanning lock.
+         *
+         * @since Ant 1.6.3
+         */
+        private var slowScanLock:Object = new Object();
+        
+        /**
+         * Exception thrown during scan.
+         *
+         * @since Ant 1.6.3
+         */
+        private var illegal:IllegalStateException = null;
+        
+        /**
+         * The maximum number of times a symbolic link may be followed
+         * during a scan.
+         *
+         * @since Ant 1.8.0
+         */
+        private var maxLevelsOfSymlinks:int = MAX_LEVELS_OF_SYMLINKS;
+        
+        
+        /**
+         * Absolute paths of all symlinks that haven't been followed but
+         * would have been if followsymlinks had been true or
+         * maxLevelsOfSymlinks had been higher.
+         *
+         * @since Ant 1.8.0
+         */
+        private var notFollowedSymlinks:Vector.<String> = new Vector.<String>();
+        
+        /**
+         * Sole constructor.
+         */
+        public function DirectoryScanner() {
+        }
+                
+        /**
+         * Test 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>
+         * and <code>str=b</code> will yield <code>true</code>.
+         *
+         * @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
+         *                <code>null</code>.
+         * @param isCaseSensitive Whether or not matching should be performed
+         *                        case sensitively.
+         *
+         * @return whether or not a given path matches the start of a given
+         * pattern up to the first "**".
+         */
+        protected static function matchPatternStart(pattern:String, str:String,
+            isCaseSensitive:Boolean = true):Boolean {
+                return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
+            }
+                
+        /**
+         * Test whether or not a given path matches a given pattern.
+         *
+         * @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
+         *                <code>null</code>.
+         * @param isCaseSensitive Whether or not matching should be performed
+         *                        case sensitively.
+         *
+         * @return <code>true</code> if the pattern matches against the string,
+         *         or <code>false</code> otherwise.
+         */
+        protected static function matchPath(pattern:String, str:String,
+            isCaseSensitive:Boolean = true):Boolean {
+                return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
+            }
+        
+        /**
+         * Test 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.
+         *                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
+         *                        case sensitively.
+         *
+         *
+         * @return <code>true</code> if the string matches against the pattern,
+         *         or <code>false</code> otherwise.
+         */
+        protected static function match(pattern:String, str:String,
+            isCaseSensitive:Boolean = true):Boolean{
+                return SelectorUtils.match(pattern, str, isCaseSensitive);
+            }
+        
+        
+        /**
+         * Get the list of patterns that should be excluded by default.
+         *
+         * @return An array of <code>String</code> based on the current
+         *         contents of the <code>defaultExcludes</code>
+         *         <code>Set</code>.
+         *
+         * @since Ant 1.6
+         */
+        public static function getDefaultExcludes():Vector.<String> {
+                return defaultExcludes.slice();
+        }
+        
+        /**
+         * Add a pattern to the default excludes unless it is already a
+         * default exclude.
+         *
+         * @param s   A string to add as an exclude pattern.
+         * @return    <code>true</code> if the string was added;
+         *            <code>false</code> if it already existed.
+         *
+         * @since Ant 1.6
+         */
+        public static function addDefaultExclude(s:String):Boolean {
+            if (defaultExcludes.indexOf(s) == -1)
+            {
+                defaultExcludes.push(s);
+                return true;
+            }
+            return false;
+        }
+        
+        /**
+         * Remove a string if it is a default exclude.
+         *
+         * @param s   The string to attempt to remove.
+         * @return    <code>true</code> if <code>s</code> was a default
+         *            exclude (and thus was removed);
+         *            <code>false</code> if <code>s</code> was not
+         *            in the default excludes list to begin with.
+         *
+         * @since Ant 1.6
+         */
+        public static function removeDefaultExclude(s:String):Boolean {
+            var index:int = defaultExcludes.indexOf(s);
+            if (index == -1)
+                return false;
+            defaultExcludes.splice(index, 1);
+            return true;
+        }
+        
+        /**
+         * Go back to the hardwired default exclude patterns.
+         *
+         * @since Ant 1.6
+         */
+        public static function resetDefaultExcludes():Vector.<String> {
+                defaultExcludes.length = 0;
+                for (var i:int = 0; i < DEFAULTEXCLUDES.length; i++) {
+                    defaultExcludes.push(DEFAULTEXCLUDES[i]);
+                }
+                return defaultExcludes;
+        }
+        
+        /**
+         * Set 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.
+         */
+        public function setBasedir(basedir:Object):void {
+            if (basedir is File)
+                setBasedirFile(basedir as File);
+            else
+                setBasedirFile(basedir == null ? null
+                    : new File(basedir.replace('/', File.separator).replace(
+                    '\\', File.separator)));
+        }
+        
+        /**
+         * Set the base directory to be scanned. This is the directory which is
+         * scanned recursively.
+         *
+         * @param basedir The base directory for scanning.
+         */
+        public function setBasedirFile(basedir:File):void {
+            this.basedir = basedir;
+        }
+
+        /**
+         * Return the base directory to be scanned.
+         * This is the directory which is scanned recursively.
+         *
+         * @return the base directory to be scanned.
+         */
+        public function getBasedir():File {
+            return basedir;
+        }
+        
+        /**
+         * Find out whether include exclude patterns are matched in a
+         * case sensitive way.
+         * @return whether or not the scanning is case sensitive.
+         * @since Ant 1.6
+         */
+        public function isCaseSensitive():Boolean {
+            return _isCaseSensitive;
+        }
+        
+        /**
+         * Set whether or not include and exclude patterns are matched
+         * in a case sensitive way.
+         *
+         * @param isCaseSensitive whether or not the file system should be
+         *                        regarded as a case sensitive one.
+         */
+        public function setCaseSensitive(isCaseSensitive:Boolean):void {
+            _isCaseSensitive = isCaseSensitive;
+        }
+        
+        /**
+         * Sets whether or not a missing base directory is an error
+         *
+         * @param errorOnMissingDir whether or not a missing base directory
+         *                        is an error
+         * @since Ant 1.7.1
+         */
+        public function setErrorOnMissingDir(errorOnMissingDir:Boolean):void {
+            this.errorOnMissingDir = errorOnMissingDir;
+        }
+        
+        /**
+         * Get whether or not a DirectoryScanner follows symbolic links.
+         *
+         * @return flag indicating whether symbolic links should be followed.
+         *
+         * @since Ant 1.6
+         */
+        public function isFollowSymlinks():Boolean {
+            return followSymlinks;
+        }
+        
+        /**
+         * Set whether or not symbolic links should be followed.
+         *
+         * @param followSymlinks whether or not symbolic links should be followed.
+         */
+        public function setFollowSymlinks(followSymlinks:Boolean):void {
+            this.followSymlinks = followSymlinks;
+        }
+        
+        /**
+         * The maximum number of times a symbolic link may be followed
+         * during a scan.
+         *
+         * @since Ant 1.8.0
+         */
+        public function setMaxLevelsOfSymlinks(max:int):void {
+            maxLevelsOfSymlinks = max;
+        }
+        
+        /**
+         * Set the list of include patterns to use. All '/' and '\' characters
+         * are replaced by <code>File.separator</code>, so the separator used
+         * need not match <code>File.separator</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
+         *                 should be included. If a non-<code>null</code>
+         *                 list is given, all elements must be
+         *                 non-<code>null</code>.
+         */
+        public function setIncludes(includes:Vector.<String>):void {
+            if (includes == null) {
+                this.includes = null;
+            } else {
+                this.includes = new Vector.<String>(includes.length);
+                for (var i:int = 0; i < includes.length; i++) {
+                    this.includes[i] = normalizePattern(includes[i]);
+                }
+            }
+        }
+        
+        /**
+         * Set the list of exclude patterns to use. All '/' and '\' characters
+         * are replaced by <code>File.separator</code>, so the separator used
+         * need not match <code>File.separator</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
+         *                 given, all elements must be non-<code>null</code>.
+         */
+        public function setExcludes(excludes:Vector.<String>):void {
+            if (excludes == null) {
+                this.excludes = null;
+            } else {
+                this.excludes = new Vector.<String>(excludes.length);
+                for (var i:int = 0; i < excludes.length; i++) {
+                    this.excludes[i] = normalizePattern(excludes[i]);
+                }
+            }
+        }
+        
+        /**
+         * Add to the list of exclude patterns to use. All '/' and '\'
+         * characters are replaced by <code>File.separator</code>, so
+         * the separator used need not match <code>File.separator</code>.
+         * <p>
+         * When a pattern ends with a '/' or '\', "**" is appended.
+         *
+         * @param excludes A list of exclude patterns.
+         *                 May be <code>null</code>, in which case the
+         *                 exclude patterns don't get changed at all.
+         *
+         * @since Ant 1.6.3
+         */
+        public function addExcludes(excludes:Vector.<String>):void {
+            if (excludes != null && excludes.length > 0) {
+                if (this.excludes != null && this.excludes.length > 0) {
+                    var tmp:Vector.<String> = this.excludes.slice();
+                    for (var i:int = 0; i < excludes.length; i++) {
+                        tmp.push(
+                            normalizePattern(excludes[i]));
+                    }
+                    this.excludes = tmp;
+                } else {
+                    setExcludes(excludes);
+                }
+            }
+        }
+        
+        /**
+         * All '/' and '\' characters are replaced by
+         * <code>File.separator</code>, so the separator used need not
+         * match <code>File.separator</code>.
+         *
+         * <p> When a pattern ends with a '/' or '\', "**" is appended.
+         *
+         * @since Ant 1.6.3
+         */
+        private function normalizePattern(p:String):String {
+            var pattern:String = p.replace(/\//g, File.separator)
+                .replace(/\\/g, File.separator);
+            if (pattern.charAt(pattern.length - 1) == File.separator) {
+                pattern += SelectorUtils.DEEP_TREE_MATCH;
+            }
+            return pattern;
+        }
+        
+        /**
+         * Set the selectors that will select the filelist.
+         *
+         * @param selectors specifies the selectors to be invoked on a scan.
+         */
+        public function setSelectors(selectors:Vector.<FileSelector>):void {
+            this.selectors = selectors;
+        }
+        
+        /**
+         * Return 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
+         *         been found so far have been included.
+         */
+        public function isEverythingIncluded():Boolean {
+            return everythingIncluded;
+        }
+        
+        /**
+         * Scan for files which match at least one include pattern and don't match
+         * any exclude patterns. If there are selectors then the files must pass
+         * muster there, as well.  Scans under basedir, if set; otherwise the
+         * include patterns without leading wildcards specify the absolute paths of
+         * the files that may be included.
+         *
+         * @exception IllegalStateException if the base directory was set
+         *            incorrectly (i.e. if it doesn't exist or isn't a directory).
+         */
+        public function scan():void 
+        {
+            var savedBase:File = basedir;
+            try {
+                    illegal = null;
+                    clearResults();
+                    
+                    // set in/excludes to reasonable defaults if needed:
+                    var nullIncludes:Boolean = (includes == null);
+                    includes = nullIncludes
+                        ? Vector.<String>([SelectorUtils.DEEP_TREE_MATCH]) : includes;
+                    var nullExcludes:Boolean = (excludes == null);
+                    excludes = nullExcludes ? new Vector.<String>(0) : excludes;
+                    
+                    if (basedir != null && !followSymlinks
+                        && basedir.isSymbolicLink) {
+                        notFollowedSymlinks.push(basedir.nativePath);
+                        basedir = null;
+                    }
+                    
+                    if (basedir == null) {
+                        // if no basedir and no includes, nothing to do:
+                        if (nullIncludes) {
+                            return;
+                        }
+                    } else {
+                        if (!basedir.exists) {
+                            if (errorOnMissingDir) {
+                                illegal = new IllegalStateException("basedir "
+                                    + basedir
+                                    + DOES_NOT_EXIST_POSTFIX);
+                            } else {
+                                // Nothing to do - basedir does not exist
+                                return;
+                            }
+                        } else if (!basedir.isDirectory) {
+                            illegal = new IllegalStateException("basedir "
+                                + basedir
+                                + " is not a"
+                                + " directory.");
+                        }
+                        if (illegal != null) {
+                            throw illegal;
+                        }
+                    }
+                    if (isIncludedPath(TokenizedPath.EMPTY_PATH)) {
+                        if (!isExcludedPath(TokenizedPath.EMPTY_PATH)) {
+                            if (isSelected("", basedir)) {
+                                dirsIncluded.push("");
+                            } else {
+                                dirsDeselected.push("");
+                            }
+                        } else {
+                            dirsExcluded.push("");
+                        }
+                    } else {
+                        dirsNotIncluded.push("");
+                    }
+                    checkIncludePatterns();
+                    clearCaches();
+                    includes = nullIncludes ? null : includes;
+                    excludes = nullExcludes ? null : excludes;
+            } catch (ex:IOException) {
+                throw new BuildException(ex.message);
+            } finally {
+                basedir = savedBase;
+            }
+        }
+        
+        /**
+         * This routine is actually checking all the include patterns in
+         * order to avoid scanning everything under base dir.
+         * @since Ant 1.6
+         */
+        private function checkIncludePatterns():void 
+        {
+            ensureNonPatternSetsReady();
+            var newroots:Dictionary = new Dictionary();
+            
+            // put in the newroots map the include patterns without
+            // wildcard tokens
+            for (var i:int = 0; i < includePatterns.length; i++) {
+                var pattern:String = includePatterns[i].toString();
+                if (!shouldSkipPattern(pattern)) {
+                    newroots[includePatterns[i].rtrimWildcardTokens()] =
+                        pattern;
+                }
+            }
+            for each (var entry:Dictionary in includeNonPatterns.entrySet()) {
+                for (var p:String in entry)
+                {
+                    pattern = p;
+                    break;
+                }
+                if (!shouldSkipPattern(pattern)) {
+                    newroots[entry.getValue()] = pattern;
+                }
+            }
+            
+            if (newroots.hasOwnProperty(TokenizedPath.EMPTY_PATH)
+                && basedir != null) {
+                // we are going to scan everything anyway
+                scandir(basedir, "", true);
+            } else {
+                var canonBase:File = null;
+                if (basedir != null) {
+                    try {
+                        canonBase = new File(basedir.nativePath);
+                        canonBase.canonicalize();
+                    } catch (ex:IOException) {
+                        throw new BuildException(ex.message);
+                    }
+                }
+                // only scan directories that can include matched files or
+                // directories
+                for each (entry in newroots.entrySet()) {
+                    var currentPath:TokenizedPath;
+                    for (p in entry)
+                    {
+                        currentPath = p as TokenizedPath;
+                        break;
+                    }
+                    var currentelement:String = currentPath.toString();
+                    if (basedir == null
+                        && !FileUtils.isAbsolutePath(currentelement)) {
+                        continue;
+                    }
+                    var myfile:File = new File(basedir.nativePath + File.separator + currentelement);
+                    
+                    if (myfile.exists) {
+                        // may be on a case insensitive file system.  We want
+                        // the results to show what's really on the disk, so
+                        // we need to double check.
+                        try {
+                            var myCanonFile:File = new File(myfile.nativePath);
+                            myCanonFile.canonicalize();
+                            var path:String = (basedir == null)
+                                ? myCanonFile.nativePath
+                                : FILE_UTILS.removeLeadingPath(canonBase,
+                                    myCanonFile);
+                            if (!path == currentelement) {
+                                myfile = currentPath.findFile(basedir, true);
+                                if (myfile != null && basedir != null) {
+                                    currentelement = FILE_UTILS.removeLeadingPath(
+                                        basedir, myfile);
+                                    if (!currentPath.toString()
+                                        == currentelement) {
+                                        currentPath =
+                                            new TokenizedPath(currentelement);
+                                    }
+                                }
+                            }
+                        } catch (ex:IOException) {
+                            throw new BuildException(ex.message);
+                        }
+                    }
+                    
+                    if ((myfile == null || !myfile.exists) && !isCaseSensitive()) {
+                        var f:File = currentPath.findFile(basedir, false);
+                        if (f != null && f.exists) {
+                            // adapt currentelement to the case we've
+                            // actually found
+                            currentelement = (basedir == null)
+                                ? f.nativePath
+                                : FILE_UTILS.removeLeadingPath(basedir, f);
+                            myfile = f;
+                            currentPath = new TokenizedPath(currentelement);
+                        }
+                    }
+                    
+                    if (myfile != null && myfile.exists) {
+                        if (!followSymlinks && currentPath.isSymlink(basedir)) {
+                            if (!isExcludedPath(currentPath)) {
+                                notFollowedSymlinks.push(myfile.nativePath);
+                            }
+                            continue;
+                        }
+                        if (myfile.isDirectory) {
+                            if (isIncludedPath(currentPath)
+                                && currentelement.length > 0) {
+                                accountForIncludedDir(currentPath, myfile, true);
+                            }  else {
+                                scandirTokenizedPath(myfile, currentPath, true);
+                            }
+                        } else {
+                            var originalpattern:String;
+                            for (var q:* in entry)
+                            {
+                                originalpattern = entry[q];
+                            }
+                            var included:Boolean = isCaseSensitive()
+                                ? originalpattern == currentelement
+                                : originalpattern.toUpperCase() == currentelement.toUpperCase();
+                            if (included) {
+                                accountForIncludedFile(currentPath, myfile);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        
+        /**
+         * true if the pattern specifies a relative path without basedir
+         * or an absolute path not inside basedir.
+         *
+         * @since Ant 1.8.0
+         */
+        private function shouldSkipPattern(pattern:String):Boolean {
+            if (FileUtils.isAbsolutePath(pattern)) {
+                //skip abs. paths not under basedir, if set:
+                if (basedir != null
+                    && !SelectorUtils.matchPatternStart(pattern,
+                        basedir.nativePath,
+                        isCaseSensitive())) {
+                    return true;
+                }
+            } else if (basedir == null) {
+                //skip non-abs. paths if basedir == null:
+                return true;
+            }
+            return false;
+        }
+        
+        /**
+         * Clear the result caches for a scan.
+         */
+        protected function clearResults():void {
+            filesIncluded    = new Vector.<String>();
+            filesNotIncluded = new Vector.<String>();
+            filesExcluded    = new Vector.<String>();
+            filesDeselected  = new Vector.<String>();
+            dirsIncluded     = new Vector.<String>();
+            dirsNotIncluded  = new Vector.<String>();
+            dirsExcluded     = new Vector.<String>();
+            dirsDeselected   = new Vector.<String>();
+            everythingIncluded = (basedir != null);
+            scannedDirs.length = 0;
+            notFollowedSymlinks.length = 0;
+        }
+        
+        /**
+         * 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
+         * directories which can't possibly hold any included files/directories.
+         * <p>
+         * Returns immediately if a slow scan has already been completed.
+         */
+        protected function slowScan():void 
+        {
+            try {
+                    // set in/excludes to reasonable defaults if needed:
+                    var nullIncludes:Boolean = (includes == null);
+                    includes = nullIncludes
+                        ? Vector.<String>([SelectorUtils.DEEP_TREE_MATCH]) : includes;
+                    var nullExcludes:Boolean = (excludes == null);
+                    excludes = nullExcludes ? new Vector.<String>(0) : excludes;
+                    
+                    var excl:Vector.<String> = dirsExcluded.slice();
+                    
+                    var notIncl:Vector.<String> = dirsNotIncluded.slice();
+                    
+                    ensureNonPatternSetsReady();
+                    
+                    processSlowScan(excl);
+                    processSlowScan(notIncl);
+                    clearCaches();
+                    includes = nullIncludes ? null : includes;
+                    excludes = nullExcludes ? null : excludes;
+            } finally {
+                    haveSlowResults = true;
+                    slowScanning = false;
+                    slowScanLock.notifyAll();
+            }
+        }
+        
+        private function processSlowScan(arr:Vector.<String>):void {
+            for (var i:int = 0; i < arr.length; i++) {
+                var path:TokenizedPath  = new TokenizedPath(arr[i]);
+                if (!couldHoldIncludedPath(path) || contentsExcluded(path)) {
+                    scandirTokenizedPath(new File(basedir.nativePath + File.separator + arr[i]), path, false);
+                }
+            }
+        }
+        
+        /**
+         * Scan the given directory for files and directories. Found files and
+         * directories are placed in their respective collections, based on the
+         * 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
+         *              dir). Must not be <code>null</code>.
+         * @param fast  Whether or not this call is part of a fast scan.
+         *
+         * @see #filesIncluded
+         * @see #filesNotIncluded
+         * @see #filesExcluded
+         * @see #dirsIncluded
+         * @see #dirsNotIncluded
+         * @see #dirsExcluded
+         * @see #slowScan
+         */
+        protected function scandir(dir:File, vpath:String, fast:Boolean):void {
+            scandirTokenizedPath(dir, new TokenizedPath(vpath), fast);
+        }
+        
+        /**
+         * Scan the given directory for files and directories. Found files and
+         * directories are placed in their respective collections, based on the
+         * 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 path 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.
+         *
+         * @see #filesIncluded
+         * @see #filesNotIncluded
+         * @see #filesExcluded
+         * @see #dirsIncluded
+         * @see #dirsNotIncluded
+         * @see #dirsExcluded
+         * @see #slowScan
+         */
+        private function scandirTokenizedPath(dir:File, path:TokenizedPath, fast:Boolean):void {
+            if (dir == null) {
+                throw new BuildException("dir must not be null.");
+            }
+            var arr:Array = dir.getDirectoryListing();
+            var arr2:Array = [];
+            for each (var f:File in arr)
+                arr2.push(f.nativePath);
+            var newfiles:Vector.<String> = Vector.<String>(arr2);;
+            if (newfiles == null) {
+                if (!dir.exists) {
+                    throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX);
+                } else if (!dir.isDirectory) {
+                    throw new BuildException(dir + " is not a directory.");
+                } else {
+                    throw new BuildException("IO error scanning directory '"
+                        + dir.nativePath + "'");
+                }
+            }
+            _scandir(dir, path, fast, newfiles, new Vector.<String>());
+        }
+        
+        private function _scandir(dir:File, path:TokenizedPath, fast:Boolean,
+            newfiles:Vector.<String>, directoryNamesFollowed:Vector.<String>):void {
+                var vpath:String = path.toString();
+                if (vpath.length > 0 && vpath.charAt(vpath.length - 1) != File.separator) {
+                    vpath += File.separator;
+                }
+                
+                // avoid double scanning of directories, can only happen in fast mode
+                if (fast && hasBeenScanned(vpath)) {
+                    return;
+                }
+                if (!followSymlinks) {
+                    var noLinks:Vector.<String> = new Vector.<String>();
+                    for (i = 0; i < newfiles.length; i++) {
+                        try {
+                            if (new File(dir + File.separator + newfiles[i]).isSymbolicLink) {
+                                var name:String = vpath + newfiles[i];
+                                var file:File = new File(dir.nativePath + File.separator + newfiles[i]);
+                                (file.isDirectory
+                                    ? dirsExcluded : filesExcluded).push(name);
+                                if (!isExcluded(name)) {
+                                    notFollowedSymlinks.push(file.nativePath);
+                                }
+                            } else {
+                                noLinks.push(newfiles[i]);
+                            }
+                        } catch (ioe:IOException) {
+                            var msg:String = "IOException caught while checking "
+                                + "for links, couldn't get canonical path!";
+                            // will be caught and redirected to Ant's logging system
+                            Ant.ant.output(msg);
+                            noLinks.push(newfiles[i]);
+                        }
+                    }
+                    newfiles = noLinks.slice();
+                } else {
+                    directoryNamesFollowed.unshift(dir.nativePath);
+                }
+                
+                for (var i:int = 0; i < newfiles.length; i++) {
+                    name = vpath + newfiles[i];
+                    var newPath:TokenizedPath = new TokenizedPath("").initAsChild(path, newfiles[i]);
+                    file = new File(dir.nativePath + File.separator + newfiles[i]);
+                    var arr:Array = file.getDirectoryListing();
+                    var arr2:Array = [];
+                    for each (var f:File in arr)
+                        arr2.push(f.nativePath);
+                    var children:Vector.<String> = Vector.<String>(arr2);
+                    if (children == null || (children.length == 0 && !file.isDirectory)) {
+                        if (isIncludedPath(newPath)) {
+                            accountForIncludedFile(newPath, file);
+                        } else {
+                            everythingIncluded = false;
+                            filesNotIncluded.push(name);
+                        }
+                    } else { // dir
+                        
+                        if (followSymlinks
+                            && causesIllegalSymlinkLoop(newfiles[i], dir,
+                                directoryNamesFollowed)) {
+                            // will be caught and redirected to Ant's logging system
+                            Ant.ant.output("skipping symbolic link "
+                                + file.nativePath
+                                + " -- too many levels of symbolic"
+                                + " links.");
+                            notFollowedSymlinks.push(file.nativePath);
+                            continue;
+                        }
+                        
+                        if (isIncludedPath(newPath)) {
+                            accountForIncludedDir(newPath, file, fast, children,
+                                directoryNamesFollowed);
+                        } else {
+                            everythingIncluded = false;
+                            dirsNotIncluded.push(name);
+                            if (fast && couldHoldIncludedPath(newPath)
+                                && !contentsExcluded(newPath)) {
+                                _scandir(file, newPath, fast, children,
+                                    directoryNamesFollowed);
+                            }
+                        }
+                        if (!fast) {
+                            _scandir(file, newPath, fast, children, directoryNamesFollowed);
+                        }
+                    }
+                }
+                
+                if (followSymlinks) {
+                    directoryNamesFollowed.shift();
+                }
+            }
+        
+        /**
+         * Process included file.
+         * @param name  path of the file relative to the directory of the FileSet.
+         * @param file  included File.
+         */
+        private function accountForIncludedFile(name:TokenizedPath, file:File):void {
+            processIncluded(name, file, filesIncluded, filesExcluded,
+                filesDeselected);
+        }
+        
+        /**
+         * Process included directory.
+         * @param name path of the directory relative to the directory of
+         *             the FileSet.
+         * @param file directory as File.
+         * @param fast whether to perform fast scans.
+         */
+        private function accountForIncludedDir(name:TokenizedPath,
+            file:File, fast:Boolean,
+            children:Vector.<String> = null,
+            directoryNamesFollowed:Vector.<String> = null):void {
+                processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+                if (fast && couldHoldIncludedPath(name) && !contentsExcluded(name)) {
+                    _scandir(file, name, fast, children, directoryNamesFollowed);
+                }
+            }
+        
+        private function processIncluded(path:TokenizedPath,
+            file:File, inc:Vector.<String>, exc:Vector.<String>,
+            des:Vector.<String>):void {
+                var name:String = path.toString();
+                if (inc.indexOf(name) != -1 || 
+                    exc.indexOf(name) != -1 || 
+                    des.indexOf(name) != -1) {
+                    return;
+                }
+                
+                var included:Boolean = false;
+                if (isExcludedPath(path)) {
+                    exc.push(name);
+                } else if (isSelected(name, file)) {
+                    included = true;
+                    inc.push(name);
+                } else {
+                    des.push(name);
+                }
+                everythingIncluded = everythingIncluded || included;
+            }
+        
+        /**
+         * Test whether or not a name matches against 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 at least one
+         *         include pattern, or <code>false</code> otherwise.
+         */
+        protected function isIncluded(name:String):Boolean {
+            return isIncludedPath(new TokenizedPath(name));
+        }
+        
+        /**
+         * Test whether or not a name matches against 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 at least one
+         *         include pattern, or <code>false</code> otherwise.
+         */
+        private function isIncludedPath(path:TokenizedPath):Boolean {
+            ensureNonPatternSetsReady();
+            
+            if (isCaseSensitive()
+                ? includeNonPatterns.containsKey(path.toString())
+                : includeNonPatterns.containsKey(path.toString().toUpperCase())) {
+                return true;
+            }
+            for (var i:int = 0; i < includePatterns.length; i++) {
+                if (includePatterns[i].matchPath(path, isCaseSensitive())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        /**
+         * Test 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
+         *         least one include pattern, or <code>false</code> otherwise.
+         */
+        protected function couldHoldIncluded(name:String):Boolean {
+            return couldHoldIncludedPath(new TokenizedPath(name));
+        }
+        
+        /**
+         * Test whether or not a name matches the start of at least one include
+         * pattern.
+         *
+         * @param tokenizedName The name to match. Must not be <code>null</code>.
+         * @return <code>true</code> when the name matches against the start of at
+         *         least one include pattern, or <code>false</code> otherwise.
+         */
+        private function couldHoldIncludedPath(tokenizedName:TokenizedPath):Boolean {
+            for (var i:int = 0; i < includePatterns.length; i++) {
+                if (couldHoldIncludedWithIncludes(tokenizedName, includePatterns[i])) {
+                    return true;
+                }
+            }
+            for each (var iter:TokenizedPath in includeNonPatterns) {
+                if (couldHoldIncludedWithIncludes(tokenizedName,
+                    iter.toPattern())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        /**
+         * Test whether or not a name matches the start of the given
+         * include pattern.
+         *
+         * @param tokenizedName The name to match. Must not be <code>null</code>.
+         * @return <code>true</code> when the name matches against the start of the
+         *         include pattern, or <code>false</code> otherwise.
+         */
+        private function couldHoldIncludedWithIncludes(tokenizedName:TokenizedPath,
+            tokenizedInclude:TokenizedPattern):Boolean {
+                return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive())
+                    && isMorePowerfulThanExcludes(tokenizedName.toString())
+                    && isDeeper(tokenizedInclude, tokenizedName);
+            }
+        
+        /**
+         * Verify that a pattern specifies files deeper
+         * than the level of the specified file.
+         * @param pattern the pattern to check.
+         * @param name the name to check.
+         * @return whether the pattern is deeper than the name.
+         * @since Ant 1.6.3
+         */
+        private function isDeeper(pattern:TokenizedPattern, name:TokenizedPath):Boolean {
+            return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH)
+                || pattern.depth() > name.depth();
+        }
+        
+        /**
+         *  Find out whether one particular include pattern is more powerful
+         *  than all the excludes.
+         *  Note:  the power comparison is based on the length of the include pattern
+         *  and of the exclude patterns without the wildcards.
+         *  Ideally the comparison should be done based on the depth
+         *  of the match; that is to say how many file separators have been matched
+         *  before the first ** or the end of the pattern.
+         *
+         *  IMPORTANT : this function should return false "with care".
+         *
+         *  @param name the relative path to test.
+         *  @return true if there is no exclude pattern more powerful than
+         *  this include pattern.
+         *  @since Ant 1.6
+         */
+        private function isMorePowerfulThanExcludes(name:String):Boolean {
+            const soughtexclude:String  =
+                name + File.separator + SelectorUtils.DEEP_TREE_MATCH;
+            for (var counter:int = 0; counter < excludePatterns.length; counter++) {
+                if (excludePatterns[counter].toString() == soughtexclude)  {
+                    return false;
+                }
+            }
+            return true;
+        }
+        
+        /**
+         * Test whether all contents of the specified directory must be excluded.
+         * @param path the path to check.
+         * @return whether all the specified directory's contents are excluded.
+         */
+        /* package */ private function contentsExcluded(path:TokenizedPath):Boolean {
+            for (var i:int = 0; i < excludePatterns.length; i++) {
+                if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH)
+                    && excludePatterns[i].withoutLastToken()
+                    .matchPath(path, isCaseSensitive())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        /**
+         * Test whether or not a name matches against at least one exclude
+         * pattern.
+         *
+         * @param name The name to match. Must not be <code>null</code>.
+         * @return <code>true</code> when the name matches against at least one
+         *         exclude pattern, or <code>false</code> otherwise.
+         */
+        protected function isExcluded(name:String):Boolean {
+            return isExcludedPath(new TokenizedPath(name));
+        }
+        
+        /**
+         * Test whether or not a name matches against at least one exclude
+         * pattern.
+         *
+         * @param name The name to match. Must not be <code>null</code>.
+         * @return <code>true</code> when the name matches against at least one
+         *         exclude pattern, or <code>false</code> otherwise.
+         */
+        private function isExcludedPath(name:TokenizedPath):Boolean {
+            ensureNonPatternSetsReady();
+            
+            if (isCaseSensitive()
+                ? excludeNonPatterns.hasOwnProperty(name.toString())
+                : excludeNonPatterns.hasOwnProperty(name.toString().toUpperCase())) {
+                return true;
+            }
+            for (var i:int = 0; i < excludePatterns.length; i++) {
+                if (excludePatterns[i].matchPath(name, isCaseSensitive())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        
+        /**
+         * Test whether a file 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 function isSelected(name:String, file:File):Boolean {
+            if (selectors != null) {
+                for (var i:int = 0; i < selectors.length; i++) {
+                    if (!selectors[i].isSelected(basedir, name, file)) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        
+        /**
+         * Return 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.
+         *
+         * @return the names of the files which matched at least one of the
+         *         include patterns and none of the exclude patterns.
+         */
+        public function getIncludedFiles():Vector.<String> {
+            var files:Vector.<String>;
+                files = filesIncluded.slice();
+            files.sort(0);
+            return files;
+        }
+        
+        /**
+         * Return the count of included files.
+         * @return <code>int</code>.
+         * @since Ant 1.6.3
+         */
+        public function getIncludedFilesCount():int {
+            if (filesIncluded == null) {
+                throw new IllegalStateException("Must call scan() first");
+            }
+            return filesIncluded.length;
+        }
+        
+        /**
+         * Return 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
+         *         patterns.
+         *
+         * @see #slowScan
+         */
+        public function getNotIncludedFiles():Vector.<String> {
+            slowScan();
+            var files:Vector.<String> = filesNotIncluded.slice();
+            return files;
+        }
+        
+        /**
+         * Return 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
+         *         include patterns and at least one of the exclude patterns.
+         *
+         * @see #slowScan
+         */
+        public function getExcludedFiles():Vector.<String> {
+            slowScan();
+            var files:Vector.<String> = filesExcluded.slice();
+            return files;
+        }
+        
+        /**
+         * <p>Return the names of the files which were selected out and
+         * therefore not ultimately included.</p>
+         *
+         * <p>The names are relative to the base directory. This involves
+         * performing a slow scan if one has not already been completed.</p>
+         *
+         * @return the names of the files which were deselected.
+         *
+         * @see #slowScan
+         */
+        public function getDeselectedFiles():Vector.<String> {
+            slowScan();
+            var files:Vector.<String> = filesDeselected.slice();
+            return files;
+        }
+        
+        /**
+         * Return 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.
+         *
+         * @return the names of the directories which matched at least one of the
+         * include patterns and none of the exclude patterns.
+         */
+        public function getIncludedDirectories():Vector.<String> {
+            var directories:Vector.<String>;
+                directories = dirsIncluded.slice();
+            directories.sort(0);
+            return directories;
+        }
+        
+        /**
+         * Return the count of included directories.
+         * @return <code>int</code>.
+         * @since Ant 1.6.3
+         */
+        public function getIncludedDirsCount():int {
+            if (dirsIncluded == null) {
+                throw new IllegalStateException("Must call scan() first");
+            }
+            return dirsIncluded.length;
+        }
+        
+        /**
+         * Return the names of the directories 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 directories which matched none of the include
+         * patterns.
+         *
+         * @see #slowScan
+         */
+        public function getNotIncludedDirectories():Vector.<String> {
+            slowScan();
+            var directories:Vector.<String> = dirsNotIncluded.slice();
+            return directories;
+        }
+        
+        /**
+         * Return 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
+         * include patterns and at least one of the exclude patterns.
+         *
+         * @see #slowScan
+         */
+        public function getExcludedDirectories():Vector.<String> {
+            slowScan();
+            var directories:Vector.<String> = dirsExcluded.slice();
+            return directories;
+        }
+        
+        /**
+         * <p>Return the names of the directories which were selected out and
+         * therefore not ultimately included.</p>
+         *
+         * <p>The names are relative to the base directory. This involves
+         * performing a slow scan if one has not already been completed.</p>
+         *
+         * @return the names of the directories which were deselected.
+         *
+         * @see #slowScan
+         */
+        public function getDeselectedDirectories():Vector.<String> {
+            slowScan();
+            var directories:Vector.<String> = dirsDeselected.slice();
+            return directories;
+        }
+        
+        /**
+         * Absolute paths of all symbolic links that haven't been followed
+         * but would have been followed had followsymlinks been true or
+         * maxLevelsOfSymlinks been bigger.
+         *
+         * @since Ant 1.8.0
+         */
+        public function getNotFollowedSymlinks():Vector.<String> {
+            var links:Vector.<String>;
+                links = notFollowedSymlinks.slice();
+            links.sort(0);
+            return links;
+        }
+        
+        /**
+         * Add default exclusions to the current exclusions set.
+         */
+        public function addDefaultExcludes():void 
+        {
+            var excludesLength:int = excludes == null ? 0 : excludes.length;
+            var newExcludes:Vector.<String>;
+            var defaultExcludesTemp:Vector.<String> = getDefaultExcludes();
+            newExcludes = defaultExcludesTemp.slice();
+            for (var i:int = 0; i < defaultExcludesTemp.length; i++) {
+                newExcludes.push(
+                    defaultExcludesTemp[i].replace(/\//g, File.separator)
+                    .replace(/\\/g, File.separator));
+            }
+            excludes = newExcludes;
+        }
+        
+        /**
+         * Get the named resource.
+         * @param name path name of the file relative to the dir attribute.
+         *
+         * @return the resource with the given name.
+         * @since Ant 1.5.2
+         */
+        public function getResource(name:String):Resource {
+            return new FileResource(basedir, name);
+        }
+        
+        /**
+         * Has the directory with the given path relative to the base
+         * directory already been scanned?
+         *
+         * <p>Registers the given directory as scanned as a side effect.</p>
+         *
+         * @since Ant 1.6
+         */
+        private function hasBeenScanned(vpath:String):Boolean {
+            return !scannedDirs.push(vpath);
+        }
+        
+        /**
+         * This method is of interest for testing purposes.  The returned
+         * Set is live and should not be modified.
+         * @return the Set of relative directory names that have been scanned.
+         */
+        /* package-private */private function getScannedDirs():Vector.<String> {
+            return scannedDirs;
+        }
+        
+        /**
+         * Clear internal caches.
+         *
+         * @since Ant 1.6
+         */
+        private function clearCaches():void {
+            includeNonPatterns.clear();
+            excludeNonPatterns.clear();
+            includePatterns = null;
+            excludePatterns = null;
+            areNonPatternSetsReady = false;
+        }
+        
+        /**
+         * Ensure that the in|exclude &quot;patterns&quot;
+         * have been properly divided up.
+         *
+         * @since Ant 1.6.3
+         */
+        /* package */private function ensureNonPatternSetsReady():void {
+            if (!areNonPatternSetsReady) {
+                includePatterns = fillNonPatternSet(includeNonPatterns, includes);
+                excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
+                areNonPatternSetsReady = true;
+            }
+        }
+        
+        /**
+         * Add all patterns that are not real patterns (do not contain
+         * wildcards) to the set and returns the real patterns.
+         *
+         * @param map Map to populate.
+         * @param patterns String[] of patterns.
+         * @since Ant 1.8.0
+         */
+        private function fillNonPatternSet(map:Object, patterns:Vector.<String>):Vector.<TokenizedPattern> {
+            var al:Vector.<TokenizedPattern> = new Vector.<TokenizedPattern>(patterns.length);
+            for (var i:int = 0; i < patterns.length; i++) {
+                if (!SelectorUtils.hasWildcards(patterns[i])) {
+                    var s:String = isCaseSensitive()
+                        ? patterns[i] : patterns[i].toUpperCase();
+                    map[s] = new TokenizedPath(s);
+                } else {
+                    al.push(new TokenizedPattern(patterns[i]));
+                }
+            }
+            return al;
+        }
+        
+        /**
+         * Would following the given directory cause a loop of symbolic
+         * links deeper than allowed?
+         *
+         * <p>Can only happen if the given directory has been seen at
+         * least more often than allowed during the current scan and it is
+         * a symbolic link and enough other occurences of the same name
+         * higher up are symbolic links that point to the same place.</p>
+         *
+         * @since Ant 1.8.0
+         */
+        private function causesIllegalSymlinkLoop(dirName:String, parent:File,
+            directoryNamesFollowed:Vector.<String>):Boolean {
+                try {
+                    if (directoryNamesFollowed.length >= maxLevelsOfSymlinks
+                        && CollectionUtils.frequency(directoryNamesFollowed, dirName)
+                        >= maxLevelsOfSymlinks
+                        && new File(parent.nativePath + File.separator + dirName).isSymbolicLink) {
+                        
+                        var files:Vector.<String> = new Vector.<String>();
+                        var f:File = FILE_UTILS.resolveFile(parent, dirName);
+                        f.canonicalize();
+                        var target:String = f.nativePath;
+                        files.push(target);
+                        
+                        var relPath:String = "";
+                        for each (var dir:String in directoryNamesFollowed) {
+                            relPath += "../";
+                            if (dirName == dir) {
+                                f = FILE_UTILS.resolveFile(parent, relPath + dir);
+                                f.canonicalize();
+                                files.push(f.nativePath);
+                                if (files.length > maxLevelsOfSymlinks
+                                    && CollectionUtils.frequency(files, target)
+                                    > maxLevelsOfSymlinks) {
+                                    return true;
+                                }
+                            }
+                        }
+                        
+                    }
+                    return false;
+                } catch (ex:IOException) {
+                    throw new BuildException("Caught error while checking for"
+                        + " symbolic links" + ex.message);
+                }
+                return false;
+            }
+        
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/3a58a60e/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileProvider.as
----------------------------------------------------------------------
diff --git a/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileProvider.as b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileProvider.as
new file mode 100644
index 0000000..e8a4773
--- /dev/null
+++ b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileProvider.as
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.ant.tags.filesetClasses
+{
+    import flash.filesystem.File;
+    
+    public interface FileProvider
+    {
+        function getFile():File;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/3a58a60e/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileResource.as
----------------------------------------------------------------------
diff --git a/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileResource.as b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileResource.as
new file mode 100644
index 0000000..3d00d27
--- /dev/null
+++ b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileResource.as
@@ -0,0 +1,146 @@
+/*
+*  Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  See the NOTICE file distributed with
+*  this work for additional information regarding copyright ownership.
+*  The ASF licenses this file to You under the Apache License, Version 2.0
+*  (the "License"); you may not use this file except in compliance with
+*  the License.  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*/
+package org.apache.flex.ant.tags.filesetClasses
+{
+    import flash.filesystem.File;
+    import org.apache.flex.ant.tags.filesetClasses.exceptions.BuildException;
+    
+    /**
+     * Ported from org.apache.tools.ant.types.Resource.java on 12/3/13. 
+     * Describes a "File-like" resource (File, ZipEntry, etc.).
+     *
+     * This class is meant to be used by classes needing to record path
+     * and date/time information about a file, a zip entry or some similar
+     * resource (URL, archive in a version control repository, ...).
+     *
+     * @since Ant 1.5.2
+     * @see org.apache.tools.ant.types.resources.Touchable
+     */
+    public class FileResource extends Resource implements FileProvider
+    {
+        private static const FILE_UTILS:FileUtils = FileUtils.getFileUtils();
+
+        private var file:File;
+        private var baseDir:File;
+
+        /**
+         * Construct a new FileResource using the specified basedir and relative name.
+         * @param b      the basedir as File.
+         * @param name   the relative filename.
+         */
+        public function FileResource(b:File, name:String) {
+            super(name);
+            this.baseDir = b;
+            this.file = FILE_UTILS.resolveFile(b, name);
+        }
+
+        /**
+         * Set the File for this FileResource.
+         * @param f the File to be represented.
+         */
+        public function setFile(f:File):void {
+            checkAttributesAllowed();
+            file = f;
+            if (f != null && (getBaseDir() == null || !FILE_UTILS.isLeadingPath(getBaseDir(), f))) {
+                setBaseDir(f.parent);
+            }
+        }
+        
+        /**
+         * Get the file represented by this FileResource.
+         * @return the File.
+         */
+        public function getFile():File {
+            if (isReference()) {
+                return FileResource(getCheckedRef()).getFile();
+            }
+            dieOnCircularReference();
+                if (file == null) {
+                    //try to resolve file set via basedir/name property setters:
+                    var d:File = getBaseDir();
+                    var n:String = super.getName();
+                    if (n != null) {
+                        setFile(FILE_UTILS.resolveFile(d, n));
+                    }
+                }
+            return file;
+        }
+        
+        /**
+         * Set the basedir for this FileResource.
+         * @param b the basedir as File.
+         */
+        public function setBaseDir(b:File):void {
+            checkAttributesAllowed();
+            baseDir = b;
+        }
+        
+        /**
+         * Return the basedir to which the name is relative.
+         * @return the basedir as File.
+         */
+        public function getBaseDir():File {
+            if (isReference()) {
+                return FileResource(getCheckedRef()).getBaseDir();
+            }
+            dieOnCircularReference();
+            return baseDir;
+        }
+        
+        /**
+         * Overrides the super version.
+         * @param r the Reference to set.
+         */
+        override public function setRefid(r:Reference):void {
+            if (file != null || baseDir != null) {
+                throw tooManyAttributes();
+            }
+            super.setRefid(r);
+        }
+        
+        /**
+         * Get the name of this FileResource.  If the basedir is set,
+         * the name will be relative to that.  Otherwise the basename
+         * only will be returned.
+         * @return the name of this resource.
+         */
+        override public function getName():String {
+            if (isReference()) {
+                return Resource(getCheckedRef()).getName();
+            }
+            var b:File = getBaseDir();
+            return b == null ? getNotNullFile().name
+                : FILE_UTILS.removeLeadingPath(b, getNotNullFile());
+        }
+
+        /**
+         * Get the file represented by this FileResource, ensuring it is not null.
+         * @return the not-null File.
+         * @throws BuildException if file is null.
+         */
+        protected function getNotNullFile():File {
+            if (getFile() == null) {
+                throw new BuildException("file attribute is null!");
+            }
+            dieOnCircularReference();
+            return getFile();
+        }
+        
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/3a58a60e/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileSelector.as
----------------------------------------------------------------------
diff --git a/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileSelector.as b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileSelector.as
new file mode 100644
index 0000000..92c9376
--- /dev/null
+++ b/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileSelector.as
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.ant.tags.filesetClasses
+{
+    import flash.filesystem.File;
+            
+    public interface FileSelector
+    {
+        function isSelected(base:File, name:String, file:File):Boolean;
+   }
+}
\ No newline at end of file


Mime
View raw message