mbenson 2005/03/11 11:34:00
Modified: src/main/org/apache/tools/ant Tag: ANT_16_BRANCH
DirectoryScanner.java
Log:
Merge DirectoryScanner improvements;
disable ClasspathUtilsTest if tests.and.ant.share.classloader
PR: 33118
Revision Changes Path
No revision
No revision
1.64.2.10 +593 -362 ant/src/main/org/apache/tools/ant/DirectoryScanner.java
Index: DirectoryScanner.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/DirectoryScanner.java,v
retrieving revision 1.64.2.9
retrieving revision 1.64.2.10
diff -u -r1.64.2.9 -r1.64.2.10
--- DirectoryScanner.java 27 Jan 2005 22:31:54 -0000 1.64.2.9
+++ DirectoryScanner.java 11 Mar 2005 19:34:00 -0000 1.64.2.10
@@ -19,21 +19,22 @@
import java.io.File;
import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
import java.util.Arrays;
-import java.util.Enumeration;
+import java.util.Vector;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.ArrayList;
import java.util.Hashtable;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
+import java.util.Enumeration;
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.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.SelectorScanner;
import org.apache.tools.ant.util.FileUtils;
/**
@@ -164,6 +165,15 @@
"**/.DS_Store"
};
+ /** Helper. */
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ /** iterations for case-sensitive scanning. */
+ private static final boolean[] CS_SCAN_ONLY = new boolean[] {true};
+
+ /** iterations for non-case-sensitive scanning. */
+ private static final boolean[] CS_THEN_NON_CS = new boolean[] {true,
false};
+
/**
* Patterns which should be excluded by default.
*
@@ -186,8 +196,9 @@
/** 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.
+ /**
+ * The files which matched at least one include and no excludes
+ * and were selected.
*/
protected Vector filesIncluded;
@@ -200,8 +211,9 @@
*/
protected Vector filesExcluded;
- /** The directories which matched at least one include and no excludes
- * and were selected.
+ /**
+ * The directories which matched at least one include and no excludes
+ * and were selected.
*/
protected Vector dirsIncluded;
@@ -214,13 +226,15 @@
*/
protected Vector dirsExcluded;
- /** The files which matched at least one include and no excludes and
- * which a selector discarded.
+ /**
+ * The files which matched at least one include and no excludes and
+ * which a selector discarded.
*/
protected Vector filesDeselected;
- /** The directories which matched at least one include and no excludes
- * but which a selector discarded.
+ /**
+ * The directories which matched at least one include and no excludes
+ * but which a selector discarded.
*/
protected Vector dirsDeselected;
@@ -240,20 +254,126 @@
*/
private boolean followSymlinks = true;
- /** Helper. */
- private static final FileUtils FILE_UTILS = FileUtils.newFileUtils();
-
/** Whether or not everything tested so far has been included. */
protected boolean everythingIncluded = true;
/**
+ * Temporary table to speed up the various scanning methods.
+ *
+ * @since Ant 1.6
+ */
+ private Map fileListMap = new HashMap();
+
+ /**
+ * List of all scanned directories.
+ *
+ * @since Ant 1.6
+ */
+ private Set scannedDirs = new HashSet();
+
+ /**
+ * Set of all include patterns that are full file names and don't
+ * contain any wildcards.
+ *
+ * <p>If this instance is not case sensitive, the file names get
+ * turned to lower case.</p>
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ *
+ * @since Ant 1.6.3
+ */
+ private Set includeNonPatterns = new HashSet();
+
+ /**
+ * Set of all include patterns that are full file names and don't
+ * contain any wildcards.
+ *
+ * <p>If this instance is not case sensitive, the file names get
+ * turned to lower case.</p>
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ *
+ * @since Ant 1.6.3
+ */
+ private Set excludeNonPatterns = new HashSet();
+
+ /**
+ * Array of all include patterns that contain wildcards.
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ *
+ * @since Ant 1.6.3
+ */
+ private String[] includePatterns;
+
+ /**
+ * Array of all exclude patterns that contain wildcards.
+ *
+ * <p>Gets lazily initialized on the first invocation of
+ * isIncluded or isExcluded and cleared at the end of the scan
+ * method (cleared in clearCaches, actually).</p>
+ *
+ * @since Ant 1.6.3
+ */
+ private String[] 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 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 Object slowScanLock = new Object();
+
+ /**
+ * Exception thrown during scan.
+ *
+ * @since Ant 1.6.3
+ */
+ private IllegalStateException illegal = null;
+
+ /**
* Sole constructor.
*/
public DirectoryScanner() {
}
/**
- * Tests whether or not a given path matches the start of a given
+ * 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
@@ -271,8 +391,9 @@
protected static boolean matchPatternStart(String pattern, String str) {
return SelectorUtils.matchPatternStart(pattern, str);
}
+
/**
- * Tests whether or not a given path matches the start of a given
+ * 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
@@ -295,7 +416,7 @@
}
/**
- * Tests whether or not a given path matches a given pattern.
+ * Test whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be
* <code>null</code>.
@@ -310,7 +431,7 @@
}
/**
- * Tests whether or not a given path matches a given pattern.
+ * Test whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be
* <code>null</code>.
@@ -328,7 +449,7 @@
}
/**
- * Tests whether or not a string matches against a pattern.
+ * 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
@@ -346,7 +467,7 @@
}
/**
- * Tests whether or not a string matches against a pattern.
+ * 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
@@ -387,9 +508,8 @@
* 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.
+ * @return <code>true</code> if the string was added;
+ * <code>false</code> if it already existed.
*
* @since Ant 1.6
*/
@@ -406,9 +526,9 @@
*
* @param s The string to attempt to remove.
* @return <code>true</code> if <code>s</code> was a default
- * exclude (and thus was removed),
+ * exclude (and thus was removed);
* <code>false</code> if <code>s</code> was not
- * in the default excludes list to begin with
+ * in the default excludes list to begin with.
*
* @since Ant 1.6
*/
@@ -417,20 +537,19 @@
}
/**
- * Go back to the hard wired default exclude patterns
+ * Go back to the hardwired default exclude patterns.
*
* @since Ant 1.6
*/
public static void resetDefaultExcludes() {
defaultExcludes = new Vector();
-
for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
defaultExcludes.add(DEFAULTEXCLUDES[i]);
}
}
/**
- * Sets the base directory to be scanned. This is the directory which is
+ * 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>.
@@ -444,68 +563,69 @@
}
/**
- * Sets the base directory to be scanned. This is the directory which is
+ * Set the base directory to be scanned. This is the directory which is
* scanned recursively.
*
* @param basedir The base directory for scanning.
* Should not be <code>null</code>.
*/
- public void setBasedir(File basedir) {
+ public synchronized void setBasedir(File basedir) {
this.basedir = basedir;
}
/**
- * Returns the base directory to be scanned.
+ * Return the base directory to be scanned.
* This is the directory which is scanned recursively.
*
* @return the base directory to be scanned
*/
- public File getBasedir() {
+ 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
+ * case sensitive way.
+ * @return whether or not the scanning is case sensitive.
+ * @since Ant 1.6
*/
- public boolean isCaseSensitive() {
+ public synchronized boolean isCaseSensitive() {
return isCaseSensitive;
}
+
/**
- * Sets whether or not include and exclude patterns are matched
- * in a case sensitive way
+ * 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
+ * regarded as a case sensitive one.
*/
- public void setCaseSensitive(boolean isCaseSensitive) {
+ public synchronized void setCaseSensitive(boolean isCaseSensitive) {
this.isCaseSensitive = isCaseSensitive;
}
/**
- * gets whether or not a DirectoryScanner follows symbolic links
+ * Get whether or not a DirectoryScanner follows symbolic links.
*
- * @return flag indicating whether symbolic links should be followed
+ * @return flag indicating whether symbolic links should be followed.
*
- * @since ant 1.6
+ * @since Ant 1.6
*/
- public boolean isFollowSymlinks() {
+ public synchronized boolean isFollowSymlinks() {
return followSymlinks;
}
/**
- * Sets whether or not symbolic links should be followed.
+ * Set whether or not symbolic links should be followed.
*
- * @param followSymlinks whether or not symbolic links should be followed
+ * @param followSymlinks whether or not symbolic links should be
followed.
*/
- public void setFollowSymlinks(boolean followSymlinks) {
+ public synchronized void setFollowSymlinks(boolean followSymlinks) {
this.followSymlinks = followSymlinks;
}
/**
- * Sets the list of include patterns to use. All '/' and '\' characters
+ * 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>
@@ -515,28 +635,21 @@
* 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>.
+ * non-<code>null</code>.
*/
- public void setIncludes(String[] includes) {
+ public synchronized void setIncludes(String[] includes) {
if (includes == null) {
this.includes = null;
} else {
this.includes = new String[includes.length];
for (int i = 0; i < includes.length; i++) {
- String pattern;
- pattern = includes[i].replace('/',
File.separatorChar).replace(
- '\\', File.separatorChar);
- if (pattern.endsWith(File.separator)) {
- pattern += "**";
- }
- this.includes[i] = pattern;
+ this.includes[i] = normalizePattern(includes[i]);
}
}
}
-
/**
- * Sets the list of exclude patterns to use. All '/' and '\' characters
+ * 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>
@@ -547,47 +660,88 @@
* should be excluded. If a non-<code>null</code> list is
* given, all elements must be non-<code>null</code>.
*/
- public void setExcludes(String[] excludes) {
+ public synchronized void setExcludes(String[] excludes) {
if (excludes == null) {
this.excludes = null;
} else {
this.excludes = new String[excludes.length];
for (int i = 0; i < excludes.length; i++) {
- String pattern;
- pattern = excludes[i].replace('/',
File.separatorChar).replace(
- '\\', File.separatorChar);
- if (pattern.endsWith(File.separator)) {
- pattern += "**";
+ 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(String[] excludes) {
+ if (excludes != null && excludes.length > 0) {
+ if (this.excludes != null && this.excludes.length > 0) {
+ 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[i] = pattern;
+ 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(String p) {
+ String pattern = p.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+ if (pattern.endsWith(File.separator)) {
+ pattern += "**";
+ }
+ return pattern;
+ }
/**
- * Sets the selectors that will select the filelist.
+ * Set the selectors that will select the filelist.
*
- * @param selectors specifies the selectors to be invoked on a scan
+ * @param selectors specifies the selectors to be invoked on a scan.
*/
- public void setSelectors(FileSelector[] selectors) {
+ public synchronized void setSelectors(FileSelector[] selectors) {
this.selectors = selectors;
}
-
/**
- * Returns whether or not the scanner has included all the files or
+ * 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 boolean isEverythingIncluded() {
+ public synchronized boolean isEverythingIncluded() {
return everythingIncluded;
}
/**
- * Scans the base directory for files which match at least one include
+ * Scan the base directory 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.
*
@@ -596,68 +750,87 @@
* or isn't a directory).
*/
public void scan() throws IllegalStateException {
- if (basedir == null) {
- throw new IllegalStateException("No basedir set");
- }
- if (!basedir.exists()) {
- throw new IllegalStateException("basedir " + basedir
- + " does not exist");
- }
- if (!basedir.isDirectory()) {
- throw new IllegalStateException("basedir " + basedir
- + " is not a directory");
- }
-
- if (includes == null) {
- // No includes supplied, so set it to 'matches all'
- includes = new String[1];
- includes[0] = "**";
- }
- if (excludes == null) {
- excludes = new String[0];
+ synchronized (scanLock) {
+ if (scanning) {
+ while (scanning) {
+ try {
+ scanLock.wait();
+ } catch (InterruptedException e) {
+ continue;
+ }
+ }
+ if (illegal != null) {
+ throw illegal;
+ }
+ return;
+ }
+ scanning = true;
}
+ try {
+ synchronized (this) {
+ illegal = null;
+ clearResults();
+
+ // set in/excludes to reasonable defaults if needed:
+ boolean nullIncludes = (includes == null);
+ includes = nullIncludes ? new String[] {"**"} : includes;
+ boolean nullExcludes = (excludes == null);
+ excludes = nullExcludes ? new String[0] : excludes;
- filesIncluded = new Vector();
- filesNotIncluded = new Vector();
- filesExcluded = new Vector();
- filesDeselected = new Vector();
- dirsIncluded = new Vector();
- dirsNotIncluded = new Vector();
- dirsExcluded = new Vector();
- dirsDeselected = new Vector();
-
- if (isIncluded("")) {
- if (!isExcluded("")) {
- if (isSelected("", basedir)) {
- dirsIncluded.addElement("");
+ if (basedir == null) {
+ illegal = new IllegalStateException("No basedir set");
} else {
- dirsDeselected.addElement("");
+ if (!basedir.exists()) {
+ illegal = new IllegalStateException("basedir " +
basedir
+ + " does not exist");
+ }
+ if (!basedir.isDirectory()) {
+ illegal = new IllegalStateException("basedir " +
basedir
+ + " is not a
directory");
+ }
}
- } else {
- dirsExcluded.addElement("");
+ if (illegal != null) {
+ throw illegal;
+ }
+ if (isIncluded("")) {
+ if (!isExcluded("")) {
+ if (isSelected("", basedir)) {
+ dirsIncluded.addElement("");
+ } else {
+ dirsDeselected.addElement("");
+ }
+ } else {
+ dirsExcluded.addElement("");
+ }
+ } else {
+ dirsNotIncluded.addElement("");
+ }
+ checkIncludePatterns();
+ clearCaches();
+ includes = nullIncludes ? null : includes;
+ excludes = nullExcludes ? null : excludes;
+ }
+ } finally {
+ synchronized (scanLock) {
+ scanning = false;
+ scanLock.notifyAll();
}
- } else {
- dirsNotIncluded.addElement("");
}
- checkIncludePatterns();
- clearCaches();
}
/**
- * this routine is actually checking all the include patterns in
- * order to avoid scanning everything under base dir
- * @since ant1.6
+ * 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() {
Hashtable newroots = new Hashtable();
// put in the newroots vector the include patterns without
// wildcard tokens
for (int icounter = 0; icounter < includes.length; icounter++) {
- String newpattern =
- SelectorUtils.rtrimWildcardTokens(includes[icounter]);
- newroots.put(newpattern, includes[icounter]);
+ newroots.put(SelectorUtils.rtrimWildcardTokens(
+ includes[icounter]), includes[icounter]);
}
-
if (newroots.containsKey("")) {
// we are going to scan everything anyway
scandir(basedir, "", true);
@@ -672,7 +845,6 @@
} catch (IOException ex) {
throw new BuildException(ex);
}
-
while (enum2.hasMoreElements()) {
String currentelement = (String) enum2.nextElement();
String originalpattern = (String)
newroots.get(currentelement);
@@ -687,7 +859,7 @@
String path = FILE_UTILS.removeLeadingPath(canonBase,
canonFile);
if (!path.equals(currentelement) || ON_VMS) {
- myfile = findFile(basedir, currentelement);
+ myfile = findFile(basedir, currentelement, true);
if (myfile != null) {
currentelement =
FILE_UTILS.removeLeadingPath(basedir,
@@ -698,9 +870,8 @@
throw new BuildException(ex);
}
}
-
- if ((myfile == null || !myfile.exists()) &&
!isCaseSensitive) {
- File f = findFileCaseInsensitive(basedir,
currentelement);
+ if ((myfile == null || !myfile.exists()) &&
!isCaseSensitive()) {
+ File f = findFile(basedir, currentelement, false);
if (f.exists()) {
// adapt currentelement to the case we've
// actually found
@@ -709,13 +880,11 @@
myfile = f;
}
}
-
if (myfile != null && myfile.exists()) {
if (!followSymlinks
&& isSymlink(basedir, currentelement)) {
continue;
}
-
if (myfile.isDirectory()) {
if (isIncluded(currentelement)
&& currentelement.length() > 0) {
@@ -732,12 +901,10 @@
scandir(myfile, currentelement, true);
}
} else {
- if (isCaseSensitive
- && originalpattern.equals(currentelement)) {
- accountForIncludedFile(currentelement, myfile);
- } else if (!isCaseSensitive
- && originalpattern
- .equalsIgnoreCase(currentelement)) {
+ boolean included = isCaseSensitive()
+ ? originalpattern.equals(currentelement)
+ :
originalpattern.equalsIgnoreCase(currentelement);
+ if (included) {
accountForIncludedFile(currentelement, myfile);
}
}
@@ -747,6 +914,22 @@
}
/**
+ * Clear the result caches for a scan.
+ */
+ protected synchronized void clearResults() {
+ filesIncluded = new Vector();
+ filesNotIncluded = new Vector();
+ filesExcluded = new Vector();
+ filesDeselected = new Vector();
+ dirsIncluded = new Vector();
+ dirsNotIncluded = new Vector();
+ dirsExcluded = new Vector();
+ dirsDeselected = new Vector();
+ everythingIncluded = (basedir != null);
+ scannedDirs.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
@@ -755,35 +938,54 @@
* Returns immediately if a slow scan has already been completed.
*/
protected void slowScan() {
- if (haveSlowResults) {
- return;
- }
-
- String[] excl = new String[dirsExcluded.size()];
- dirsExcluded.copyInto(excl);
-
- String[] notIncl = new String[dirsNotIncluded.size()];
- dirsNotIncluded.copyInto(notIncl);
-
- for (int i = 0; i < excl.length; i++) {
- if (!couldHoldIncluded(excl[i])) {
- scandir(new File(basedir, excl[i]),
- excl[i] + File.separator, false);
+ synchronized (slowScanLock) {
+ if (haveSlowResults) {
+ return;
}
+ if (slowScanning) {
+ while (slowScanning) {
+ try {
+ slowScanLock.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ return;
+ }
+ slowScanning = true;
}
+ try {
+ synchronized (this) {
- for (int i = 0; i < notIncl.length; i++) {
- if (!couldHoldIncluded(notIncl[i])) {
- scandir(new File(basedir, notIncl[i]),
- notIncl[i] + File.separator, false);
+ String[] excl = new String[dirsExcluded.size()];
+ dirsExcluded.copyInto(excl);
+
+ String[] notIncl = new String[dirsNotIncluded.size()];
+ dirsNotIncluded.copyInto(notIncl);
+
+ for (int i = 0; i < excl.length; i++) {
+ if (!couldHoldIncluded(excl[i])) {
+ scandir(new File(basedir, excl[i]),
+ excl[i] + File.separator, false);
+ }
+ }
+ for (int i = 0; i < notIncl.length; i++) {
+ if (!couldHoldIncluded(notIncl[i])) {
+ scandir(new File(basedir, notIncl[i]),
+ notIncl[i] + File.separator, false);
+ }
+ }
+ }
+ } finally {
+ synchronized (slowScanLock) {
+ haveSlowResults = true;
+ slowScanning = false;
+ slowScanLock.notifyAll();
}
}
-
- haveSlowResults = true;
}
/**
- * Scans the given directory for files and directories. Found files and
+ * 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.
@@ -810,7 +1012,6 @@
} else if (!dir.isDirectory()) {
throw new BuildException(dir + " is not a directory.");
}
-
// avoid double scanning of directories, can only happen in fast mode
if (fast && hasBeenScanned(vpath)) {
return;
@@ -828,19 +1029,15 @@
throw new BuildException("IO error scanning directory "
+ dir.getAbsolutePath());
}
-
if (!followSymlinks) {
Vector noLinks = new Vector();
for (int i = 0; i < newfiles.length; i++) {
try {
if (FILE_UTILS.isSymbolicLink(dir, newfiles[i])) {
String name = vpath + newfiles[i];
- File file = new File(dir, newfiles[i]);
- if (file.isDirectory()) {
- dirsExcluded.addElement(name);
- } else {
- filesExcluded.addElement(name);
- }
+ File file = new File(dir, newfiles[i]);
+ (file.isDirectory()
+ ? dirsExcluded : filesExcluded).addElement(name);
} else {
noLinks.addElement(newfiles[i]);
}
@@ -855,10 +1052,9 @@
newfiles = new String[noLinks.size()];
noLinks.copyInto(newfiles);
}
-
for (int i = 0; i < newfiles.length; i++) {
String name = vpath + newfiles[i];
- File file = new File(dir, newfiles[i]);
+ File file = new File(dir, newfiles[i]);
if (file.isDirectory()) {
if (isIncluded(name)) {
accountForIncludedDir(name, file, fast);
@@ -882,67 +1078,60 @@
}
}
}
+
/**
- * process included file
- * @param name path of the file relative to the directory of the fileset
- * @param file included file
+ * Process included file.
+ * @param name path of the file relative to the directory of the
FileSet.
+ * @param file included File.
*/
private void accountForIncludedFile(String name, File file) {
- if (!filesIncluded.contains(name)
- && !filesExcluded.contains(name)
- && !filesDeselected.contains(name)) {
-
- if (!isExcluded(name)) {
- if (isSelected(name, file)) {
- filesIncluded.addElement(name);
- } else {
- everythingIncluded = false;
- filesDeselected.addElement(name);
- }
- } else {
- everythingIncluded = false;
- filesExcluded.addElement(name);
- }
+ if (filesIncluded.contains(name)
+ || filesExcluded.contains(name)
+ || filesDeselected.contains(name)) {
+ return;
}
+ boolean included = false;
+ if (isExcluded(name)) {
+ filesExcluded.addElement(name);
+ } else if (isSelected(name, file)) {
+ included = true;
+ filesIncluded.addElement(name);
+ } else {
+ filesDeselected.addElement(name);
+ }
+ everythingIncluded &= included;
}
/**
- *
+ * Process included directory.
* @param name path of the directory relative to the directory of
- * the fileset
- * @param file directory as file
- * @param fast
+ * the FileSet.
+ * @param file directory as File.
+ * @param fast whether to perform fast scans.
*/
private void accountForIncludedDir(String name, File file, boolean fast)
{
- if (!dirsIncluded.contains(name)
- && !dirsExcluded.contains(name)
- && !dirsDeselected.contains(name)) {
-
- if (!isExcluded(name)) {
- if (isSelected(name, file)) {
- dirsIncluded.addElement(name);
- if (fast) {
- scandir(file, name + File.separator, fast);
- }
- } else {
- everythingIncluded = false;
- dirsDeselected.addElement(name);
- if (fast && couldHoldIncluded(name)) {
- scandir(file, name + File.separator, fast);
- }
- }
-
- } else {
- everythingIncluded = false;
- dirsExcluded.addElement(name);
- if (fast && couldHoldIncluded(name)) {
- scandir(file, name + File.separator, fast);
- }
- }
+ if (dirsIncluded.contains(name)
+ || dirsExcluded.contains(name)
+ || dirsDeselected.contains(name)) {
+ return;
+ }
+ boolean included = false;
+ if (isExcluded(name)) {
+ dirsExcluded.addElement(name);
+ } else if (isSelected(name, file)) {
+ included = true;
+ dirsIncluded.addElement(name);
+ } else {
+ dirsDeselected.addElement(name);
+ }
+ everythingIncluded &= included;
+ if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+ scandir(file, name + File.separator, fast);
}
}
+
/**
- * Tests whether or not a name matches against at least one include
+ * 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>.
@@ -950,8 +1139,15 @@
* include pattern, or <code>false</code> otherwise.
*/
protected boolean isIncluded(String name) {
- for (int i = 0; i < includes.length; i++) {
- if (matchPath(includes[i], name, isCaseSensitive)) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? includeNonPatterns.contains(name)
+ : includeNonPatterns.contains(name.toUpperCase())) {
+ return true;
+ }
+ for (int i = 0; i < includePatterns.length; i++) {
+ if (matchPath(includePatterns[i], name, isCaseSensitive())) {
return true;
}
}
@@ -959,7 +1155,7 @@
}
/**
- * Tests whether or not a name matches the start of at least one include
+ * 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>.
@@ -968,30 +1164,44 @@
*/
protected boolean couldHoldIncluded(String name) {
for (int i = 0; i < includes.length; i++) {
- if (matchPatternStart(includes[i], name, isCaseSensitive)) {
- if (isMorePowerfulThanExcludes(name, includes[i])) {
- return true;
- }
+ if (matchPatternStart(includes[i], name, isCaseSensitive())
+ && isMorePowerfulThanExcludes(name, includes[i])
+ && isDeeper(includes[i], name)) {
+ return true;
}
}
return false;
}
/**
- * 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 that one want to test
- * @param includepattern one include pattern
- * @return true if there is no exclude pattern more powerful than this
include pattern
- * @since ant1.6
+ * 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(String pattern, String name) {
+ Vector p = SelectorUtils.tokenizePath(pattern);
+ Vector n = SelectorUtils.tokenizePath(name);
+ return p.contains("**") || p.size() > n.size();
+ }
+
+ /**
+ * 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.
+ * @param includepattern one include pattern.
+ * @return true if there is no exclude pattern more powerful than this
include pattern.
+ * @since Ant 1.6
*/
private boolean isMorePowerfulThanExcludes(String name, String
includepattern) {
String soughtexclude = name + File.separator + "**";
@@ -1002,8 +1212,26 @@
}
return true;
}
+
+ /**
+ * Test whether all contents of the specified directory must be excluded.
+ * @param name the directory name to check.
+ * @return whether all the specified directory's contents are excluded.
+ */
+ private boolean contentsExcluded(String name) {
+ name = (name.endsWith(File.separator)) ? name : name +
File.separator;
+ for (int i = 0; i < excludes.length; i++) {
+ String e = excludes[i];
+ if (e.endsWith("**") && SelectorUtils.matchPath(
+ e.substring(0, e.length() - 2), name, isCaseSensitive())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
- * Tests whether or not a name matches against at least one exclude
+ * 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>.
@@ -1011,8 +1239,15 @@
* exclude pattern, or <code>false</code> otherwise.
*/
protected boolean isExcluded(String name) {
- for (int i = 0; i < excludes.length; i++) {
- if (matchPath(excludes[i], name, isCaseSensitive)) {
+ ensureNonPatternSetsReady();
+
+ if (isCaseSensitive()
+ ? excludeNonPatterns.contains(name)
+ : excludeNonPatterns.contains(name.toUpperCase())) {
+ return true;
+ }
+ for (int i = 0; i < excludePatterns.length; i++) {
+ if (matchPath(excludePatterns[i], name, isCaseSensitive())) {
return true;
}
}
@@ -1020,10 +1255,10 @@
}
/**
- * Tests whether a name should be selected.
+ * 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
+ * @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.
*/
@@ -1039,14 +1274,17 @@
}
/**
- * Returns the names of the files which matched at least one of the
+ * Return the names of the files which matched at least one of the
* include patterns and 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() {
+ public synchronized String[] getIncludedFiles() {
+ if (filesIncluded == null) {
+ throw new IllegalStateException();
+ }
String[] files = new String[filesIncluded.size()];
filesIncluded.copyInto(files);
Arrays.sort(files);
@@ -1055,10 +1293,10 @@
/**
* Return the count of included files.
- * @return <CODE>int</CODE>.
+ * @return <code>int</code>.
* @since Ant 1.6.3
*/
- public int getIncludedFilesCount() {
+ public synchronized int getIncludedFilesCount() {
if (filesIncluded == null) {
throw new IllegalStateException();
}
@@ -1066,7 +1304,7 @@
}
/**
- * Returns the names of the files which matched none of the include
+ * 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.
*
@@ -1075,7 +1313,7 @@
*
* @see #slowScan
*/
- public String[] getNotIncludedFiles() {
+ public synchronized String[] getNotIncludedFiles() {
slowScan();
String[] files = new String[filesNotIncluded.size()];
filesNotIncluded.copyInto(files);
@@ -1083,7 +1321,7 @@
}
/**
- * Returns the names of the files which matched at least one of the
+ * Return the names of the files which matched at least one of the
* include patterns and at 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.
@@ -1093,7 +1331,7 @@
*
* @see #slowScan
*/
- public String[] getExcludedFiles() {
+ public synchronized String[] getExcludedFiles() {
slowScan();
String[] files = new String[filesExcluded.size()];
filesExcluded.copyInto(files);
@@ -1101,7 +1339,7 @@
}
/**
- * <p>Returns the names of the files which were selected out and
+ * <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
@@ -1111,7 +1349,7 @@
*
* @see #slowScan
*/
- public String[] getDeselectedFiles() {
+ public synchronized String[] getDeselectedFiles() {
slowScan();
String[] files = new String[filesDeselected.size()];
filesDeselected.copyInto(files);
@@ -1119,14 +1357,17 @@
}
/**
- * Returns the names of the directories which matched at least one of the
+ * Return the names of the directories which matched at least one of the
* include patterns and 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() {
+ public synchronized String[] getIncludedDirectories() {
+ if (dirsIncluded == null) {
+ throw new IllegalStateException();
+ }
String[] directories = new String[dirsIncluded.size()];
dirsIncluded.copyInto(directories);
Arrays.sort(directories);
@@ -1135,10 +1376,10 @@
/**
* Return the count of included directories.
- * @return <CODE>int</CODE>.
+ * @return <code>int</code>.
* @since Ant 1.6.3
*/
- public int getIncludedDirsCount() {
+ public synchronized int getIncludedDirsCount() {
if (dirsIncluded == null) {
throw new IllegalStateException();
}
@@ -1146,7 +1387,7 @@
}
/**
- * Returns the names of the directories which matched none of the include
+ * 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.
*
@@ -1155,7 +1396,7 @@
*
* @see #slowScan
*/
- public String[] getNotIncludedDirectories() {
+ public synchronized String[] getNotIncludedDirectories() {
slowScan();
String[] directories = new String[dirsNotIncluded.size()];
dirsNotIncluded.copyInto(directories);
@@ -1163,7 +1404,7 @@
}
/**
- * Returns the names of the directories which matched at least one of the
+ * Return the names of the directories which matched at least one of the
* include patterns and at least one of the exclude patterns.
* The names are relative to the base directory. This involves
* performing a slow scan if one has not already been completed.
@@ -1173,7 +1414,7 @@
*
* @see #slowScan
*/
- public String[] getExcludedDirectories() {
+ public synchronized String[] getExcludedDirectories() {
slowScan();
String[] directories = new String[dirsExcluded.size()];
dirsExcluded.copyInto(directories);
@@ -1181,7 +1422,7 @@
}
/**
- * <p>Returns the names of the directories which were selected out and
+ * <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
@@ -1191,7 +1432,7 @@
*
* @see #slowScan
*/
- public String[] getDeselectedDirectories() {
+ public synchronized String[] getDeselectedDirectories() {
slowScan();
String[] directories = new String[dirsDeselected.size()];
dirsDeselected.copyInto(directories);
@@ -1199,9 +1440,9 @@
}
/**
- * Adds default exclusions to the current exclusions set.
+ * Add default exclusions to the current exclusions set.
*/
- public void addDefaultExcludes() {
+ public synchronized void addDefaultExcludes() {
int excludesLength = excludes == null ? 0 : excludes.length;
String[] newExcludes;
newExcludes = new String[excludesLength + defaultExcludes.size()];
@@ -1218,29 +1459,23 @@
}
/**
- * Get the named resource
+ * 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 Resource getResource(String name) {
+ public synchronized Resource getResource(String name) {
File f = FILE_UTILS.resolveFile(basedir, name);
return new Resource(name, f.exists(), f.lastModified(),
f.isDirectory(), f.length());
}
/**
- * temporary table to speed up the various scanning methods below
- *
- * @since Ant 1.6
- */
- private Map fileListMap = new HashMap();
-
- /**
- * Returns a cached result of list performed on file, if
+ * Return a cached result of list performed on file, if
* available. Invokes the method and caches the result otherwise.
*
+ * @param file File (dir) to list.
* @since Ant 1.6
*/
private String[] list(File file) {
@@ -1255,97 +1490,51 @@
}
/**
- * From <code>base</code> traverse the filesystem in a case
- * insensitive manner in order to find a file that matches the
- * given name.
- *
- * @return File object that points to the file in question. if it
- * hasn't been found it will simply be <code>new File(base,
- * path)</code>.
- *
- * @since Ant 1.6
- */
- private File findFileCaseInsensitive(File base, String path) {
- File f = findFileCaseInsensitive(base,
- SelectorUtils.tokenizePath(path));
- return f == null ? new File(base, path) : f;
- }
-
- /**
- * From <code>base</code> traverse the filesystem in a case
- * insensitive manner in order to find a file that matches the
- * given stack of names.
- *
- * @return File object that points to the file in question or null.
- *
- * @since Ant 1.6
- */
- private File findFileCaseInsensitive(File base, Vector pathElements) {
- if (pathElements.size() == 0) {
- return base;
- } else {
- if (!base.isDirectory()) {
- return null;
- }
- String[] files = list(base);
- if (files == null) {
- throw new BuildException("IO error scanning directory "
- + base.getAbsolutePath());
- }
- String current = (String) pathElements.remove(0);
- for (int i = 0; i < files.length; i++) {
- if (files[i].equals(current)) {
- base = new File(base, files[i]);
- return findFileCaseInsensitive(base, pathElements);
- }
- }
- for (int i = 0; i < files.length; i++) {
- if (files[i].equalsIgnoreCase(current)) {
- base = new File(base, files[i]);
- return findFileCaseInsensitive(base, pathElements);
- }
- }
- }
- return null;
- }
-
- /**
* From <code>base</code> traverse the filesystem in order to find
* a file that matches the given name.
*
+ * @param base base File (dir).
+ * @param path file path.
+ * @param cs whether to scan case-sensitively.
* @return File object that points to the file in question or null.
*
- * @since Ant 1.6
+ * @since Ant 1.6.3
*/
- private File findFile(File base, String path) {
- return findFile(base, SelectorUtils.tokenizePath(path));
+ private File findFile(File base, String path, boolean cs) {
+ return findFile(base, SelectorUtils.tokenizePath(path), cs);
}
/**
* From <code>base</code> traverse the filesystem in order to find
* a file that matches the given stack of names.
*
+ * @param base base File (dir).
+ * @param pathElements Vector of path elements (dirs...file).
+ * @param cs whether to scan case-sensitively.
* @return File object that points to the file in question or null.
*
- * @since Ant 1.6
+ * @since Ant 1.6.3
*/
- private File findFile(File base, Vector pathElements) {
+ private File findFile(File base, Vector pathElements, boolean cs) {
if (pathElements.size() == 0) {
return base;
- } else {
- if (!base.isDirectory()) {
- return null;
- }
- String[] files = list(base);
- if (files == null) {
- throw new BuildException("IO error scanning directory "
- + base.getAbsolutePath());
- }
- String current = (String) pathElements.remove(0);
- for (int i = 0; i < files.length; i++) {
- if (files[i].equals(current)) {
- base = new File(base, files[i]);
- return findFile(base, pathElements);
+ }
+ if (!base.isDirectory()) {
+ return null;
+ }
+ String[] files = list(base);
+ if (files == null) {
+ throw new BuildException("IO error scanning directory "
+ + base.getAbsolutePath());
+ }
+ String current = (String) pathElements.remove(0);
+
+ boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS;
+ for (int i = 0; i < matchCase.length; i++) {
+ for (int j = 0; j < files.length; j++) {
+ if (matchCase[i] ? files[j].equals(current)
+ : files[j].equalsIgnoreCase(current)) {
+ return findFile(new File(base, files[j]), pathElements,
cs);
}
}
}
@@ -1355,6 +1544,8 @@
/**
* Do we have to traverse a symlink when trying to reach path from
* basedir?
+ * @param base base File (dir).
+ * @param path file path.
* @since Ant 1.6
*/
private boolean isSymlink(File base, String path) {
@@ -1364,37 +1555,27 @@
/**
* Do we have to traverse a symlink when trying to reach path from
* basedir?
+ * @param base base File (dir).
+ * @param pathElements Vector of path elements (dirs...file).
* @since Ant 1.6
*/
private boolean isSymlink(File base, Vector pathElements) {
if (pathElements.size() > 0) {
String current = (String) pathElements.remove(0);
try {
- if (FILE_UTILS.isSymbolicLink(base, current)) {
- return true;
- } else {
- base = new File(base, current);
- return isSymlink(base, pathElements);
- }
+ return FILE_UTILS.isSymbolicLink(base, current)
+ || isSymlink(new File(base, current), pathElements);
} catch (IOException ioe) {
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);
- return false;
}
}
return false;
}
/**
- * List of all scanned directories.
- *
- * @since Ant 1.6
- */
- private Set scannedDirs = new HashSet();
-
- /**
* Has the directory with the given path relative to the base
* directory already been scanned?
*
@@ -1407,12 +1588,62 @@
}
/**
+ * 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 getScannedDirs() {
+ return scannedDirs;
+ }
+
+ /**
* Clear internal caches.
*
* @since Ant 1.6
*/
- private void clearCaches() {
+ private synchronized void clearCaches() {
fileListMap.clear();
- scannedDirs.clear();
+ includeNonPatterns.clear();
+ excludeNonPatterns.clear();
+ includePatterns = null;
+ excludePatterns = null;
+ areNonPatternSetsReady = false;
+ }
+
+ /**
+ * Ensure that the in|exclude "patterns"
+ * have been properly divided up.
+ *
+ * @since Ant 1.6.3
+ */
+ private 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 set Set to populate.
+ * @param patterns String[] of patterns.
+ * @since Ant 1.6.3
+ */
+ private String[] fillNonPatternSet(Set set, String[] patterns) {
+ ArrayList al = new ArrayList(patterns.length);
+ for (int i = 0; i < patterns.length; i++) {
+ if (!SelectorUtils.hasWildcards(patterns[i])) {
+ set.add(isCaseSensitive() ? patterns[i]
+ : patterns[i].toUpperCase());
+ } else {
+ al.add(patterns[i]);
+ }
+ }
+ return set.size() == 0 ? patterns
+ : (String[]) al.toArray(new String[al.size()]);
+ }
+
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]