aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java1900
1 files changed, 1900 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java
new file mode 100644
index 00000000..709779a7
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/DirectoryScanner.java
@@ -0,0 +1,1900 @@
+/*
+ * 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.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.types.selectors.TokenizedPath;
+import org.apache.tools.ant.types.selectors.TokenizedPattern;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SymbolicLinkUtils;
+import org.apache.tools.ant.util.VectorSet;
+
+/**
+ * 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 &lt; 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 {
+
+ /** Is OpenVMS the operating system we're running on? */
+ private static final boolean ON_VMS = Os.isFamily("openvms");
+
+ /**
+ * 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.
+ */
+ @Deprecated
+ protected static final String[] DEFAULTEXCLUDES = {
+ // 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 final int MAX_LEVELS_OF_SYMLINKS = 5;
+ /**
+ * The end of the exception message if something that should be
+ * there doesn't exist.
+ */
+ public static final String DOES_NOT_EXIST_POSTFIX = " does not exist.";
+
+ /** Helper. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** Helper. */
+ private static final SymbolicLinkUtils SYMLINK_UTILS =
+ SymbolicLinkUtils.getSymbolicLinkUtils();
+
+ /**
+ * Patterns which should be excluded by default.
+ *
+ * @see #addDefaultExcludes()
+ */
+ private static final Set<String> defaultExcludes = new HashSet<String>();
+ static {
+ resetDefaultExcludes();
+ }
+
+ // CheckStyle:VisibilityModifier OFF - bc
+
+ /** The base directory to be scanned. */
+ protected File basedir;
+
+ /** The patterns for the files to be included. */
+ protected String[] includes;
+
+ /** The patterns for the files to be excluded. */
+ protected String[] 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<String> filesIncluded;
+
+ /** The files which did not match any includes or selectors. */
+ protected Vector<String> filesNotIncluded;
+
+ /**
+ * The files which matched at least one include and at least
+ * one exclude.
+ */
+ protected Vector<String> filesExcluded;
+
+ /**
+ * The directories which matched at least one include and no excludes
+ * and were selected.
+ */
+ protected Vector<String> dirsIncluded;
+
+ /** The directories which were found and did not match any includes. */
+ protected Vector<String> dirsNotIncluded;
+
+ /**
+ * The directories which matched at least one include and at least one
+ * exclude.
+ */
+ protected Vector<String> dirsExcluded;
+
+ /**
+ * The files which matched at least one include and no excludes and
+ * which a selector discarded.
+ */
+ protected Vector<String> filesDeselected;
+
+ /**
+ * The directories which matched at least one include and no excludes
+ * but which a selector discarded.
+ */
+ protected Vector<String> 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.
+ */
+ protected boolean isCaseSensitive = true;
+
+ /**
+ * Whether a missing base directory is an error.
+ * @since Ant 1.7.1
+ */
+ protected boolean errorOnMissingDir = true;
+
+ /**
+ * Whether or not symbolic links should be followed.
+ *
+ * @since Ant 1.5
+ */
+ private boolean followSymlinks = true;
+
+ /** Whether or not everything tested so far has been included. */
+ protected boolean everythingIncluded = true;
+
+ // CheckStyle:VisibilityModifier ON
+
+ /**
+ * List of all scanned directories.
+ *
+ * @since Ant 1.6
+ */
+ private final Set<String> scannedDirs = new HashSet<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 final Map<String, TokenizedPath> includeNonPatterns = 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 final Map<String, TokenizedPath> excludeNonPatterns = 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 TokenizedPattern[] includePatterns;
+
+ /**
+ * Array of all exclude patterns that contain wildcards.
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ */
+ private TokenizedPattern[] excludePatterns;
+
+ /**
+ * Have the non-pattern sets and pattern arrays for in- and
+ * excludes been initialized?
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean areNonPatternSetsReady = false;
+
+ /**
+ * Scanning flag.
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean scanning = false;
+
+ /**
+ * Scanning lock.
+ *
+ * @since Ant 1.6.3
+ */
+ private final Object scanLock = new Object();
+
+ /**
+ * Slow scanning flag.
+ *
+ * @since Ant 1.6.3
+ */
+ private boolean slowScanning = false;
+
+ /**
+ * Slow scanning lock.
+ *
+ * @since Ant 1.6.3
+ */
+ private final Object slowScanLock = new Object();
+
+ /**
+ * Exception thrown during scan.
+ *
+ * @since Ant 1.6.3
+ */
+ private IllegalStateException illegal = null;
+
+ /**
+ * The maximum number of times a symbolic link may be followed
+ * during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ private int maxLevelsOfSymlinks = 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 final Set<String> notFollowedSymlinks = new HashSet<String>();
+
+ /**
+ * Sole constructor.
+ */
+ public 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>.
+ *
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ protected static boolean matchPatternStart(final String pattern, final String str) {
+ return SelectorUtils.matchPatternStart(pattern, str);
+ }
+
+ /**
+ * 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 boolean matchPatternStart(final String pattern, final String str,
+ final boolean isCaseSensitive) {
+ 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>.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ protected static boolean matchPath(final String pattern, final String str) {
+ return SelectorUtils.matchPath(pattern, str);
+ }
+
+ /**
+ * 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 boolean matchPath(final String pattern, final String str,
+ final boolean isCaseSensitive) {
+ 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>.
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(final String pattern, final String str) {
+ return SelectorUtils.match(pattern, str);
+ }
+
+ /**
+ * 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 boolean match(final String pattern, final String str,
+ final boolean isCaseSensitive) {
+ 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 String[] getDefaultExcludes() {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.toArray(new String[defaultExcludes
+ .size()]);
+ }
+ }
+
+ /**
+ * 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 boolean addDefaultExclude(final String s) {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.add(s);
+ }
+ }
+
+ /**
+ * 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 boolean removeDefaultExclude(final String s) {
+ synchronized (defaultExcludes) {
+ return defaultExcludes.remove(s);
+ }
+ }
+
+ /**
+ * Go back to the hardwired default exclude patterns.
+ *
+ * @since Ant 1.6
+ */
+ public static void resetDefaultExcludes() {
+ synchronized (defaultExcludes) {
+ defaultExcludes.clear();
+ for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
+ defaultExcludes.add(DEFAULTEXCLUDES[i]);
+ }
+ }
+ }
+
+ /**
+ * 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 void setBasedir(final String basedir) {
+ setBasedir(basedir == null ? (File) null
+ : new File(basedir.replace('/', File.separatorChar).replace(
+ '\\', File.separatorChar)));
+ }
+
+ /**
+ * Set the base directory to be scanned. This is the directory which is
+ * scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ */
+ public synchronized void setBasedir(final File basedir) {
+ 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 synchronized File getBasedir() {
+ 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 synchronized boolean isCaseSensitive() {
+ 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 synchronized void setCaseSensitive(final boolean isCaseSensitive) {
+ this.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 void setErrorOnMissingDir(final boolean errorOnMissingDir) {
+ 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 synchronized boolean isFollowSymlinks() {
+ return followSymlinks;
+ }
+
+ /**
+ * Set whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed.
+ */
+ public synchronized void setFollowSymlinks(final boolean followSymlinks) {
+ this.followSymlinks = followSymlinks;
+ }
+
+ /**
+ * The maximum number of times a symbolic link may be followed
+ * during a scan.
+ *
+ * @since Ant 1.8.0
+ */
+ public void setMaxLevelsOfSymlinks(final int max) {
+ maxLevelsOfSymlinks = max;
+ }
+
+ /**
+ * Set 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
+ * should be included. If a non-<code>null</code>
+ * list is given, all elements must be
+ * non-<code>null</code>.
+ */
+ public synchronized void setIncludes(final String[] includes) {
+ if (includes == null) {
+ this.includes = null;
+ } else {
+ this.includes = new String[includes.length];
+ for (int i = 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.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
+ * given, all elements must be non-<code>null</code>.
+ */
+ public synchronized void setExcludes(final String[] excludes) {
+ if (excludes == null) {
+ this.excludes = null;
+ } else {
+ this.excludes = new String[excludes.length];
+ for (int i = 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.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>, in which case the
+ * exclude patterns don't get changed at all.
+ *
+ * @since Ant 1.6.3
+ */
+ public synchronized void addExcludes(final String[] excludes) {
+ if (excludes != null && excludes.length > 0) {
+ if (this.excludes != null && this.excludes.length > 0) {
+ final String[] tmp = new String[excludes.length
+ + this.excludes.length];
+ System.arraycopy(this.excludes, 0, tmp, 0,
+ this.excludes.length);
+ for (int i = 0; i < excludes.length; i++) {
+ tmp[this.excludes.length + i] =
+ normalizePattern(excludes[i]);
+ }
+ this.excludes = tmp;
+ } else {
+ setExcludes(excludes);
+ }
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @since Ant 1.6.3
+ */
+ private static String normalizePattern(final String p) {
+ String pattern = p.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ if (pattern.endsWith(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 synchronized void setSelectors(final FileSelector[] selectors) {
+ 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 synchronized boolean isEverythingIncluded() {
+ 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 void scan() throws IllegalStateException {
+ synchronized (scanLock) {
+ if (scanning) {
+ while (scanning) {
+ try {
+ scanLock.wait();
+ } catch (final InterruptedException e) {
+ continue;
+ }
+ }
+ if (illegal != null) {
+ throw illegal;
+ }
+ return;
+ }
+ scanning = true;
+ }
+ final File savedBase = basedir;
+ try {
+ synchronized (this) {
+ illegal = null;
+ clearResults();
+
+ // set in/excludes to reasonable defaults if needed:
+ final boolean nullIncludes = (includes == null);
+ includes = nullIncludes
+ ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes;
+ final boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
+
+ if (basedir != null && !followSymlinks
+ && SYMLINK_UTILS.isSymbolicLink(basedir)) {
+ notFollowedSymlinks.add(basedir.getAbsolutePath());
+ 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 (isIncluded(TokenizedPath.EMPTY_PATH)) {
+ if (!isExcluded(TokenizedPath.EMPTY_PATH)) {
+ if (isSelected("", basedir)) {
+ dirsIncluded.addElement("");
+ } else {
+ dirsDeselected.addElement("");
+ }
+ } else {
+ dirsExcluded.addElement("");
+ }
+ } else {
+ dirsNotIncluded.addElement("");
+ }
+ checkIncludePatterns();
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ basedir = savedBase;
+ synchronized (scanLock) {
+ scanning = false;
+ scanLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * This routine is actually checking all the include patterns in
+ * order to avoid scanning everything under base dir.
+ * @since Ant 1.6
+ */
+ private void checkIncludePatterns() {
+ ensureNonPatternSetsReady();
+ final Map<TokenizedPath, String> newroots = new HashMap<TokenizedPath, String>();
+
+ // put in the newroots map the include patterns without
+ // wildcard tokens
+ for (int i = 0; i < includePatterns.length; i++) {
+ final String pattern = includePatterns[i].toString();
+ if (!shouldSkipPattern(pattern)) {
+ newroots.put(includePatterns[i].rtrimWildcardTokens(),
+ pattern);
+ }
+ }
+ for (final Map.Entry<String, TokenizedPath> entry : includeNonPatterns.entrySet()) {
+ final String pattern = entry.getKey();
+ if (!shouldSkipPattern(pattern)) {
+ newroots.put(entry.getValue(), pattern);
+ }
+ }
+
+ if (newroots.containsKey(TokenizedPath.EMPTY_PATH)
+ && basedir != null) {
+ // we are going to scan everything anyway
+ scandir(basedir, "", true);
+ } else {
+ File canonBase = null;
+ if (basedir != null) {
+ try {
+ canonBase = basedir.getCanonicalFile();
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+ // only scan directories that can include matched files or
+ // directories
+ for (final Map.Entry<TokenizedPath, String> entry : newroots.entrySet()) {
+ TokenizedPath currentPath = entry.getKey();
+ String currentelement = currentPath.toString();
+ if (basedir == null
+ && !FileUtils.isAbsolutePath(currentelement)) {
+ continue;
+ }
+ File myfile = new File(basedir, 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 {
+ final String path = (basedir == null)
+ ? myfile.getCanonicalPath()
+ : FILE_UTILS.removeLeadingPath(canonBase,
+ myfile.getCanonicalFile());
+ if (!path.equals(currentelement) || ON_VMS) {
+ myfile = currentPath.findFile(basedir, true);
+ if (myfile != null && basedir != null) {
+ currentelement = FILE_UTILS.removeLeadingPath(
+ basedir, myfile);
+ if (!currentPath.toString()
+ .equals(currentelement)) {
+ currentPath =
+ new TokenizedPath(currentelement);
+ }
+ }
+ }
+ } catch (final IOException ex) {
+ throw new BuildException(ex);
+ }
+ }
+
+ if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
+ final File f = currentPath.findFile(basedir, false);
+ if (f != null && f.exists()) {
+ // adapt currentelement to the case we've
+ // actually found
+ currentelement = (basedir == null)
+ ? f.getAbsolutePath()
+ : FILE_UTILS.removeLeadingPath(basedir, f);
+ myfile = f;
+ currentPath = new TokenizedPath(currentelement);
+ }
+ }
+
+ if (myfile != null && myfile.exists()) {
+ if (!followSymlinks && currentPath.isSymlink(basedir)) {
+ accountForNotFollowedSymlink(currentPath, myfile);
+ continue;
+ }
+ if (myfile.isDirectory()) {
+ if (isIncluded(currentPath)
+ && currentelement.length() > 0) {
+ accountForIncludedDir(currentPath, myfile, true);
+ } else {
+ scandir(myfile, currentPath, true);
+ }
+ } else if (myfile.isFile()) {
+ final String originalpattern = entry.getValue();
+ final boolean included = isCaseSensitive()
+ ? originalpattern.equals(currentelement)
+ : originalpattern.equalsIgnoreCase(currentelement);
+ 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 boolean shouldSkipPattern(final String pattern) {
+ if (FileUtils.isAbsolutePath(pattern)) {
+ //skip abs. paths not under basedir, if set:
+ if (basedir != null
+ && !SelectorUtils.matchPatternStart(pattern,
+ basedir.getAbsolutePath(),
+ 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 synchronized void clearResults() {
+ filesIncluded = new VectorSet<String>();
+ filesNotIncluded = new VectorSet<String>();
+ filesExcluded = new VectorSet<String>();
+ filesDeselected = new VectorSet<String>();
+ dirsIncluded = new VectorSet<String>();
+ dirsNotIncluded = new VectorSet<String>();
+ dirsExcluded = new VectorSet<String>();
+ dirsDeselected = new VectorSet<String>();
+ everythingIncluded = (basedir != null);
+ scannedDirs.clear();
+ notFollowedSymlinks.clear();
+ }
+
+ /**
+ * 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 void slowScan() {
+ synchronized (slowScanLock) {
+ if (haveSlowResults) {
+ return;
+ }
+ if (slowScanning) {
+ while (slowScanning) {
+ try {
+ slowScanLock.wait();
+ } catch (final InterruptedException e) {
+ // Empty
+ }
+ }
+ return;
+ }
+ slowScanning = true;
+ }
+ try {
+ synchronized (this) {
+
+ // set in/excludes to reasonable defaults if needed:
+ final boolean nullIncludes = (includes == null);
+ includes = nullIncludes
+ ? new String[] {SelectorUtils.DEEP_TREE_MATCH} : includes;
+ final boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
+
+ final String[] excl = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(excl);
+
+ final String[] notIncl = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(notIncl);
+
+ ensureNonPatternSetsReady();
+
+ processSlowScan(excl);
+ processSlowScan(notIncl);
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } finally {
+ synchronized (slowScanLock) {
+ haveSlowResults = true;
+ slowScanning = false;
+ slowScanLock.notifyAll();
+ }
+ }
+ }
+
+ private void processSlowScan(final String[] arr) {
+ for (int i = 0; i < arr.length; i++) {
+ final TokenizedPath path = new TokenizedPath(arr[i]);
+ if (!couldHoldIncluded(path) || contentsExcluded(path)) {
+ scandir(new File(basedir, 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 void scandir(final File dir, final String vpath, final boolean fast) {
+ scandir(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 void scandir(final File dir, final TokenizedPath path, final boolean fast) {
+ if (dir == null) {
+ throw new BuildException("dir must not be null.");
+ }
+ final String[] newfiles = dir.list();
+ 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.getAbsolutePath() + "'");
+ }
+ }
+ scandir(dir, path, fast, newfiles, new LinkedList<String>());
+ }
+
+ private void scandir(final File dir, final TokenizedPath path, final boolean fast,
+ String[] newfiles, final LinkedList<String> directoryNamesFollowed) {
+ String vpath = path.toString();
+ if (vpath.length() > 0 && !vpath.endsWith(File.separator)) {
+ vpath += File.separator;
+ }
+
+ // avoid double scanning of directories, can only happen in fast mode
+ if (fast && hasBeenScanned(vpath)) {
+ return;
+ }
+ if (!followSymlinks) {
+ final ArrayList<String> noLinks = new ArrayList<String>();
+ for (int i = 0; i < newfiles.length; i++) {
+ try {
+ if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) {
+ final String name = vpath + newfiles[i];
+ final File file = new File(dir, newfiles[i]);
+ if (file.isDirectory()) {
+ dirsExcluded.addElement(name);
+ } else if (file.isFile()) {
+ filesExcluded.addElement(name);
+ }
+ accountForNotFollowedSymlink(name, file);
+ } else {
+ noLinks.add(newfiles[i]);
+ }
+ } catch (final IOException ioe) {
+ final String msg = "IOException caught while checking "
+ + "for links, couldn't get canonical path!";
+ // will be caught and redirected to Ant's logging system
+ System.err.println(msg);
+ noLinks.add(newfiles[i]);
+ }
+ }
+ newfiles = (noLinks.toArray(new String[noLinks.size()]));
+ } else {
+ directoryNamesFollowed.addFirst(dir.getName());
+ }
+
+ for (int i = 0; i < newfiles.length; i++) {
+ final String name = vpath + newfiles[i];
+ final TokenizedPath newPath = new TokenizedPath(path, newfiles[i]);
+ final File file = new File(dir, newfiles[i]);
+ final String[] children = file.list();
+ if (children == null || (children.length == 0 && file.isFile())) {
+ if (isIncluded(newPath)) {
+ accountForIncludedFile(newPath, file);
+ } else {
+ everythingIncluded = false;
+ filesNotIncluded.addElement(name);
+ }
+ } else if (file.isDirectory()) { // dir
+
+ if (followSymlinks
+ && causesIllegalSymlinkLoop(newfiles[i], dir,
+ directoryNamesFollowed)) {
+ // will be caught and redirected to Ant's logging system
+ System.err.println("skipping symbolic link "
+ + file.getAbsolutePath()
+ + " -- too many levels of symbolic"
+ + " links.");
+ notFollowedSymlinks.add(file.getAbsolutePath());
+ continue;
+ }
+
+ if (isIncluded(newPath)) {
+ accountForIncludedDir(newPath, file, fast, children,
+ directoryNamesFollowed);
+ } else {
+ everythingIncluded = false;
+ dirsNotIncluded.addElement(name);
+ if (fast && couldHoldIncluded(newPath)
+ && !contentsExcluded(newPath)) {
+ scandir(file, newPath, fast, children,
+ directoryNamesFollowed);
+ }
+ }
+ if (!fast) {
+ scandir(file, newPath, fast, children, directoryNamesFollowed);
+ }
+ }
+ }
+
+ if (followSymlinks) {
+ directoryNamesFollowed.removeFirst();
+ }
+ }
+
+ /**
+ * Process included file.
+ * @param name path of the file relative to the directory of the FileSet.
+ * @param file included File.
+ */
+ private void accountForIncludedFile(final TokenizedPath name, final File file) {
+ 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 void accountForIncludedDir(final TokenizedPath name, final File file,
+ final boolean fast) {
+ processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name, fast);
+ }
+ }
+
+ private void accountForIncludedDir(final TokenizedPath name,
+ final File file, final boolean fast,
+ final String[] children,
+ final LinkedList<String> directoryNamesFollowed) {
+ processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected);
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name, fast, children, directoryNamesFollowed);
+ }
+ }
+
+ private void accountForNotFollowedSymlink(final String name, final File file) {
+ accountForNotFollowedSymlink(new TokenizedPath(name), file);
+ }
+
+ private void accountForNotFollowedSymlink(final TokenizedPath name, final File file) {
+ if (!isExcluded(name) &&
+ (isIncluded(name)
+ || (file.isDirectory() && couldHoldIncluded(name)
+ && !contentsExcluded(name)))) {
+ notFollowedSymlinks.add(file.getAbsolutePath());
+ }
+ }
+
+ private void processIncluded(final TokenizedPath path,
+ final File file, final Vector<String> inc, final Vector<String> exc,
+ final Vector<String> des) {
+ final String name = path.toString();
+ if (inc.contains(name) || exc.contains(name) || des.contains(name)) {
+ return;
+ }
+
+ boolean included = false;
+ if (isExcluded(path)) {
+ exc.add(name);
+ } else if (isSelected(name, file)) {
+ included = true;
+ inc.add(name);
+ } else {
+ des.add(name);
+ }
+ 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 boolean isIncluded(final String name) {
+ return isIncluded(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 boolean isIncluded(final TokenizedPath path) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? includeNonPatterns.containsKey(path.toString())
+ : includeNonPatterns.containsKey(path.toString().toUpperCase())) {
+ return true;
+ }
+ for (int i = 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 boolean couldHoldIncluded(final String name) {
+ return couldHoldIncluded(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 boolean couldHoldIncluded(final TokenizedPath tokenizedName) {
+ for (int i = 0; i < includePatterns.length; i++) {
+ if (couldHoldIncluded(tokenizedName, includePatterns[i])) {
+ return true;
+ }
+ }
+ for (final Iterator<TokenizedPath> iter = includeNonPatterns.values().iterator();
+ iter.hasNext();) {
+ if (couldHoldIncluded(tokenizedName,
+ iter.next().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 boolean couldHoldIncluded(final TokenizedPath tokenizedName,
+ final TokenizedPattern tokenizedInclude) {
+ 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 boolean isDeeper(final TokenizedPattern pattern, final TokenizedPath name) {
+ 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 boolean isMorePowerfulThanExcludes(final String name) {
+ final String soughtexclude =
+ name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH;
+ for (int counter = 0; counter < excludePatterns.length; counter++) {
+ if (excludePatterns[counter].toString().equals(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 */ boolean contentsExcluded(final TokenizedPath path) {
+ for (int i = 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 boolean isExcluded(final String name) {
+ return isExcluded(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 boolean isExcluded(final TokenizedPath name) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? excludeNonPatterns.containsKey(name.toString())
+ : excludeNonPatterns.containsKey(name.toString().toUpperCase())) {
+ return true;
+ }
+ for (int i = 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 boolean isSelected(final String name, final File file) {
+ if (selectors != null) {
+ for (int i = 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 String[] getIncludedFiles() {
+ String[] files;
+ synchronized (this) {
+ if (filesIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ files = new String[filesIncluded.size()];
+ filesIncluded.copyInto(files);
+ }
+ Arrays.sort(files);
+ return files;
+ }
+
+ /**
+ * Return the count of included files.
+ * @return <code>int</code>.
+ * @since Ant 1.6.3
+ */
+ public synchronized int getIncludedFilesCount() {
+ if (filesIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ return filesIncluded.size();
+ }
+
+ /**
+ * 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 synchronized String[] getNotIncludedFiles() {
+ slowScan();
+ final String[] files = new String[filesNotIncluded.size()];
+ filesNotIncluded.copyInto(files);
+ 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 synchronized String[] getExcludedFiles() {
+ slowScan();
+ final String[] files = new String[filesExcluded.size()];
+ filesExcluded.copyInto(files);
+ 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 synchronized String[] getDeselectedFiles() {
+ slowScan();
+ final String[] files = new String[filesDeselected.size()];
+ filesDeselected.copyInto(files);
+ 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 String[] getIncludedDirectories() {
+ String[] directories;
+ synchronized (this) {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ directories = new String[dirsIncluded.size()];
+ dirsIncluded.copyInto(directories);
+ }
+ Arrays.sort(directories);
+ return directories;
+ }
+
+ /**
+ * Return the count of included directories.
+ * @return <code>int</code>.
+ * @since Ant 1.6.3
+ */
+ public synchronized int getIncludedDirsCount() {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException("Must call scan() first");
+ }
+ return dirsIncluded.size();
+ }
+
+ /**
+ * 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 synchronized String[] getNotIncludedDirectories() {
+ slowScan();
+ final String[] directories = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(directories);
+ 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 synchronized String[] getExcludedDirectories() {
+ slowScan();
+ final String[] directories = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(directories);
+ 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 synchronized String[] getDeselectedDirectories() {
+ slowScan();
+ final String[] directories = new String[dirsDeselected.size()];
+ dirsDeselected.copyInto(directories);
+ 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.
+ *
+ * @return sorted array of not followed symlinks
+ * @since Ant 1.8.0
+ * @see #notFollowedSymlinks
+ */
+ public synchronized String[] getNotFollowedSymlinks() {
+ String[] links;
+ synchronized (this) {
+ links = notFollowedSymlinks
+ .toArray(new String[notFollowedSymlinks.size()]);
+ }
+ Arrays.sort(links);
+ return links;
+ }
+
+ /**
+ * Add default exclusions to the current exclusions set.
+ */
+ public synchronized void addDefaultExcludes() {
+ final int excludesLength = excludes == null ? 0 : excludes.length;
+ String[] newExcludes;
+ final String[] defaultExcludesTemp = getDefaultExcludes();
+ newExcludes = new String[excludesLength + defaultExcludesTemp.length];
+ if (excludesLength > 0) {
+ System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
+ }
+ for (int i = 0; i < defaultExcludesTemp.length; i++) {
+ newExcludes[i + excludesLength] =
+ defaultExcludesTemp[i].replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ }
+ 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 synchronized Resource getResource(final String name) {
+ 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 boolean hasBeenScanned(final String vpath) {
+ return !scannedDirs.add(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 */ Set<String> getScannedDirs() {
+ return scannedDirs;
+ }
+
+ /**
+ * Clear internal caches.
+ *
+ * @since Ant 1.6
+ */
+ private synchronized void clearCaches() {
+ 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 */ synchronized void ensureNonPatternSetsReady() {
+ 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 TokenizedPattern[] fillNonPatternSet(final Map<String, TokenizedPath> map, final String[] patterns) {
+ final ArrayList<TokenizedPattern> al = new ArrayList<TokenizedPattern>(patterns.length);
+ for (int i = 0; i < patterns.length; i++) {
+ if (!SelectorUtils.hasWildcards(patterns[i])) {
+ final String s = isCaseSensitive()
+ ? patterns[i] : patterns[i].toUpperCase();
+ map.put(s, new TokenizedPath(s));
+ } else {
+ al.add(new TokenizedPattern(patterns[i]));
+ }
+ }
+ return al.toArray(new TokenizedPattern[al.size()]);
+ }
+
+ /**
+ * 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 occurrences of the same name
+ * higher up are symbolic links that point to the same place.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ private boolean causesIllegalSymlinkLoop(final String dirName, final File parent,
+ final LinkedList<String> directoryNamesFollowed) {
+ try {
+ if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks
+ && CollectionUtils.frequency(directoryNamesFollowed, dirName)
+ >= maxLevelsOfSymlinks
+ && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) {
+
+ final ArrayList<String> files = new ArrayList<String>();
+ File f = FILE_UTILS.resolveFile(parent, dirName);
+ final String target = f.getCanonicalPath();
+ files.add(target);
+
+ String relPath = "";
+ for (final String dir : directoryNamesFollowed) {
+ relPath += "../";
+ if (dirName.equals(dir)) {
+ f = FILE_UTILS.resolveFile(parent, relPath + dir);
+ files.add(f.getCanonicalPath());
+ if (files.size() > maxLevelsOfSymlinks
+ && CollectionUtils.frequency(files, target)
+ > maxLevelsOfSymlinks) {
+ return true;
+ }
+ }
+ }
+
+ }
+ return false;
+ } catch (final IOException ex) {
+ throw new BuildException("Caught error while checking for"
+ + " symbolic links", ex);
+ }
+ }
+
+}