git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@696298 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -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? | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||