Browse Source

avoid some tokenization (should really pay of in the next iteration/s) and replace recursion with iteration in findFile/isSymLink - right now we lose memoization of File.list but I'll reintroduce this later.

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@696298 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 17 years ago
parent
commit
2b925503d7
4 changed files with 209 additions and 139 deletions
  1. +31
    -119
      src/main/org/apache/tools/ant/DirectoryScanner.java
  2. +1
    -12
      src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java
  3. +126
    -2
      src/main/org/apache/tools/ant/types/selectors/TokenizedPath.java
  4. +51
    -6
      src/main/org/apache/tools/ant/types/selectors/TokenizedPattern.java

+ 31
- 119
src/main/org/apache/tools/ant/DirectoryScanner.java View File

@@ -186,12 +186,6 @@ public class DirectoryScanner
/** 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.
*
@@ -901,25 +895,39 @@ public class DirectoryScanner
* @since Ant 1.6
*/
private void checkIncludePatterns() {
ensureNonPatternSetsReady();
Map newroots = new HashMap();
// put in the newroots map the include patterns without
// wildcard tokens
int wildcardPatternIndex = 0;
for (int i = 0; i < includes.length; i++) {
boolean wildcards = SelectorUtils.hasWildcards(includes[i]);
if (FileUtils.isAbsolutePath(includes[i])) {
//skip abs. paths not under basedir, if set:
if (basedir != null
&& !SelectorUtils.matchPatternStart(includes[i],
basedir.getAbsolutePath(), isCaseSensitive())) {
if (wildcards) {
wildcardPatternIndex++;
}
continue;
}
} else if (basedir == null) {
//skip non-abs. paths if basedir == null:
if (wildcards) {
wildcardPatternIndex++;
}
continue;
}
newroots.put(SelectorUtils.rtrimWildcardTokens(
includes[i]), includes[i]);
if (wildcards) {
newroots.put(includePatterns[wildcardPatternIndex++]
.rtrimWildcardTokens(), includes[i]);
} else {
newroots.put(new TokenizedPath(includes[i]), includes[i]);
}
}
if (newroots.containsKey("") && basedir != null) {
if (newroots.containsKey(TokenizedPath.EMPTY_PATH)
&& basedir != null) {
// we are going to scan everything anyway
scandir(basedir, "", true);
} else {
@@ -937,12 +945,12 @@ public class DirectoryScanner
}
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String currentelement = (String) entry.getKey();
TokenizedPath currentPath = (TokenizedPath) entry.getKey();
String currentelement = currentPath.toString();
if (basedir == null
&& !FileUtils.isAbsolutePath(currentelement)) {
continue;
}
String originalpattern = (String) entry.getValue();
File myfile = new File(basedir, currentelement);

if (myfile.exists()) {
@@ -955,18 +963,24 @@ public class DirectoryScanner
: FILE_UTILS.removeLeadingPath(canonBase,
getCanonicalFile(myfile));
if (!path.equals(currentelement) || ON_VMS) {
myfile = findFile(basedir, currentelement, true);
myfile = currentPath.findFile(basedir, true);
if (myfile != null && basedir != null) {
currentelement = FILE_UTILS.removeLeadingPath(
basedir, myfile);
if (!currentPath.toString()
.equals(currentelement)) {
currentPath =
new TokenizedPath(currentelement);
}
}
}
} catch (IOException ex) {
throw new BuildException(ex);
}
}

if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
File f = findFile(basedir, currentelement, false);
File f = currentPath.findFile(basedir, false);
if (f != null && f.exists()) {
// adapt currentelement to the case we've
// actually found
@@ -974,11 +988,12 @@ public class DirectoryScanner
? f.getAbsolutePath()
: FILE_UTILS.removeLeadingPath(basedir, f);
myfile = f;
currentPath = new TokenizedPath(currentelement);
}
}

if (myfile != null && myfile.exists()) {
if (!followSymlinks
&& isSymlink(basedir, currentelement)) {
if (!followSymlinks && currentPath.isSymlink(basedir)) {
continue;
}
if (myfile.isDirectory()) {
@@ -997,6 +1012,7 @@ public class DirectoryScanner
scandir(myfile, currentelement, true);
}
} else {
String originalpattern = (String) entry.getValue();
boolean included = isCaseSensitive()
? originalpattern.equals(currentelement)
: originalpattern.equalsIgnoreCase(currentelement);
@@ -1641,110 +1657,6 @@ public class DirectoryScanner
return files;
}

/**
* 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.3
*/
private File findFile(File base, String path, boolean cs) {
if (FileUtils.isAbsolutePath(path)) {
if (base == null) {
String[] s = FILE_UTILS.dissect(path);
base = new File(s[0]);
path = s[1];
} else {
File f = FILE_UTILS.normalize(path);
String s = FILE_UTILS.removeLeadingPath(base, f);
if (s.equals(f.getAbsolutePath())) {
//removing base from path yields no change; path
//not child of base
return null;
}
path = s;
}
}
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.3
*/
private File findFile(File base, Vector pathElements, boolean cs) {
if (pathElements.size() == 0) {
return base;
}
String current = (String) pathElements.remove(0);
if (base == null) {
return findFile(new File(current), pathElements, cs);
}
if (!base.isDirectory()) {
return null;
}
String[] files = list(base);
if (files == null) {
throw new BuildException("IO error scanning directory "
+ base.getAbsolutePath());
}
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);
}
}
}
return null;
}

/**
* 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) {
return isSymlink(base, SelectorUtils.tokenizePath(path));
}

/**
* 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 {
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;
}

/**
* Has the directory with the given path relative to the base
* directory already been scanned?


+ 1
- 12
src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java View File

@@ -698,18 +698,7 @@ public final class SelectorUtils {
* @return the leftmost part of the pattern without wildcards
*/
public static String rtrimWildcardTokens(String input) {
String[] tokens = tokenizePathAsArray(input);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < tokens.length; i++) {
if (hasWildcards(tokens[i])) {
break;
}
if (i > 0 && sb.charAt(sb.length() - 1) != File.separatorChar) {
sb.append(File.separator);
}
sb.append(tokens[i]);
}
return sb.toString();
return new TokenizedPattern(input).rtrimWildcardTokens().toString();
}
}


+ 126
- 2
src/main/org/apache/tools/ant/types/selectors/TokenizedPath.java View File

@@ -18,12 +18,30 @@

package org.apache.tools.ant.types.selectors;

import java.io.File;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.util.FileUtils;

/**
* Container for a path that has been split into its components.
* @since 1.8.0
*/
public class TokenizedPath {

/**
* Instance that holds no tokens at all.
*/
public static final TokenizedPath EMPTY_PATH =
new TokenizedPath("", new String[0]);

/** 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};

private final String path;
private final String tokenizedPath[];

@@ -33,10 +51,14 @@ public class TokenizedPath {
* <code>null</code>.
*/
public TokenizedPath(String path) {
this.path = path;
this.tokenizedPath = SelectorUtils.tokenizePathAsArray(path);
this(path, SelectorUtils.tokenizePathAsArray(path));
}
/* package */ TokenizedPath(String path, String[] tokens) {
this.path = path;
this.tokenizedPath = tokens;
}

/**
* @return The original path String
*/
@@ -54,4 +76,106 @@ public class TokenizedPath {
/* package */ String[] getTokens() {
return tokenizedPath;
}

/**
* From <code>base</code> traverse the filesystem in order to find
* a file that matches the given name.
*
* @param base base File (dir).
* @param cs whether to scan case-sensitively.
* @return File object that points to the file in question or null.
*/
public File findFile(File base, final boolean cs) {
String[] tokens = tokenizedPath;
if (FileUtils.isAbsolutePath(path)) {
if (base == null) {
String[] s = FILE_UTILS.dissect(path);
base = new File(s[0]);
tokens = SelectorUtils.tokenizePathAsArray(s[1]);
} else {
File f = FILE_UTILS.normalize(path);
String s = FILE_UTILS.removeLeadingPath(base, f);
if (s.equals(f.getAbsolutePath())) {
//removing base from path yields no change; path
//not child of base
return null;
}
tokens = SelectorUtils.tokenizePathAsArray(s);
}
}
return findFile(base, tokens, cs);
}

/**
* Do we have to traverse a symlink when trying to reach path from
* basedir?
* @param base base File (dir).
*/
public boolean isSymlink(File base) {
for (int i = 0; i < tokenizedPath.length; i++) {
try {
if (FILE_UTILS.isSymbolicLink(base, tokenizedPath[i])) {
return true;
}
base = new File(base, tokenizedPath[i]);
} catch (java.io.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;
}

/**
* true if the original paths are equal.
*/
public boolean equals(Object o) {
return o instanceof TokenizedPath
&& path.equals(((TokenizedPath) o).path);
}

public int hashCode() {
return path.hashCode();
}

/**
* 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) - must not be null.
* @param pathElements array of path elements (dirs...file).
* @param cs whether to scan case-sensitively.
* @return File object that points to the file in question or null.
*/
private static File findFile(File base, final String[] pathElements,
final boolean cs) {
for (int current = 0; current < pathElements.length; current++) {
if (!base.isDirectory()) {
return null;
}
String[] files = base.list();
if (files == null) {
throw new BuildException("IO error scanning directory "
+ base.getAbsolutePath());
}
boolean found = false;
boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS;
for (int i = 0; !found && i < matchCase.length; i++) {
for (int j = 0; !found && j < files.length; j++) {
if (matchCase[i]
? files[j].equals(pathElements[current])
: files[j].equalsIgnoreCase(pathElements[current])) {
base = new File(base, files[j]);
found = true;
}
}
}
if (!found) {
return null;
}
}
return pathElements.length == 0 && !base.isDirectory() ? null : base;
}
}

+ 51
- 6
src/main/org/apache/tools/ant/types/selectors/TokenizedPattern.java View File

@@ -18,13 +18,16 @@

package org.apache.tools.ant.types.selectors;

import java.io.File;

/**
* Provides reusable path pattern matching. PathPattern is preferable to equivalent
* SelectorUtils methods if you need to execute multiple matching with the same pattern
* because here the pattern itself will be parsed only once.
* Provides reusable path pattern matching. PathPattern is preferable
* to equivalent SelectorUtils methods if you need to execute multiple
* matching with the same pattern because here the pattern itself will
* be parsed only once.
* @see SelectorUtils#matchPath(String, String)
* @see SelectorUtils#matchPath(String, String, boolean)
* @since 1.8
* @since 1.8.0
*/
public class TokenizedPattern {

@@ -37,10 +40,14 @@ public class TokenizedPattern {
* <code>null</code>.
*/
public TokenizedPattern(String pattern) {
this.pattern = pattern;
this.tokenizedPattern = SelectorUtils.tokenizePathAsArray(pattern);
this(pattern, SelectorUtils.tokenizePathAsArray(pattern));
}
private TokenizedPattern(String pattern, String[] tokens) {
this.pattern = pattern;
this.tokenizedPattern = tokens;
}

/**
* Tests whether or not a given path matches a given pattern.
*
@@ -90,6 +97,18 @@ public class TokenizedPattern {
return pattern;
}

/**
* true if the original patterns are equal.
*/
public boolean equals(Object o) {
return o instanceof TokenizedPattern
&& pattern.equals(((TokenizedPattern) o).pattern);
}

public int hashCode() {
return pattern.hashCode();
}

/**
* The depth (or length) of a pattern.
*/
@@ -108,4 +127,30 @@ public class TokenizedPattern {
}
return false;
}

/**
* Returns a new TokenizedPath where all tokens of this pattern to
* the right containing wildcards have been removed
* @return the leftmost part of the pattern without wildcards
*/
public TokenizedPath rtrimWildcardTokens() {
StringBuffer sb = new StringBuffer();
int newLen = 0;
for (; newLen < tokenizedPattern.length; newLen++) {
if (SelectorUtils.hasWildcards(tokenizedPattern[newLen])) {
break;
}
if (newLen > 0
&& sb.charAt(sb.length() - 1) != File.separatorChar) {
sb.append(File.separator);
}
sb.append(tokenizedPattern[newLen]);
}
if (newLen == 0) {
return TokenizedPath.EMPTY_PATH;
}
String[] newPats = new String[newLen];
System.arraycopy(tokenizedPattern, 0, newPats, 0, newLen);
return new TokenizedPath(sb.toString(), newPats);
}
}

Loading…
Cancel
Save