From 5716c15248acdb7dba42d951cf8273ee87cc6846 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Mon, 14 May 2012 17:53:39 -0700 Subject: Original upload of the glob library This is a few extracted classes from Ant, under Apache License Public 2.0 Change-Id: Id1b91ebab95e8d746787d40d5a1a820bd77bff8d --- src/org/apache/tools/ant/BuildException.java | 79 +++ .../tools/ant/taskdefs/condition/Condition.java | 35 ++ .../apache/tools/ant/taskdefs/condition/Os.java | 322 ++++++++++ .../tools/ant/types/selectors/SelectorUtils.java | 647 +++++++++++++++++++++ src/org/apache/tools/ant/util/FileUtils.java | 189 ++++++ 5 files changed, 1272 insertions(+) create mode 100644 src/org/apache/tools/ant/BuildException.java create mode 100644 src/org/apache/tools/ant/taskdefs/condition/Condition.java create mode 100644 src/org/apache/tools/ant/taskdefs/condition/Os.java create mode 100644 src/org/apache/tools/ant/types/selectors/SelectorUtils.java create mode 100644 src/org/apache/tools/ant/util/FileUtils.java (limited to 'src/org') diff --git a/src/org/apache/tools/ant/BuildException.java b/src/org/apache/tools/ant/BuildException.java new file mode 100644 index 0000000..f547cd7 --- /dev/null +++ b/src/org/apache/tools/ant/BuildException.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant; + +/** + * Signals an error condition during a build + */ +public class BuildException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a build exception with no descriptive information. + */ + public BuildException() { + super(); + } + + /** + * Constructs an exception with the given descriptive message. + * + * @param message A description of or information about the exception. + * Should not be null. + */ + public BuildException(String message) { + super(message); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * + * @param message A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + */ + public BuildException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + /** + * Constructs an exception with the given exception as a root cause. + * + * @param cause The exception that might have caused this one. + * Should not be null. + */ + public BuildException(Throwable cause) { + super(cause); + } + + /** + * Returns the nested exception, if any. + * + * @return the nested exception, or null if no + * exception is associated with this one + * @deprecated Use {@link #getCause} instead. + */ + @Deprecated + public Throwable getException() { + return getCause(); + } +} diff --git a/src/org/apache/tools/ant/taskdefs/condition/Condition.java b/src/org/apache/tools/ant/taskdefs/condition/Condition.java new file mode 100644 index 0000000..62adbf3 --- /dev/null +++ b/src/org/apache/tools/ant/taskdefs/condition/Condition.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs.condition; + +import org.apache.tools.ant.BuildException; + +/** + * Interface for conditions to use inside the <condition> task. + * + */ +public interface Condition { + /** + * Is this condition true? + * @return true if the condition is true + * @exception BuildException if an error occurs + */ + boolean eval() throws BuildException; +} + diff --git a/src/org/apache/tools/ant/taskdefs/condition/Os.java b/src/org/apache/tools/ant/taskdefs/condition/Os.java new file mode 100644 index 0000000..4e90e35 --- /dev/null +++ b/src/org/apache/tools/ant/taskdefs/condition/Os.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs.condition; + +import org.apache.tools.ant.BuildException; + +import java.util.Locale; + +/** + * Condition that tests the OS type. + * + * @since Ant 1.4 + */ +public class Os implements Condition { + private static final String OS_NAME = + System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + private static final String OS_ARCH = + System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); + private static final String OS_VERSION = + System.getProperty("os.version").toLowerCase(Locale.ENGLISH); + private static final String PATH_SEP = + System.getProperty("path.separator"); + + /** + * OS family to look for + */ + private String family; + /** + * Name of OS + */ + private String name; + /** + * version of OS + */ + private String version; + /** + * OS architecture + */ + private String arch; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_9X = "win9x"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_VMS = "openvms"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + /** OS family that can be tested for. {@value} */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889 + * @see https://issues.apache.org/jira/browse/HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + /** + * Default constructor + * + */ + public Os() { + //default + } + + /** + * Constructor that sets the family attribute + * @param family a String value + */ + public Os(String family) { + setFamily(family); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
+ * Possible values:
+ * + */ + public void setFamily(String f) { + family = f.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS name + * + * @param name The OS name + */ + public void setName(String name) { + this.name = name.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS architecture + * + * @param arch The OS architecture + */ + public void setArch(String arch) { + this.arch = arch.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS version + * + * @param version The OS version + */ + public void setVersion(String version) { + this.version = version.toLowerCase(Locale.ENGLISH); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * @return true if the os matches. + * @throws BuildException if there is an error. + * @see Os#setFamily(String) + */ + @Override + public boolean eval() throws BuildException { + return isOs(family, name, arch, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * @param family the family to check for + * @return true if the OS matches + * @since 1.5 + */ + public static boolean isFamily(String family) { + return isOs(family, null, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isName(String name) { + return isOs(null, name, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isArch(String arch) { + return isOs(null, null, arch, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isVersion(String version) { + return isOs(null, null, null, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isOs(String family, String name, String arch, + String version) { + boolean retValue = false; + + if (family != null || name != null || arch != null + || version != null) { + + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + + if (family != null) { + + //windows probing logic relies on the word 'windows' in + //the OS + boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1; + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + //there are only four 9x platforms that we look for + is9x = (OS_NAME.indexOf("95") >= 0 + || OS_NAME.indexOf("98") >= 0 + || OS_NAME.indexOf("me") >= 0 + //wince isn't really 9x, but crippled enough to + //be a muchness. Ant doesnt run on CE, anyway. + || OS_NAME.indexOf("ce") >= 0); + isNT = !is9x; + } + if (family.equals(FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(FAMILY_9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(FAMILY_OS2)) { + isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1; + } else if (family.equals(FAMILY_NETWARE)) { + isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1; + } else if (family.equals(FAMILY_DOS)) { + isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE); + } else if (family.equals(FAMILY_MAC)) { + isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1 + || OS_NAME.indexOf(DARWIN) > -1; + } else if (family.equals(FAMILY_TANDEM)) { + isFamily = OS_NAME.indexOf("nonstop_kernel") > -1; + } else if (family.equals(FAMILY_UNIX)) { + isFamily = PATH_SEP.equals(":") + && !isFamily(FAMILY_VMS) + && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x") + || OS_NAME.indexOf(DARWIN) > -1); + } else if (family.equals(FAMILY_ZOS)) { + isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1 + || OS_NAME.indexOf("os/390") > -1; + } else if (family.equals(FAMILY_OS400)) { + isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1; + } else if (family.equals(FAMILY_VMS)) { + isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1; + } else { + throw new BuildException( + "Don\'t know how to detect os family \"" + + family + "\""); + } + } + if (name != null) { + isName = name.equals(OS_NAME); + } + if (arch != null) { + isArch = arch.equals(OS_ARCH); + } + if (version != null) { + isVersion = version.equals(OS_VERSION); + } + retValue = isFamily && isName && isArch && isVersion; + } + return retValue; + } +} diff --git a/src/org/apache/tools/ant/types/selectors/SelectorUtils.java b/src/org/apache/tools/ant/types/selectors/SelectorUtils.java new file mode 100644 index 0000000..388d4d5 --- /dev/null +++ b/src/org/apache/tools/ant/types/selectors/SelectorUtils.java @@ -0,0 +1,647 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.types.selectors; + +import org.apache.tools.ant.util.FileUtils; + +import java.io.File; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + *

This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *

+ *

This is a Singleton.

+ * + * @since 1.5 + */ +public final class SelectorUtils { + + /** + * The pattern that matches an arbitrary number of directories. + * @since Ant 1.8.0 + */ + public static final String DEEP_TREE_MATCH = "**"; + + private static final SelectorUtils instance = new SelectorUtils(); + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Private Constructor + */ + private SelectorUtils() { + } + + /** + * Retrieves the instance of the Singleton. + * @return singleton instance + */ + public static SelectorUtils getInstance() { + return instance; + } + + /** + * Tests 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 + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str) { + return matchPatternStart(pattern, str, true); + } + + /** + * Tests 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 + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if (str.startsWith(File.separator) + != pattern.startsWith(File.separator)) { + return false; + } + + String[] patDirs = tokenizePathAsArray(pattern); + String[] strDirs = tokenizePathAsArray(str); + return matchPatternStart(patDirs, strDirs, isCaseSensitive); + } + + + /** + * Tests 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 + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param patDirs The tokenized pattern to match against. Must not be + * null. + * @param strDirs The tokenized path to match. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + static boolean matchPatternStart(String[] patDirs, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = patDirs[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + + // CheckStyle:SimplifyBooleanReturnCheck OFF + // Check turned off as the code needs the comments for the various + // code paths. + if (strIdxStart > strIdxEnd) { + // String is exhausted + return true; + } else if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else { + // pattern now holds ** while string is not exhausted + // this will generate false positives but we can live with that. + return true; + } + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPath + * + * @see TokenizedPath + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), true); + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPattern + * + * @see TokenizedPattern + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str, + boolean isCaseSensitive) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive); + } + + /** + * Core implementation of matchPath. It is isolated so that it + * can be called from TokenizedPattern. + */ + static boolean matchPath(String[] tokenizedPattern, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = tokenizedPattern.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } else { + if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxEnd]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = tokenizedPattern[patIdxStart + j + 1]; + String subStr = strDirs[strIdxStart + i + j]; + if (!match(subPat, subStr, isCaseSensitive)) { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + + return true; + } + + /** + * Tests 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 + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str) { + return match(pattern, str, true); + } + + /** + * Tests 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 + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * @param caseSensitive Whether or not matching should be performed + * case sensitively. + * + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str, + boolean caseSensitive) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for (int i = 0; i < patArr.length; i++) { + if (patArr[i] == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (different(caseSensitive, ch, strArr[i])) { + return false; // Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while (true) { + ch = patArr[patIdxStart]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxStart])) { + return false; // Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // Process characters after last star + while (true) { + ch = patArr[patIdxEnd]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxEnd])) { + return false; // Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + ch = patArr[patIdxStart + j + 1]; + if (ch != '?') { + if (different(caseSensitive, ch, + strArr[strIdxStart + i + j])) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + private static boolean allStars(char[] chars, int start, int end) { + for (int i = start; i <= end; ++i) { + if (chars[i] != '*') { + return false; + } + } + return true; + } + + private static boolean different( + boolean caseSensitive, char ch, char other) { + return caseSensitive + ? ch != other + : Character.toUpperCase(ch) != Character.toUpperCase(other); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * File.separator. + * + * @param path Path to tokenize. Must not be null. + * + * @return a Vector of path elements from the tokenized path + */ + @SuppressWarnings("rawtypes") + public static Vector tokenizePath (String path) { + return tokenizePath(path, File.separator); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * + * @param path Path to tokenize. Must not be null. + * @param separator the separator against which to tokenize. + * + * @return a Vector of path elements from the tokenized path + * @since Ant 1.6 + */ + @SuppressWarnings({ + "rawtypes", "unchecked" + }) + public static Vector tokenizePath (String path, String separator) { + Vector ret = new Vector(); + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + ret.add(s[0]); + path = s[1]; + } + StringTokenizer st = new StringTokenizer(path, separator); + while (st.hasMoreTokens()) { + ret.addElement(st.nextToken()); + } + return ret; + } + + /** + * Same as {@link #tokenizePath tokenizePath} but hopefully faster. + */ + /*package*/ static String[] tokenizePathAsArray(String path) { + String root = null; + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + root = s[0]; + path = s[1]; + } + char sep = File.separatorChar; + int start = 0; + int len = path.length(); + int count = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + count++; + } + start = pos + 1; + } + } + if (len != start) { + count++; + } + String[] l = new String[count + ((root == null) ? 0 : 1)]; + + if (root != null) { + l[0] = root; + count = 1; + } else { + count = 0; + } + start = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + String tok = path.substring(start, pos); + l[count++] = tok; + } + start = pos + 1; + } + } + if (len != start) { + String tok = path.substring(start); + l[count/*++*/] = tok; + } + return l; + } + + /** + * Returns dependency information on these two files. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original file + * @param target the file being compared against + * @param granularity the amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(File src, File target, int granularity) { + if (!src.exists()) { + return false; + } + if (!target.exists()) { + return true; + } + if ((src.lastModified() - granularity) > target.lastModified()) { + return true; + } + return false; + } + + /** + * "Flattens" a string by removing all whitespace (space, tab, linefeed, + * carriage return, and formfeed). This uses StringTokenizer and the + * default set of tokens as documented in the single arguement constructor. + * + * @param input a String to remove all whitespace. + * @return a String that has had all whitespace removed. + */ + public static String removeWhitespace(String input) { + StringBuffer result = new StringBuffer(); + if (input != null) { + StringTokenizer st = new StringTokenizer(input); + while (st.hasMoreTokens()) { + result.append(st.nextToken()); + } + } + return result.toString(); + } + + /** + * Tests if a string contains stars or question marks + * @param input a String which one wants to test for containing wildcard + * @return true if the string contains at least a star or a question mark + */ + public static boolean hasWildcards(String input) { + return (input.indexOf('*') != -1 || input.indexOf('?') != -1); + } +} + diff --git a/src/org/apache/tools/ant/util/FileUtils.java b/src/org/apache/tools/ant/util/FileUtils.java new file mode 100644 index 0000000..2774a17 --- /dev/null +++ b/src/org/apache/tools/ant/util/FileUtils.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.util; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.condition.Os; + +import java.io.File; +import java.util.Random; + +/** + * This class also encapsulates methods which allow Files to be + * referred to using abstract path names which are translated to native + * system file paths at runtime as well as copying files or setting + * their last modification time. + * + */ +public class FileUtils { + private static final int DELETE_RETRY_SLEEP_MILLIS = 10; + private static final int EXPAND_SPACE = 50; + private static final FileUtils PRIMARY_INSTANCE = new FileUtils(); + + //get some non-crypto-grade randomness from various places. + private static Random rand = new Random(System.currentTimeMillis() + + Runtime.getRuntime().freeMemory()); + + private static final boolean ON_NETWARE = Os.isFamily("netware"); + private static final boolean ON_DOS = Os.isFamily("dos"); + private static final boolean ON_WIN9X = Os.isFamily("win9x"); + private static final boolean ON_WINDOWS = Os.isFamily("windows"); + + static final int BUF_SIZE = 8192; + + + /** + * The granularity of timestamps under FAT. + */ + public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000; + + /** + * The granularity of timestamps under Unix. + */ + public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000; + + /** + * The granularity of timestamps under the NT File System. + * NTFS has a granularity of 100 nanoseconds, which is less + * than 1 millisecond, so we round this up to 1 millisecond. + */ + public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1; + + /** + * A one item cache for fromUri. + * fromUri is called for each element when parseing ant build + * files. It is a costly operation. This just caches the result + * of the last call. + */ + private Object cacheFromUriLock = new Object(); + private String cacheFromUriRequest = null; + private String cacheFromUriResponse = null; + + /** + * Factory method. + * + * @return a new instance of FileUtils. + * @deprecated since 1.7. + * Use getFileUtils instead, + * FileUtils do not have state. + */ + @Deprecated + public static FileUtils newFileUtils() { + return new FileUtils(); + } + + /** + * Method to retrieve The FileUtils, which is shared by all users of this + * method. + * @return an instance of FileUtils. + * @since Ant 1.6.3 + */ + public static FileUtils getFileUtils() { + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected FileUtils() { + } + + /** + * Verifies that the specified filename represents an absolute path. + * Differs from new java.io.File("filename").isAbsolute() in that a path + * beginning with a double file separator--signifying a Windows UNC--must + * at minimum match "\\a\b" to be considered an absolute path. + * @param filename the filename to be checked. + * @return true if the filename represents an absolute path. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.6.3 + */ + public static boolean isAbsolutePath(String filename) { + int len = filename.length(); + if (len == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + if (!(ON_DOS || ON_NETWARE)) { + return (c == sep); + } + if (c == sep) { + // CheckStyle:MagicNumber OFF + if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) { + return false; + } + // CheckStyle:MagicNumber ON + int nextsep = filename.indexOf(sep, 2); + return nextsep > 2 && nextsep + 1 < len; + } + int colon = filename.indexOf(':'); + return (Character.isLetter(c) && colon == 1 + && filename.length() > 2 && filename.charAt(2) == sep) + || (ON_NETWARE && colon > 0); + } + + /** + * Dissect the specified absolute path. + * @param path the path to dissect. + * @return String[] {root, remaining path}. + * @throws java.lang.NullPointerException if path is null. + * @since Ant 1.7 + */ + public String[] dissect(String path) { + char sep = File.separatorChar; + path = path.replace('/', sep).replace('\\', sep); + + // make sure we are dealing with an absolute path + if (!isAbsolutePath(path)) { + throw new BuildException(path + " is not an absolute path"); + } + String root = null; + int colon = path.indexOf(':'); + if (colon > 0 && (ON_DOS || ON_NETWARE)) { + + int next = colon + 1; + root = path.substring(0, next); + char[] ca = path.toCharArray(); + root += sep; + //remove the initial separator; the root has it. + next = (ca[next] == sep) ? next + 1 : next; + + StringBuffer sbPath = new StringBuffer(); + // Eliminate consecutive slashes after the drive spec: + for (int i = next; i < ca.length; i++) { + if (ca[i] != sep || ca[i - 1] != sep) { + sbPath.append(ca[i]); + } + } + path = sbPath.toString(); + } else if (path.length() > 1 && path.charAt(1) == sep) { + // UNC drive + int nextsep = path.indexOf(sep, 2); + nextsep = path.indexOf(sep, nextsep + 1); + root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path; + path = path.substring(root.length()); + } else { + root = File.separator; + path = path.substring(1); + } + return new String[] {root, path}; + } + +} -- cgit v1.2.3