diff --git a/WHATSNEW b/WHATSNEW index 5c32d82b9..b6189cb84 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -109,6 +109,9 @@ Fixed bugs: Other changes: -------------- +* Selector Elements now provide a way to create filesets based on + sophisticated selection criteria. + * Gzip and Bzip2 files can now be constructed in the fly when using the tar task without having to create the intermediate tar file on disk. The Untar task can also untar GZip and BZip2 files on the fly diff --git a/src/main/org/apache/tools/ant/DirectoryScanner.java b/src/main/org/apache/tools/ant/DirectoryScanner.java index d06c5081a..12b46560e 100644 --- a/src/main/org/apache/tools/ant/DirectoryScanner.java +++ b/src/main/org/apache/tools/ant/DirectoryScanner.java @@ -58,29 +58,37 @@ import java.io.File; import java.util.Vector; import java.util.StringTokenizer; +import org.apache.tools.ant.types.selectors.SelectorScanner; +import org.apache.tools.ant.types.selectors.FileSelector; +import org.apache.tools.ant.types.selectors.SelectorUtils; + /** * Class for scanning a directory for files/directories which match certain * criteria. *
- * These criteria consist of a set of include and exclude patterns. With these - * patterns, you can select which files you want to have included, and which - * files you want to have excluded. + * These criteria consist of selectors and patterns which have been specified. + * With the selectors you can select which files you want to have included. + * Files which are not selected are excluded. With patterns you can include + * or exclude files based on their filename. *
* The idea is simple. A given directory is recursively scanned for all files - * and directories. Each file/directory is matched against a set of include + * and directories. Each file/directory is matched against a set of selectors, + * including special support for matching against filenames with include and * and exclude patterns. Only files/directories which match at least one - * pattern of the include pattern list, and don't match any pattern of the - * exclude pattern list will be placed in the list of files/directories found. + * pattern of the include pattern list or other file selector, and don't match + * any pattern of the exclude pattern list or fail to match against a required + * selector will be placed in the list of files/directories found. *
* When no list of include patterns is supplied, "**" will be used, which * means that everything will be matched. When no list of exclude patterns is - * supplied, an empty list is used, such that nothing will be excluded. + * supplied, an empty list is used, such that nothing will be excluded. When + * no selectors are supplied, none are applied. *
- * The pattern matching is done as follows:
+ * The filename pattern matching is done as follows:
* The name to be matched is split up in path segments. A path segment is the
* name of a directory or file, which is bounded by
* File.separator
('/' under UNIX, '\' under Windows).
- * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
+ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
* "def","ghi" and "xyz.java".
* The same is done for the pattern against which should be matched.
*
@@ -136,11 +144,12 @@ import java.util.StringTokenizer; * This will scan a directory called test for .class files, but excludes all * files in all proper subdirectories of a directory called "modules" * - * @author Arnout J. Kuiper + * @author Arnout J. Kuiper * ajkuiper@wxs.nl * @author Magesh Umasankar + * @author Bruce Atherton */ -public class DirectoryScanner implements FileScanner { +public class DirectoryScanner implements FileScanner, SelectorScanner { /** * Patterns which should be excluded by default. @@ -170,36 +179,53 @@ public class DirectoryScanner implements FileScanner { /** The patterns for the files to be excluded. */ protected String[] excludes; - /** The files which matched at least one include and no excludes. */ + /** 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. + */ protected Vector filesIncluded; - /** The files which did not match any includes. */ + /** The files which did not match any includes or selectors. */ protected Vector filesNotIncluded; - /** - * The files which matched at least one include and at least + /** + * The files which matched at least one include and at least * one exclude. */ protected Vector filesExcluded; - /** The directories which matched at least one include and no excludes. */ + /** The directories which matched at least one include and no excludes + * and were selected. + */ protected Vector dirsIncluded; /** The directories which were found and did not match any includes. */ protected Vector dirsNotIncluded; - /** - * The directories which matched at least one include and at least one + /** + * The directories which matched at least one include and at least one * exclude. */ protected Vector dirsExcluded; + /** 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. + */ + protected Vector dirsDeselected; + /** Whether or not our results were built by a slow scan. */ protected boolean haveSlowResults = false; - /** - * Whether or not the file system should be treated as a case sensitive - * one. + /** + * Whether or not the file system should be treated as a case sensitive + * one. */ protected boolean isCaseSensitive = true; @@ -213,241 +239,87 @@ public class DirectoryScanner implements FileScanner { } /** - * Tests whether or not a given path matches the start of a given + * 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
+ * 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
+ * @param pattern The pattern to match against. Must not be
* null
.
- * @param str The path to match, as a String. Must not be
+ * @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
+ *
+ * @return whether or not a given path matches the start of a given
* pattern up to the first "**".
*/
protected static boolean matchPatternStart(String pattern, String str) {
- return matchPatternStart(pattern, str, true);
+ return SelectorUtils.matchPatternStart(pattern, str);
}
/**
- * Tests whether or not a given path matches the start of a given
+ * 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
+ * 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
+ * @param pattern The pattern to match against. Must not be
* null
.
- * @param str The path to match, as a String. Must not be
+ * @param str The path to match, as a String. Must not be
* null
.
- * @param isCaseSensitive Whether or not matching should be performed
+ * @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
- *
- * @return whether or not a given path matches the start of a given
+ *
+ * @return whether or not a given path matches the start of a given
* pattern up to the first "**".
*/
protected 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;
- }
-
- Vector patDirs = tokenizePath (pattern);
- Vector strDirs = tokenizePath (str);
-
- int patIdxStart = 0;
- int patIdxEnd = patDirs.size() - 1;
- int strIdxStart = 0;
- int strIdxEnd = strDirs.size() - 1;
-
- // up to first '**'
- while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
- String patDir = (String) patDirs.elementAt(patIdxStart);
- if (patDir.equals("**")) {
- break;
- }
- if (!match(patDir, (String) strDirs.elementAt(strIdxStart), isCaseSensitive)) {
- return false;
- }
- patIdxStart++;
- strIdxStart++;
- }
-
- 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;
- }
+ return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
}
/**
* Tests whether or not a given path matches a given pattern.
*
- * @param pattern The pattern to match against. Must not be
+ * @param pattern The pattern to match against. Must not be
* null
.
- * @param str The path to match, as a String. Must not be
+ * @param str The path to match, as a String. Must not be
* null
.
*
- * @return true
if the pattern matches against the string,
+ * @return true
if the pattern matches against the string,
* or false
otherwise.
*/
protected static boolean matchPath(String pattern, String str) {
- return matchPath(pattern, str, true);
+ return SelectorUtils.matchPath(pattern, str);
}
/**
* Tests whether or not a given path matches a given pattern.
*
- * @param pattern The pattern to match against. Must not be
+ * @param pattern The pattern to match against. Must not be
* null
.
- * @param str The path to match, as a String. Must not be
+ * @param str The path to match, as a String. Must not be
* null
.
- * @param isCaseSensitive Whether or not matching should be performed
+ * @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
- * @return true
if the pattern matches against the string,
+ * @return true
if the pattern matches against the string,
* or false
otherwise.
*/
- protected static boolean matchPath(String pattern, String str,
+ protected static boolean matchPath(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;
- }
-
- Vector patDirs = tokenizePath (pattern);
- Vector strDirs = tokenizePath (str);
-
- int patIdxStart = 0;
- int patIdxEnd = patDirs.size() - 1;
- int strIdxStart = 0;
- int strIdxEnd = strDirs.size() - 1;
-
- // up to first '**'
- while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
- String patDir = (String) patDirs.elementAt(patIdxStart);
- if (patDir.equals("**")) {
- break;
- }
- if (!match(patDir, (String) strDirs.elementAt(strIdxStart), isCaseSensitive)) {
- return false;
- }
- patIdxStart++;
- strIdxStart++;
- }
- if (strIdxStart > strIdxEnd) {
- // String is exhausted
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (!patDirs.elementAt(i).equals("**")) {
- 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 = (String) patDirs.elementAt(patIdxEnd);
- if (patDir.equals("**")) {
- break;
- }
- if (!match(patDir, (String) strDirs.elementAt(strIdxEnd), isCaseSensitive)) {
- return false;
- }
- patIdxEnd--;
- strIdxEnd--;
- }
- if (strIdxStart > strIdxEnd) {
- // String is exhausted
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (!patDirs.elementAt(i).equals("**")) {
- return false;
- }
- }
- return true;
- }
-
- while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
- int patIdxTmp = -1;
- for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
- if (patDirs.elementAt(i).equals("**")) {
- 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
- = (String) patDirs.elementAt(patIdxStart + j + 1);
- String subStr
- = (String) strDirs.elementAt(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 (!patDirs.elementAt(i).equals("**")) {
- return false;
- }
- }
-
- return true;
+ return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
}
/**
- * Tests whether or not a string matches against a pattern.
+ * 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.
+ *
+ * @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
.
@@ -456,211 +328,62 @@ strLoop:
* or false
otherwise.
*/
public static boolean match(String pattern, String str) {
- return match(pattern, str, true);
+ return SelectorUtils.match(pattern, str);
}
/**
- * Tests whether or not a string matches against a pattern.
+ * 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.
+ *
+ * @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 isCaseSensitive Whether or not matching should be performed
+ * @param isCaseSensitive Whether or not matching should be performed
* case sensitively.
*
*
- * @return true
if the string matches against the pattern,
+ * @return true
if the string matches against the pattern,
* or false
otherwise.
*/
- protected static boolean match(String pattern, String str,
+ protected static boolean match(String pattern, String str,
boolean isCaseSensitive) {
- 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 (isCaseSensitive && ch != strArr[i]) {
- return false;// Character mismatch
- }
- if (!isCaseSensitive && Character.toUpperCase(ch) !=
- Character.toUpperCase(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 ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
- if (ch != '?') {
- if (isCaseSensitive && ch != strArr[strIdxStart]) {
- return false;// Character mismatch
- }
- if (!isCaseSensitive && Character.toUpperCase(ch) !=
- Character.toUpperCase(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.
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (patArr[i] != '*') {
- return false;
- }
- }
- return true;
- }
-
- // Process characters after last star
- while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
- if (ch != '?') {
- if (isCaseSensitive && ch != strArr[strIdxEnd]) {
- return false;// Character mismatch
- }
- if (!isCaseSensitive && Character.toUpperCase(ch) !=
- Character.toUpperCase(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.
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (patArr[i] != '*') {
- return false;
- }
- }
- return true;
- }
-
- // 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 (isCaseSensitive
- && ch != strArr[strIdxStart + i + j]) {
- continue strLoop;
- }
- if (!isCaseSensitive && Character.toUpperCase(ch) !=
- Character.toUpperCase(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.
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (patArr[i] != '*') {
- return false;
- }
- }
- return true;
+ return SelectorUtils.match(pattern, str, isCaseSensitive);
}
/**
* 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
*/
private static Vector tokenizePath (String path) {
- Vector ret = new Vector();
- StringTokenizer st = new StringTokenizer(path, File.separator);
- while (st.hasMoreTokens()) {
- ret.addElement(st.nextToken());
- }
- return ret;
+ return SelectorUtils.tokenizePath(path);
}
-
+
/**
* Sets 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
.
*
- * @param basedir The base directory to scan.
+ * @param basedir The base directory to scan.
* Must not be null
.
*/
public void setBasedir(String basedir) {
- setBasedir(new File(basedir.replace('/', File.separatorChar).replace('\\', File.separatorChar)));
+ setBasedir(new File(basedir.replace('/', File.separatorChar).replace(
+ '\\', File.separatorChar)));
}
/**
- * Sets the base directory to be scanned. This is the directory which is
+ * Sets the base directory to be scanned. This is the directory which is
* scanned recursively.
*
- * @param basedir The base directory for scanning.
+ * @param basedir The base directory for scanning.
* Should not be null
.
*/
public void setBasedir(File basedir) {
@@ -668,7 +391,7 @@ strLoop:
}
/**
- * Returns the base directory to be scanned.
+ * Returns the base directory to be scanned.
* This is the directory which is scanned recursively.
*
* @return the base directory to be scanned
@@ -680,7 +403,7 @@ strLoop:
/**
* Sets whether or not the file system should be regarded as case sensitive.
*
- * @param isCaseSensitive whether or not the file system should be
+ * @param isCaseSensitive whether or not the file system should be
* regarded as a case sensitive one
*/
public void setCaseSensitive(boolean isCaseSensitive) {
@@ -688,16 +411,16 @@ strLoop:
}
/**
- * Sets the list of include patterns to use. All '/' and '\' characters
- * are replaced by File.separatorChar
, so the separator used
+ * Sets the list of include 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 includes A list of include patterns.
- * May be null
, indicating that all files
+ * May be null
, indicating that all files
* should be included. If a non-null
- * list is given, all elements must be
+ * list is given, all elements must be
* non-null
.
*/
public void setIncludes(String[] includes) {
@@ -707,7 +430,8 @@ strLoop:
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);
+ pattern = includes[i].replace('/', File.separatorChar).replace(
+ '\\', File.separatorChar);
if (pattern.endsWith(File.separator)) {
pattern += "**";
}
@@ -716,16 +440,17 @@ strLoop:
}
}
+
/**
- * Sets the list of exclude patterns to use. All '/' and '\' characters
- * are replaced by File.separatorChar
, so the separator used
+ * Sets 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
, indicating that no files
- * should be excluded. If a non-null
list is
+ * @param excludes A list of exclude patterns.
+ * May be null
, indicating that no files
+ * should be excluded. If a non-null
list is
* given, all elements must be non-null
.
*/
public void setExcludes(String[] excludes) {
@@ -735,7 +460,8 @@ strLoop:
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);
+ pattern = excludes[i].replace('/', File.separatorChar).replace(
+ '\\', File.separatorChar);
if (pattern.endsWith(File.separator)) {
pattern += "**";
}
@@ -744,11 +470,22 @@ strLoop:
}
}
+
+ /**
+ * Sets the selectors that will select the filelist.
+ *
+ * @param selectors specifies the selectors to be invoked on a scan
+ */
+ public void setSelectors(FileSelector[] selectors) {
+ this.selectors = selectors;
+ }
+
+
/**
* Returns 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
+ * @return true
if all files and directories which have
* been found so far have been included.
*/
public boolean isEverythingIncluded() {
@@ -757,9 +494,10 @@ strLoop:
/**
* Scans the base directory for files which match at least one include
- * pattern and don't match any exclude patterns.
+ * pattern and don't match any exclude patterns. If there are selectors
+ * then the files must pass muster there, as well.
*
- * @exception IllegalStateException if the base directory was set
+ * @exception IllegalStateException if the base directory was set
* incorrectly (i.e. if it is null
, doesn't exist,
* or isn't a directory).
*/
@@ -788,13 +526,19 @@ strLoop:
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("")) {
- dirsIncluded.addElement("");
+ if (isSelected("",basedir)) {
+ dirsIncluded.addElement("");
+ } else {
+ dirsDeselected.addElement("");
+ }
} else {
dirsExcluded.addElement("");
}
@@ -806,8 +550,8 @@ strLoop:
/**
* 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
+ * list of excluded/included files/directories, whereas a fast scan
+ * will only have full results for included files, as it ignores
* directories which can't possibly hold any included files/directories.
*
* Returns immediately if a slow scan has already been completed.
@@ -843,12 +587,12 @@ strLoop:
/**
* Scans the given directory for files and directories. Found files and
* directories are placed in their respective collections, based on the
- * matching of includes and excludes. When a directory is found, it is
- * scanned recursively.
+ * matching of includes, excludes, and the selectors. When a directory
+ * is found, it is scanned recursively.
*
* @param dir The directory to scan. Must not be null
.
- * @param vpath The path relative to the base directory (needed to
- * prevent problems with an absolute path when using
+ * @param vpath The path relative to the base directory (needed to
+ * prevent problems with an absolute path when using
* dir). Must not be null
.
* @param fast Whether or not this call is part of a fast scan.
*
@@ -881,10 +625,19 @@ strLoop:
if (file.isDirectory()) {
if (isIncluded(name)) {
if (!isExcluded(name)) {
- dirsIncluded.addElement(name);
- if (fast) {
- scandir(file, name + File.separator, fast);
+ 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);
@@ -905,7 +658,12 @@ strLoop:
} else if (file.isFile()) {
if (isIncluded(name)) {
if (!isExcluded(name)) {
- filesIncluded.addElement(name);
+ if (isSelected(name,file)) {
+ filesIncluded.addElement(name);
+ } else {
+ everythingIncluded = false;
+ filesDeselected.addElement(name);
+ }
} else {
everythingIncluded = false;
filesExcluded.addElement(name);
@@ -919,7 +677,7 @@ strLoop:
}
/**
- * Tests whether or not a name matches against at least one include
+ * Tests whether or not a name matches against at least one include
* pattern.
*
* @param name The name to match. Must not be null
.
@@ -936,11 +694,11 @@ strLoop:
}
/**
- * Tests whether or not a name matches the start of at least one include
+ * Tests whether or not a name matches the start of at least one include
* pattern.
*
* @param name The name to match. Must not be null
.
- * @return true
when the name matches against the start of at
+ * @return true
when the name matches against the start of at
* least one include pattern, or false
otherwise.
*/
protected boolean couldHoldIncluded(String name) {
@@ -953,7 +711,7 @@ strLoop:
}
/**
- * Tests whether or not a name matches against at least one exclude
+ * Tests whether or not a name matches against at least one exclude
* pattern.
*
* @param name The name to match. Must not be null
.
@@ -970,7 +728,26 @@ strLoop:
}
/**
- * Returns the names of the files which matched at least one of the
+ * Tests whether a name should be selected.
+ *
+ * @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.
+ */
+ protected boolean isSelected(String name, File file) {
+ if (selectors != null) {
+ for (int i = 0; i < selectors.length; i++) {
+ if ((selectors[i].isSelected(basedir, name, file)) == false) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns 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.
*
@@ -981,19 +758,19 @@ strLoop:
int count = filesIncluded.size();
String[] files = new String[count];
for (int i = 0; i < count; i++) {
- files[i] = (String) filesIncluded.elementAt(i);
+ files[i] = (String)filesIncluded.elementAt(i);
}
return files;
}
/**
- * Returns the names of the files which matched none of the include
+ * Returns 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.
*
- * @return the names of the files which matched none of the include
+ * @return the names of the files which matched none of the include
* patterns.
- *
+ *
* @see #slowScan
*/
public String[] getNotIncludedFiles() {
@@ -1001,20 +778,20 @@ strLoop:
int count = filesNotIncluded.size();
String[] files = new String[count];
for (int i = 0; i < count; i++) {
- files[i] = (String) filesNotIncluded.elementAt(i);
+ files[i] = (String)filesNotIncluded.elementAt(i);
}
return files;
}
/**
- * Returns the names of the files which matched at least one of the
+ * Returns 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.
*
- * @return 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 at least one of the exclude patterns.
- *
+ *
* @see #slowScan
*/
public String[] getExcludedFiles() {
@@ -1022,13 +799,32 @@ strLoop:
int count = filesExcluded.size();
String[] files = new String[count];
for (int i = 0; i < count; i++) {
- files[i] = (String) filesExcluded.elementAt(i);
+ files[i] = (String)filesExcluded.elementAt(i);
+ }
+ return files;
+ }
+
+ /**
+ * Returns the names of the files which were selected. The names
+ * are relative to the base directory. This involves performing
+ * a slow scan if one has not already been completed.
+ *
+ * @return the names of the files which were selected.
+ *
+ * @see #slowScan
+ */
+ public String[] getDeselectedFiles() {
+ slowScan();
+ int count = filesDeselected.size();
+ String[] files = new String[count];
+ for (int i = 0; i < count; i++) {
+ files[i] = (String)filesDeselected.elementAt(i);
}
return files;
}
/**
- * Returns the names of the directories which matched at least one of the
+ * Returns 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.
*
@@ -1039,7 +835,7 @@ strLoop:
int count = dirsIncluded.size();
String[] directories = new String[count];
for (int i = 0; i < count; i++) {
- directories[i] = (String) dirsIncluded.elementAt(i);
+ directories[i] = (String)dirsIncluded.elementAt(i);
}
return directories;
}
@@ -1051,7 +847,7 @@ strLoop:
*
* @return the names of the directories which matched none of the include
* patterns.
- *
+ *
* @see #slowScan
*/
public String[] getNotIncludedDirectories() {
@@ -1059,20 +855,20 @@ strLoop:
int count = dirsNotIncluded.size();
String[] directories = new String[count];
for (int i = 0; i < count; i++) {
- directories[i] = (String) dirsNotIncluded.elementAt(i);
+ directories[i] = (String)dirsNotIncluded.elementAt(i);
}
return directories;
}
/**
- * Returns the names of the directories which matched at least one of the
+ * Returns 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.
- *
- * @return 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.
- *
+ *
* @see #slowScan
*/
public String[] getExcludedDirectories() {
@@ -1080,7 +876,26 @@ strLoop:
int count = dirsExcluded.size();
String[] directories = new String[count];
for (int i = 0; i < count; i++) {
- directories[i] = (String) dirsExcluded.elementAt(i);
+ directories[i] = (String)dirsExcluded.elementAt(i);
+ }
+ return directories;
+ }
+
+ /**
+ * Returns the names of the directories which were selected. The names
+ * are relative to the base directory. This involves performing a
+ * slow scan if one has not already been completed.
+ *
+ * @return the names of the directories which were selected.
+ *
+ * @see #slowScan
+ */
+ public String[] getDeselectedDirectories() {
+ slowScan();
+ int count = dirsDeselected.size();
+ String[] directories = new String[count];
+ for (int i = 0; i < count; i++) {
+ directories[i] = (String)dirsDeselected.elementAt(i);
}
return directories;
}
@@ -1096,7 +911,8 @@ strLoop:
System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
}
for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
- newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace('/', File.separatorChar).replace('\\', File.separatorChar);
+ newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].replace('/',
+ File.separatorChar).replace('\\', File.separatorChar);
}
excludes = newExcludes;
}
diff --git a/src/main/org/apache/tools/ant/types/AbstractFileSet.java b/src/main/org/apache/tools/ant/types/AbstractFileSet.java
index 6e457d056..00b4a4bb6 100644
--- a/src/main/org/apache/tools/ant/types/AbstractFileSet.java
+++ b/src/main/org/apache/tools/ant/types/AbstractFileSet.java
@@ -57,6 +57,7 @@ import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.FileScanner;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.*;
import java.io.File;
import java.util.Stack;
@@ -70,23 +71,26 @@ import java.util.Enumeration;
*
*
Common base class for DirSet and FileSet.
* - * @author Arnout J. Kuiper + * @author Arnout J. Kuiper * @author Stefano Mazzocchi * @author Sam Ruby * @author Jon S. Stevens * @author Stefan Bodewig * @author Magesh Umasankar + * @author Bruce Atherton */ -public abstract class AbstractFileSet extends DataType implements Cloneable { - +public abstract class AbstractFileSet extends DataType implements Cloneable, + SelectorContainer { + private PatternSet defaultPatterns = new PatternSet(); private Vector additionalPatterns = new Vector(); + private Vector selectors = new Vector(); private File dir; private boolean useDefaultExcludes = true; private boolean isCaseSensitive = true; - + public AbstractFileSet() { super(); } @@ -104,7 +108,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable { * Makes this instance in effect a reference to another instance. * *You must not set another attribute or nest elements inside - * this element if you make it a reference.
+ * this element if you make it a reference. */ public void setRefid(Reference r) throws BuildException { if (dir != null || defaultPatterns.hasPatterns()) { @@ -158,7 +162,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable { } return defaultPatterns.createInclude(); } - + /** * add a name entry on the include files list */ @@ -168,7 +172,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable { } return defaultPatterns.createIncludesFile(); } - + /** * add a name entry on the exclude list */ @@ -188,7 +192,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable { } return defaultPatterns.createExcludesFile(); } - + /** * Appendsincludes
to the current list of include
* patterns.
@@ -224,7 +228,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
/**
* Sets the name of the file containing the includes patterns.
*
- * @param incl The file to fetch the include patterns from.
+ * @param incl The file to fetch the include patterns from.
*/
public void setIncludesfile(File incl) throws BuildException {
if (isReference()) {
@@ -237,7 +241,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
/**
* Sets the name of the file containing the includes patterns.
*
- * @param excl The file to fetch the exclude patterns from.
+ * @param excl The file to fetch the exclude patterns from.
*/
public void setExcludesfile(File excl) throws BuildException {
if (isReference()) {
@@ -250,7 +254,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
/**
* Sets whether default exclusions should be used or not.
*
- * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+ * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
* should be used, "false"|"off"|"no" when they
* shouldn't be used.
*/
@@ -272,8 +276,8 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
this.isCaseSensitive = isCaseSensitive;
}
-
-
+
+
/**
* sets the name used for this datatype instance.
*/
@@ -290,14 +294,14 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
}
}
}
-
+
String classname = getClass().getName();
-
+
int dotIndex = classname.lastIndexOf(".");
if (dotIndex == -1) {
return classname;
}
- return classname.substring(dotIndex + 1);
+ return classname.substring(dotIndex + 1);
}
/**
@@ -326,12 +330,12 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
ds.scan();
return ds;
}
-
+
public void setupDirectoryScanner(FileScanner ds, Project p) {
if (ds == null) {
throw new IllegalArgumentException("ds cannot be null");
}
-
+
ds.setBasedir(dir);
final int count = additionalPatterns.size();
@@ -340,11 +344,16 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
defaultPatterns.append((PatternSet) o, p);
}
- p.log(getDataTypeName() + ": Setup scanner in dir " + dir +
+ p.log(getDataTypeName() + ": Setup scanner in dir " + dir +
" with " + defaultPatterns, Project.MSG_DEBUG);
-
+
ds.setIncludes(defaultPatterns.getIncludePatterns(p));
ds.setExcludes(defaultPatterns.getExcludePatterns(p));
+ if (ds instanceof SelectorScanner) {
+ SelectorScanner ss = (SelectorScanner)ds;
+ ss.setSelectors(getSelectors(p));
+ }
+
if (useDefaultExcludes) {
ds.addDefaultExcludes();
}
@@ -353,7 +362,7 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
/**
* Performs the check for circular references and returns the
- * referenced FileSet.
+ * referenced FileSet.
*/
protected AbstractFileSet getRef(Project p) {
if (!checked) {
@@ -361,10 +370,10 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
stk.push(this);
dieOnCircularReference(stk, p);
}
-
+
Object o = ref.getReferencedObject(p);
if (!getClass().isAssignableFrom(o.getClass())) {
- String msg = ref.getRefId() + " doesn\'t denote a "
+ String msg = ref.getRefId() + " doesn\'t denote a "
+ getDataTypeName();
throw new BuildException(msg);
} else {
@@ -372,4 +381,153 @@ public abstract class AbstractFileSet extends DataType implements Cloneable {
}
}
+ // SelectorContainer methods
+
+ /**
+ * Indicates whether there are any selectors here.
+ *
+ * @return whether any selectors are in this container
+ */
+ public boolean hasSelectors() {
+ return !(selectors.isEmpty());
+ }
+
+ /**
+ * Gives the count of the number of selectors in this container
+ *
+ * @return the number of selectors in this container
+ */
+ public int selectorCount() {
+ return selectors.size();
+ }
+
+ /**
+ * Returns the set of selectors as an array.
+ *
+ * @return an array of selectors in this container
+ */
+ public FileSelector[] getSelectors(Project p) {
+ if (isReference()) {
+ return getRef(p).getSelectors(p);
+ } else {
+ FileSelector[] result = new FileSelector[selectors.size()];
+ selectors.copyInto(result);
+ return result;
+ }
+ }
+
+ /**
+ * Returns an enumerator for accessing the set of selectors.
+ *
+ * @return an enumerator that goes through each of the selectors
+ */
+ public Enumeration selectorElements() {
+ return selectors.elements();
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new selector to add
+ */
+ public void appendSelector(FileSelector selector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ selectors.addElement(selector);
+ }
+
+ /* Methods below all implement the static selectors */
+
+ /**
+ * add an "And" selector entry on the selector list
+ */
+ public void addAnd(AndSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an "Or" selector entry on the selector list
+ */
+ public void addOr(OrSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a "Not" selector entry on the selector list
+ */
+ public void addNot(NotSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a "None" selector entry on the selector list
+ */
+ public void addNone(NoneSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a majority selector entry on the selector list
+ */
+ public void addMajority(MajoritySelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector date entry on the selector list
+ */
+ public void addDateselect(DateSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector size entry on the selector list
+ */
+ public void addSizeselect(SizeSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a selector filename entry on the selector list
+ */
+ public void addFilenameselect(FilenameSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add an extended selector entry on the selector list
+ */
+ public void addExtendSelect(ExtendSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a contains selector entry on the selector list
+ */
+ public void addContainsSelect(ContainsSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a present selector entry on the selector list
+ */
+ public void addPresentSelect(PresentSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a depth selector entry on the selector list
+ */
+ public void addDepthSelect(DepthSelector selector) {
+ appendSelector(selector);
+ }
+
+ /**
+ * add a depends selector entry on the selector list
+ */
+ public void addDependSelect(DependSelector selector) {
+ appendSelector(selector);
+ }
+
}
diff --git a/src/main/org/apache/tools/ant/types/selectors/AndSelector.java b/src/main/org/apache/tools/ant/types/selectors/AndSelector.java
new file mode 100644
index 000000000..51345571e
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/selectors/AndSelector.java
@@ -0,0 +1,111 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * Subclasses can override this method to provide checking of their + * state. So long as they call validate() from isSelected(), this will + * be called automatically (unless they override validate()).
+ *Implementations should check for incorrect settings and call + * setError() as necessary.
+ */ + public void verifySettings() { + } + + + + /** + * Subclasses can use this to throw the requisite exception + * in isSelected() in the case of an error condition. + */ + public void validate() { + verifySettings(); + if (getError() != null) { + throw new BuildException(errmsg); + } + } + + /** + * Method that each selector will implement to create their + * selection behaviour. If there is a problem with the setup + * of a selector, it can throw a BuildException to indicate + * the problem. + * + * @param basedir A java.io.File object for the base directory + * @param filename The name of the file to check + * @param file A File object for this filename + * @return whether the file should be selected or not + */ + public abstract boolean isSelected(File basedir, String filename, + File file); + +} + + diff --git a/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java b/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java new file mode 100644 index 000000000..ac3a97f6c --- /dev/null +++ b/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java @@ -0,0 +1,312 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + *This implementation validates the container by calling + * verifySettings() and then validates each contained selector + * provided that the selector implements the validate interface. + *
+ *Ordinarily, this will validate all the elements of a selector + * container even if the isSelected() method of some elements is + * never called. This has two effects:
+ *
+ */
+ public static class TimeComparisons extends EnumeratedAttribute {
+ public String[] getValues() {
+ return new String[] {"before", "after", "equal"};
+ }
+ }
+
+}
+
+
diff --git a/src/main/org/apache/tools/ant/types/selectors/DependSelector.java b/src/main/org/apache/tools/ant/types/selectors/DependSelector.java
new file mode 100644
index 000000000..ceefdd7f9
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/selectors/DependSelector.java
@@ -0,0 +1,186 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * 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.
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example,
+ * This is not a general purpose test and should only be used if you
+ * can live with false positives. For example,
+ * To get around this complexity, a number of standards bodies
+ * have proposed the 2^10 standard, and at least one has adopted
+ * it. But we are still left with a populace that isn't clear on
+ * how capitalization should work.
+ *
+ * We therefore ignore capitalization as much as possible.
+ * Completely mixed case is not possible, but all upper and lower
+ * forms are accepted for all long and short forms. Since we have
+ * no need to work with the 0.001 case, this practice works here.
+ *
+ * This function translates all the long and short forms that a
+ * unit prefix can occur in and translates them into a single
+ * multiplier.
+ *
+ * @param units The units to compare the size to, using an
+ * EnumeratedAttribute
+ */
+ public void setUnits(ByteUnits units) {
+ int i = units.getIndex();
+ multiplier = 0;
+ if ((i > -1) && (i < 4)) {
+ multiplier = 1000;
+ }
+ else if ((i > 3) && (i < 9)) {
+ multiplier = 1024;
+ }
+ else if ((i > 8) && (i < 13)) {
+ multiplier = 1000000;
+ }
+ else if ((i > 12) && (i < 18)) {
+ multiplier = 1048576;
+ }
+ else if ((i > 17) && (i < 22)) {
+ multiplier = 1000000000L;
+ }
+ else if ((i > 21) && (i < 27)) {
+ multiplier = 1073741824L;
+ }
+ else if ((i > 26) && (i < 31)) {
+ multiplier = 1000000000000L;
+ }
+ else if ((i > 30) && (i < 36)) {
+ multiplier = 1099511627776L;
+ }
+ if ((multiplier > 0) && (size > -1)) {
+ sizelimit = size * multiplier;
+ }
+ }
+
+ /**
+ * This specifies when the file should be selected, whether it be
+ * when the file matches a particular size, when it is smaller,
+ * or whether it is larger.
+ *
+ * @param cmp The comparison to perform, an EnumeratedAttribute
+ */
+ public void setWhen(SizeComparisons cmp) {
+ this.cmp = cmp.getIndex();
+ }
+
+ /**
+ * When using this as a dynamic selector, this method will be called.
+ * It translates each parameter into the appropriate setXXX() call.
+ *
+ * @param parameters the complete set of parameters for this selector
+ */
+ public void setParameters(Parameter[] parameters) {
+ super.setParameters(parameters);
+ if (parameters != null) {
+ for (int i = 0; i < parameters.length; i++) {
+ String paramname = parameters[i].getName();
+ if (SIZE_KEY.equalsIgnoreCase(paramname)) {
+ try {
+ setSize(new Long(parameters[i].getValue()
+ ).longValue());
+ } catch (NumberFormatException nfe) {
+ setError("Invalid size setting "
+ + parameters[i].getValue());
+ }
+ }
+ else if (UNITS_KEY.equalsIgnoreCase(paramname)) {
+ ByteUnits units = new ByteUnits();
+ units.setValue(parameters[i].getValue());
+ setUnits(units);
+ }
+ else if (WHEN_KEY.equalsIgnoreCase(paramname)) {
+ SizeComparisons cmp = new SizeComparisons();
+ cmp.setValue(parameters[i].getValue());
+ setWhen(cmp);
+ }
+ else {
+ setError("Invalid parameter " + paramname);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the size attribute has been set (to a positive value),
+ * that the multiplier has a valid setting, and that the size limit
+ * is valid. Since the latter is a calculated value, this can only
+ * fail due to a programming error.
+ * If a problem is detected, the setError() method is called.
+ *
+ * This treats the standard SI units as representing powers of ten,
+ * as they should. If you want the powers of 2 that approximate
+ * the SI units, use the first two characters followed by a
+ *
+ * This binary prefix system is approved by the IEC and appears on
+ * its way for approval by other agencies, but it is not an SI
+ * standard. It disambiguates things for us, though.
+ */
+ public static class ByteUnits extends EnumeratedAttribute {
+ public String[] getValues() {
+ return new String[] {"K", "k", "kilo", "KILO",
+ "Ki", "KI", "ki", "kibi", "KIBI",
+ "M", "m", "mega", "MEGA",
+ "Mi", "MI", "mi", "mebi", "MEBI",
+ "G", "g", "giga", "GIGA",
+ "Gi", "GI", "gi", "gibi", "GIBI",
+ "T", "t", "tera", "TERA",
+ /* You wish! */ "Ti", "TI", "ti", "tebi", "TEBI"
+ };
+ }
+ }
+
+ /**
+ * Enumerated attribute with the values for size comparison.
+ */
+ public static class SizeComparisons extends EnumeratedAttribute {
+ public String[] getValues() {
+ return new String[] {"less", "more", "equal"};
+ }
+ }
+
+}
+
negate
boolean, but by doing things
+ * this way, we get some documentation on how the system works.
+ * A user looking at the documentation should clearly understand
+ * that the ONLY files whose presence is being tested are those
+ * that already exist in the source directory, hence the lack of
+ * a destonly
option.
+ *
+ * @param fp An attribute set to either srconly
both.
+ */
+ public void setPresent(FilePresence fp) {
+ if (fp.getIndex() == 0) {
+ destmustexist = false;
+ }
+ }
+
+ /**
+ * Checks to make sure all settings are kosher. In this case, it
+ * means that the targetdir attribute has been set and we have a mapper.
+ */
+ public void verifySettings() {
+ if (targetdir == null) {
+ setError("The targetdir attribute is required.");
+ }
+ if (mapperElement == null) {
+ map = new IdentityMapper();
+ }
+ else {
+ map = mapperElement.getImplementation();
+ }
+ if (map == null) {
+ setError("Could not set 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 "**".
+ * 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;
+ }
+
+ Vector patDirs = tokenizePath (pattern);
+ Vector strDirs = tokenizePath (str);
+
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.size()-1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.size()-1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = (String)patDirs.elementAt(patIdxStart);
+ if (patDir.equals("**")) {
+ break;
+ }
+ if (!match(patDir,(String)strDirs.elementAt(strIdxStart),
+ isCaseSensitive)) {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+
+ 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.
+ *
+ * @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) {
+ return matchPath(pattern, str, true);
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @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) {
+ // 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;
+ }
+
+ Vector patDirs = tokenizePath (pattern);
+ Vector strDirs = tokenizePath (str);
+
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.size()-1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.size()-1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = (String)patDirs.elementAt(patIdxStart);
+ if (patDir.equals("**")) {
+ break;
+ }
+ if (!match(patDir,(String)strDirs.elementAt(strIdxStart),
+ isCaseSensitive)) {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!patDirs.elementAt(i).equals("**")) {
+ 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 = (String)patDirs.elementAt(patIdxEnd);
+ if (patDir.equals("**")) {
+ break;
+ }
+ if (!match(patDir,(String)strDirs.elementAt(strIdxEnd),
+ isCaseSensitive)) {
+ return false;
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!patDirs.elementAt(i).equals("**")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = patIdxStart+1; i <= patIdxEnd; i++) {
+ if (patDirs.elementAt(i).equals("**")) {
+ 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 = (String)patDirs.elementAt(patIdxStart+j+1);
+ String subStr = (String)strDirs.elementAt(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 (!patDirs.elementAt(i).equals("**")) {
+ 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 isCaseSensitive 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 isCaseSensitive) {
+ 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 (isCaseSensitive && ch != strArr[i]) {
+ return false;// Character mismatch
+ }
+ if (!isCaseSensitive && Character.toUpperCase(ch) !=
+ Character.toUpperCase(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((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
+ if (ch != '?') {
+ if (isCaseSensitive && ch != strArr[strIdxStart]) {
+ return false;// Character mismatch
+ }
+ if (!isCaseSensitive && Character.toUpperCase(ch) !=
+ Character.toUpperCase(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.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Process characters after last star
+ while((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
+ if (ch != '?') {
+ if (isCaseSensitive && ch != strArr[strIdxEnd]) {
+ return false;// Character mismatch
+ }
+ if (!isCaseSensitive && Character.toUpperCase(ch) !=
+ Character.toUpperCase(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.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // 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 (isCaseSensitive && ch != strArr[strIdxStart+i+j]) {
+ continue strLoop;
+ }
+ if (!isCaseSensitive && Character.toUpperCase(ch) !=
+ Character.toUpperCase(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.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 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
+ */
+ public static Vector tokenizePath (String path) {
+ Vector ret = new Vector();
+ StringTokenizer st = new StringTokenizer(path,File.separator);
+ while (st.hasMoreTokens()) {
+ ret.addElement(st.nextToken());
+ }
+ return ret;
+ }
+
+ /**
+ * Helper method which corrects paths to use forward slashes.
+ *
+ * @param pattern the path pattern which needs correcting
+ * @return corrected pattern
+ */
+ public static String fixPath(String pattern) {
+ return pattern.replace('/',File.separatorChar).replace('\\',
+ File.separatorChar);
+ }
+
+ /**
+ * 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;
+ }
+
+}
+
diff --git a/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java b/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java
new file mode 100644
index 000000000..3ed87a44b
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java
@@ -0,0 +1,313 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * bi
. So 1024 (2^10) becomes kibi
,
+ * 1048576 (2^20) becomes mebi
, 1073741824 (2^30)
+ * becomes gibi
, and so on. The symbols are also
+ * accepted, and these are the first letter capitalized followed
+ * by an i
. Ki
, Mi
,
+ * Gi
, and so on. Capitalization variations on these
+ * are also accepted.
+ *