From f61c1bcd3a4a540bac7ae97559f37c49bfee75d8 Mon Sep 17 00:00:00 2001
From: Magesh Umasankar
- * 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
*
@@ -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,
* This is not a general purpose test and should only be used if you
- * can live with false positives. For example,
* When a pattern ends with a '/' or '\', "**" is appended.
*
* @param includes A list of include patterns.
- * May be
* When a pattern ends with a '/' or '\', "**" is appended.
*
- * @param excludes A list of exclude patterns.
- * May be
* 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 Common base class for DirSet and FileSet. You must not set another attribute or nest elements inside
- * this element if you make it a reference.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.
* 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 "**".
* 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
.
* 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
.
* 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.
* 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;
*
*
includes
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.
+ *