Return-Path: Delivered-To: apmail-ant-dev-archive@www.apache.org Received: (qmail 75508 invoked from network); 11 Mar 2005 19:34:08 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 11 Mar 2005 19:34:08 -0000 Received: (qmail 33179 invoked by uid 500); 11 Mar 2005 19:34:06 -0000 Delivered-To: apmail-ant-dev-archive@ant.apache.org Received: (qmail 33097 invoked by uid 500); 11 Mar 2005 19:34:05 -0000 Mailing-List: contact dev-help@ant.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Ant Developers List" Reply-To: "Ant Developers List" Delivered-To: mailing list dev@ant.apache.org Received: (qmail 33083 invoked by uid 500); 11 Mar 2005 19:34:05 -0000 Received: (qmail 33036 invoked by uid 99); 11 Mar 2005 19:34:04 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from minotaur.apache.org (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Fri, 11 Mar 2005 11:34:02 -0800 Received: (qmail 75270 invoked by uid 1818); 11 Mar 2005 19:34:00 -0000 Date: 11 Mar 2005 19:34:00 -0000 Message-ID: <20050311193400.75269.qmail@minotaur.apache.org> From: mbenson@apache.org To: ant-cvs@apache.org Subject: cvs commit: ant/src/main/org/apache/tools/ant DirectoryScanner.java X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N 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. + * + *

If this instance is not case sensitive, the file names get + * turned to lower case.

+ * + *

Gets lazily initialized on the first invocation of + * isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @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. + * + *

If this instance is not case sensitive, the file names get + * turned to lower case.

+ * + *

Gets lazily initialized on the first invocation of + * isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @since Ant 1.6.3 + */ + private Set excludeNonPatterns = new HashSet(); + + /** + * Array of all include patterns that contain wildcards. + * + *

Gets lazily initialized on the first invocation of + * isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @since Ant 1.6.3 + */ + private String[] includePatterns; + + /** + * Array of all exclude patterns that contain wildcards. + * + *

Gets lazily initialized on the first invocation of + * isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @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 "**". *

* 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 "**". *

* 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 * null. @@ -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 * null. @@ -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:
* '*' means zero or more characters
* '?' 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:
* '*' means zero or more characters
* '?' means one and only one character @@ -387,9 +508,8 @@ * default exclude. * * @param s A string to add as an exclude pattern. - * @return true if the string was added - * false if it already - * existed. + * @return true if the string was added; + * false if it already existed. * * @since Ant 1.6 */ @@ -406,9 +526,9 @@ * * @param s The string to attempt to remove. * @return true if s was a default - * exclude (and thus was removed), + * exclude (and thus was removed); * false if s 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 * File.separatorChar, so the separator used need not match * File.separatorChar. @@ -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 null. */ - 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 File.separatorChar, so the separator used * need not match File.separatorChar. *

@@ -515,28 +635,21 @@ * May be null, indicating that all files * should be included. If a non-null * list is given, all elements must be - * non-null. + * non-null. */ - 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 File.separatorChar, so the separator used * need not match File.separatorChar. *

@@ -547,47 +660,88 @@ * should be excluded. If a non-null list is * given, all elements must be non-null. */ - 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 File.separatorChar, so + * the separator used need not match File.separatorChar. + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. + * May be null, 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 + * File.separatorChar, so the separator used need not + * match File.separatorChar. + * + *

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 true 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 null. @@ -950,8 +1139,15 @@ * include pattern, or false 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 null. @@ -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 null. @@ -1011,8 +1239,15 @@ * exclude pattern, or false 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 false when the selectors says that the file * should not be selected, true 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 int. + * @return int. * @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 @@ } /** - *

Returns the names of the files which were selected out and + *

Return the names of the files which were selected out and * therefore not ultimately included.

* *

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 int. + * @return int. * @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 @@ } /** - *

Returns the names of the directories which were selected out and + *

Return the names of the directories which were selected out and * therefore not ultimately included.

* *

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 base 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 new File(base, - * path). - * - * @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 base 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 base 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 base 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: dev-unsubscribe@ant.apache.org For additional commands, e-mail: dev-help@ant.apache.org