From fbb9886634198d8377104bef9ec697fadeb134df Mon Sep 17 00:00:00 2001
From: Matthew Jason Benson
+A file-like entity can be abstracted to the concept of a resource.
+In addition to providing access to file-like attributes, a resource
+implementation should, when possible, provide the means to read content
+from and/or write content to the underlying entity. Although the resource
+concept was introduced in Ant 1.5.2, resources are available for
+explicit use beginning in Ant 1.7.
+ A basic resource. Other resource types derive from this basic
+type; as such all its attributes are available, though in most cases
+irrelevant attributes will be ignored. This and all resource
+implementations are also usable as single-element
+Resource Collections.
+ Represents a file accessible via local filesystem conventions. Represents an entry in a ZIP archive. Represents a URL. The classpath along which to search for a Java resource
+ can also be specified by means of one or more nested
+ Represents a Java String. As such a string is readable but not writable. Represents an Ant property.
+A Resource Collection is an abstraction of an entity that groups
+together a number of resources. Several of
+Ant's "legacy" datatypes have been modified to behave as Resource Collections:
+Resources
+The built-in resource types are:
+
+
+
+
+resource
+
+
+
+
+
+
+ Attribute
+ Description
+ Required
+
+
+ name
+ The name of this resource
+ No
+
+
+ exists
+ Whether this resource exists
+ No, default true
+
+
+ lastmodified
+ The last modification time of this resource
+ No
+
+
+ directory
+ Whether this resource is directory-like
+ No, default false
+
+
+size
+ The size of this resource
+ No
+ file
+
+
+
+
+
+
+ Attribute
+ Description
+ Required
+
+
+ file
+ The file represented by this resource
+ Yes
+
+
+base
+ The base directory of this resource. When this
+ attribute is set, attempts to access the name of the resource
+ will yield a path relative to this location.
+ No
+ zipentry
+
+
+
+
+
+
+ Attribute
+ Description
+ Required
+
+
+ zipfile
+ The zip file containing this resource
+ Yes
+
+
+ name
+ The name of the archived resource
+ Yes
+
+
+encoding
+ The encoding of the zipfile
+ No;
+ platform default used if unspecified
+ url
+
+
+
+
+
+ Attribute
+ Description
+ Required
+
+
+ url
+ The url to expose
+ Exactly one of these
+
+
+ file
+ The file to expose as a file: url
+
+
+ javaresource
+ The Java resource to expose as a jar: url
+
+
+classpath
+ The classpath to use when establishing the URL
+ for a Java resource
+ No
+ classpath
elements.
+string
+
+
+
+
+
+
+ Attribute
+ Description
+ Required
+
+
+value
+ The value of this resource
+ Yes
+ property
+
+
+
+
+
+
+ Attribute
+ Description
+ Required
+
+
+name
+ The property name
+ Yes
+
+Resource Collections
+
+
+
A generic resource collection, designed for use with
+ references.
+ For example, if a third-party Ant task generates a Resource Collection
+ of an unknown type, it can still be accessed via a
+ <resources>
collection. The secondary use of this
+ collection type is as a container of other resource collections,
+ preserving the order of nested collections as well as
+ duplicate resources (contrast with union).
+
A group of files. These files are matched by absolute patterns
+ taken from a number of PatternSets.
+ These can be specified as nested <patternset>
+ elements. In addition, <files>
holds an implicit
+ PatternSet and supports the nested <include>
,
+ <includesfile>
, <exclude>
+ and <excludesfile>
elements of PatternSet directly,
+ as well as PatternSet's attributes.
+
File Selectors are available as nested
+ elements. A file must be selected by all selectors in order to be included;
+ <files>
is thus equivalent to an
+ <and>
file selector container.
+
More simply put, this type is equivalent to a + fileset with no base directory. + Please note that without a base directory, + filesystem scanning is based entirely on include and exclude patterns. + A filename (or any) + selector can only influence the scanning process after + the file has been included based on pattern-based selection. +
+ +Attribute | +Description | +Required | +
includes | +comma- or space-separated list of patterns + of files that must be included | +At least one of these | +
includesfile | +the name of a file; each line of this file is + taken to be an include pattern. | +|
excludes | +comma- or space-separated list of patterns + of files that must be excluded | +No, default none + (except default excludes when true) | +
excludesfile | +the name of a file; each line of this file is + taken to be an exclude pattern. | +|
defaultexcludes | +Whether + default excludes + should be used | +No, default true | +
casesensitive | +Whether patterns are case-sensitive | +No, default true | +
followsymlinks | +Whether to follow symbolic links + (see note below) | +No, default true | +
Note: All files/directories for which +the canonical path is different from its path are considered symbolic +links. On Unix systems this usually means the file really is a +symbolic link but it may lead to false results on other +platforms. +
+ +Restricts another nested resource collection using resource selectors: +
++ +Parameters specified as nested elements
+A single resource collection is required.
+Nested resource selectors are used to "narrow down" the included + resources. These are patterned after file + selectors but are, unsurprisingly, targeted to resources. + Several built-in resource selectors are available in the internal + antlib +
+ +org.apache.tools.ant.types.resources.selectors
: ++
+ +- name - select resources by name.
+- exists - select existing resources.
+- date - select resources by date.
+- type - select resources by type.
+- size - select resources by size.
+- instanceof + - select resources by class or Ant datatype.
+- and - "and" nested resource selectors.
+- or - "or" nested resource selectors.
+- not - "not" a nested resource selector.
+- none + - select resources selected by no nested resource selectors.
+- majority - select resources selected + by a majority of nested resource selectors.
+name
+Selects resources by name.
++
+ ++ +Attribute +Description +Required ++ +name +The name pattern to test +Yes ++ +casesensitive +Whether name comparisons are case-sensitive +No, default true +exists
+Selects existing resources.
+ +date
+Selects resources by date.
++
+ ++ +Attribute +Description +Required ++ +millis +The comparison date/time in ms since January 1, 1970 +One of these ++ +datetime +The formatted comparison date/time ++ +pattern +SimpleDateFormat-compatible pattern + for use with the +datetime
attribute+ No, default is "MM/DD/YYYY HH:MM AM_or_PM" ++ +granularity +The number of milliseconds leeway to use when + comparing file modification times. This is needed because not + every file system supports tracking the last modified time to + the millisecond level. +No; default varies by platform: + FAT filesystems = 2 sec; Unix = 1 sec; NTFS = 1 ms. ++ +when +One of "before", "after", "equal" +No, default "equal" +type
+Selects resources by type (file or directory).
++
+ ++ +Attribute +Description +Required ++ +type +One of "file", "dir" +Yes +size
+Selects resources by size.
++
+ ++ +Attribute +Description +Required ++ +size +The size to compare +Yes ++ +when +One of "equal", "eq", "greater", "gt", "less", "lt", + "ge" (greater or equal), "ne" (not equal), "le" (less or equal) +No, default "equal" +instanceof
+Selects resources by type.
++
+ ++ +Attribute +Description +Required ++ +class +The class of which the resource must be an instance +One of these ++ +type +The Ant type that must + be assignable from the resource ++ +uri +The uri in which type must be defined +No +and
+Selects a resource if it is selected by all nested resource selectors.
+ +or
+Selects a resource if it is selected + by at least one nested resource selector.
+ +not
+Negates the selection result of the single + nested resource selector allowed.
+ +none
+Selects a resource if it is selected + by no nested resource selectors.
+ +majority
+Selects a resource if it is selected + by the majority of nested resource selectors.
++
+ ++ +Attribute +Description +Required ++ +allowtie +Whether a tie (when there is an even number + of nested resource selectors) is considered a majority +No, default true +
Sorts another nested resource collection according to the resources' +natural order, or by one or more nested resource comparators:
+++ +Parameters specified as nested elements
+A single resource collection is required.
+The sort can be controlled and customized by specifying one or more + resource comparators. Resources can be sorted according to multiple + criteria; the first specified is the "outermost", while the last + specified is the "innermost". Several built-in resource comparators + are available in the internal antlib +
+ +org.apache.tools.ant.types.resources.comparators
: ++
+ +- name - sort resources by name
+- exists - sort resources by existence
+- date - sort resources by date
+- type - sort resources by type
+- size - sort resources by size
+- content - sort resources by content
+- reverse - reverse the natural sort order, + or a single nested resource comparator
+name
+Sort resources by name.
+ +exists
+Sort resources by existence. + Not existing is considered "less than" existing.
+ +date
+Sort resources by date.
+ +type
+Sort resources by type (file or directory). + Because directories contain files, they are considered "greater".
+ +size
+Sort resources by size.
+ +content
+Sort resources by content.
++
+ ++ +Attribute +Description +Required ++ +binary +Whether content should be compared in binary mode. + If false, content will be compared without regard to + platform-specific line-ending conventions. +No, default true +reverse
+Reverse the natural sort order, or a single nested comparator.
+ +
++ +The following resource collections implement set operations:
++
+ +- union
+- intersect
+- difference
+union
+Union of nested resource collections.
+ +intersect
+Intersection of nested resource collections.
+ +difference
+Difference of nested resource collections.
+ +
Copyright © 2005 The Apache Software Foundation. All rights +Reserved.
+ + + diff --git a/docs/manual/conceptstypeslist.html b/docs/manual/conceptstypeslist.html index 6c3ad720a..f69d28a40 100644 --- a/docs/manual/conceptstypeslist.html +++ b/docs/manual/conceptstypeslist.html @@ -31,6 +31,8 @@ PropertySetnull
, the system
* console is used.
*/
- private File destinationFile = null;
+ private File destinationFile;
/**
* Whether or not the stream should be appended if the destination file
* exists.
* Defaults to false
.
*/
- private boolean append = false;
+ private boolean append;
/**
* Stores the input file encoding.
*/
- private String encoding = null;
+ private String encoding;
/** Stores the output file encoding. */
- private String outputEncoding = null;
+ private String outputEncoding;
/** Stores the binary attribute */
- private boolean binary = false;
+ private boolean binary;
// Child elements.
@@ -106,25 +122,49 @@ public class Concat extends Task {
* Stores a collection of file sets and/or file lists, used to
* select multiple files for concatenation.
*/
- private Vector sources = new Vector();
+ private Resources rc;
/** for filtering the concatenated */
- private Vector filterChains = null;
+ private Vector filterChains;
/** ignore dates on input files */
- private boolean forceOverwrite = true;
+ private boolean forceOverwrite = true;
/** String to place at the start of the concatented stream */
- private TextElement footer;
+ private TextElement footer;
/** String to place at the end of the concatented stream */
- private TextElement header;
+ private TextElement header;
/** add missing line.separator to files **/
- private boolean fixLastLine = false;
+ private boolean fixLastLine = false;
/** endofline for fixlast line */
- private String eolString = System.getProperty("line.separator");
+ private String eolString;
/** outputwriter */
- private Writer outputWriter = null;
+ private Writer outputWriter = null;
- /** internal variable - used to collect the source files from sources */
- private Vector sourceFiles = new Vector();
+ /**
+ * Construct a new Concat task.
+ */
+ public Concat() {
+ reset();
+ }
+
+ /**
+ * Reset state to default.
+ */
+ public void reset() {
+ append = false;
+ forceOverwrite = true;
+ destinationFile = null;
+ encoding = null;
+ outputEncoding = null;
+ fixLastLine = false;
+ filterChains = null;
+ footer = null;
+ header = null;
+ binary = false;
+ outputWriter = null;
+ textBuffer = null;
+ eolString = System.getProperty("line.separator");
+ rc = null;
+ }
// Attribute setters.
@@ -187,7 +227,7 @@ public class Concat extends Task {
*/
public Path createPath() {
Path path = new Path(getProject());
- sources.addElement(path);
+ add(path);
return path;
}
@@ -196,7 +236,7 @@ public class Concat extends Task {
* @param set the set of files
*/
public void addFileset(FileSet set) {
- sources.addElement(set);
+ add(set);
}
/**
@@ -204,7 +244,17 @@ public class Concat extends Task {
* @param list the list of files
*/
public void addFilelist(FileList list) {
- sources.addElement(list);
+ add(list);
+ }
+
+ /**
+ * Add an arbitrary ResourceCollection.
+ * @param c the ResourceCollection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection c) {
+ rc = (rc == null) ? new Resources() : rc;
+ rc.add(c);
}
/**
@@ -284,9 +334,9 @@ public class Concat extends Task {
}
/**
- * set the output writer, this is to allow
- * concat to be used as a nested element
- * @param outputWriter the output writer
+ * Set the output writer. This is to allow
+ * concat to be used as a nested element.
+ * @param outputWriter the output writer.
* @since Ant 1.6
*/
public void setWriter(Writer outputWriter) {
@@ -294,23 +344,20 @@ public class Concat extends Task {
}
/**
- * set the binary attribute.
- * if true, concat will concatenate the files
- * byte for byte. This mode does not allow
- * any filtering, or other modifications
- * to the input streams.
- * The default value is false.
- * @since ant 1.6.2
- * @param binary if true, enable binary mode
+ * Set the binary attribute. If true, concat will concatenate the files
+ * byte for byte. This mode does not allow any filtering or other
+ * modifications to the input streams. The default value is false.
+ * @since Ant 1.6.2
+ * @param binary if true, enable binary mode.
*/
public void setBinary(boolean binary) {
this.binary = binary;
}
/**
- * This method checks the attributes and performs the concatenation.
+ * Validate configuration options.
*/
- private void checkAndExecute() {
+ private ResourceCollection validate() {
// treat empty nested text as no text
sanitizeText();
@@ -319,9 +366,8 @@ public class Concat extends Task {
if (binary) {
if (destinationFile == null) {
throw new BuildException(
- "DestFile attribute is required for binary concatenation");
+ "destfile attribute is required for binary concatenation");
}
-
if (textBuffer != null) {
throw new BuildException(
"Nested text is incompatible with binary concatenation");
@@ -344,187 +390,117 @@ public class Concat extends Task {
"Nested header or footer is incompatible with binary concatenation");
}
}
-
if (destinationFile != null && outputWriter != null) {
throw new BuildException(
"Cannot specify both a destination file and an output writer");
}
-
// Sanity check our inputs.
- if (sources.size() == 0 && textBuffer == null) {
+ if (rc == null && textBuffer == null) {
// Nothing to concatenate!
throw new BuildException(
- "At least one file must be provided, or some text.");
+ "At least one resource must be provided, or some text.");
}
-
- // If using filesets, disallow inline text. This is similar to
- // using GNU 'cat' with file arguments -- stdin is simply
- // ignored.
- if (sources.size() > 0 && textBuffer != null) {
- throw new BuildException(
- "Cannot include inline text when using filesets.");
- }
-
- // Iterate thru the sources - paths, filesets and filelists
- for (Enumeration e = sources.elements(); e.hasMoreElements();) {
- Object o = e.nextElement();
- if (o instanceof Path) {
- Path path = (Path) o;
- checkAddFiles(null, path.list());
-
- } else if (o instanceof FileSet) {
- FileSet fileSet = (FileSet) o;
- DirectoryScanner scanner =
- fileSet.getDirectoryScanner(getProject());
- checkAddFiles(fileSet.getDir(getProject()),
- scanner.getIncludedFiles());
-
- } else if (o instanceof FileList) {
- FileList fileList = (FileList) o;
- checkAddFiles(fileList.getDir(getProject()),
- fileList.getFiles(getProject()));
- }
- }
-
- // check if the files are outofdate
- if (destinationFile != null && !forceOverwrite
- && (sourceFiles.size() > 0) && destinationFile.exists()) {
- boolean outofdate = false;
- for (int i = 0; i < sourceFiles.size(); ++i) {
- File file = (File) sourceFiles.elementAt(i);
- if (file.lastModified() > destinationFile.lastModified()) {
- outofdate = true;
- break;
+ if (rc != null) {
+ // If using resources, disallow inline text. This is similar to
+ // using GNU 'cat' with file arguments -- stdin is simply
+ // ignored.
+ if (textBuffer != null) {
+ throw new BuildException(
+ "Cannot include inline text when using resources.");
+ }
+ Restrict noexistRc = new Restrict();
+ noexistRc.add(NOT_EXISTS);
+ noexistRc.add(rc);
+ for (Iterator i = noexistRc.iterator(); i.hasNext();) {
+ log(i.next() + " does not exist.", Project.MSG_ERR);
+ }
+ if (destinationFile != null) {
+ for (Iterator i = rc.iterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof FileResource) {
+ File f = ((FileResource) o).getFile();
+ if (FILE_UTILS.fileNameEquals(f, destinationFile)) {
+ throw new BuildException("Input file \""
+ + f + "\" is the same as the output file.");
+ }
+ }
+ }
+ }
+ Restrict existRc = new Restrict();
+ existRc.add(EXISTS);
+ existRc.add(rc);
+ boolean outofdate = destinationFile == null || forceOverwrite;
+ if (!outofdate) {
+ for (Iterator i = existRc.iterator(); !outofdate && i.hasNext();) {
+ Resource r = (Resource) i.next();
}
}
if (!outofdate) {
log(destinationFile + " is up-to-date.", Project.MSG_VERBOSE);
- return; // no need to do anything
+ return null; // no need to do anything
}
- }
-
- // Do nothing if all the sources are not present
- // And textBuffer is null
- if (textBuffer == null && sourceFiles.size() == 0
- && header == null && footer == null) {
- log("No existing files and no nested text, doing nothing",
- Project.MSG_INFO);
- return;
- }
-
- if (binary) {
- binaryCat();
+ return existRc;
} else {
- cat();
+ StringResource s = new StringResource();
+ s.setProject(getProject());
+ s.setValue(textBuffer.toString());
+ return s;
}
}
/**
- * execute the concat task.
+ * Execute the concat task.
*/
public void execute() {
- try {
- checkAndExecute();
- } finally {
- resetTask();
+ ResourceCollection c = validate();
+ if (c == null) {
+ return;
}
- }
-
- /**
- * Reset state to default.
- */
- public void reset() {
- append = false;
- forceOverwrite = true;
- destinationFile = null;
- encoding = null;
- outputEncoding = null;
- fixLastLine = false;
- sources.removeAllElements();
- sourceFiles.removeAllElements();
- filterChains = null;
- footer = null;
- header = null;
- }
-
- /**
- * reset the used variables to allow the same task
- * instance to be used again.
- */
- private void resetTask() {
- sourceFiles.clear();
- }
-
- private void checkAddFiles(File base, String[] filenames) {
- for (int i = 0; i < filenames.length; ++i) {
- File file = new File(base, filenames[i]);
- if (!file.exists()) {
- log("File " + file + " does not exist.", Project.MSG_ERR);
- continue;
- }
- if (destinationFile != null
- && FILE_UTILS.fileNameEquals(destinationFile, file)) {
- throw new BuildException("Input file \""
- + file + "\" "
- + "is the same as the output file.");
- }
- sourceFiles.addElement(file);
+ // Do nothing if no resources (including nested text)
+ if (c.size() < 1 && header == null && footer == null) {
+ log("No existing resources and no nested text, doing nothing",
+ Project.MSG_INFO);
+ return;
+ }
+ if (binary) {
+ binaryCat(c);
+ } else {
+ cat(c);
}
}
/** perform the binary concatenation */
- private void binaryCat() {
- log("Binary concatenation of " + sourceFiles.size()
- + " files to " + destinationFile);
+ private void binaryCat(ResourceCollection c) {
+ log("Binary concatenation of " + c.size()
+ + " resources to " + destinationFile);
FileOutputStream out = null;
- FileInputStream in = null;
+ InputStream in = null;
byte[] buffer = new byte[BUFFER_SIZE];
try {
try {
out = new FileOutputStream(destinationFile);
} catch (Exception t) {
- throw new BuildException(
- "Unable to open " + destinationFile
- + " for writing", t);
+ throw new BuildException("Unable to open "
+ + destinationFile + " for writing", t);
}
- for (Iterator i = sourceFiles.iterator(); i.hasNext();) {
- File sourceFile = (File) i.next();
- try {
- in = new FileInputStream(sourceFile);
- } catch (Exception t) {
- throw new BuildException(
- "Unable to open input file " + sourceFile,
- t);
- }
- int count = 0;
- do {
- try {
- count = in.read(buffer, 0, buffer.length);
- } catch (Exception t) {
- throw new BuildException(
- "Unable to read from " + sourceFile, t);
- }
- try {
- if (count > 0) {
- out.write(buffer, 0, count);
- }
- } catch (Exception t) {
- throw new BuildException(
- "Unable to write to " + destinationFile, t);
- }
- } while (count > 0);
-
+ try {
+ in = new ConcatResourceInputStream(c);
+ ((ConcatResourceInputStream) in).setManagingComponent(this);
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ Thread t = new Thread(new StreamPumper(in, out));
+ t.start();
+ try {
+ t.join();
+ } catch (InterruptedException e) {
try {
- in.close();
- } catch (Exception t) {
- throw new BuildException(
- "Unable to close " + sourceFile, t);
+ t.join();
+ } catch (InterruptedException ee) {
}
- in = null;
}
} finally {
FileUtils.close(in);
-
if (out != null) {
try {
out.close();
@@ -537,13 +513,11 @@ public class Concat extends Task {
}
/** perform the concatenation */
- private void cat() {
+ private void cat(ResourceCollection c) {
OutputStream os = null;
- Reader reader = null;
- char[] buffer = new char[BUFFER_SIZE];
+ char[] buffer = new char[BUFFER_SIZE];
try {
-
PrintWriter writer = null;
if (outputWriter != null) {
@@ -558,11 +532,9 @@ public class Concat extends Task {
if (!parent.exists()) {
parent.mkdirs();
}
-
os = new FileOutputStream(destinationFile.getAbsolutePath(),
append);
}
-
if (outputEncoding == null) {
writer = new PrintWriter(
new BufferedWriter(
@@ -573,7 +545,6 @@ public class Concat extends Task {
new OutputStreamWriter(os, outputEncoding)));
}
}
-
if (header != null) {
if (header.getFiltering()) {
concatenate(
@@ -582,16 +553,9 @@ public class Concat extends Task {
writer.print(header.getValue());
}
}
-
- if (textBuffer != null) {
- reader = new StringReader(
- getProject().replaceProperties(textBuffer.substring(0)));
- } else {
- reader = new MultiReader();
+ if (c.size() > 0) {
+ concatenate(buffer, writer, new MultiReader(c));
}
-
- concatenate(buffer, writer, reader);
-
if (footer != null) {
if (footer.getFiltering()) {
concatenate(
@@ -600,34 +564,18 @@ public class Concat extends Task {
writer.print(footer.getValue());
}
}
-
writer.flush();
if (os != null) {
os.flush();
}
-
} catch (IOException ioex) {
throw new BuildException("Error while concatenating: "
+ ioex.getMessage(), ioex);
} finally {
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException ignore) {
- // ignore
- }
- }
- if (os != null) {
- try {
- os.close();
- } catch (IOException ignore) {
- // ignore
- }
- }
+ FILE_UTILS.close(os);
}
}
-
/** Concatenate a single reader to the writer using buffer */
private void concatenate(char[] buffer, Writer writer, Reader in)
throws IOException {
@@ -639,7 +587,6 @@ public class Concat extends Task {
helper.setProject(getProject());
in = new BufferedReader(helper.getAssembledReader());
}
-
while (true) {
int nRead = in.read(buffer, 0, buffer.length);
if (nRead == -1) {
@@ -647,7 +594,6 @@ public class Concat extends Task {
}
writer.write(buffer, 0, nRead);
}
-
writer.flush();
}
@@ -796,34 +742,34 @@ public class Concat extends Task {
* a single stream.
*/
private class MultiReader extends Reader {
- private int pos = 0;
private Reader reader = null;
private int lastPos = 0;
private char[] lastChars = new char[eolString.length()];
private boolean needAddSeparator = false;
+ private Iterator i;
+
+ private MultiReader(ResourceCollection c) {
+ i = c.iterator();
+ }
private Reader getReader() throws IOException {
- if (reader == null) {
- log("Concating file " + sourceFiles.elementAt(pos),
- Project.MSG_VERBOSE);
- if (encoding == null) {
- reader = new BufferedReader(
- new FileReader((File) sourceFiles.elementAt(pos)));
- } else {
- // invoke the zoo of io readers
- reader = new BufferedReader(
- new InputStreamReader(
- new FileInputStream(
- (File) sourceFiles.elementAt(pos)),
- encoding));
- }
- for (int i = 0; i < lastChars.length; ++i) {
- lastChars[i] = 0;
- }
+ if (reader == null && i.hasNext()) {
+ Resource r = (Resource) i.next();
+ log("Concating " + r.toLongString(), Project.MSG_VERBOSE);
+ InputStream is = r.getInputStream();
+ reader = new BufferedReader(encoding == null
+ ? new InputStreamReader(is)
+ : new InputStreamReader(is, encoding));
+ Arrays.fill(lastChars, (char) 0);
}
return reader;
}
+ private void nextReader() throws IOException {
+ close();
+ reader = null;
+ }
+
/**
* Read a character from the current reader object. Advance
* to the next if the reader is finished.
@@ -840,12 +786,10 @@ public class Concat extends Task {
}
return ret;
}
-
- while (pos < sourceFiles.size()) {
+ while (getReader() != null) {
int ch = getReader().read();
if (ch == -1) {
- reader.close();
- reader = null;
+ nextReader();
if (fixLastLine && isMissingEndOfLine()) {
needAddSeparator = true;
lastPos = 0;
@@ -854,7 +798,6 @@ public class Concat extends Task {
addLastChar((char) ch);
return ch;
}
- pos++;
}
return -1;
}
@@ -871,13 +814,12 @@ public class Concat extends Task {
throws IOException {
int amountRead = 0;
- while (pos < sourceFiles.size() || (needAddSeparator)) {
+ while (getReader() != null || needAddSeparator) {
if (needAddSeparator) {
cbuf[off] = eolString.charAt(lastPos++);
if (lastPos >= eolString.length()) {
lastPos = 0;
needAddSeparator = false;
- pos++;
}
len--;
off++;
@@ -889,13 +831,10 @@ public class Concat extends Task {
}
int nRead = getReader().read(cbuf, off, len);
if (nRead == -1 || nRead == 0) {
- reader.close();
- reader = null;
+ nextReader();
if (fixLastLine && isMissingEndOfLine()) {
needAddSeparator = true;
lastPos = 0;
- } else {
- pos++;
}
} else {
if (fixLastLine) {
@@ -931,6 +870,7 @@ public class Concat extends Task {
reader.close();
}
}
+
/**
* if checking for end of line at end of file
* add a character to the lastchars buffer
diff --git a/src/main/org/apache/tools/ant/taskdefs/Length.java b/src/main/org/apache/tools/ant/taskdefs/Length.java
index 285a9f5bb..a7d3cd1d7 100755
--- a/src/main/org/apache/tools/ant/taskdefs/Length.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Length.java
@@ -21,18 +21,21 @@ import java.io.File;
import java.io.PrintStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
-import java.util.Vector;
import java.util.Iterator;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.taskdefs.condition.Condition;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.Comparison;
+import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.PropertyOutputStream;
/**
* Gets lengths: of files/resources, byte size; of strings, length (optionally trimmed).
@@ -52,9 +55,9 @@ public class Length extends Task implements Condition {
private String string;
private Boolean trim;
private String mode = ALL;
- private When when = When.EQUAL;
+ private Comparison when = Comparison.EQUAL;
private Long length;
- private Vector filesets;
+ private Resources resources;
/**
* The property in which the length will be stored.
@@ -69,9 +72,7 @@ public class Length extends Task implements Condition {
* @param file the File
whose length to retrieve.
*/
public synchronized void setFile(File file) {
- FileSet fs = new FileSet();
- fs.setFile(file);
- add(fs);
+ add(new FileResource(file));
}
/**
@@ -79,11 +80,20 @@ public class Length extends Task implements Condition {
* @param fs the FileSet
to add.
*/
public synchronized void add(FileSet fs) {
- if (fs == null) {
+ add((ResourceCollection) fs);
+ }
+
+ /**
+ * Add a ResourceCollection.
+ * @param c the ResourceCollection
to add.
+ * @since Ant 1.7
+ */
+ public synchronized void add(ResourceCollection c) {
+ if (c == null) {
return;
}
- filesets = (filesets == null) ? new Vector() : filesets;
- filesets.add(fs);
+ resources = (resources == null) ? new Resources() : resources;
+ resources.add(c);
}
/**
@@ -95,12 +105,22 @@ public class Length extends Task implements Condition {
}
/**
- * Set the comparison criteria for use as a Condition:
- * "equal", "greater", "less". Default is "equal".
+ * Set the comparison for use as a Condition.
* @param w EnumeratedAttribute When.
+ * @see org.apache.tools.ant.types.Comparison
*/
public synchronized void setWhen(When w) {
- when = w;
+ setWhen((Comparison) w);
+ }
+
+ /**
+ * Set the comparison for use as a Condition.
+ * @param c Comparison.
+ * @see org.apache.tools.ant.types.Comparison
+ * @since Ant 1.7
+ */
+ public synchronized void setWhen(Comparison c) {
+ when = c;
}
/**
@@ -142,7 +162,7 @@ public class Length extends Task implements Condition {
public void execute() {
validate();
PrintStream ps = new PrintStream((property != null)
- ? (OutputStream) new PropertyOutputStream()
+ ? (OutputStream) new PropertyOutputStream(getProject(), property)
: (OutputStream) new LogOutputStream(this, Project.MSG_INFO));
if (STRING.equals(mode)) {
@@ -173,27 +193,23 @@ public class Length extends Task implements Condition {
handleResources(h);
ell = new Long(h.getLength());
}
- int w = when.getIndex();
- int comp = ell.compareTo(length);
- return (w == 0 && comp == 0)
- || (w == 1 && comp > 0)
- || (w == 2 && comp < 0);
+ return when.evaluate(ell.compareTo(length));
}
private void validate() {
if (string != null) {
- if (filesets != null && filesets.size() > 0) {
+ if (resources != null) {
throw new BuildException("the string length function"
- + " is incompatible with the file length function");
+ + " is incompatible with the file/resource length function");
}
if (!(STRING.equals(mode))) {
throw new BuildException("the mode attribute is for use"
+ " with the file/resource length function");
}
- } else if (filesets != null) {
+ } else if (resources != null) {
if (!(EACH.equals(mode) || ALL.equals(mode))) {
throw new BuildException("invalid mode setting for"
- + " file length function: \"" + mode + "\"");
+ + " file/resource length function: \"" + mode + "\"");
} else if (trim != null) {
throw new BuildException("the trim attribute is"
+ " for use with the string length function only");
@@ -201,30 +217,20 @@ public class Length extends Task implements Condition {
} else {
throw new BuildException("you must set either the string attribute"
+ " or specify one or more files using the file attribute or"
- + " nested filesets");
+ + " nested resource collections");
}
}
private void handleResources(Handler h) {
- for (Iterator i = filesets.iterator(); i.hasNext();) {
- FileSet fs = (FileSet) i.next();
- DirectoryScanner ds = fs.getDirectoryScanner(getProject());
- String[] f = ds.getIncludedFiles();
- for (int j = 0; j < f.length; j++) {
- Resource r = ds.getResource(f[j]);
- if (!r.isExists()) {
- log(r.getName() + " does not exist", Project.MSG_ERR);
- } else if (r.isDirectory()) {
- log(r.getName() + " is a directory; length unspecified",
- Project.MSG_ERR);
- } else {
- //force a full path:
- File basedir = ds.getBasedir();
- String s = FileUtils.getFileUtils().resolveFile(
- basedir, r.getName()).getAbsolutePath();
- h.handle(new Resource(s, true,
- r.getLastModified(), false, r.getSize()));
- }
+ for (Iterator i = resources.iterator(); i.hasNext();) {
+ Resource r = (Resource) i.next();
+ if (!r.isExists()) {
+ log(r + " does not exist", Project.MSG_ERR);
+ } else if (r.isDirectory()) {
+ log(r + " is a directory; length unspecified",
+ Project.MSG_ERR);
+ } else {
+ h.handle(r);
}
}
h.complete();
@@ -251,27 +257,8 @@ public class Length extends Task implements Condition {
/**
* EnumeratedAttribute for the when attribute.
*/
- public static class When extends EnumeratedAttribute {
- private static final String[] VALUES
- = new String[] {"equal", "greater", "less"};
-
- private static final When EQUAL = new When("equal");
-
- public When() {
- }
- public When(String value) {
- setValue(value);
- }
- public String[] getValues() {
- return VALUES;
- }
- }
-
- private class PropertyOutputStream extends ByteArrayOutputStream {
- public void close() {
- getProject().setNewProperty(
- property, new String(toByteArray()).trim());
- }
+ public static class When extends Comparison {
+ //extend Comparison; retain for BC only
}
private abstract class Handler {
@@ -292,7 +279,7 @@ public class Length extends Task implements Condition {
super(ps);
}
protected void handle(Resource r) {
- ps.print(r.getName());
+ ps.print(r.toString());
ps.print(" : ");
//when writing to the log, we'll see what's happening:
long size = r.getSize();
@@ -312,7 +299,7 @@ public class Length extends Task implements Condition {
protected synchronized void handle(Resource r) {
long size = r.getSize();
if (size == Resource.UNKNOWN_SIZE) {
- log("Size unknown for " + r.getName(), Project.MSG_WARN);
+ log("Size unknown for " + r.toString(), Project.MSG_WARN);
} else {
accum += size;
}
diff --git a/src/main/org/apache/tools/ant/taskdefs/PathConvert.java b/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
index 224048ff5..c93d1b2aa 100644
--- a/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
+++ b/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
@@ -17,21 +17,23 @@
package org.apache.tools.ant.taskdefs;
import java.io.File;
-import java.util.StringTokenizer;
-import java.util.Vector;
import java.util.List;
+import java.util.Vector;
import java.util.ArrayList;
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Project;
+import java.util.StringTokenizer;
import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.DirSet;
-import org.apache.tools.ant.types.EnumeratedAttribute;
-import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.Mapper;
import org.apache.tools.ant.types.FileSet;
-import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.Reference;
-import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.resources.Union;
import org.apache.tools.ant.util.FileNameMapper;
/**
@@ -43,11 +45,16 @@ import org.apache.tools.ant.util.FileNameMapper;
*/
public class PathConvert extends Task {
+ /**
+ * Set if we're running on windows
+ */
+ private static boolean onWindows = Os.isFamily("dos");
+
// Members
/**
* Path to be converted
*/
- private Path path = null;
+ private Union path = null;
/**
* Reference to path/fileset to convert
*/
@@ -60,10 +67,6 @@ public class PathConvert extends Task {
* Set when targetOS is set to windows
*/
private boolean targetWindows = false;
- /**
- * Set if we're running on windows
- */
- private boolean onWindows = false;
/**
* Set if we should create a new property even if the result is empty
*/
@@ -92,7 +95,6 @@ public class PathConvert extends Task {
* Construct a new instance of the PathConvert task.
*/
public PathConvert() {
- onWindows = Os.isFamily("dos");
}
/**
@@ -164,17 +166,36 @@ public class PathConvert extends Task {
}
/**
- * Create a nested PATH element.
+ * Create a nested path element.
* @return a Path to be used by Ant reflection.
*/
public Path createPath() {
if (isReference()) {
throw noChildrenAllowed();
}
+ Path result = new Path(getProject());
+ add(result);
+ return result;
+ }
+
+ /**
+ * Add an arbitrary ResourceCollection.
+ * @param rc the ResourceCollection to add.
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ getPath().add(rc);
+ }
+
+ private synchronized Union getPath() {
if (path == null) {
- path = new Path(getProject());
+ path = new Union();
+ path.setProject(getProject());
}
- return path.createPath();
+ return path;
}
/**
@@ -283,28 +304,19 @@ public class PathConvert extends Task {
* @throws BuildException if something is invalid.
*/
public void execute() throws BuildException {
- Path savedPath = path;
+ Union savedPath = path;
String savedPathSep = pathSep; // may be altered in validateSetup
String savedDirSep = dirSep; // may be altered in validateSetup
try {
// If we are a reference, create a Path from the reference
if (isReference()) {
- path = new Path(getProject()).createPath();
- Object obj = refid.getReferencedObject(getProject());
-
- if (obj instanceof Path) {
- path.setRefid(refid);
- } else if (obj instanceof FileSet) {
- path.addFileset((FileSet) obj);
- } else if (obj instanceof DirSet) {
- path.addDirset((DirSet) obj);
- } else if (obj instanceof FileList) {
- path.addFilelist((FileList) obj);
- } else {
- throw new BuildException("'refid' does not refer to a "
- + "path, fileset, dirset, or filelist.");
+ Object o = refid.getReferencedObject(getProject());
+ if (!(o instanceof ResourceCollection)) {
+ throw new BuildException("refid '" + refid.getRefId()
+ + "' does not refer to a resource collection.");
}
+ getPath().add((ResourceCollection) o);
}
validateSetup(); // validate our setup
@@ -355,9 +367,13 @@ public class PathConvert extends Task {
// unless setonempty == false
if (setonempty || rslt.length() > 0) {
String value = rslt.toString();
- log("Set property " + property + " = " + value,
- Project.MSG_VERBOSE);
- getProject().setNewProperty(property, value);
+ if (property == null) {
+ log(value);
+ } else {
+ log("Set property " + property + " = " + value,
+ Project.MSG_VERBOSE);
+ getProject().setNewProperty(property, value);
+ }
}
} finally {
path = savedPath;
@@ -423,7 +439,6 @@ public class PathConvert extends Task {
addMapper(m);
}
-
/**
* Validate that all our parameters have been properly initialized.
*
@@ -434,9 +449,6 @@ public class PathConvert extends Task {
if (path == null) {
throw new BuildException("You must specify a path to convert");
}
- if (property == null) {
- throw new BuildException("You must specify a property");
- }
// Determine the separator strings. The dirsep and pathsep attributes
// override the targetOS settings.
String dsep = File.separator;
@@ -464,7 +476,7 @@ public class PathConvert extends Task {
* @return BuildException.
*/
private BuildException noChildrenAllowed() {
- return new BuildException("You must not specify nested <pathelement>
values.
*/
- public class PathElement {
+ public class PathElement implements ResourceCollection {
private String[] parts;
/**
@@ -106,6 +108,19 @@ public class Path extends DataType implements Cloneable {
public String[] getParts() {
return parts;
}
+
+ public Iterator iterator() {
+ return new FileResourceIterator(null, parts);
+ }
+
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ public int size() {
+ return parts == null ? 0 : parts.length;
+ }
+
}
/**
@@ -125,7 +140,6 @@ public class Path extends DataType implements Cloneable {
*/
public Path(Project project) {
setProject(project);
- elements = new Vector();
}
/**
@@ -135,22 +149,17 @@ public class Path extends DataType implements Cloneable {
* @throws BuildException on error
*/
public void setLocation(File location) throws BuildException {
- if (isReference()) {
- throw tooManyAttributes();
- }
+ checkAttributesAllowed();
createPathElement().setLocation(location);
}
-
/**
* Parses a path definition and creates single PathElements.
* @param path the String
path definition.
* @throws BuildException on error
*/
public void setPath(String path) throws BuildException {
- if (isReference()) {
- throw tooManyAttributes();
- }
+ checkAttributesAllowed();
createPathElement().setPath(path);
}
@@ -163,10 +172,9 @@ public class Path extends DataType implements Cloneable {
* @throws BuildException on error
*/
public void setRefid(Reference r) throws BuildException {
- if (!elements.isEmpty()) {
+ if (!getResourceCollections().isEmpty()) {
throw tooManyAttributes();
}
- elements.addElement(r);
super.setRefid(r);
}
@@ -180,7 +188,7 @@ public class Path extends DataType implements Cloneable {
throw noChildrenAllowed();
}
PathElement pe = new PathElement();
- elements.addElement(pe);
+ add(pe);
return pe;
}
@@ -190,11 +198,7 @@ public class Path extends DataType implements Cloneable {
* @throws BuildException on error
*/
public void addFileset(FileSet fs) throws BuildException {
- if (isReference()) {
- throw noChildrenAllowed();
- }
- elements.addElement(fs);
- setChecked(false);
+ add(fs);
}
/**
@@ -203,11 +207,7 @@ public class Path extends DataType implements Cloneable {
* @throws BuildException on error
*/
public void addFilelist(FileList fl) throws BuildException {
- if (isReference()) {
- throw noChildrenAllowed();
- }
- elements.addElement(fl);
- setChecked(false);
+ add(fl);
}
/**
@@ -216,11 +216,7 @@ public class Path extends DataType implements Cloneable {
* @throws BuildException on error
*/
public void addDirset(DirSet dset) throws BuildException {
- if (isReference()) {
- throw noChildrenAllowed();
- }
- elements.addElement(dset);
- setChecked(false);
+ add(dset);
}
/**
@@ -230,12 +226,7 @@ public class Path extends DataType implements Cloneable {
* @since Ant 1.6
*/
public void add(Path path) throws BuildException {
- if (isReference()) {
- throw noChildrenAllowed();
- }
- elements.addElement(path);
- setChecked(false);
-
+ add((ResourceCollection) path);
}
/**
@@ -244,12 +235,8 @@ public class Path extends DataType implements Cloneable {
* @throws BuildException on error
*/
public Path createPath() throws BuildException {
- if (isReference()) {
- throw noChildrenAllowed();
- }
Path p = new Path(getProject());
- elements.addElement(p);
- setChecked(false);
+ add(p);
return p;
}
@@ -261,17 +248,12 @@ public class Path extends DataType implements Cloneable {
if (other == null) {
return;
}
- String[] l = other.list();
- for (int i = 0; i < l.length; i++) {
- if (elements.indexOf(l[i]) == -1) {
- elements.addElement(l[i]);
- }
- }
+ add(other);
}
- /**
+ /**
* Adds the components on the given path which exist to this
- * Path. Components that don't exist, aren't added.
+ * Path. Components that don't exist aren't added.
*
* @param source - source path whose components are examined for existence
*/
@@ -309,92 +291,29 @@ public class Path extends DataType implements Cloneable {
}
/**
- * Returns all path elements defined by this and nested path objects.
- * @return list of path elements.
- */
- public String[] list() {
- if (!isChecked()) {
- // make sure we don't have a circular reference here
- Stack stk = new Stack();
- stk.push(this);
- dieOnCircularReference(stk, getProject());
- }
-
- Vector result = new Vector(2 * elements.size());
- for (int i = 0; i < elements.size(); i++) {
- Object o = elements.elementAt(i);
- if (o instanceof Reference) {
- Reference r = (Reference) o;
- o = r.getReferencedObject(getProject());
- // we only support references to paths right now
- if (!(o instanceof Path)) {
- String msg = r.getRefId() + " doesn\'t denote a path " + o;
- throw new BuildException(msg);
- }
+ * Override Union.getCollection()
+ * so we can check our children first.
+ * @return a Collection.
+ */
+ protected Collection getCollection() {
+ for (Iterator i = getResourceCollections().iterator(); i.hasNext();) {
+ ResourceCollection rc = (ResourceCollection) i.next();
+ if (!(rc.isFilesystemOnly())) {
+ throw new BuildException(getDataTypeName()
+ + " allows only filesystem resources.");
}
-
- if (o instanceof String) {
- // obtained via append
- addUnlessPresent(result, (String) o);
- } else if (o instanceof PathElement) {
- String[] parts = ((PathElement) o).getParts();
- if (parts == null) {
- throw new BuildException("You must either set location or"
- + " path on toString()
+ * prefixed by a type description.
+ * @return this Resource formatted as a long String.
+ * @since Ant 1.7
+ */
+ public final String toLongString() {
+ return isReference() ? ((Resource) getCheckedRef()).toLongString()
+ : getDataTypeName() + " \"" + toString() + '"';
+ }
+
+ /**
+ * Overrides the base version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (name != null
+ || exists != null
+ || lastmodified != null
+ || directory != null
+ || size != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
}
}
diff --git a/src/main/org/apache/tools/ant/types/ResourceCollection.java b/src/main/org/apache/tools/ant/types/ResourceCollection.java
new file mode 100755
index 000000000..672701997
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/ResourceCollection.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.util.Iterator;
+
+/**
+ * Interface describing a collection of Resources.
+ * @since Ant 1.7
+ */
+public interface ResourceCollection {
+
+ /**
+ * Get an Iterator over the contents of this ResourceCollection, whose elements
+ * are org.apache.tools.ant.types.Resource
instances.
+ * @return an Iterator of Resources.
+ */
+ public Iterator iterator();
+
+ /**
+ * Learn the number of contained Resources.
+ * @return number of elements as int.
+ */
+ public int size();
+
+ /**
+ * Indicate whether this ResourceCollection is composed entirely of
+ * Resources accessible via local filesystem conventions. If true,
+ * all Resources returned from this ResourceCollection should be
+ * instances of FileResource.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public boolean isFilesystemOnly();
+
+}
diff --git a/src/main/org/apache/tools/ant/types/ZipFileSet.java b/src/main/org/apache/tools/ant/types/ZipFileSet.java
index f39a7f387..331ed64b3 100644
--- a/src/main/org/apache/tools/ant/types/ZipFileSet.java
+++ b/src/main/org/apache/tools/ant/types/ZipFileSet.java
@@ -18,6 +18,7 @@ package org.apache.tools.ant.types;
import java.io.File;
import java.util.Stack;
+import java.util.Iterator;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
@@ -62,6 +63,8 @@ public class ZipFileSet extends FileSet {
private boolean fileModeHasBeenSet = false;
private boolean dirModeHasBeenSet = false;
+ private String encoding = null;
+
/** Constructor for ZipFileSet */
public ZipFileSet() {
super();
@@ -89,6 +92,7 @@ public class ZipFileSet extends FileSet {
dirMode = fileset.dirMode;
fileModeHasBeenSet = fileset.fileModeHasBeenSet;
dirModeHasBeenSet = fileset.dirModeHasBeenSet;
+ encoding = fileset.encoding;
}
/**
@@ -98,9 +102,7 @@ public class ZipFileSet extends FileSet {
* @throws BuildException on error
*/
public void setDir(File dir) throws BuildException {
- if (isReference()) {
- throw tooManyAttributes();
- }
+ checkAttributesAllowed();
if (srcFile != null) {
throw new BuildException("Cannot set both dir and src attributes");
} else {
@@ -116,9 +118,7 @@ public class ZipFileSet extends FileSet {
* @param srcFile The zip file from which to extract entries.
*/
public void setSrc(File srcFile) {
- if (isReference()) {
- throw tooManyAttributes();
- }
+ checkAttributesAllowed();
if (hasDir) {
throw new BuildException("Cannot set both dir and src attributes");
}
@@ -189,6 +189,24 @@ public class ZipFileSet extends FileSet {
return fullpath;
}
+ /**
+ * Set the encoding used for this ZipFileSet.
+ * @param enc encoding as String.
+ * @since Ant 1.7
+ */
+ public void setEncoding(String enc) {
+ this.encoding = enc;
+ }
+
+ /**
+ * Get the encoding used for this ZipFileSet.
+ * @return String encoding.
+ * @since Ant 1.7
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
/**
* Return the DirectoryScanner associated with this FileSet.
* If the ZipFileSet defines a source Zip file, then a ZipScanner
@@ -200,16 +218,60 @@ public class ZipFileSet extends FileSet {
if (isReference()) {
return getRef(p).getDirectoryScanner(p);
}
- if (srcFile != null) {
- ZipScanner zs = new ZipScanner();
- zs.setSrc(srcFile);
- super.setDir(p.getBaseDir());
- setupDirectoryScanner(zs, p);
- zs.init();
- return zs;
- } else {
+ if (srcFile == null) {
return super.getDirectoryScanner(p);
}
+ ZipScanner zs = new ZipScanner();
+ zs.setSrc(srcFile);
+ super.setDir(p.getBaseDir());
+ setupDirectoryScanner(zs, p);
+ zs.init();
+ zs.setEncoding(encoding);
+ return zs;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return Iterator of Resources.
+ * @since Ant 1.7
+ */
+ public Iterator iterator() {
+ if (isReference()) {
+ return ((ResourceCollection) (getRef(getProject()))).iterator();
+ }
+ if (srcFile == null) {
+ return super.iterator();
+ }
+ ZipScanner zs = (ZipScanner) getDirectoryScanner(getProject());
+ return zs.getResourceFiles();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return size of the collection as int.
+ * @since Ant 1.7
+ */
+ public int size() {
+ if (isReference()) {
+ return ((ResourceCollection) (getRef(getProject()))).size();
+ }
+ if (srcFile == null) {
+ return super.size();
+ }
+ ZipScanner zs = (ZipScanner) getDirectoryScanner(getProject());
+ return zs.getIncludedFilesCount();
+ }
+
+ /**
+ * Indicate whether this ResourceCollection is composed entirely of
+ * Resources accessible via local filesystem conventions. If true,
+ * all Resources returned from this ResourceCollection should be
+ * instances of FileResource.
+ * @return whether this is a filesystem-only resource collection.
+ * @since Ant 1.7
+ */
+ public boolean isFilesystemOnly() {
+ return srcFile == null;
}
/**
@@ -297,11 +359,7 @@ public class ZipFileSet extends FileSet {
* @return the abstract fileset instance
*/
protected AbstractFileSet getRef(Project p) {
- if (!isChecked()) {
- Stack stk = new Stack();
- stk.push(this);
- dieOnCircularReference(stk, p);
- }
+ dieOnCircularReference(p);
Object o = getRefid().getReferencedObject(p);
if (o instanceof ZipFileSet) {
return (AbstractFileSet) o;
@@ -319,6 +377,7 @@ public class ZipFileSet extends FileSet {
throw new BuildException(msg);
}
}
+
/**
* Return a ZipFileSet that has the same properties
* as this one.
diff --git a/src/main/org/apache/tools/ant/types/ZipScanner.java b/src/main/org/apache/tools/ant/types/ZipScanner.java
index 5c733d684..23cdaf9c5 100644
--- a/src/main/org/apache/tools/ant/types/ZipScanner.java
+++ b/src/main/org/apache/tools/ant/types/ZipScanner.java
@@ -19,14 +19,20 @@ package org.apache.tools.ant.types;
import java.io.File;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Vector;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Iterator;
+import java.util.ArrayList;
import java.util.Hashtable;
+import java.util.Comparator;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.zip.ZipException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.resources.ZipResource;
+import org.apache.tools.ant.types.resources.FileResourceIterator;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
@@ -44,14 +50,31 @@ public class ZipScanner extends DirectoryScanner {
* The zip file which should be scanned.
*/
protected File srcFile;
+
/**
* to record the last scanned zip file with its modification date
*/
private Resource lastScannedResource;
+
+ /**
+ * record list of all file zip entries
+ */
+ private TreeMap fileEntries = new TreeMap();
+
/**
- * record list of all zip entries
+ * record list of all directory zip entries
*/
- private Hashtable myentries;
+ private TreeMap dirEntries = new TreeMap();
+
+ /**
+ * record list of matching file zip entries
+ */
+ private TreeMap matchFileEntries = new TreeMap();
+
+ /**
+ * record list of matching directory zip entries
+ */
+ private TreeMap matchDirEntries = new TreeMap();
/**
* encoding of file names.
@@ -60,6 +83,17 @@ public class ZipScanner extends DirectoryScanner {
*/
private String encoding;
+ /**
+ * Don't scan when we have no zipfile.
+ * @since Ant 1.7
+ */
+ public void scan() {
+ if (srcFile == null) {
+ return;
+ }
+ super.scan();
+ }
+
/**
* Sets the srcFile for scanning. This is the jar or zip file that
* is scanned for matching entries.
@@ -88,23 +122,25 @@ public class ZipScanner extends DirectoryScanner {
* include patterns and none of the exclude patterns.
*/
public String[] getIncludedFiles() {
- if (srcFile != null) {
- Vector myvector = new Vector();
- // first check if the archive needs to be scanned again
- scanme();
- for (Enumeration e = myentries.elements(); e.hasMoreElements();) {
- Resource myresource = (Resource) e.nextElement();
- if (!myresource.isDirectory() && match(myresource.getName())) {
- myvector.addElement(myresource.getName());
- }
- }
- String[] files = new String[myvector.size()];
- myvector.copyInto(files);
- Arrays.sort(files);
- return files;
- } else {
+ if (srcFile == null) {
return super.getIncludedFiles();
}
+ scanme();
+ Set s = matchFileEntries.keySet();
+ return (String[]) (s.toArray(new String[s.size()]));
+ }
+
+ /**
+ * Override parent implementation.
+ * @return count of included files.
+ * @since Ant 1.7
+ */
+ public int getIncludedFilesCount() {
+ if (srcFile == null) {
+ return super.getIncludedFilesCount();
+ }
+ scanme();
+ return matchFileEntries.size();
}
/**
@@ -116,23 +152,51 @@ public class ZipScanner extends DirectoryScanner {
* include patterns and none of the exclude patterns.
*/
public String[] getIncludedDirectories() {
- if (srcFile != null) {
- Vector myvector = new Vector();
- // first check if the archive needs to be scanned again
- scanme();
- for (Enumeration e = myentries.elements(); e.hasMoreElements();) {
- Resource myresource = (Resource) e.nextElement();
- if (myresource.isDirectory() && match(myresource.getName())) {
- myvector.addElement(myresource.getName());
- }
- }
- String[] files = new String[myvector.size()];
- myvector.copyInto(files);
- Arrays.sort(files);
- return files;
- } else {
+ if (srcFile == null) {
return super.getIncludedDirectories();
}
+ scanme();
+ Set s = matchDirEntries.keySet();
+ return (String[]) (s.toArray(new String[s.size()]));
+ }
+
+ /**
+ * Override parent implementation.
+ * @return count of included directories.
+ * @since Ant 1.7
+ */
+ public int getIncludedDirsCount() {
+ if (srcFile == null) {
+ return super.getIncludedDirsCount();
+ }
+ scanme();
+ return matchDirEntries.size();
+ }
+
+ /**
+ * Get the set of Resources that represent files.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ /* package-private for now */ Iterator getResourceFiles() {
+ if (srcFile == null) {
+ return new FileResourceIterator(getBasedir(), getIncludedFiles());
+ }
+ scanme();
+ return matchFileEntries.values().iterator();
+ }
+
+ /**
+ * Get the set of Resources that represent directories.
+ * @return an Iterator of Resources.
+ * @since Ant 1.7
+ */
+ /* package-private for now */ Iterator getResourceDirectories() {
+ if (srcFile == null) {
+ return new FileResourceIterator(getBasedir(), getIncludedDirectories());
+ }
+ scanme();
+ return matchDirEntries.values().iterator();
}
/**
@@ -165,6 +229,7 @@ public class ZipScanner extends DirectoryScanner {
}
/**
+ * Get the named Resource.
* @param name path name of the file sought in the archive
* @return the resource
* @since Ant 1.5.2
@@ -172,34 +237,35 @@ public class ZipScanner extends DirectoryScanner {
public Resource getResource(String name) {
if (srcFile == null) {
return super.getResource(name);
- } else if (name.equals("")) {
+ }
+ if (name.equals("")) {
// special case in ZIPs, we do not want this thing included
return new Resource("", true, Long.MAX_VALUE, true);
}
-
// first check if the archive needs to be scanned again
scanme();
- if (myentries.containsKey(name)) {
- return (Resource) myentries.get(name);
- } else if (myentries.containsKey(name + "/")) {
- return (Resource) myentries.get(name + "/");
- } else {
- return new Resource(name);
+ if (fileEntries.containsKey(name)) {
+ return (Resource) fileEntries.get(name);
+ }
+ name = trimSeparator(name);
+
+ if (dirEntries.containsKey(name)) {
+ return (Resource) dirEntries.get(name);
}
+ return new Resource(name);
}
/**
* if the datetime of the archive did not change since
* lastScannedResource was initialized returns immediately else if
* the archive has not been scanned yet, then all the zip entries
- * are put into the vector myentries as a vector of the resource
- * type
+ * are put into the appropriate tables.
*/
private void scanme() {
+ //do not use a FileResource b/c it pulls File info from the filesystem:
Resource thisresource = new Resource(srcFile.getAbsolutePath(),
srcFile.exists(),
srcFile.lastModified());
-
// spare scanning again and again
if (lastScannedResource != null
&& lastScannedResource.getName().equals(thisresource.getName())
@@ -207,10 +273,15 @@ public class ZipScanner extends DirectoryScanner {
== thisresource.getLastModified()) {
return;
}
-
+ init();
ZipEntry entry = null;
ZipFile zf = null;
- myentries = new Hashtable();
+
+ fileEntries.clear();
+ dirEntries.clear();
+ matchFileEntries.clear();
+ matchDirEntries.clear();
+
try {
try {
zf = new ZipFile(srcFile, encoding);
@@ -219,15 +290,23 @@ public class ZipScanner extends DirectoryScanner {
} catch (IOException ex) {
throw new BuildException("problem opening " + srcFile, ex);
}
-
Enumeration e = zf.getEntries();
while (e.hasMoreElements()) {
entry = (ZipEntry) e.nextElement();
- myentries.put(new String(entry.getName()),
- new Resource(entry.getName(), true,
- entry.getTime(),
- entry.isDirectory(),
- entry.getSize()));
+ Resource r = new ZipResource(srcFile, encoding, entry);
+ String name = entry.getName();
+ if (entry.isDirectory()) {
+ name = trimSeparator(name);
+ dirEntries.put(name, r);
+ if (match(name)) {
+ matchDirEntries.put(name, r);
+ }
+ } else {
+ fileEntries.put(name, r);
+ if (match(name)) {
+ matchFileEntries.put(name, r);
+ }
+ }
}
} finally {
if (zf != null) {
@@ -241,4 +320,9 @@ public class ZipScanner extends DirectoryScanner {
// record data about the last scanned resource
lastScannedResource = thisresource;
}
+
+ private static String trimSeparator(String s) {
+ return s.endsWith("/") ? s.substring(0, s.length() - 1) : s;
+ }
+
}
diff --git a/src/main/org/apache/tools/ant/types/defaults.properties b/src/main/org/apache/tools/ant/types/defaults.properties
index d1d1fa9c6..05ee6c4d9 100644
--- a/src/main/org/apache/tools/ant/types/defaults.properties
+++ b/src/main/org/apache/tools/ant/types/defaults.properties
@@ -1,8 +1,4 @@
-classfileset=org.apache.tools.ant.types.optional.depend.ClassfileSet
description=org.apache.tools.ant.types.Description
-dirset=org.apache.tools.ant.types.DirSet
-filelist=org.apache.tools.ant.types.FileList
-fileset=org.apache.tools.ant.types.FileSet
filterchain=org.apache.tools.ant.types.FilterChain
filterreader=org.apache.tools.ant.types.AntFilterReader
filterset=org.apache.tools.ant.types.FilterSet
@@ -20,19 +16,15 @@ compositemapper=org.apache.tools.ant.util.CompositeMapper
chainedmapper=org.apache.tools.ant.util.ChainedMapper
filtermapper=org.apache.tools.ant.types.mappers.FilterMapper
-path=org.apache.tools.ant.types.Path
patternset=org.apache.tools.ant.types.PatternSet
regexp=org.apache.tools.ant.types.RegularExpression
substitution=org.apache.tools.ant.types.Substitution
xmlcatalog=org.apache.tools.ant.types.XMLCatalog
extensionSet=org.apache.tools.ant.taskdefs.optional.extension.ExtensionSet
extension=org.apache.tools.ant.taskdefs.optional.extension.ExtensionAdapter
-libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet
selector=org.apache.tools.ant.types.selectors.SelectSelector
signedselector=org.apache.tools.ant.types.selectors.SignedSelector
-zipfileset=org.apache.tools.ant.types.ZipFileSet
scriptfilter=org.apache.tools.ant.types.optional.ScriptFilter
-propertyset=org.apache.tools.ant.types.PropertySet
assertions=org.apache.tools.ant.types.Assertions
concatfilter=org.apache.tools.ant.filters.ConcatFilter
issigned=org.apache.tools.ant.taskdefs.condition.IsSigned
@@ -45,3 +37,28 @@ xor=org.apache.tools.ant.taskdefs.condition.Xor
parsersupports=org.apache.tools.ant.taskdefs.condition.ParserSupports
scriptmapper=org.apache.tools.ant.types.optional.ScriptMapper
isfailure=org.apache.tools.ant.taskdefs.condition.IsFailure
+
+#ResourceCollections:
+dirset=org.apache.tools.ant.types.DirSet
+filelist=org.apache.tools.ant.types.FileList
+fileset=org.apache.tools.ant.types.FileSet
+path=org.apache.tools.ant.types.Path
+propertyset=org.apache.tools.ant.types.PropertySet
+zipfileset=org.apache.tools.ant.types.ZipFileSet
+classfileset=org.apache.tools.ant.types.optional.depend.ClassfileSet
+libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet
+files=org.apache.tools.ant.types.resources.Files
+restrict=org.apache.tools.ant.types.resources.Restrict
+union=org.apache.tools.ant.types.resources.Union
+difference=org.apache.tools.ant.types.resources.Difference
+intersect=org.apache.tools.ant.types.resources.Intersect
+sort=org.apache.tools.ant.types.resources.Sort
+resources=org.apache.tools.ant.types.resources.Resources
+
+#Resources (single-element ResourceCollections):
+resource=org.apache.tools.ant.types.Resource
+file=org.apache.tools.ant.types.resources.FileResource
+url=org.apache.tools.ant.types.resources.URLResource
+string=org.apache.tools.ant.types.resources.StringResource
+zipentry=org.apache.tools.ant.types.resources.ZipResource
+propertyresource=org.apache.tools.ant.types.resources.PropertyResource
diff --git a/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java b/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java
new file mode 100755
index 000000000..b86cde118
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/BaseResourceCollectionContainer.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.List;
+import java.util.Stack;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.NoSuchElementException;
+import java.util.ConcurrentModificationException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Base class for ResourceCollections that nest multiple ResourceCollections.
+ * @since Ant 1.7
+ */
+public abstract class BaseResourceCollectionContainer
+ extends DataType implements ResourceCollection, Cloneable {
+ private List rc = new ArrayList();
+ private Collection coll = null;
+
+ /**
+ * Add a ResourceCollection to the container.
+ * @param c the ResourceCollection to add.
+ * @throws BuildException on error.
+ */
+ public synchronized void add(ResourceCollection c) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (c == null) {
+ return;
+ }
+ rc.add(c);
+ FailFast.invalidate(this);
+ coll = null;
+ setChecked(false);
+ }
+
+ /**
+ * Add the Collection of ResourceCollections to the container.
+ * @param c the Collection whose elements to add.
+ * @throws BuildException on error.
+ */
+ public synchronized void addAll(Collection c) throws BuildException {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ try {
+ for (Iterator i = c.iterator(); i.hasNext();) {
+ add((ResourceCollection) i.next());
+ }
+ } catch (ClassCastException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract. The Iterator returned
+ * will throw ConcurrentModificationExceptions if ResourceCollections
+ * are added to this container while the Iterator is in use.
+ * @return a "fail-fast" Iterator.
+ */
+ public synchronized final Iterator iterator() {
+ if (isReference()) {
+ return ((BaseResourceCollectionContainer) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ cacheCollection();
+ return new FailFast(this, coll.iterator());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return ((BaseResourceCollectionContainer) getCheckedRef()).size();
+ }
+ dieOnCircularReference();
+ cacheCollection();
+ return coll.size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((BaseResourceCollectionContainer) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ //first the easy way, if all children are filesystem-only, return true:
+ boolean goEarly = true;
+ for (Iterator i = rc.iterator(); goEarly && i.hasNext();) {
+ goEarly &= ((ResourceCollection) i.next()).isFilesystemOnly();
+ }
+ if (goEarly) {
+ return true;
+ }
+ /* now check each Resource in case the child only
+ lets through files from any children IT may have: */
+ cacheCollection();
+ for (Iterator i = coll.iterator(); i.hasNext();) {
+ if (!(i.next() instanceof FileResource)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (Iterator i = rc.iterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof DataType) {
+ stk.push(o);
+ invokeCircularReferenceCheck((DataType) o, stk, p);
+ stk.pop();
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Get the nested ResourceCollections.
+ * @return List.
+ */
+ protected synchronized final List getResourceCollections() {
+ dieOnCircularReference();
+ return Collections.unmodifiableList(rc);
+ }
+
+ /**
+ * Template method for subclasses to return a Collection object of Resources.
+ * @return Collection.
+ */
+ protected abstract Collection getCollection();
+
+ /**
+ * Implement clone. The set of nested resource
+ * collections is shallowly cloned.
+ * @return a cloned instance.
+ */
+ public Object clone() {
+ try {
+ BaseResourceCollectionContainer c
+ = (BaseResourceCollectionContainer) super.clone();
+ c.rc = new ArrayList(rc);
+ c.coll = null;
+ return c;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Format this BaseResourceCollectionContainer as a String.
+ * @return a descriptive String
.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getCheckedRef().toString();
+ }
+ cacheCollection();
+ if (coll.size() == 0) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ for (Iterator i = coll.iterator(); i.hasNext();) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(i.next());
+ }
+ return sb.toString();
+ }
+
+ private synchronized void cacheCollection() {
+ coll = (coll == null) ? getCollection() : coll;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Difference.java b/src/main/org/apache/tools/ant/types/resources/Difference.java
new file mode 100755
index 000000000..dbaf80663
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Difference.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.util.List;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection representing the difference between
+ * two or more nested ResourceCollections.
+ * @since Ant 1.7
+ */
+public class Difference extends BaseResourceCollectionContainer {
+
+ /**
+ * Calculate the difference of the nested ResourceCollections.
+ * @return a Collection of Resources.
+ */
+ protected Collection getCollection() {
+ List rc = getResourceCollections();
+ int size = rc.size();
+ if (size < 2) {
+ throw new BuildException("The difference of " + size
+ + " resource collection" + ((size == 1) ? "" : "s")
+ + " is undefined.");
+ }
+ HashSet hs = new HashSet();
+ ArrayList al = new ArrayList();
+ for (Iterator rcIter = rc.iterator(); rcIter.hasNext();) {
+ for (Iterator r = nextRC(rcIter).iterator(); r.hasNext();) {
+ Object next = r.next();
+ if (hs.add(next)) {
+ al.add(next);
+ } else {
+ al.remove(next);
+ }
+ }
+ }
+ return al;
+ }
+
+ private static ResourceCollection nextRC(Iterator i) {
+ return (ResourceCollection) i.next();
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/FailFast.java b/src/main/org/apache/tools/ant/types/resources/FailFast.java
new file mode 100755
index 000000000..d95f68daa
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/FailFast.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.WeakHashMap;
+import java.util.NoSuchElementException;
+import java.util.ConcurrentModificationException;
+
+/**
+ * Helper class for ResourceCollections to return Iterators
+ * that fail on changes to the object.
+ * @since Ant 1.7
+ */
+/*package-private*/ class FailFast implements Iterator {
+ private static final WeakHashMap map = new WeakHashMap();
+
+ /**
+ * Invalidate any in-use Iterators from the specified Object.
+ * @param o the parent Object.
+ */
+ static synchronized void invalidate(Object o) {
+ Set s = (Set) (map.get(o));
+ if (s != null) {
+ s.clear();
+ }
+ }
+
+ private static synchronized void add(FailFast f) {
+ Set s = (Set) (map.get(f.parent));
+ if (s == null) {
+ s = new HashSet();
+ map.put(f.parent, s);
+ }
+ s.add(f);
+ }
+
+ private static synchronized void remove(FailFast f) {
+ Set s = (Set) (map.get(f.parent));
+ if (s != null) {
+ s.remove(f);
+ }
+ }
+
+ private static synchronized void failFast(FailFast f) {
+ Set s = (Set) (map.get(f.parent));
+ if (!s.contains(f)) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ private Object parent;
+ private Iterator wrapped;
+
+ /**
+ * Construct a new FailFast Iterator wrapping the specified Iterator
+ * and dependent upon the specified parent Object.
+ * @param o the parent Object.
+ * @param i the wrapped Iterator.
+ */
+ FailFast(Object o, Iterator i) {
+ if (o == null) {
+ throw new IllegalArgumentException("parent object is null");
+ }
+ if (i == null) {
+ throw new IllegalArgumentException("cannot wrap null iterator");
+ }
+ parent = o;
+ if (i.hasNext()) {
+ wrapped = i;
+ add(this);
+ }
+ }
+
+ /**
+ * Fulfill the Iterator contract.
+ * @return true if there are more elements.
+ */
+ public boolean hasNext() {
+ if (wrapped == null) {
+ return false;
+ }
+ failFast(this);
+ return wrapped.hasNext();
+ }
+
+ /**
+ * Fulfill the Iterator contract.
+ * @return the next element.
+ * @throws NoSuchElementException if no more elements.
+ */
+ public Object next() {
+ if (wrapped == null || !wrapped.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ failFast(this);
+ try {
+ return wrapped.next();
+ } finally {
+ if (!wrapped.hasNext()) {
+ wrapped = null;
+ remove(this);
+ }
+ }
+ }
+
+ /**
+ * Fulfill the Iterator contract.
+ * @throws UnsupportedOperationException always.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+}
+
diff --git a/src/main/org/apache/tools/ant/types/resources/FileResource.java b/src/main/org/apache/tools/ant/types/resources/FileResource.java
new file mode 100755
index 000000000..224cc1736
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/FileResource.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * A Resource representation of a File.
+ * @since Ant 1.7
+ */
+public class FileResource extends Resource implements Touchable {
+
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static final int NULL_FILE
+ = Resource.getMagicNumber("null file".getBytes());
+
+ private File file;
+ private File base;
+
+ /**
+ * Default constructor.
+ */
+ public FileResource() {
+ }
+
+ /**
+ * Construct a new FileResource using the specified base File and relative name.
+ * @param b the base File (directory).
+ * @param name the relative filename.
+ */
+ public FileResource(File b, String name) {
+ setFile(FILE_UTILS.resolveFile(b, name));
+ setBase(b);
+ }
+
+ /**
+ * Construct a new FileResource from a File.
+ * @param f the File represented.
+ */
+ public FileResource(File f) {
+ setFile(f);
+ }
+
+ /**
+ * Constructor for Ant attribute introspection.
+ * @param p the Project against which to resolve s
.
+ * @param s the absolute or Project-relative filename as a String.
+ * @see org.apache.tools.ant.IntrospectionHelper
+ */
+ public FileResource(Project p, String s) {
+ this(p.resolveFile(s));
+ setProject(p);
+ }
+
+ /**
+ * Set the File for this FileResource.
+ * @param f the File to be represented.
+ */
+ public void setFile(File f) {
+ checkAttributesAllowed();
+ file = f;
+ }
+
+ /**
+ * Get the file represented by this FileResource.
+ * @return the File.
+ */
+ public File getFile() {
+ return isReference() ? ((FileResource) getCheckedRef()).getFile() : file;
+ }
+
+ /**
+ * Set the base File for this FileResource.
+ * @param b the base File.
+ */
+ public void setBase(File b) {
+ checkAttributesAllowed();
+ base = b;
+ }
+
+ /**
+ * Return the base to which the name is relative.
+ * @return the base File.
+ */
+ public File getBase() {
+ return isReference()
+ ? ((FileResource) getCheckedRef()).getBase() : base;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (file != null || base != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Get the name of this FileResource relative to its base, if any.
+ * @return the name of this resource.
+ */
+ public String getName() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getName();
+ }
+ File b = getBase();
+ return b == null ? getNotNullFile().getAbsolutePath()
+ : FILE_UTILS.removeLeadingPath(b, getNotNullFile());
+ }
+
+ /**
+ * Learn whether this file exists.
+ * @return true if this resource exists.
+ */
+ public boolean isExists() {
+ return isReference() ? ((Resource) getCheckedRef()).isExists()
+ : getNotNullFile().exists();
+ }
+
+ /**
+ * Get the modification time in milliseconds since 01.01.1970 .
+ * @return 0 if the resource does not exist.
+ */
+ public long getLastModified() {
+ return isReference()
+ ? ((Resource) getCheckedRef()).getLastModified()
+ : getNotNullFile().lastModified();
+ }
+
+ /**
+ * Learn whether the resource is a directory.
+ * @return boolean flag indicating if the resource is a directory.
+ */
+ public boolean isDirectory() {
+ return isReference() ? ((Resource) getCheckedRef()).isDirectory()
+ : getNotNullFile().isDirectory();
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist.
+ */
+ public long getSize() {
+ return isReference() ? ((Resource) getCheckedRef()).getSize()
+ : getNotNullFile().length();
+ }
+
+ /**
+ * Return an InputStream for reading the contents of this Resource.
+ * @return an InputStream object.
+ * @throws IOException if an error occurs.
+ */
+ public InputStream getInputStream() throws IOException {
+ return isReference()
+ ? ((Resource) getCheckedRef()).getInputStream()
+ : new FileInputStream(getNotNullFile());
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ File f = getNotNullFile();
+ if (f.exists()) {
+ if (f.isFile()) {
+ f.delete();
+ }
+ } else {
+ File p = f.getParentFile();
+ if (p != null && !(p.exists())) {
+ p.mkdirs();
+ }
+ }
+ return new FileOutputStream(f);
+ }
+
+ /**
+ * Compare this FileResource to another Resource.
+ * @param another the other Resource against which to compare.
+ * @return a negative integer, zero, or a positive integer as this FileResource
+ * is less than, equal to, or greater than the specified Resource.
+ */
+ public int compareTo(Object another) {
+ if (isReference()) {
+ return ((Comparable) getCheckedRef()).compareTo(another);
+ }
+ return this.equals(another) ? 0 : super.compareTo(another);
+ }
+
+ /**
+ * Compare another Object to this FileResource for equality.
+ * @param another the other Object to compare.
+ * @return true if another is a FileResource representing the same file.
+ */
+ public boolean equals(Object another) {
+ if (this == another) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(another);
+ }
+ if (!(another.getClass().equals(getClass()))) {
+ return false;
+ }
+ FileResource otherfr = (FileResource) another;
+ return getFile() == null
+ ? otherfr.getFile() == null
+ : getFile().equals(otherfr.getFile());
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return MAGIC * (getFile() == null ? NULL_FILE : getFile().hashCode());
+ }
+
+ /**
+ * Get the string representation of this Resource.
+ * @return this FileResource formatted as a String.
+ */
+ public String toString() {
+ return isReference() ? getCheckedRef().toString()
+ : FILE_UTILS.normalize(file.getAbsolutePath()).getAbsolutePath();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this Resource is a FileResource.
+ */
+ public boolean isFilesystemOnly() {
+ return !isReference()
+ || ((FileResource) getCheckedRef()).isFilesystemOnly();
+ }
+
+ /**
+ * Implement the Touchable interface.
+ * @param modTime new last modification time.
+ */
+ public void touch(long modTime) {
+ if (isReference()) {
+ ((FileResource) getCheckedRef()).touch(modTime);
+ return;
+ }
+ getNotNullFile().setLastModified(modTime);
+ }
+
+ /**
+ * Get the file represented by this FileResource, ensuring it is not null.
+ * @return the not-null File.
+ * @throws BuildException if file is null.
+ */
+ protected File getNotNullFile() {
+ if (getFile() == null) {
+ throw new BuildException("file attribute is null!");
+ }
+ return getFile();
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java b/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java
new file mode 100755
index 000000000..d217cb5fe
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/FileResourceIterator.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+/**
+ * Iterator of FileResources from filenames.
+ * @since Ant 1.7
+ */
+public class FileResourceIterator implements Iterator {
+ private File basedir;
+ private String[] files;
+ private int pos = 0;
+
+ /**
+ * Construct a new FileResourceIterator.
+ */
+ public FileResourceIterator() {
+ }
+
+ /**
+ * Construct a new FileResourceIterator relative to the specified
+ * base directory.
+ * @param f the base directory of this instance.
+ */
+ public FileResourceIterator(File f) {
+ basedir = f;
+ }
+
+ /**
+ * Construct a new FileResourceIterator over the specified filenames,
+ * relative to the specified base directory.
+ * @param f the base directory of this instance.
+ * @param files the String[] of filenames.
+ */
+ public FileResourceIterator(File f, String[] s) {
+ this(f);
+ addFiles(s);
+ }
+
+ /**
+ * Add an array of filenames to this FileResourceIterator.
+ * @param s the filenames to add.
+ */
+ public void addFiles(String[] s) {
+ int start = (files == null) ? 0 : files.length;
+ String[] newfiles = new String[start + s.length];
+ if (start > 0) {
+ System.arraycopy(files, 0, newfiles, 0, start);
+ }
+ files = newfiles;
+ System.arraycopy(s, 0, files, start, s.length);
+ }
+
+ /**
+ * Find out whether this FileResourceIterator has more elements.
+ * @return whether there are more Resources to iterate over.
+ */
+ public boolean hasNext() {
+ return pos < files.length;
+ }
+
+ /**
+ * Get the next element from this FileResourceIterator.
+ * @return the next Object.
+ */
+ public Object next() {
+ return nextResource();
+ }
+
+ /**
+ * Not implemented.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Convenience method to return the next resource.
+ * @return the next File.
+ */
+ public FileResource nextResource() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return new FileResource(basedir, files[pos++]);
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Files.java b/src/main/org/apache/tools/ant/types/resources/Files.java
new file mode 100755
index 000000000..52a534c3a
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Files.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.io.File;
+import java.util.Stack;
+import java.util.Arrays;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.AbstractSelectorContainer;
+
+/**
+ * ResourceCollection implementation; like AbstractFileSet with absolute paths.
+ * @since Ant 1.7
+ */
+public class Files extends AbstractSelectorContainer
+ implements Cloneable, ResourceCollection {
+
+ private static final Iterator EMPTY_ITERATOR
+ = Collections.EMPTY_SET.iterator();
+
+ private PatternSet defaultPatterns = new PatternSet();
+ private Vector additionalPatterns = new Vector();
+ private Vector selectors = new Vector();
+
+ private boolean useDefaultExcludes = true;
+ private boolean caseSensitive = true;
+ private boolean followSymlinks = true;
+
+ /* cached DirectoryScanner instance */
+ private DirectoryScanner ds = null;
+
+ /**
+ * Construct a new Files
collection.
+ */
+ public Files() {
+ super();
+ }
+
+ /**
+ * Construct a new Files
collection, shallowly cloned
+ * from the specified Files
.
+ * @param f the Files
to use as a template.
+ */
+ protected Files(Files f) {
+ this.defaultPatterns = f.defaultPatterns;
+ this.additionalPatterns = f.additionalPatterns;
+ this.selectors = f.selectors;
+ this.useDefaultExcludes = f.useDefaultExcludes;
+ this.caseSensitive = f.caseSensitive;
+ this.followSymlinks = f.followSymlinks;
+ this.ds = f.ds;
+ setProject(f.getProject());
+ }
+
+ /**
+ * Make 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.
+ * @param r theReference
to use.
+ */
+ public void setRefid(Reference r) throws BuildException {
+ if (hasPatterns(defaultPatterns)) {
+ throw tooManyAttributes();
+ }
+ if (!additionalPatterns.isEmpty()) {
+ throw noChildrenAllowed();
+ }
+ if (!selectors.isEmpty()) {
+ throw noChildrenAllowed();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Create a nested patternset.
+ * @return PatternSet
.
+ */
+ public synchronized PatternSet createPatternSet() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ PatternSet patterns = new PatternSet();
+ additionalPatterns.addElement(patterns);
+ ds = null;
+ return patterns;
+ }
+
+ /**
+ * Add a name entry to the include list.
+ * @return PatternSet.NameEntry
.
+ */
+ public synchronized PatternSet.NameEntry createInclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createInclude();
+ }
+
+ /**
+ * Add a name entry to the include files list.
+ * @return PatternSet.NameEntry
.
+ */
+ public synchronized PatternSet.NameEntry createIncludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createIncludesFile();
+ }
+
+ /**
+ * Add a name entry to the exclude list.
+ * @return PatternSet.NameEntry
.
+ */
+ public synchronized PatternSet.NameEntry createExclude() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createExclude();
+ }
+
+ /**
+ * Add a name entry to the excludes files list.
+ * @return PatternSet.NameEntry
.
+ */
+ public synchronized PatternSet.NameEntry createExcludesFile() {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ ds = null;
+ return defaultPatterns.createExcludesFile();
+ }
+
+ /**
+ * Append includes
to the current list of include
+ * patterns.
+ *
+ * Patterns may be separated by a comma or a space.
+ * + * @param includes theString
containing the include patterns.
+ */
+ public synchronized void setIncludes(String includes) {
+ checkAttributesAllowed();
+ defaultPatterns.setIncludes(includes);
+ ds = null;
+ }
+
+ /**
+ * Append includes
to the current list of include
+ * patterns.
+ *
+ * @param includes array containing the include patterns.
+ */
+ public synchronized void appendIncludes(String[] includes) {
+ checkAttributesAllowed();
+ if (includes != null) {
+ for (int i = 0; i < includes.length; i++) {
+ defaultPatterns.createInclude().setName(includes[i]);
+ }
+ ds = null;
+ }
+ }
+
+ /**
+ * Append excludes
to the current list of exclude
+ * patterns.
+ *
+ * Patterns may be separated by a comma or a space.
+ * + * @param excludes theString
containing the exclude patterns.
+ */
+ public synchronized void setExcludes(String excludes) {
+ checkAttributesAllowed();
+ defaultPatterns.setExcludes(excludes);
+ ds = null;
+ }
+
+ /**
+ * Append excludes
to the current list of include
+ * patterns.
+ *
+ * @param excludes array containing the exclude patterns.
+ */
+ public synchronized void appendExcludes(String[] excludes) {
+ checkAttributesAllowed();
+ if (excludes != null) {
+ for (int i = 0; i < excludes.length; i++) {
+ defaultPatterns.createExclude().setName(excludes[i]);
+ }
+ ds = null;
+ }
+ }
+
+ /**
+ * Set the File
containing the includes patterns.
+ *
+ * @param incl File
instance.
+ */
+ public synchronized void setIncludesfile(File incl) throws BuildException {
+ checkAttributesAllowed();
+ defaultPatterns.setIncludesfile(incl);
+ ds = null;
+ }
+
+ /**
+ * Set the File
containing the excludes patterns.
+ *
+ * @param excl File
instance.
+ */
+ public synchronized void setExcludesfile(File excl) throws BuildException {
+ checkAttributesAllowed();
+ defaultPatterns.setExcludesfile(excl);
+ ds = null;
+ }
+
+ /**
+ * Set whether default exclusions should be used or not.
+ *
+ * @param useDefaultExcludes boolean
.
+ */
+ public synchronized void setDefaultexcludes(boolean useDefaultExcludes) {
+ checkAttributesAllowed();
+ this.useDefaultExcludes = useDefaultExcludes;
+ ds = null;
+ }
+
+ /**
+ * Get whether default exclusions should be used or not.
+ */
+ public synchronized boolean getDefaultexcludes() {
+ return (isReference())
+ ? getRef().getDefaultexcludes() : useDefaultExcludes;
+ }
+
+ /**
+ * Set case-sensitivity of the Files collection.
+ *
+ * @param caseSensitive boolean
.
+ */
+ public synchronized void setCaseSensitive(boolean caseSensitive) {
+ checkAttributesAllowed();
+ this.caseSensitive = caseSensitive;
+ ds = null;
+ }
+
+ /**
+ * Find out if this Files collection is case-sensitive.
+ *
+ * @return boolean
indicating whether the Files
+ * collection is case-sensitive.
+ */
+ public synchronized boolean isCaseSensitive() {
+ return (isReference())
+ ? getRef().isCaseSensitive() : caseSensitive;
+ }
+
+ /**
+ * Set whether or not symbolic links should be followed.
+ *
+ * @param followSymlinks whether or not symbolic links should be followed.
+ */
+ public synchronized void setFollowSymlinks(boolean followSymlinks) {
+ checkAttributesAllowed();
+ this.followSymlinks = followSymlinks;
+ ds = null;
+ }
+
+ /**
+ * Find out whether symbolic links should be followed.
+ *
+ * @return boolean
indicating whether symbolic links
+ * should be followed.
+ */
+ public synchronized boolean isFollowSymlinks() {
+ return (isReference())
+ ? getRef().isFollowSymlinks() : followSymlinks;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public synchronized Iterator iterator() {
+ if (isReference()) {
+ return getRef().iterator();
+ }
+ ensureDirectoryScannerSetup();
+ ds.scan();
+ int fct = ds.getIncludedFilesCount();
+ int dct = ds.getIncludedDirsCount();
+ if (fct + dct == 0) {
+ return EMPTY_ITERATOR;
+ }
+ FileResourceIterator result = new FileResourceIterator();
+ if (fct > 0) {
+ result.addFiles(ds.getIncludedFiles());
+ }
+ if (dct > 0) {
+ result.addFiles(ds.getIncludedDirectories());
+ }
+ return result;
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return getRef().size();
+ }
+ ensureDirectoryScannerSetup();
+ ds.scan();
+ return ds.getIncludedFilesCount() + ds.getIncludedDirsCount();
+ }
+
+ /**
+ * Find out whether this Files collection has patterns.
+ *
+ * @return whether any patterns are in this container.
+ */
+ public synchronized boolean hasPatterns() {
+ if (isReference()) {
+ return getRef().hasPatterns();
+ }
+ if (hasPatterns(defaultPatterns)) {
+ return true;
+ }
+ for (Iterator i = additionalPatterns.iterator(); i.hasNext();) {
+ if (hasPatterns((PatternSet) i.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add a new selector into this container.
+ *
+ * @param selector the new FileSelector
to add.
+ */
+ public synchronized void appendSelector(FileSelector selector) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ super.appendSelector(selector);
+ ds = null;
+ }
+
+ /**
+ * Format this Files collection as a String.
+ * @return a descriptive String
.
+ */
+ public String toString() {
+ if (isReference()) {
+ return getRef().toString();
+ }
+ Iterator i = iterator();
+ if (!i.hasNext()) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ while (i.hasNext()) {
+ if (sb.length() > 0) {
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(i.next());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Create a deep clone of this instance, except for the nested selectors
+ * (the list of selectors is a shallow clone of this instance's list).
+ * @return a cloned Object.
+ */
+ public synchronized Object clone() {
+ if (isReference()) {
+ return getRef().clone();
+ }
+ try {
+ Files f = (Files) super.clone();
+ f.defaultPatterns = (PatternSet) defaultPatterns.clone();
+ f.additionalPatterns = new Vector(additionalPatterns.size());
+ for (Iterator iter = additionalPatterns.iterator(); iter.hasNext();) {
+ PatternSet ps = (PatternSet) iter.next();
+ f.additionalPatterns.add(ps.clone());
+ }
+ f.selectors = new Vector(selectors);
+ return f;
+ } catch (CloneNotSupportedException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Get the merged include patterns for this Files collection.
+ * @param p Project instance.
+ * @return the include patterns of the default pattern set and all
+ * nested patternsets.
+ */
+ public String[] mergeIncludes(Project p) {
+ return mergePatterns(p).getIncludePatterns(p);
+ }
+
+ /**
+ * Get the merged exclude patterns for this Files collection.
+ * @param p Project instance.
+ * @return the exclude patterns of the default pattern set and all
+ * nested patternsets.
+ */
+ public String[] mergeExcludes(Project p) {
+ return mergePatterns(p).getExcludePatterns(p);
+ }
+
+ /**
+ * Get the merged patterns for this Files collection.
+ * @param p Project instance.
+ * @return the default patternset merged with the additional sets
+ * in a new PatternSet instance.
+ */
+ public synchronized PatternSet mergePatterns(Project p) {
+ if (isReference()) {
+ return getRef().mergePatterns(p);
+ }
+ PatternSet ps = new PatternSet();
+ ps.append(defaultPatterns, p);
+ final int count = additionalPatterns.size();
+ for (int i = 0; i < count; i++) {
+ Object o = additionalPatterns.elementAt(i);
+ ps.append((PatternSet) o, p);
+ }
+ return ps;
+ }
+
+ /**
+ * Always returns true.
+ * @return true indicating that all elements of a Files collection
+ * will be FileResources.
+ */
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Perform the check for circular references and return the
+ * referenced Files collection.
+ * @return FileCollection
.
+ */
+ protected Files getRef() {
+ return (Files) getCheckedRef();
+ }
+
+ private synchronized void ensureDirectoryScannerSetup() {
+ if (ds == null) {
+ ds = new DirectoryScanner();
+ PatternSet ps = mergePatterns(getProject());
+ ds.setIncludes(ps.getIncludePatterns(getProject()));
+ ds.setExcludes(ps.getExcludePatterns(getProject()));
+ ds.setSelectors(getSelectors(getProject()));
+ if (useDefaultExcludes) {
+ ds.addDefaultExcludes();
+ }
+ ds.setCaseSensitive(caseSensitive);
+ ds.setFollowSymlinks(followSymlinks);
+ }
+ }
+
+ private boolean hasPatterns(PatternSet ps) {
+ return ps.getIncludePatterns(getProject()).length > 0
+ || ps.getExcludePatterns(getProject()).length > 0;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java b/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java
new file mode 100755
index 000000000..e828440df
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/ImmutableResourceException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.types.resources;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when an attempt is made to get an OutputStream
+ * from an immutable Resource.
+ * @since Ant 1.7
+ */
+public class ImmutableResourceException extends IOException {
+
+ /**
+ * Default constructor.
+ */
+ public ImmutableResourceException() {
+ super();
+ }
+
+ /**
+ * Construct a new ImmutableResourceException with the specified message.
+ * @param s the message String.
+ */
+ public ImmutableResourceException(String s) {
+ super(s);
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Intersect.java b/src/main/org/apache/tools/ant/types/resources/Intersect.java
new file mode 100755
index 000000000..f1947e546
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Intersect.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection representing the intersection
+ * of multiple nested ResourceCollections.
+ * @since Ant 1.7
+ */
+public class Intersect extends BaseResourceCollectionContainer {
+
+ /**
+ * Calculate the intersection of the nested ResourceCollections.
+ * @return a Collection of Resources.
+ */
+ protected Collection getCollection() {
+ List rcs = getResourceCollections();
+ int size = rcs.size();
+ if (size < 2) {
+ throw new BuildException("The intersection of " + size
+ + " resource collection" + ((size == 1) ? "" : "s")
+ + " is undefined.");
+ }
+ ArrayList al = new ArrayList();
+ Iterator rc = rcs.iterator();
+ al.addAll(collect(rc.next()));
+ while (rc.hasNext()) {
+ al.retainAll(collect(rc.next()));
+ }
+ return al;
+ }
+
+ private ArrayList collect(Object o) {
+ ArrayList result = new ArrayList();
+ for (Iterator i = ((ResourceCollection) o).iterator(); i.hasNext();) {
+ result.add(i.next());
+ }
+ return result;
+ }
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/PropertyResource.java b/src/main/org/apache/tools/ant/types/resources/PropertyResource.java
new file mode 100755
index 000000000..b99b7eb64
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/PropertyResource.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.ByteArrayInputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.PropertyOutputStream;
+
+/**
+ * Exposes an Ant property as a Resource.
+ * @since Ant 1.7
+ */
+public class PropertyResource extends Resource {
+
+ /** Magic number */
+ private static final int PROPERTY_MAGIC
+ = Resource.getMagicNumber("PropertyResource".getBytes());
+
+ private static final InputStream UNSET = new InputStream() {
+ public int read() {
+ return -1;
+ }
+ };
+
+ /**
+ * Default constructor.
+ */
+ public PropertyResource() {
+ }
+
+ /**
+ * Construct a new PropertyResource with the specified name.
+ * @param n the String name of this PropertyResource (Ant property name/key).
+ */
+ public PropertyResource(Project p, String n) {
+ super(n);
+ setProject(p);
+ }
+
+ /**
+ * Get the value of this PropertyResource.
+ * @return the value of the specified Property.
+ */
+ public String getValue() {
+ Project p = getProject();
+ return p == null ? null : p.getProperty(getName());
+ }
+
+ /**
+ * Find out whether this Resource exists.
+ * @return true if the Property is set, false otherwise.
+ */
+ public boolean isExists() {
+ return getValue() != null;
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public long getSize() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getSize();
+ }
+ return isExists() ? (long) getValue().length() : 0L;
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return super.hashCode() * PROPERTY_MAGIC;
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ return isExists() ? new ByteArrayInputStream(getValue().getBytes()) : UNSET;
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ if (isExists()) {
+ throw new ImmutableResourceException();
+ }
+ return new PropertyOutputStream(getProject(), getName());
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Resources.java b/src/main/org/apache/tools/ant/types/resources/Resources.java
new file mode 100755
index 000000000..a20bb2bd5
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Resources.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.types.resources;
+
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.AbstractCollection;
+import java.util.NoSuchElementException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Generic ResourceCollection: Either stores nested ResourceCollections,
+ * making no attempt to remove duplicates, or references another ResourceCollection.
+ * @since Ant 1.7
+ */
+public class Resources extends DataType implements ResourceCollection {
+
+ private class MyCollection extends AbstractCollection {
+ int size;
+
+ MyCollection() {
+ size = 0;
+ for (Iterator rci = rc.iterator(); rci.hasNext();) {
+ size += ((ResourceCollection) rci.next()).size();
+ }
+ }
+ public int size() {
+ return size;
+ }
+ public Iterator iterator() {
+ return new MyIterator();
+ }
+ private class MyIterator implements Iterator {
+ Iterator rci = rc.iterator();
+ Iterator ri = null;
+ public boolean hasNext() {
+ if ((ri == null || !ri.hasNext()) && rci.hasNext()) {
+ ri = ((ResourceCollection) rci.next()).iterator();
+ }
+ return ri != null && ri.hasNext();
+ }
+ public Object next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return ri.next();
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ private Vector rc = null;
+ private Collection coll = null;
+
+ /**
+ * Add a ResourceCollection.
+ * @param c the ResourceCollection to add.
+ */
+ public synchronized void add(ResourceCollection c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ rc = (rc == null) ? new Vector() : rc;
+ rc.add(c);
+ FailFast.invalidate(this);
+ coll = null;
+ setChecked(false);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public synchronized Iterator iterator() {
+ if (isReference()) {
+ return getRef().iterator();
+ }
+ validate();
+ return new FailFast(this, coll.iterator());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return getRef().size();
+ }
+ validate();
+ return coll.size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return true if all Resources represent files.
+ */
+ public boolean isFilesystemOnly() {
+ if (isReference()) {
+ return getRef().isFilesystemOnly();
+ }
+ validate();
+ //first the easy way, if all children are filesystem-only, return true:
+ boolean goEarly = true;
+ for (Iterator i = rc.iterator(); goEarly && i.hasNext();) {
+ goEarly &= ((ResourceCollection) i.next()).isFilesystemOnly();
+ }
+ if (goEarly) {
+ return true;
+ }
+ /* now check each Resource in case the child only
+ lets through files from any children IT may have: */
+ for (Iterator i = coll.iterator(); i.hasNext();) {
+ if (!(i.next() instanceof FileResource)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Overrides the version of DataType to recurse on all DataType
+ * child elements that may have been added.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (Iterator i = rc.iterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof DataType) {
+ invokeCircularReferenceCheck((DataType) o, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+ /**
+ * Resolves references, allowing any ResourceCollection.
+ * @return the referenced ResourceCollection.
+ */
+ private ResourceCollection getRef() {
+ return (ResourceCollection) getCheckedRef(
+ ResourceCollection.class, "ResourceCollection");
+ }
+
+ private synchronized void validate() {
+ dieOnCircularReference();
+ if (rc == null || rc.size() == 0) {
+ throw new BuildException("Resources: no resources specified.");
+ }
+ coll = (coll == null) ? new MyCollection() : coll;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Restrict.java b/src/main/org/apache/tools/ant/types/resources/Restrict.java
new file mode 100755
index 000000000..fa591b49f
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Restrict.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.types.resources.selectors.ResourceSelectorContainer;
+
+/**
+ * ResourceCollection that allows a number of selectors to be
+ * applied to a single ResourceCollection for the purposes of
+ * restricting or narrowing results.
+ * @since Ant 1.7
+ */
+public class Restrict
+ extends ResourceSelectorContainer implements ResourceCollection {
+
+ private static final String ONE_NESTED_MESSAGE
+ = "Restriction is to be applied to exactly one nested resource collection.";
+
+ private ResourceCollection rc;
+
+ /**
+ * Add the ResourceCollection.
+ * @param c the ResourceCollection to add.
+ */
+ public synchronized void add(ResourceCollection c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (rc != null) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ rc = c;
+ }
+
+ /**
+ * Add a ResourceSelector.
+ * @param the ResourceSelector to add.
+ */
+ public synchronized void add(ResourceSelector s) {
+ super.add(s);
+ FailFast.invalidate(this);
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return an Iterator of Resources.
+ */
+ public final synchronized Iterator iterator() {
+ if (isReference()) {
+ return ((Restrict) getCheckedRef()).iterator();
+ }
+ dieOnCircularReference();
+ if (rc == null) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ return new FailFast(this, getCollection().iterator());
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return number of elements as int.
+ */
+ public synchronized int size() {
+ if (isReference()) {
+ return ((Restrict) getCheckedRef()).size();
+ }
+ dieOnCircularReference();
+ return getCollection().size();
+ }
+
+ /**
+ * Fulfill the ResourceCollection contract.
+ * @return whether this is a filesystem-only resource collection.
+ */
+ public synchronized boolean isFilesystemOnly() {
+ if (isReference()) {
+ return ((Restrict) getCheckedRef()).isFilesystemOnly();
+ }
+ dieOnCircularReference();
+ if (rc == null) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ //first the easy way, if child is filesystem-only, return true:
+ if (rc.isFilesystemOnly()) {
+ return true;
+ }
+ /* now check each Resource in case the child only
+ lets through files from any children IT may have: */
+ for (Iterator i = getCollection().iterator(); i.hasNext();) {
+ if (!(i.next() instanceof FileResource)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Restrict the nested ResourceCollection based on the nested selectors.
+ * @return a Collection of Resources.
+ */
+ protected Collection getCollection() {
+ ArrayList result = new ArrayList();
+outer: for (Iterator ri = rc.iterator(); ri.hasNext();) {
+ Resource r = (Resource) ri.next();
+ for (Iterator i = getSelectors(); i.hasNext();) {
+ if (!((ResourceSelector) (i.next())).isSelected(r)) {
+ continue outer;
+ }
+ }
+ result.add(r);
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Sort.java b/src/main/org/apache/tools/ant/types/resources/Sort.java
new file mode 100755
index 000000000..79dbe31b5
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Sort.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.util.List;
+import java.util.Stack;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.comparators.ResourceComparator;
+
+/**
+ * ResourceCollection that sorts another ResourceCollection.
+ * @since Ant 1.7
+ */
+public class Sort extends BaseResourceCollectionContainer {
+ private static final String ONE_NESTED_MESSAGE
+ = "Sorting is to be applied to exactly one nested resource collection.";
+
+ private Stack compStack = new Stack();
+
+ /**
+ * Sort the contained elements.
+ * @return a Collection of Resources.
+ */
+ protected Collection getCollection() {
+ List rcs = getResourceCollections();
+ if (rcs.size() != 1) {
+ throw new BuildException(ONE_NESTED_MESSAGE);
+ }
+ Iterator nested = ((ResourceCollection) (rcs.get(0))).iterator();
+ if (!(nested.hasNext())) {
+ return Collections.EMPTY_SET;
+ }
+ ArrayList al = new ArrayList();
+ while (nested.hasNext()) {
+ al.add(nested.next());
+ }
+ if (compStack.empty()) {
+ Collections.sort(al);
+ } else {
+ for (Stack s = (Stack) compStack.clone(); !s.empty();) {
+ Collections.sort(al, (ResourceComparator) s.pop());
+ }
+ }
+ return al;
+ }
+
+ /**
+ * Add a ResourceComparator to this Sort ResourceCollection.
+ * If multiple ResourceComparator are added, they will be processed in LIFO order.
+ * @param c the ResourceComparator to add.
+ */
+ public void add(ResourceComparator c) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ compStack.push(c);
+ }
+
+ /**
+ * Overrides the BaseResourceCollectionContainer version
+ * to recurse on nested ResourceComparators.
+ * @param stk the stack of data types to use (recursively).
+ * @param p the project to use to dereference the references.
+ * @throws BuildException on error.
+ */
+ protected void dieOnCircularReference(Stack stk, Project p)
+ throws BuildException {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (Iterator i = compStack.iterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof DataType) {
+ stk.push(o);
+ invokeCircularReferenceCheck((DataType) o, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/StringResource.java b/src/main/org/apache/tools/ant/types/resources/StringResource.java
new file mode 100755
index 000000000..41cf93370
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/StringResource.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.types.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FilterOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Exposes a string as a Resource.
+ * @since Ant 1.7
+ */
+public class StringResource extends Resource {
+
+ /** Magic number */
+ private static final int STRING_MAGIC
+ = Resource.getMagicNumber("StringResource".getBytes());
+
+ private String encoding = null;
+
+ /**
+ * Default constructor.
+ */
+ public StringResource() {
+ }
+
+ /**
+ * Construct a StringResource with the supplied value.
+ * @param value the value of this StringResource.
+ */
+ public StringResource(String value) {
+ setValue(value);
+ }
+
+ /**
+ * Enforce String immutability.
+ * @param s the new name/value for this StringResource.
+ */
+ public synchronized void setName(String s) {
+ if (getName() != null) {
+ throw new BuildException(new ImmutableResourceException());
+ }
+ super.setName(s);
+ }
+
+ /**
+ * The value attribute is a semantically superior alias for the name attribute.
+ * @param s the String's value.
+ */
+ public synchronized void setValue(String s) {
+ setName(s);
+ }
+
+ /**
+ * Synchronize access.
+ * @return the name/value of this StringResource.
+ */
+ public synchronized String getName() {
+ return super.getName();
+ }
+
+ /**
+ * Get the value of this StringResource.
+ * @return the represented String.
+ */
+ public synchronized String getValue() {
+ return getName();
+ }
+
+ /**
+ * Set the encoding to be used for this StringResource.
+ * @param s the encoding name.
+ */
+ public synchronized void setEncoding(String s) {
+ encoding = s;
+ }
+
+ /**
+ * Get the encoding used by this StringResource.
+ * @return the encoding name.
+ */
+ public synchronized String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public synchronized long getSize() {
+ return isReference()
+ ? ((Resource) getCheckedRef()).getSize()
+ : (long) getContent().length();
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public synchronized int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return super.hashCode() * STRING_MAGIC;
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ //I can't get my head around this; is encoding treatment needed here?
+ return
+ //new oata.util.ReaderInputStream(new InputStreamReader(
+ new ByteArrayInputStream(getContent().getBytes())
+ //, encoding), encoding)
+ ;
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ if (getValue() != null) {
+ throw new ImmutableResourceException();
+ }
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ return new FilterOutputStream(baos) {
+ public void close() throws IOException {
+ super.close();
+ StringResource.this.setValue(encoding == null
+ ? baos.toString() : baos.toString(encoding));
+ }
+ };
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (encoding != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Get the content of this StringResource.
+ * @return a String; if the Project has been set properties
+ * replacement will be attempted.
+ */
+ protected synchronized String getContent() {
+ if (isReference()) {
+ return ((StringResource) getCheckedRef()).getContent();
+ }
+ String value = getValue();
+ if (value == null) {
+ return value;
+ }
+ return getProject() == null
+ ? value : getProject().replaceProperties(value);
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Touchable.java b/src/main/org/apache/tools/ant/types/resources/Touchable.java
new file mode 100755
index 000000000..9960678e2
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Touchable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+/**
+ * Interface to be implemented by "touchable" resources; i.e. those
+ * whose modification time can be altered.
+ * @since Ant 1.7
+ */
+public interface Touchable {
+ void touch(long modTime);
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/URLResource.java b/src/main/org/apache/tools/ant/types/resources/URLResource.java
new file mode 100755
index 000000000..df8e4f2c7
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/URLResource.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Exposes a URL as a Resource.
+ * @since Ant 1.7
+ */
+public class URLResource extends Resource {
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+ private static final int NULL_URL
+ = Resource.getMagicNumber("null URL".getBytes());
+
+ private URL url;
+ private String javaResource;
+ private URLConnection conn;
+ private Path classpath;
+
+ /**
+ * Default constructor.
+ */
+ public URLResource() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param u the URL to expose.
+ */
+ public URLResource(URL u) {
+ setURL(u);
+ }
+
+ /**
+ * Convenience constructor.
+ * @param f the File to set as a URL.
+ */
+ public URLResource(File f) {
+ setFile(f);
+ }
+
+ /**
+ * String constructor for Ant attribute introspection.
+ * @param u String representation of this URL.
+ * @see org.apache.tools.ant.IntrospectionHelper
+ */
+ public URLResource(String u) {
+ this(newURL(u));
+ }
+
+ /**
+ * Set the URL for this URLResource.
+ * @param u the URL to expose.
+ */
+ public synchronized void setURL(URL u) {
+ checkAttributesAllowed();
+ url = u;
+ }
+
+ /**
+ * Set the URL from a File.
+ * @param f the File to set as a URL.
+ */
+ public synchronized void setFile(File f) {
+ try {
+ setURL(FILE_UTILS.getFileURL(f));
+ } catch (MalformedURLException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ /**
+ * Set the resource name with which to expose a Java resource.
+ * @param s the Java resource name.
+ * @see java.lang.ClassLoader#getResource()
+ */
+ public synchronized void setJavaResource(String s) {
+ checkAttributesAllowed();
+ javaResource = s;
+ }
+
+ /**
+ * Set the classpath for this URLResource.
+ * @param p the Path against which to resolve Java resources.
+ */
+ public synchronized void setClasspath(Path p) {
+ checkAttributesAllowed();
+ addConfiguredClasspath(p);
+ }
+
+ /**
+ * Create a nested classpath element.
+ * @return a Path object.
+ */
+ public synchronized void addConfiguredClasspath(Path p) {
+ checkChildrenAllowed();
+ if (classpath == null) {
+ classpath = new Path(getProject());
+ }
+ classpath.add(p);
+ }
+
+ /**
+ * Get the URL used by this URLResource.
+ * @return a URL object.
+ */
+ public synchronized URL getURL() {
+ if (isReference()) {
+ return ((URLResource) getCheckedRef()).getURL();
+ }
+ if (url == null && javaResource != null) {
+ ClassLoader cl = null;
+ AntClassLoader acl = null;
+ if (classpath != null) {
+ acl = getProject().createClassLoader(classpath);
+ cl = acl;
+ } else {
+ cl = getClass().getClassLoader();
+ if (cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ }
+ if (cl != null) {
+ setURL(cl.getResource(javaResource));
+ if (acl != null) {
+ acl.cleanup();
+ }
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public synchronized void setRefid(Reference r) {
+ //not using the accessor in this case to avoid side effects
+ if (url != null || javaResource != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Get the name of this URLResource
+ * (its file component minus the leading separator).
+ * @return the name of this resource.
+ */
+ public synchronized String getName() {
+ return isReference() ? ((Resource) getCheckedRef()).getName()
+ : getURL().getFile().substring(1);
+ }
+
+ /**
+ * Return this URLResource formatted as a String.
+ * @return a String representation of this URLResource.
+ */
+ public synchronized String toString() {
+ return isReference()
+ ? getCheckedRef().toString() : String.valueOf(getURL());
+ }
+
+ /**
+ * Find out whether the URL exists .
+ * @return true if this resource exists.
+ */
+ public synchronized boolean isExists() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isExists();
+ }
+ if (getURL() == null) {
+ return false;
+ }
+ try {
+ connect();
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Tells the modification time in milliseconds since 01.01.1970 .
+ *
+ * @return 0 if the resource does not exist to mirror the behavior
+ * of {@link java.io.File File}.
+ */
+ public synchronized long getLastModified() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getLastModified();
+ }
+ if (!isExists()) {
+ return 0L;
+ }
+ try {
+ connect();
+ return conn.getLastModified();
+ } catch (IOException e) {
+ return 0L;
+ }
+ }
+
+ /**
+ * Tells if the resource is a directory.
+ * @return boolean whether the resource is a directory.
+ */
+ public synchronized boolean isDirectory() {
+ return isReference()
+ ? ((Resource) getCheckedRef()).isDirectory()
+ : getName().endsWith("/");
+ }
+
+ /**
+ * Get the size of this Resource.
+ * @return the size, as a long, 0 if the Resource does not exist (for
+ * compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+ */
+ public synchronized long getSize() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getSize();
+ }
+ if (!isExists()) {
+ return 0L;
+ }
+ try {
+ connect();
+ return conn.getContentLength();
+ } catch (IOException e) {
+ return UNKNOWN_SIZE;
+ }
+ }
+
+ /**
+ * Test whether an Object equals this URLResource.
+ * @param another the other Object to compare.
+ * @return true if the specified Object is equal to this Resource.
+ */
+ public synchronized boolean equals(Object another) {
+ if (this == another) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(another);
+ }
+ if (!(another.getClass().equals(getClass()))) {
+ return false;
+ }
+ URLResource otheru = (URLResource) another;
+ return getURL() == null
+ ? otheru.getURL() == null
+ : getURL().equals(otheru.getURL());
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public synchronized int hashCode() {
+ if (isReference()) {
+ return getCheckedRef().hashCode();
+ }
+ return MAGIC * ((getURL() == null) ? NULL_URL : getURL().hashCode());
+ }
+
+ /**
+ * Get an InputStream for the Resource.
+ * @return an InputStream containing this Resource's content.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if InputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ connect();
+ try {
+ return conn.getInputStream();
+ } finally {
+ conn = null;
+ }
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public synchronized OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ connect();
+ try {
+ return conn.getOutputStream();
+ } finally {
+ conn = null;
+ }
+ }
+
+ /**
+ * Ensure that we have a connection.
+ */
+ protected synchronized void connect() throws IOException {
+ URL u = getURL();
+ if (u == null) {
+ throw new BuildException("URL not set");
+ }
+ if (conn == null) {
+ try {
+ conn = u.openConnection();
+ conn.connect();
+ } catch (IOException e) {
+ log(e.toString(), Project.MSG_ERR);
+ conn = null;
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Finalize this URLResource.
+ * @throws Throwable on error.
+ */
+ protected void finalize() throws Throwable {
+ conn = null;
+ super.finalize();
+ }
+
+ private static URL newURL(String u) {
+ try {
+ return new URL(u);
+ } catch (MalformedURLException e) {
+ throw new BuildException(e);
+ }
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/Union.java b/src/main/org/apache/tools/ant/types/resources/Union.java
new file mode 100755
index 000000000..11c3a6703
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/Union.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * ResourceCollection representing the union of multiple nested ResourceCollections.
+ * @since Ant 1.7
+ */
+public class Union extends BaseResourceCollectionContainer {
+
+ /**
+ * Returns all Resources in String format. Moved up from
+ * Path for convenience.
+ * @return String array of Resources.
+ */
+ public String[] list() {
+ if (isReference()) {
+ return ((Union) getCheckedRef()).list();
+ }
+ Collection result = getCollection(true);
+ return (String[]) (result.toArray(new String[result.size()]));
+ }
+
+ /**
+ * Unify the contained Resources.
+ * @return a Collection of Resources.
+ */
+ protected Collection getCollection() {
+ return getCollection(false);
+ }
+
+ /**
+ * Unify the contained Resources.
+ * @param asString indicates whether the resulting Collection
+ * should contain Strings instead of Resources.
+ * @return a Collection of Resources.
+ */
+ protected Collection getCollection(boolean asString) {
+ List rc = getResourceCollections();
+ if (rc.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+ //preserve order-encountered using a list; enforce set logic manually:
+ ArrayList union = new ArrayList(rc.size() * 2);
+ for (Iterator rcIter = rc.iterator(); rcIter.hasNext();) {
+ for (Iterator r = nextRC(rcIter).iterator(); r.hasNext();) {
+ Object o = r.next();
+ if (asString) {
+ o = o.toString();
+ }
+ if (!(union.contains(o))) {
+ union.add(o);
+ }
+ }
+ }
+ return union;
+ }
+
+ private static ResourceCollection nextRC(Iterator i) {
+ return (ResourceCollection) i.next();
+ }
+}
+
diff --git a/src/main/org/apache/tools/ant/types/resources/ZipResource.java b/src/main/org/apache/tools/ant/types/resources/ZipResource.java
new file mode 100755
index 000000000..debbe76dc
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/ZipResource.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.FilterInputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipFile;
+import org.apache.tools.zip.ZipEntry;
+
+/**
+ * A Resource representation of an entry in a zipfile.
+ * @since Ant 1.7
+ */
+public class ZipResource extends Resource {
+ private static final int NULL_ZIPFILE
+ = Resource.getMagicNumber("null zipfile".getBytes());
+
+ private String encoding;
+ private File zipfile;
+ private boolean haveEntry = false;
+
+ /**
+ * Default constructor.
+ */
+ public ZipResource() {
+ }
+
+ /**
+ * Construct a ZipResource representing the specified
+ * entry in the specified zipfile.
+ * @param z the zipfile as File.
+ * @param enc the encoding used for filenames.
+ * @param e the ZipEntry.
+ */
+ public ZipResource(File z, String enc, ZipEntry e) {
+ setEntry(e);
+ setZipfile(z);
+ setEncoding(enc);
+ }
+
+ /**
+ * Set the zipfile that holds this ZipResource.
+ * @param z the zipfile as a File.
+ */
+ public void setZipfile(File z) {
+ checkAttributesAllowed();
+ zipfile = z;
+ }
+
+ /**
+ * Get the zipfile that holds this ZipResource.
+ * @return the zipfile as a File.
+ */
+ public File getZipfile() {
+ return isReference()
+ ? ((ZipResource) getCheckedRef()).getZipfile() : zipfile;
+ }
+
+ /**
+ * Set the encoding to use with the zipfile.
+ * @param enc the String encoding.
+ */
+ public void setEncoding(String enc) {
+ checkAttributesAllowed();
+ encoding = enc;
+ }
+
+ /**
+ * Get the encoding to use with the zipfile.
+ * @return String encoding.
+ */
+ public String getEncoding() {
+ return isReference()
+ ? ((ZipResource) getCheckedRef()).getEncoding() : encoding;
+ }
+
+ /**
+ * Get the last modified date of this ZipResource.
+ * @return the last modification date.
+ */
+ public long getLastModified() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getLastModified();
+ }
+ checkEntry();
+ return super.getLastModified();
+ }
+
+ /**
+ * Get the size of this ZipResource.
+ * @return the long size of this ZipResource.
+ */
+ public long getSize() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getSize();
+ }
+ checkEntry();
+ return super.getSize();
+ }
+
+ /**
+ * Learn whether this ZipResource represents a directory.
+ * @return boolean flag indicating whether the zip entry is a directory.
+ */
+ public boolean isDirectory() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isDirectory();
+ }
+ checkEntry();
+ return super.isDirectory();
+ }
+
+ /**
+ * Find out whether this ZipResource represents an existing Resource.
+ * @return boolean existence flag.
+ */
+ public boolean isExists() {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).isExists();
+ }
+ checkEntry();
+ return super.isExists();
+ }
+
+ /**
+ * Overrides the super version.
+ * @param r the Reference to set.
+ */
+ public void setRefid(Reference r) {
+ if (encoding != null || zipfile != null) {
+ throw tooManyAttributes();
+ }
+ super.setRefid(r);
+ }
+
+ /**
+ * Return an InputStream for reading the contents of this Resource.
+ * @return an InputStream object.
+ * @throws IOException if the zip file cannot be opened,
+ * or the entry cannot be read.
+ */
+ public InputStream getInputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getInputStream();
+ }
+ final ZipFile z = new ZipFile(getZipfile(), getEncoding());
+ return new FilterInputStream(z.getInputStream(z.getEntry(getName()))) {
+ public void close() throws IOException {
+ FileUtils.close(in);
+ z.close();
+ }
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ }
+
+ /**
+ * Get an OutputStream for the Resource.
+ * @return an OutputStream to which content can be written.
+ * @throws IOException if unable to provide the content of this
+ * Resource as a stream.
+ * @throws UnsupportedOperationException if OutputStreams are not
+ * supported for this Resource type.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ if (isReference()) {
+ return ((Resource) getCheckedRef()).getOutputStream();
+ }
+ throw new UnsupportedOperationException(
+ "Use the zip task for zip output.");
+ }
+
+ /**
+ * Compare this ZipResource to another Resource.
+ * @param another the other Resource against which to compare.
+ * @return a negative integer, zero, or a positive integer as this ZipResource
+ * is less than, equal to, or greater than the specified Resource.
+ */
+ public int compareTo(Object another) {
+ return this.equals(another) ? 0 : super.compareTo(another);
+ }
+
+ /**
+ * Compare another Object to this ZipResource for equality.
+ * @param another the other Object to compare.
+ * @return true if another is a ZipResource representing
+ * the same entry in the same zipfile.
+ */
+ public boolean equals(Object another) {
+ if (this == another) {
+ return true;
+ }
+ if (isReference()) {
+ return getCheckedRef().equals(another);
+ }
+ if (!(another.getClass().equals(getClass()))) {
+ return false;
+ }
+ ZipResource r = (ZipResource) another;
+ return getZipfile().equals(r.getZipfile())
+ && getName().equals(r.getName());
+ }
+
+ /**
+ * Get the hash code for this Resource.
+ * @return hash code as int.
+ */
+ public int hashCode() {
+ return super.hashCode()
+ * (getZipfile() == null ? NULL_ZIPFILE : getZipfile().hashCode());
+ }
+
+ /**
+ * Format this ZipResource as a String.
+ * @return String representatation of this ZipResource.
+ */
+ public String toString() {
+ return isReference() ? getCheckedRef().toString()
+ : getZipfile().toString() + ':' + getName();
+ }
+
+ private synchronized void checkEntry() throws BuildException {
+ if (haveEntry) {
+ return;
+ }
+ String name = getName();
+ if (name == null) {
+ throw new BuildException("zip entry name not set");
+ }
+ File f = getZipfile();
+ if (f == null) {
+ throw new BuildException("zipfile attribute not set");
+ }
+ if (!f.exists()) {
+ throw new BuildException(f.getAbsolutePath() + " does not exist.");
+ }
+ if (f.isDirectory()) {
+ throw new BuildException(f + " denotes a directory.");
+ }
+ ZipFile z = null;
+ try {
+ z = new ZipFile(f, getEncoding());
+ setEntry(z.getEntry(name));
+ } catch (IOException e) {
+ log(e.getMessage(), Project.MSG_DEBUG);
+ throw new BuildException(e);
+ } finally {
+ if (z != null) {
+ try {
+ z.close();
+ } catch (IOException e) {
+ //?
+ }
+ }
+ }
+ }
+
+ private synchronized void setEntry(ZipEntry e) {
+ haveEntry = true;
+ if (e == null) {
+ super.setExists(false);
+ return;
+ }
+ super.setName(e.getName());
+ super.setExists(true);
+ super.setLastModified(e.getTime());
+ super.setDirectory(e.isDirectory());
+ super.setSize(e.getSize());
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Content.java b/src/main/org/apache/tools/ant/types/resources/comparators/Content.java
new file mode 100755
index 000000000..d212cf037
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Content.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Compares Resources by content.
+ * @since Ant 1.7
+ */
+public class Content extends ResourceComparator {
+
+ private boolean binary = true;
+
+ /**
+ * Set binary mode for this Content ResourceComparator. If this
+ * attribute is set to false, Resource content will be compared
+ * ignoring platform line-ending conventions.
+ * Default is true
.
+ * @param b whether to compare content in binary mode.
+ */
+ public void setBinary(boolean b) {
+ binary = b;
+ }
+
+ /**
+ * Learn whether this Content ResourceComparator is operating in binary mode.
+ * @return boolean binary flag.
+ */
+ public boolean isBinary() {
+ return binary;
+ }
+
+ /**
+ * Compare two Resources by content.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws BuildException if I/O errors occur.
+ * @see org.apache.tools.ant.util.FileUtils#compareContent(Resource, Resource, boolean).
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ try {
+ return FileUtils.getFileUtils().compareContent(foo, bar, !binary);
+ } catch (IOException e) {
+ throw new BuildException(e);
+ }
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Date.java b/src/main/org/apache/tools/ant/types/resources/comparators/Date.java
new file mode 100755
index 000000000..e8aee0486
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Date.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by last modification date.
+ * @since Ant 1.7
+ */
+public class Date extends ResourceComparator {
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ return (int) (foo.getLastModified() - bar.getLastModified());
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java b/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java
new file mode 100755
index 000000000..1930a334d
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Exists.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by existence. Not existing is "less than" existing.
+ * @since Ant 1.7
+ */
+public class Exists extends ResourceComparator {
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ boolean f = foo.isExists();
+ if (f == bar.isExists()) {
+ return 0;
+ }
+ return f ? 1 : -1;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Name.java b/src/main/org/apache/tools/ant/types/resources/comparators/Name.java
new file mode 100755
index 000000000..d7e5ec787
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Name.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by name.
+ * @since Ant 1.7
+ */
+public class Name extends ResourceComparator {
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ return foo.getName().compareTo(bar.getName());
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java b/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java
new file mode 100755
index 000000000..6f7b4bf66
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/ResourceComparator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import java.util.Comparator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Abstract Resource Comparator.
+ * @since Ant 1.7
+ */
+public abstract class ResourceComparator implements Comparator {
+
+ /**
+ * Compare two objects.
+ * @param foo the first Object.
+ * @param bar the second Object.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws ClassCastException if either argument is null.
+ */
+ public final int compare(Object foo, Object bar) {
+ return resourceCompare((Resource) foo, (Resource) bar);
+ }
+
+ /**
+ * Test for equality with this ResourceComparator.
+ * @param o the Object to compare against.
+ * @return true if the specified Object equals this one.
+ */
+ public final boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ return o == this || o.getClass().equals(getClass());
+ }
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected abstract int resourceCompare(Resource foo, Resource bar);
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java b/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java
new file mode 100755
index 000000000..1ff90af4f
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Reverse.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Reverses another ResourceComparator. If no nested ResourceComparator
+ * is supplied, the compared Resources' natural order will be reversed.
+ * @since Ant 1.7
+ */
+public class Reverse extends ResourceComparator {
+ private static final String ONE_NESTED
+ = "You must not nest more than one ResourceComparator for reversal.";
+
+ private ResourceComparator nested;
+
+ /**
+ * Add the ResourceComparator to reverse.
+ * @param nested the ResourceComparator to add.
+ */
+ public void add(ResourceComparator c) {
+ if (nested != null) {
+ throw new BuildException(ONE_NESTED);
+ }
+ nested = c;
+ }
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is greater than, equal to, or less than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ return -1 * (nested == null
+ ? foo.compareTo(bar) : nested.compare(foo, bar));
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Size.java b/src/main/org/apache/tools/ant/types/resources/comparators/Size.java
new file mode 100755
index 000000000..faf13e28f
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Size.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by size.
+ * @since Ant 1.7
+ */
+public class Size extends ResourceComparator {
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ return (int) (foo.getSize() - bar.getSize());
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/comparators/Type.java b/src/main/org/apache/tools/ant/types/resources/comparators/Type.java
new file mode 100755
index 000000000..5a400cd00
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/comparators/Type.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.comparators;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Compares Resources by is-directory status. As a container
+ * of files, a directory is deemed "greater" than a file.
+ * @since Ant 1.7
+ */
+public class Type extends ResourceComparator {
+
+ /**
+ * Compare two Resources.
+ * @param foo the first Resource.
+ * @param bar the second Resource.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ */
+ protected int resourceCompare(Resource foo, Resource bar) {
+ boolean f = foo.isDirectory();
+ if (f == bar.isDirectory()) {
+ return 0;
+ }
+ return f ? 1 : -1;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/And.java b/src/main/org/apache/tools/ant/types/resources/selectors/And.java
new file mode 100755
index 000000000..d91aadb70
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/And.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * And ResourceSelector.
+ * @since Ant 1.7
+ */
+public class And extends ResourceSelectorContainer implements ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ for (Iterator i = getSelectors(); i.hasNext();) {
+ if (!((ResourceSelector) i.next()).isSelected(r)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Date.java b/src/main/org/apache/tools/ant/types/resources/selectors/Date.java
new file mode 100755
index 000000000..2ecb13276
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Date.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.TimeComparison;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Date ResourceSelector. Based on the date FileSelector, with the most
+ * notable difference being the lack of support for the includedirs attribute.
+ * It is recommended that the effect of includeDirs = "false" be achieved for
+ * resources by enclosing a "dir" Type ResourceSelector and a Date
+ * ResourceSelector in an Or ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Date implements ResourceSelector {
+ private static final String MILLIS_OR_DATETIME
+ = "Either the millis or the datetime attribute must be set.";
+ private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+ private Long millis = null;
+ private String dateTime = null;
+ private String pattern = null;
+ private TimeComparison when = TimeComparison.EQUAL;
+ private long granularity = FILE_UTILS.getFileTimestampGranularity();
+
+ /**
+ * Set the date/time in milliseconds since 1970.
+ * @param m the number of millis.
+ */
+ public synchronized void setMillis(long m) {
+ millis = new Long(m);
+ }
+
+ /**
+ * Get the date/time in ms.
+ * @return long number of millis since 1970.
+ */
+ public synchronized long getMillis() {
+ return millis == null ? -1L : millis.longValue();
+ }
+
+ /**
+ * Set the date and time as a String.
+ * @param s the date & time to use.
+ */
+ public synchronized void setDateTime(String s) {
+ dateTime = s;
+ millis = null;
+ }
+
+ /**
+ * Get the date & time in String format.
+ * @return a String representing a date & time.
+ */
+ public synchronized String getDatetime() {
+ return dateTime;
+ }
+
+ /**
+ * Set the granularity to use for this ResourceSelector.
+ * @param g the timestamp granularity.
+ */
+ public synchronized void setGranularity(long g) {
+ granularity = g;
+ }
+
+ /**
+ * Get the timestamp granularity used by this ResourceSelector.
+ * @return the long granularity.
+ */
+ public synchronized long getGranularity() {
+ return granularity;
+ }
+
+ /**
+ * Set the optional pattern to use with the datetime attribute.
+ * @param p the SimpleDateFormat-compatible pattern string.
+ */
+ public synchronized void setPattern(String p) {
+ pattern = p;
+ }
+
+ /**
+ * Get the pattern for use with the datetime attribute.
+ * @return a SimpleDateFormat-compatible pattern string.
+ */
+ public synchronized String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * Set the comparison mode.
+ * @param c a TimeComparison object.
+ */
+ public synchronized void setWhen(TimeComparison c) {
+ when = c;
+ }
+
+ /**
+ * Get the comparison mode.
+ * @return a TimeComparison object.
+ */
+ public synchronized TimeComparison getWhen() {
+ return when;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public synchronized boolean isSelected(Resource r) {
+ if (dateTime == null && millis == null) {
+ throw new BuildException(MILLIS_OR_DATETIME);
+ }
+ if (millis == null) {
+ DateFormat df = ((pattern == null)
+ ? DateFormat.getDateTimeInstance(
+ DateFormat.SHORT, DateFormat.SHORT, Locale.US)
+ : new SimpleDateFormat(pattern));
+ try {
+ long m = df.parse(dateTime).getTime();
+ if (m < 0) {
+ throw new BuildException("Date of " + dateTime
+ + " results in negative milliseconds value"
+ + " relative to epoch (January 1, 1970, 00:00:00 GMT).");
+ }
+ setMillis(m);
+ } catch (ParseException pe) {
+ throw new BuildException("Date of " + dateTime
+ + " Cannot be parsed correctly. It should be in"
+ + (pattern == null
+ ? " MM/DD/YYYY HH:MM AM_PM" : pattern) + " format.");
+ }
+ }
+ return when.evaluate(r.getLastModified(), millis.longValue(), granularity);
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java b/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java
new file mode 100755
index 000000000..1bf01a0e1
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Exists.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Exists ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Exists implements ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ return r.isExists();
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java b/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java
new file mode 100755
index 000000000..9c4bb9f40
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/InstanceOf.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * InstanceOf ResourceSelector.
+ * @since Ant 1.7
+ */
+public class InstanceOf implements ResourceSelector {
+ private static final String ONE_ONLY = "Exactly one of class|type must be set.";
+
+ private Project project;
+ private Class clazz;
+ private String type;
+ private String uri;
+
+ /**
+ * Set the Project instance for this InstanceOf selector.
+ * @param p the Project instance used for type comparisons.
+ */
+ public void setProject(Project p) {
+ project = p;
+ }
+
+ /**
+ * Set the class to compare against.
+ * @param c the class.
+ */
+ public void setClass(Class c) {
+ if (clazz != null) {
+ throw new BuildException("The class attribute has already been set.");
+ }
+ clazz = c;
+ }
+
+ /**
+ * Set the Ant type to compare against.
+ * @param s the type name.
+ */
+ public void setType(String s) {
+ type = s;
+ }
+
+ /**
+ * Set the URI in which the Ant type, if specified, should be defined.
+ * @param u the URI.
+ */
+ public void setURI(String u) {
+ uri = u;
+ }
+
+ /**
+ * Get the comparison class.
+ * @return the Class object.
+ */
+ public Class getCheckClass() {
+ return clazz;
+ }
+
+ /**
+ * Get the comparison type.
+ * @return the String typename.
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Get the type's URI.
+ * @return the String URI.
+ */
+ public String getURI() {
+ return uri;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ * @throws BuildException if an error occurs.
+ */
+ public boolean isSelected(Resource r) {
+ if ((clazz == null) == (type == null)) {
+ throw new BuildException(ONE_ONLY);
+ }
+ Class c = clazz;
+ if (type != null) {
+ if (project == null) {
+ throw new BuildException(
+ "No project set for InstanceOf ResourceSelector; "
+ + "the type attribute is invalid.");
+ }
+ AntTypeDefinition d = ComponentHelper.getComponentHelper(
+ project).getDefinition(ProjectHelper.genComponentName(uri, type));
+ if (d == null) {
+ throw new BuildException("type " + type + " not found.");
+ }
+ try {
+ c = d.innerGetTypeClass();
+ } catch (ClassNotFoundException e) {
+ throw new BuildException(e);
+ }
+ }
+ return c.isAssignableFrom(r.getClass());
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java b/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java
new file mode 100755
index 000000000..99846158b
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Majority.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Majority ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Majority
+ extends ResourceSelectorContainer implements ResourceSelector {
+
+ private boolean tie = true;
+
+ /**
+ * Set whether ties are allowed.
+ * @param b whether a tie is a pass.
+ */
+ public synchronized void setAllowtie(boolean b) {
+ tie = b;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public synchronized boolean isSelected(Resource r) {
+ int passed = 0;
+ int failed = 0;
+ int count = selectorCount();
+ boolean even = count % 2 == 0;
+ int threshold = count / 2;
+
+ for (Iterator i = getSelectors(); i.hasNext();) {
+ if (((ResourceSelector) i.next()).isSelected(r)) {
+ ++passed;
+ if (passed > threshold || (even && tie && passed == threshold)) {
+ return true;
+ }
+ } else {
+ ++failed;
+ if (failed > threshold || (even && !tie && failed == threshold)) {
+ return false;
+ }
+ }
+ }
+ //dummy
+ return false;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Name.java b/src/main/org/apache/tools/ant/types/resources/selectors/Name.java
new file mode 100755
index 000000000..b0ac0e7f8
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Name.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
+/**
+ * Name ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Name implements ResourceSelector {
+ private String pattern;
+ private boolean cs = true;
+
+ /**
+ * Set the pattern to compare names against.
+ * @param n the pattern String to set.
+ */
+ public void setName(String n) {
+ pattern = n;
+ }
+
+ /**
+ * Get the pattern used by this Name ResourceSelector.
+ * @return the String selection pattern.
+ */
+ public String getName() {
+ return pattern;
+ }
+
+ /**
+ * Set whether the name comparisons are case-sensitive.
+ * @param b boolean case-sensitivity flag.
+ */
+ public void setCaseSensitive(boolean b) {
+ cs = b;
+ }
+
+ /**
+ * Learn whether this Name ResourceSelector is case-sensitive.
+ * @return boolean case-sensitivity flag.
+ */
+ public boolean isCaseSensitive() {
+ return cs;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ String n = r.getName();
+ if (SelectorUtils.match(pattern, n, cs)) {
+ return true;
+ }
+ String s = r.toString();
+ return s.equals(n) ? false : SelectorUtils.match(pattern, s, cs);
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/None.java b/src/main/org/apache/tools/ant/types/resources/selectors/None.java
new file mode 100755
index 000000000..e33144d5e
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/None.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * None ResourceSelector.
+ * @since Ant 1.7
+ */
+public class None
+ extends ResourceSelectorContainer implements ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ boolean none = true;
+ for (Iterator i = getSelectors(); none && i.hasNext();) {
+ none = !((ResourceSelector) i.next()).isSelected(r);
+ }
+ return none;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Not.java b/src/main/org/apache/tools/ant/types/resources/selectors/Not.java
new file mode 100755
index 000000000..fe74f8c5e
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Not.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Not ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Not implements ResourceSelector {
+
+ private ResourceSelector sel;
+
+ /**
+ * Default constructor.
+ */
+ public Not() {
+ }
+
+ /**
+ * Convenience constructor.
+ * @param s the ResourceSelector to negate.
+ */
+ public Not(ResourceSelector s) {
+ add(s);
+ }
+
+ /**
+ * Set the ResourceSelector.
+ * @param s the ResourceSelector to negate.
+ * @throws IllegalStateException if already set.
+ */
+ public void add(ResourceSelector s) {
+ if (sel != null) {
+ throw new IllegalStateException(
+ "The Not ResourceSelector accepts a single nested ResourceSelector");
+ }
+ sel = s;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ return !(sel.isSelected(r));
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Or.java b/src/main/org/apache/tools/ant/types/resources/selectors/Or.java
new file mode 100755
index 000000000..9fafa1335
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Or.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import java.util.Iterator;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Or ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Or extends ResourceSelectorContainer implements ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ for (Iterator i = getSelectors(); i.hasNext();) {
+ if (((ResourceSelector) i.next()).isSelected(r)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java b/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java
new file mode 100755
index 000000000..4a7387fbb
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelector.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Interface for a Resource selector.
+ * @since Ant 1.7
+ */
+public interface ResourceSelector {
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r);
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.java b/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.java
new file mode 100755
index 000000000..4a215ea9a
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/ResourceSelectorContainer.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.Collections;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+
+/**
+ * ResourceSelector container.
+ * @since Ant 1.7
+ */
+public class ResourceSelectorContainer extends DataType {
+
+ private Vector v = new Vector();
+
+ /**
+ * Add a ResourceSelector to the container.
+ * @param s the ResourceSelector to add.
+ */
+ public void add(ResourceSelector s) {
+ if (isReference()) {
+ throw noChildrenAllowed();
+ }
+ if (s == null) {
+ return;
+ }
+ v.add(s);
+ setChecked(false);
+ }
+
+ /**
+ * Learn whether this ResourceSelectorContainer has selectors.
+ * @return boolean indicating whether selectors have been added to the container.
+ */
+ public boolean hasSelectors() {
+ if (isReference()) {
+ return ((ResourceSelectorContainer) getCheckedRef()).hasSelectors();
+ }
+ dieOnCircularReference();
+ return !v.isEmpty();
+ }
+
+ /**
+ * Get the count of nested selectors.
+ * @return the selector count as int.
+ */
+ public int selectorCount() {
+ if (isReference()) {
+ return ((ResourceSelectorContainer) getCheckedRef()).selectorCount();
+ }
+ dieOnCircularReference();
+ return v.size();
+ }
+
+ /**
+ * Return an Iterator over the nested selectors.
+ * @return Iterator of ResourceSelectors.
+ */
+ public Iterator getSelectors() {
+ if (isReference()) {
+ return ((ResourceSelectorContainer) getCheckedRef()).getSelectors();
+ }
+ dieOnCircularReference();
+ return Collections.unmodifiableList(v).iterator();
+ }
+
+ /**
+ * Overrides the version from DataType to recurse on nested ResourceSelectors.
+ * @param stk the Stack of references.
+ * @param p the Project to resolve against.
+ * @throws BuildException on error.
+ */
+ public void dieOnCircularReference(Stack stk, Project p) {
+ if (isChecked()) {
+ return;
+ }
+ if (isReference()) {
+ super.dieOnCircularReference(stk, p);
+ } else {
+ for (Iterator i = v.iterator(); i.hasNext();) {
+ Object o = i.next();
+ if (o instanceof DataType) {
+ stk.push(o);
+ invokeCircularReferenceCheck((DataType) o, stk, p);
+ }
+ }
+ setChecked(true);
+ }
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Size.java b/src/main/org/apache/tools/ant/types/resources/selectors/Size.java
new file mode 100755
index 000000000..45bf31fa0
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Size.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.Comparison;
+
+/**
+ * Size ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Size implements ResourceSelector {
+ private long size = -1;
+ private Comparison when = Comparison.EQUAL;
+
+ /**
+ * Set the size to compare against.
+ * @param l the long resource size.
+ */
+ public void setSize(long l) {
+ size = l;
+ }
+
+ /**
+ * Get the size compared to by this Size ResourceSelector.
+ * @return the long resource size.
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Set the comparison mode.
+ * @param c a Comparison object.
+ */
+ public void setWhen(Comparison c) {
+ when = c;
+ }
+
+ /**
+ * Get the comparison mode.
+ * @return a Comparison object.
+ */
+ public Comparison getWhen() {
+ return when;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ long diff = r.getSize() - size;
+ return when.evaluate(diff == 0 ? 0 : (int) (diff / Math.abs(diff)));
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/types/resources/selectors/Type.java b/src/main/org/apache/tools/ant/types/resources/selectors/Type.java
new file mode 100755
index 000000000..faa5d3c03
--- /dev/null
+++ b/src/main/org/apache/tools/ant/types/resources/selectors/Type.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.types.resources.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Type file/dir ResourceSelector.
+ * @since Ant 1.7
+ */
+public class Type implements ResourceSelector {
+
+ /**
+ * Implements the type attribute.
+ */
+ public static class FileDir extends EnumeratedAttribute {
+ private static final String[] VALUES = new String[] {"file", "dir"};
+
+ /**
+ * Return the possible values.
+ * @return a String array.
+ */
+ public String[] getValues() {
+ return VALUES;
+ }
+ }
+
+ private FileDir type = null;
+
+ /**
+ * Set type; file|dir.
+ * @param fd a FileDir object.
+ */
+ public void setType(FileDir fd) {
+ type = fd;
+ }
+
+ /**
+ * Return true if this Resource is selected.
+ * @param r the Resource to check.
+ * @return whether the Resource was selected.
+ */
+ public boolean isSelected(Resource r) {
+ if (type == null) {
+ throw new BuildException("The type attribute is required.");
+ }
+ int i = type.getIndex();
+ return r.isDirectory() ? i == 1 : i == 0;
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java b/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java
new file mode 100755
index 000000000..7174d0620
--- /dev/null
+++ b/src/main/org/apache/tools/ant/util/ConcatResourceInputStream.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+/**
+ * Special InputStream
that will
+ * concatenate the contents of Resources from a single ResourceCollection.
+ */
+public class ConcatResourceInputStream extends InputStream {
+
+ private static final int EOF = -1;
+ private boolean eof = false;
+ private Iterator iter;
+ private InputStream currentStream;
+ private ProjectComponent managingPc;
+ private boolean ignoreErrors = false;
+
+ /**
+ * Construct a new ConcatResourceInputStream
+ * for the specified ResourceCollection.
+ * @param rc the ResourceCollection to combine.
+ * @throws IOException if I/O errors occur.
+ */
+ public ConcatResourceInputStream(ResourceCollection rc) throws IOException {
+ iter = rc.iterator();
+ }
+
+ /**
+ * Set whether this ConcatResourceInputStream ignores errors.
+ * @param b whether to ignore errors.
+ */
+ public void setIgnoreErrors(boolean b) {
+ ignoreErrors = b;
+ }
+
+ /**
+ * Find out whether this ConcatResourceInputStream ignores errors.
+ * @return boolean ignore-errors flag.
+ */
+ public boolean isIgnoreErrors() {
+ return ignoreErrors;
+ }
+
+ // inherit doc
+ public void close() throws IOException {
+ closeCurrent();
+ eof = true;
+ }
+
+ // inherit doc
+ public int read() throws IOException {
+ if (eof) {
+ return EOF;
+ }
+ int result = readCurrent();
+ if (result == EOF) {
+ nextResource();
+ result = readCurrent();
+ }
+ return result;
+ }
+
+ /**
+ * Set a managing ProjectComponent
for
+ * this ConcatResourceInputStream
.
+ * @param task the managing Task
.
+ */
+ public void setManagingComponent(ProjectComponent pc) {
+ this.managingPc = pc;
+ }
+
+ /**
+ * Log a message with the specified logging level.
+ * @param message the String
message.
+ * @param loglevel the int
logging level.
+ */
+ public void log(String message, int loglevel) {
+ if (managingPc != null) {
+ managingPc.log(message, loglevel);
+ } else {
+ if (loglevel > Project.MSG_WARN) {
+ System.out.println(message);
+ } else {
+ System.err.println(message);
+ }
+ }
+ }
+
+ private int readCurrent() throws IOException {
+ return (eof || currentStream == null) ? EOF : currentStream.read();
+ }
+
+ private void nextResource() throws IOException {
+ closeCurrent();
+ while (iter.hasNext()) {
+ Resource r = (Resource) iter.next();
+ if (!r.isExists()) {
+ continue;
+ }
+ log("Concating " + r.toLongString(), Project.MSG_VERBOSE);
+ try {
+ currentStream = new BufferedInputStream(r.getInputStream());
+ return;
+ } catch (IOException eyeOhEx) {
+ if (!ignoreErrors) {
+ log("Failed to get input stream for " + r, Project.MSG_ERR);
+ throw eyeOhEx;
+ }
+ }
+ }
+ eof = true;
+ }
+
+ private void closeCurrent() {
+ FileUtils.close(currentStream);
+ currentStream = null;
+ }
+}
diff --git a/src/main/org/apache/tools/ant/util/FileUtils.java b/src/main/org/apache/tools/ant/util/FileUtils.java
index a0952720a..d37cfe5b0 100644
--- a/src/main/org/apache/tools/ant/util/FileUtils.java
+++ b/src/main/org/apache/tools/ant/util/FileUtils.java
@@ -45,7 +45,10 @@ import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.filters.util.ChainReaderHelper;
import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.resources.Touchable;
+import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.launch.Locator;
/**
@@ -521,139 +524,204 @@ public class FileUtils {
String inputEncoding, String outputEncoding,
Project project)
throws IOException {
+ copyResource(new FileResource(sourceFile), new FileResource(destFile),
+ filters, filterChains, overwrite, preserveLastModified,
+ inputEncoding, outputEncoding, project);
+ }
- if (overwrite || !destFile.exists()
- || destFile.lastModified() < sourceFile.lastModified()) {
+ /**
+ * Convenience method to copy content from one Resource to another.
+ * No filtering is performed.
+ *
+ * @param source the Resource to copy from.
+ * Must not be null
.
+ * @param dest the Resource to copy to.
+ * Must not be null
.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public void copyResource(Resource source, Resource dest) throws IOException {
+ copyResource(source, dest, null);
+ }
- if (destFile.exists() && destFile.isFile()) {
- destFile.delete();
- }
- // ensure that parent dir of dest file exists!
- // not using getParentFile method to stay 1.1 compat
- File parent = destFile.getParentFile();
- if (parent != null && !parent.exists()) {
- parent.mkdirs();
+ /**
+ * Convenience method to copy content from one Resource to another.
+ * No filtering is performed.
+ *
+ * @param source the Resource to copy from.
+ * Must not be null
.
+ * @param dest the Resource to copy to.
+ * Must not be null
.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public void copyResource(Resource source, Resource dest, Project project)
+ throws IOException {
+ copyResource(source, dest, null, null, false,
+ false, null, null, project);
+ }
+
+ /**
+ * Convenience method to copy content from one Resource to another
+ * specifying whether token filtering must be used, whether filter chains
+ * must be used, whether newer destination files may be overwritten and
+ * whether the last modified time of dest
file should be made
+ * equal to the last modified time of source
.
+ *
+ * @param source the Resource to copy from.
+ * Must not be null
.
+ * @param dest the Resource to copy to.
+ * Must not be null
.
+ * @param filters the collection of filters to apply to this copy.
+ * @param filterChains filterChains to apply during the copy.
+ * @param overwrite Whether or not the destination Resource should be
+ * overwritten if it already exists.
+ * @param preserveLastModified Whether or not the last modified time of
+ * the destination Resource should be set to that
+ * of the source.
+ * @param inputEncoding the encoding used to read the files.
+ * @param outputEncoding the encoding used to write the files.
+ * @param project the project instance.
+ *
+ * @throws IOException if the copying fails.
+ *
+ * @since Ant 1.7
+ */
+ public void copyResource(Resource source, Resource dest,
+ FilterSetCollection filters, Vector filterChains,
+ boolean overwrite, boolean preserveLastModified,
+ String inputEncoding, String outputEncoding,
+ Project project)
+ throws IOException {
+ if (!overwrite) {
+ long slm = source.getLastModified();
+ if (dest.isExists() && slm != 0
+ && dest.getLastModified() > slm) {
+ return;
}
- final boolean filterSetsAvailable = (filters != null
- && filters.hasFilters());
- final boolean filterChainsAvailable = (filterChains != null
- && filterChains.size() > 0);
- if (filterSetsAvailable) {
- BufferedReader in = null;
- BufferedWriter out = null;
- try {
- if (inputEncoding == null) {
- in = new BufferedReader(new FileReader(sourceFile));
- } else {
- InputStreamReader isr
- = new InputStreamReader(new FileInputStream(sourceFile),
- inputEncoding);
- in = new BufferedReader(isr);
- }
- if (outputEncoding == null) {
- out = new BufferedWriter(new FileWriter(destFile));
+ }
+ final boolean filterSetsAvailable = (filters != null
+ && filters.hasFilters());
+ final boolean filterChainsAvailable = (filterChains != null
+ && filterChains.size() > 0);
+ if (filterSetsAvailable) {
+ BufferedReader in = null;
+ BufferedWriter out = null;
+ try {
+ InputStreamReader isr = null;
+ if (inputEncoding == null) {
+ isr = new InputStreamReader(source.getInputStream());
+ } else {
+ isr = new InputStreamReader(source.getInputStream(),
+ inputEncoding);
+ }
+ in = new BufferedReader(isr);
+ OutputStreamWriter osw = null;
+ if (outputEncoding == null) {
+ osw = new OutputStreamWriter(dest.getOutputStream());
+ } else {
+ osw = new OutputStreamWriter(dest.getOutputStream(),
+ outputEncoding);
+ }
+ out = new BufferedWriter(osw);
+ if (filterChainsAvailable) {
+ ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setBufferSize(BUF_SIZE);
+ crh.setPrimaryReader(in);
+ crh.setFilterChains(filterChains);
+ crh.setProject(project);
+ Reader rdr = crh.getAssembledReader();
+ in = new BufferedReader(rdr);
+ }
+ LineTokenizer lineTokenizer = new LineTokenizer();
+ lineTokenizer.setIncludeDelims(true);
+ String newline = null;
+ String line = lineTokenizer.getToken(in);
+ while (line != null) {
+ if (line.length() == 0) {
+ // this should not happen, because the lines are
+ // returned with the end of line delimiter
+ out.newLine();
} else {
- OutputStreamWriter osw
- = new OutputStreamWriter(new FileOutputStream(destFile),
- outputEncoding);
- out = new BufferedWriter(osw);
- }
- if (filterChainsAvailable) {
- ChainReaderHelper crh = new ChainReaderHelper();
- crh.setBufferSize(BUF_SIZE);
- crh.setPrimaryReader(in);
- crh.setFilterChains(filterChains);
- crh.setProject(project);
- Reader rdr = crh.getAssembledReader();
- in = new BufferedReader(rdr);
- }
- LineTokenizer lineTokenizer = new LineTokenizer();
- lineTokenizer.setIncludeDelims(true);
- String newline = null;
- String line = lineTokenizer.getToken(in);
- while (line != null) {
- if (line.length() == 0) {
- // this should not happen, because the lines are
- // returned with the end of line delimiter
- out.newLine();
- } else {
- newline = filters.replaceTokens(line);
- out.write(newline);
- }
- line = lineTokenizer.getToken(in);
+ newline = filters.replaceTokens(line);
+ out.write(newline);
}
- } finally {
- close(out);
- close(in);
+ line = lineTokenizer.getToken(in);
}
- } else if (filterChainsAvailable
- || (inputEncoding != null
- && !inputEncoding.equals(outputEncoding))
- || (inputEncoding == null && outputEncoding != null)) {
- BufferedReader in = null;
- BufferedWriter out = null;
- try {
- if (inputEncoding == null) {
- in = new BufferedReader(new FileReader(sourceFile));
- } else {
- in =
- new BufferedReader(
- new InputStreamReader(
- new FileInputStream(sourceFile),
- inputEncoding));
- }
- if (outputEncoding == null) {
- out = new BufferedWriter(new FileWriter(destFile));
- } else {
- out =
- new BufferedWriter(
- new OutputStreamWriter(
- new FileOutputStream(destFile),
- outputEncoding));
- }
- if (filterChainsAvailable) {
- ChainReaderHelper crh = new ChainReaderHelper();
- crh.setBufferSize(BUF_SIZE);
- crh.setPrimaryReader(in);
- crh.setFilterChains(filterChains);
- crh.setProject(project);
- Reader rdr = crh.getAssembledReader();
- in = new BufferedReader(rdr);
- }
- char[] buffer = new char[BUF_SIZE];
- while (true) {
- int nRead = in.read(buffer, 0, buffer.length);
- if (nRead == -1) {
- break;
- }
- out.write(buffer, 0, nRead);
- }
- } finally {
- close(out);
- close(in);
- }
- } else {
- FileInputStream in = null;
- FileOutputStream out = null;
- try {
- in = new FileInputStream(sourceFile);
- out = new FileOutputStream(destFile);
-
- byte[] buffer = new byte[BUF_SIZE];
- int count = 0;
- do {
- out.write(buffer, 0, count);
- count = in.read(buffer, 0, buffer.length);
- } while (count != -1);
- } finally {
- close(out);
- close(in);
+ } finally {
+ close(out);
+ close(in);
+ }
+ } else if (filterChainsAvailable
+ || (inputEncoding != null
+ && !inputEncoding.equals(outputEncoding))
+ || (inputEncoding == null && outputEncoding != null)) {
+ BufferedReader in = null;
+ BufferedWriter out = null;
+ try {
+ InputStreamReader isr = null;
+ if (inputEncoding == null) {
+ isr = new InputStreamReader(source.getInputStream());
+ } else {
+ isr = new InputStreamReader(source.getInputStream(),
+ inputEncoding);
}
+ in = new BufferedReader(isr);
+ OutputStreamWriter osw = null;
+ if (outputEncoding == null) {
+ osw = new OutputStreamWriter(dest.getOutputStream());
+ } else {
+ osw = new OutputStreamWriter(dest.getOutputStream(),
+ outputEncoding);
+ }
+ out = new BufferedWriter(osw);
+ if (filterChainsAvailable) {
+ ChainReaderHelper crh = new ChainReaderHelper();
+ crh.setBufferSize(BUF_SIZE);
+ crh.setPrimaryReader(in);
+ crh.setFilterChains(filterChains);
+ crh.setProject(project);
+ Reader rdr = crh.getAssembledReader();
+ in = new BufferedReader(rdr);
+ }
+ char[] buffer = new char[BUF_SIZE];
+ while (true) {
+ int nRead = in.read(buffer, 0, buffer.length);
+ if (nRead == -1) {
+ break;
+ }
+ out.write(buffer, 0, nRead);
+ }
+ } finally {
+ close(out);
+ close(in);
}
- if (preserveLastModified) {
- setFileLastModified(destFile, sourceFile.lastModified());
+ } else {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = source.getInputStream();
+ out = dest.getOutputStream();
+
+ byte[] buffer = new byte[BUF_SIZE];
+ int count = 0;
+ do {
+ out.write(buffer, 0, count);
+ count = in.read(buffer, 0, buffer.length);
+ } while (count != -1);
+ } finally {
+ close(out);
+ close(in);
}
}
+ if (preserveLastModified && dest instanceof Touchable) {
+ setLastModified((Touchable) dest, source.getLastModified());
+ }
}
/**
@@ -665,7 +733,19 @@ public class FileUtils {
* if this is -1, the current time is used.
*/
public void setFileLastModified(File file, long time) {
- file.setLastModified((time < 0) ? System.currentTimeMillis() : time);
+ setLastModified(new FileResource(file), time);
+ }
+
+ /**
+ * Set the last modified time of an object implementing
+ * org.apache.tools.ant.types.resources.Touchable .
+ *
+ * @param t the Touchable whose modified time is to be set.
+ * @param time the time to which the last modified time is to be set.
+ * if this is -1, the current time is used.
+ */
+ public void setLastModified(Touchable t, long time) {
+ t.touch((time < 0) ? System.currentTimeMillis() : time);
}
/**
@@ -987,62 +1067,108 @@ public class FileUtils {
* @since Ant 1.6.3
*/
public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException {
- if (f1.exists() != f2.exists()) {
+ return contentEquals(new FileResource(f1), new FileResource(f2), textfile);
+ }
+
+ /**
+ * Compares the contents of two Resources.
+ *
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @param text true if the content is to be treated as text and
+ * differences in kind of line break are to be ignored.
+ *
+ * @return true if the content of the Resources is the same.
+ *
+ * @throws IOException if the Resources cannot be read.
+ * @since Ant 1.6.3
+ */
+ public boolean contentEquals(Resource r1, Resource r2, boolean text) throws IOException {
+ if (r1.isExists() != r2.isExists()) {
return false;
}
- if (!f1.exists()) {
+ if (!r1.isExists()) {
// two not existing files are equal
return true;
}
- // should the following two be switched? If f1 and f2 refer to the same file,
+ // should the following two be switched? If r1 and r2 refer to the same file,
// isn't their content equal regardless of whether that file is a directory?
- if (f1.isDirectory() || f2.isDirectory()) {
+ if (r1.isDirectory() || r2.isDirectory()) {
// don't want to compare directory contents for now
return false;
}
- if (fileNameEquals(f1, f2)) {
- // same filename => true
+ if (r1.equals(r2)) {
return true;
}
- return textfile ? textEquals(f1, f2) : binaryEquals(f1, f2);
+ if (!text && r1.getSize() != r2.getSize()) {
+ return false;
+ }
+ return compareContent(r1, r2, text) == 0;
}
/**
- * Binary compares the contents of two files.
+ * Compare the content of two Resources. A nonexistent Resource's
+ * content is "less than" that of an existing Resource; a directory-type
+ * Resource's content is "less than" that of a file-type Resource.
+ * @param r1 the Resource whose content is to be compared.
+ * @param r2 the other Resource whose content is to be compared.
+ * @param text true if the content is to be treated as text and
+ * differences in kind of line break are to be ignored.
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is less than, equal to, or greater than the second.
+ * @throws IOException if the Resources cannot be read.
+ */
+ public int compareContent(Resource r1, Resource r2, boolean text) throws IOException {
+ if (r1.equals(r2)) {
+ return 0;
+ }
+ boolean e1 = r1.isExists();
+ boolean e2 = r2.isExists();
+ if (!(e1 || e2)) {
+ return 0;
+ }
+ if (e1 != e2) {
+ return e1 ? 1 : -1;
+ }
+ boolean d1 = r1.isDirectory();
+ boolean d2 = r2.isDirectory();
+ if (d1 && d2) {
+ return 0;
+ }
+ if (d1 || d2) {
+ return d1 ? -1 : 1;
+ }
+ return text ? textCompare(r1, r2) : binaryCompare(r1, r2);
+ }
+
+ /**
+ * Binary compares the contents of two Resources.
* * simple but sub-optimal comparision algorithm. written for working * rather than fast. Better would be a block read into buffers followed * by long comparisions apart from the final 1-7 bytes. *
* - * @param f1 the file whose content is to be compared. - * @param f2 the other file whose content is to be compared. - * @return true if the content of the files is the same. - * @throws IOException if the files cannot be read. + * @param r1 the Resource whose content is to be compared. + * @param r2 the other Resource whose content is to be compared. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + * @throws IOException if the Resources cannot be read. */ - private boolean binaryEquals(File f1, File f2) throws IOException { - if (f1.length() != f2.length()) { - // different size =>false - return false; - } - + private int binaryCompare(Resource r1, Resource r2) throws IOException { InputStream in1 = null; InputStream in2 = null; try { - in1 = new BufferedInputStream(new FileInputStream(f1)); - in2 = new BufferedInputStream(new FileInputStream(f2)); + in1 = new BufferedInputStream(r1.getInputStream()); + in2 = new BufferedInputStream(r2.getInputStream()); - int expectedByte = in1.read(); - while (expectedByte != -1) { - if (expectedByte != in2.read()) { - return false; + for (int b1 = in1.read(); b1 != -1; b1 = in1.read()) { + int b2 = in2.read(); + if (b1 != b2) { + return b1 > b2 ? 1 : -1; } - expectedByte = in1.read(); } - if (in2.read() != -1) { - return false; - } - return true; + return in2.read() == -1 ? 0 : -1; } finally { close(in1); close(in2); @@ -1050,33 +1176,30 @@ public class FileUtils { } /** - * Text compares the contents of two files. - * + * Text compares the contents of two Resources. * Ignores different kinds of line endings. - * - * @param f1 the file whose content is to be compared. - * @param f2 the other file whose content is to be compared. - * @return true if the content of the files is the same. - * @throws IOException if the files cannot be read. + * @param r1 the Resource whose content is to be compared. + * @param r2 the other Resource whose content is to be compared. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + * @throws IOException if the Resources cannot be read. */ - private boolean textEquals(File f1, File f2) throws IOException { + private int textCompare(Resource r1, Resource r2) throws IOException { BufferedReader in1 = null; BufferedReader in2 = null; try { - in1 = new BufferedReader(new FileReader(f1)); - in2 = new BufferedReader(new FileReader(f2)); + in1 = new BufferedReader(new InputStreamReader(r1.getInputStream())); + in2 = new BufferedReader(new InputStreamReader(r2.getInputStream())); String expected = in1.readLine(); while (expected != null) { - if (!expected.equals(in2.readLine())) { - return false; + String actual = in2.readLine(); + if (!expected.equals(actual)) { + return expected.compareTo(actual); } expected = in1.readLine(); } - if (in2.readLine() != null) { - return false; - } - return true; + return in2.readLine() == null ? 0 : -1; } finally { close(in1); close(in2); @@ -1384,7 +1507,6 @@ public class FileUtils { return isUpToDate(sourceTime, destTime, granularity); } - /** * Returns true if the source is older than the dest. * @param source source file (should be the older). diff --git a/src/main/org/apache/tools/ant/util/PropertyOutputStream.java b/src/main/org/apache/tools/ant/util/PropertyOutputStream.java new file mode 100755 index 000000000..5aa49e1dd --- /dev/null +++ b/src/main/org/apache/tools/ant/util/PropertyOutputStream.java @@ -0,0 +1,69 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.util; + +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +import org.apache.tools.ant.Project; + +/** + * Exception thrown when an attempt is made to get an OutputStream + * from an immutable Resource. + * @since Ant 1.7 + */ +public class PropertyOutputStream extends ByteArrayOutputStream { + private Project project; + private String property; + private boolean trim; + + /** + * Construct a new PropertyOutputStream for the specified Project + * and property name, trimming the property value. + * @param p the associated Ant Project. + * @param s the String property name. + */ + public PropertyOutputStream(Project p, String s) { + this(p, s, true); + } + + /** + * Construct a new PropertyOutputStream for + * the specified Project, property name, and trim mode. + * @param p the associated Ant Project. + * @param s the String property name. + * @param b the boolean trim mode. + */ + public PropertyOutputStream(Project p, String s, boolean b) { + project = p; + property = s; + trim = b; + } + + /** + * Close the PropertyOutputStream, storing the property. + */ + public void close() { + if (project != null && property != null) { + String s = new String(toByteArray()); + project.setNewProperty(property, trim ? s.trim() : s); + } + } + +} + diff --git a/src/main/org/apache/tools/zip/ZipFile.java b/src/main/org/apache/tools/zip/ZipFile.java index cf3638a54..5605dfc19 100644 --- a/src/main/org/apache/tools/zip/ZipFile.java +++ b/src/main/org/apache/tools/zip/ZipFile.java @@ -370,27 +370,29 @@ public class ZipFile { */ private void positionAtCentralDirectory() throws IOException { - long off = archive.length() - MIN_EOCD_SIZE; - archive.seek(off); - byte[] sig = ZipOutputStream.EOCD_SIG; - int curr = archive.read(); boolean found = false; - while (curr != -1) { - if (curr == sig[0]) { - curr = archive.read(); - if (curr == sig[1]) { + long off = archive.length() - MIN_EOCD_SIZE; + if (off >= 0) { + archive.seek(off); + byte[] sig = ZipOutputStream.EOCD_SIG; + int curr = archive.read(); + while (curr != -1) { + if (curr == sig[0]) { curr = archive.read(); - if (curr == sig[2]) { + if (curr == sig[1]) { curr = archive.read(); - if (curr == sig[3]) { - found = true; - break; + if (curr == sig[2]) { + curr = archive.read(); + if (curr == sig[3]) { + found = true; + break; + } } } } + archive.seek(--off); + curr = archive.read(); } - archive.seek(--off); - curr = archive.read(); } if (!found) { throw new ZipException("archive is not a ZIP archive"); diff --git a/src/resources/org/apache/tools/ant/types/resources/comparators/antlib.xml b/src/resources/org/apache/tools/ant/types/resources/comparators/antlib.xml new file mode 100755 index 000000000..be4ec8d7f --- /dev/null +++ b/src/resources/org/apache/tools/ant/types/resources/comparators/antlib.xml @@ -0,0 +1,16 @@ +