bodewig 2003/03/28 04:41:01
Modified: . WHATSNEW
docs/manual/CoreTasks concat.html
src/etc/testcases/taskdefs concat.xml
src/main/org/apache/tools/ant/taskdefs Concat.java
src/testcases/org/apache/tools/ant/taskdefs ConcatTest.java
Log:
Various <concat> enhancements.
PR: 18166
Submitted by: Peter Reilly <peter dot reilly at corvil dot com>
Revision Changes Path
1.380 +3 -0 ant/WHATSNEW
Index: WHATSNEW
===================================================================
RCS file: /home/cvs/ant/WHATSNEW,v
retrieving revision 1.379
retrieving revision 1.380
diff -u -r1.379 -r1.380
--- WHATSNEW 28 Mar 2003 12:20:50 -0000 1.379
+++ WHATSNEW 28 Mar 2003 12:41:00 -0000 1.380
@@ -198,6 +198,9 @@
* The xml formatter for JUnit will now honor test case names set with
setName. Bugzilla Report 17040.
+* <concat> now supports filtering and can check timestamps before
+ overriding a file. Bugzilla Report 18166.
+
Changes from Ant 1.5.2 to Ant 1.5.3
===================================
1.7 +99 -10 ant/docs/manual/CoreTasks/concat.html
Index: concat.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/CoreTasks/concat.html,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- concat.html 4 Sep 2002 11:05:16 -0000 1.6
+++ concat.html 28 Mar 2003 12:41:01 -0000 1.7
@@ -14,16 +14,15 @@
<p>
Concatenates a file, or a series of files, to a single file or
the console. The destination file will be created if it does
- not exist, though the the <var>append</var> attribute may be
- used to alter this behavior.
+ not exist.
</p>
<p>
+ <a href="../CoreTypes/path.html">Path</a>s and/or
<a href="../CoreTypes/fileset.html">FileSet</a>s and/or <a
href="../CoreTypes/filelist.html">FileList</a>s are used to
select which files are to be concatenated. There is no
- singular 'file' attribute to specify a single file to cat -- a
- fileset or filelist must also be used in these cases.
+ singular 'file' attribute to specify a single file to cat.
</p>
<h3>Parameters</h3>
@@ -51,7 +50,17 @@
<td valign="top">append</td>
<td valign="top">
Specifies whether or not the file specified by 'destfile'
- should be overwritten. Defaults to "no".
+ should be appended. Defaults to "no".
+ </td>
+ <td valign="top" align="center">No</td>
+ </tr>
+ <tr>
+ <td valign="top">force</td>
+ <td valign="top">
+ Specifies whether or not the file specified by 'destfile'
+ should be written to even if it is newer than all source files.
+ <em>since Ant 1.6</em>.
+ Defaults to "yes".
</td>
<td valign="top" align="center">No</td>
</tr>
@@ -71,6 +80,15 @@
</table>
<h3>Parameters specified as nested elements</h3>
+ <h4>path</h4>
+ <p><em>since Ant 1.6</em>.</p>
+
+ <p>
+ This is a <a href="../CoreTypes/path.html">Path</a>. This is
+ used to select file files to be concatenated. Note that
+ if a file can only appear once in a path. If this is
+ an issue consider using multiple paths.
+ </p>
<h4>fileset</h4>
@@ -81,7 +99,6 @@
<i>not</i> guaranteed. If this is an issue, use multiple
filesets or consider using filelists.
</p>
-
<h4>filelist</h4>
<p>
@@ -90,6 +107,47 @@
<var>files</var> attribute will be the same order in which the
files are concatenated.
</p>
+ <h4>filterchain</h4>
+ <p><em>since Ant 1.6</em>.</p>
+ <p>The concat task supports nested
+ <a href="../CoreTypes/filterchain.html"> FilterChain</a>s.</p>
+
+ <h4>header,footer</h4>
+ <p><em>since Ant 1.6</em>.</p>
+ <p>Used to prepend or postpend text into the concatenated stream.</p>
+ <p>The text may be in-line or be in a file.</p>
+ <table border="1" cellpadding="2" cellspacing="0">
+ <tr>
+ <td valign="top"><b>Attribute</b></td>
+ <td valign="top"><b>Description</b></td>
+ <td align="center" valign="top"><b>Required</b></td>
+ </tr>
+ <tr>
+ <td valign="top">filtering</td>
+ <td valign="top">
+ Whether to filter the text provided by this sub element,
+ default is "yes".
+ <td valign="top" align = "center">No</td>
+ </tr>
+ <tr>
+ <td valign="top">file</td>
+ <td valign="top">A file to place at the head or tail of the
+ concatenated text.
+ <td valign="top" align = "center">No</td>
+ </tr>
+ <tr>
+ <td valign="top">trim</td>
+ <td valign="top">Whether to trim the value, default is "no"</td>
+ <td valign="top" align = "center">No</td>
+ </tr>
+ <tr>
+ <td valign="top">trimleading</td>
+ <td valign="top">
+ Whether to trim leading white space on each line, default is "no"
+ </td>
+ <td valign="top" align = "center">No</td>
+ </tr>
+ </table>
<h3>Examples</h3>
@@ -115,11 +173,12 @@
</concat>
</pre>
- <p><b>Concatenate a series of files, overwriting if the
- destination file exists:</b></p>
+ <p><b>Concatenate a series of files, update the destination
+ file only if is older that all the source files:</b></p>
<pre>
- <concat destfile="${docbook.dir}/all-sections.xml">
+ <concat destfile="${docbook.dir}/all-sections.xml"
+ force="no">
<filelist dir="${docbook.dir}/sections"
files="introduction.xml,overview.xml"/>
<fileset dir="${docbook.dir}"
@@ -128,10 +187,40 @@
</concat>
</pre>
+ <p><b>Concatenate a series of files, expanding ant properties</b></p>
+ <pre>
+ <concat destfile="${build.dir}/subs">
+ <path>
+ <fileset dir="${src.dir}" includes="*.xml"/>
+ <pathelement location="build.xml"/>
+ </path>
+ <filterchain>
+ <expandproperties/>
+ </filterchain>
+ </concat>
+ </pre>
+
+ <p><b>Filter the lines containing project from build.xml and output
+ them to report.output, prepending with a header</b></p>
+ <pre>
+ <concat destfile="${build.dir}/report.output">
+ <header filtering="no" trimleading="yes">
+ Lines that contain project
+ ==========================
+ </header>
+ <path path="build.xml"/>
+ <filterchain>
+ <linecontains>
+ <contains value="project"/>
+ </linecontains>
+ </filterchain>
+ </concat>
+ </pre>
+
<hr>
<p align="center">
- Copyright © 2002 Apache Software Foundation. All
+ Copyright © 2002-2003 Apache Software Foundation. All
Rights Reserved.
</p>
1.6 +101 -0 ant/src/etc/testcases/taskdefs/concat.xml
Index: concat.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/concat.xml,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- concat.xml 18 Nov 2002 15:22:52 -0000 1.5
+++ concat.xml 28 Mar 2003 12:41:01 -0000 1.6
@@ -7,6 +7,12 @@
<property name="world" value="World" />
+ <target name="cleanup">
+ <delete file="TESTDEST"/>
+ <delete file="${tmp.file}"/>
+ <delete file="${tmp.file.2}"/>
+ </target>
+
<target name="test1">
<concat>
</concat>
@@ -52,4 +58,99 @@
</concat>
</target>
+ <target name="testPath">
+ <concat destfile="${tmp.file.2}">
+ <path path="${tmp.file}"/>
+ </concat>
+ </target>
+
+ <target name="testAppend">
+ <concat destfile="${tmp.file.2}">
+ <path path="${tmp.file}"/>
+ </concat>
+ <concat destfile="${tmp.file.2}" append="true">
+ <path path="${tmp.file}"/>
+ </concat>
+ </target>
+
+ <target name="testfilter">
+ <concat destfile="${tmp.file}">@REPLACEME@</concat>
+ <concat>
+ <path path="${tmp.file}"/>
+ <filterchain>
+ <replacetokens>
+ <token key="REPLACEME" value="REPLACED"/>
+ </replacetokens>
+ </filterchain>
+ </concat>
+ </target>
+
+ <target name="testnooverwrite">
+ <touch file="${tmp.file.2}"/>
+ <!-- concat.xml is now older than tmp.file.2
+ so the following should not do anything -->
+ <concat destfile="${tmp.file.2}" force="false">
+ <path path="concat.xml"/>
+ </concat>
+ </target>
+
+ <target name="testheaderfooter">
+ <concat>
+ <header filtering="false" trim="yes">
+ header
+ </header>
+ <path path="${tmp.file}"/>
+ <footer filtering="no">footer</footer>
+ </concat>
+ </target>
+
+ <target name="testfileheader">
+ <concat>
+ <header file="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ </concat>
+ </target>
+
+ <target name="samefile">
+ <touch file="${tmp.file}"/>
+ <concat destfile="${tmp.file}">
+ <path path="${tmp.file}"/>
+ </concat>
+ </target>
+
+ <target name="testfilterinline">
+ <concat>
+ @REPLACEME@
+ <filterchain>
+ <replacetokens>
+ <token key="REPLACEME" value="REPLACED"/>
+ </replacetokens>
+ </filterchain>
+ </concat>
+ </target>
+
+ <target name="testmultireader">
+ <concat destfile="${tmp.file}">Hello, World
+ </concat>
+ <concat destfile="${tmp.file.2}">Bye, World
+ </concat>
+ <concat>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <path path="${tmp.file}"/>
+ <!-- tailfilter seems to behave a little stange, place two
+ here in case the implementation changes -->
+ <path path="${tmp.file.2}"/>
+ <path path="${tmp.file.2}"/>
+ <filterchain>
+ <tailfilter lines="2"/>
+ </filterchain>
+ </concat>
+ </target>
</project>
1.17 +410 -262 ant/src/main/org/apache/tools/ant/taskdefs/Concat.java
Index: Concat.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Concat.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- Concat.java 18 Feb 2003 13:20:24 -0000 1.16
+++ Concat.java 28 Mar 2003 12:41:01 -0000 1.17
@@ -51,18 +51,23 @@
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
+
package org.apache.tools.ant.taskdefs;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.Enumeration;
@@ -72,8 +77,12 @@
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.StringUtils; // 1.1
/**
@@ -93,6 +102,7 @@
* </pre>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Derek Slager</a>
+ * @author Peter Reilly
*/
public class Concat extends Task {
@@ -127,7 +137,20 @@
* Stores a collection of file sets and/or file lists, used to
* select multiple files for concatenation.
*/
- private Vector fileSets = new Vector(); // 1.1
+ private Vector sources = new Vector();
+
+ /** for filtering the concatenated */
+ private Vector filterChains = null;
+ /** ignore dates on input files */
+ private boolean forceOverwrite = true;
+ /** String to place at the start of the concatented stream */
+ private TextElement footer;
+ /** String to place at the end of the concatented stream */
+ private TextElement header;
+ private Vector sourceFiles = new Vector();
+
+ /** 1.1 utilities and copy utilities */
+ private static FileUtils fileUtils = FileUtils.newFileUtils();
// Constructors.
@@ -156,27 +179,54 @@
}
/**
- * Sets the encoding for the input files, used when displaying the
- * data via the console.
+ * Sets the character encoding
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
+ /**
+ * Force overwrite existing destination file
+ * @since Ant 1.6
+ */
+ public void setForce(boolean force) {
+ this.forceOverwrite = force;
+ }
+
// Nested element creators.
/**
+ * Path of files to concatenate.
+ * @since Ant 1.6
+ */
+ public Path createPath() {
+ Path path = new Path(getProject());
+ sources.addElement(path);
+ return path;
+ }
+
+ /**
* Set of files to concatenate.
*/
public void addFileset(FileSet set) {
- fileSets.addElement(set);
+ sources.addElement(set);
}
/**
* List of files to concatenate.
*/
public void addFilelist(FileList list) {
- fileSets.addElement(list);
+ sources.addElement(list);
+ }
+
+ /**
+ * Adds a FilterChain.
+ * @since Ant 1.6
+ */
+ public void addFilterChain(FilterChain filterChain) {
+ if (filterChains == null)
+ filterChains = new Vector();
+ filterChains.addElement(filterChain);
}
/**
@@ -195,6 +245,22 @@
}
/**
+ * Add a header to the concatenated output
+ * @since Ant 1.6
+ */
+ public void addHeader(TextElement el) {
+ this.header = el;
+ }
+
+ /**
+ * Add a footer to the concatenated output
+ * @since Ant 1.6
+ */
+ public void addFooter(TextElement el) {
+ this.footer = el;
+ }
+
+ /**
* This method performs the concatenation.
*/
public void execute()
@@ -204,7 +270,7 @@
sanitizeText();
// Sanity check our inputs.
- if (fileSets.size() == 0 && textBuffer == null) {
+ if (sources.size() == 0 && textBuffer == null) {
// Nothing to concatenate!
throw new BuildException("At least one file " +
"must be provided, or " +
@@ -214,69 +280,59 @@
// If using filesets, disallow inline text. This is similar to
// using GNU 'cat' with file arguments -- stdin is simply
// ignored.
- if (fileSets.size() > 0 && textBuffer != null) {
+ if (sources.size() > 0 && textBuffer != null) {
throw new BuildException("Cannot include inline text " +
"when using filesets.");
}
- boolean savedAppend = append;
- try {
- // Iterate the FileSet collection, concatenating each file as
- // it is encountered.
- for (Enumeration e = fileSets.elements(); e.hasMoreElements();) {
-
- // Root directory for files.
- File fileSetBase = null;
-
- // List of files.
- String[] srcFiles = null;
-
- // Get the next file set, which could be a FileSet or a
- // FileList instance.
- Object next = e.nextElement();
-
- if (next instanceof FileSet) {
-
- FileSet fileSet = (FileSet) next;
-
- // Get a directory scanner from the file set, which will
- // determine the files from the set which need to be
- // concatenated.
- DirectoryScanner scanner =
- fileSet.getDirectoryScanner(getProject());
-
- // Determine the root path.
- fileSetBase = fileSet.getDir(getProject());
-
- // Get the list of files.
- srcFiles = scanner.getIncludedFiles();
-
- } else if (next instanceof FileList) {
-
- FileList fileList = (FileList) next;
-
- // Determine the root path.
- fileSetBase = fileList.getDir(getProject());
-
- // Get the list of files.
- srcFiles = fileList.getFiles(getProject());
-
- }
+ // 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()));
+ }
+ }
- // Concatenate the files.
- if (srcFiles != null) {
- catFiles(fileSetBase, srcFiles);
- } else {
- log("Warning: Concat received empty fileset.",
- Project.MSG_WARN);
+ // 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;
}
}
- } finally {
- append = savedAppend;
+ if (!outofdate) {
+ log(destinationFile + " is up-to-date.",
Project.MSG_VERBOSE);
+ return; // no need to do anything
+ }
}
-
- // Now, cat the inline text, if applicable.
- catText();
+
+ // 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;
+ }
+
+ cat();
}
/**
@@ -284,254 +340,346 @@
*/
public void reset() {
append = false;
+ forceOverwrite = true;
destinationFile = null;
encoding = null;
- fileSets = new Vector();
+ sources.removeAllElements();
+ sourceFiles.removeAllElements();
+ filterChains = null;
+ footer = null;
+ header = null;
}
- /**
- * This method concatenates a series of files to a single
- * destination.
- *
- * @param base the base directory for the list of file names.
- *
- * @param files the names of the files to be concatenated,
- * relative to the <code>base</code>.
- */
- private void catFiles(File base, String[] files) {
-
- // First, create a list of absolute paths for the input files.
- Vector inputFileNames = new Vector();
- for (int i = 0; i < files.length; i++) {
-
- File current = new File(base, files[i]);
-
- // Make sure the file exists. This will rarely fail when
- // using file sets, but it could be rather common when
- // using file lists.
- if (!current.exists()) {
- // File does not exist, log an error and continue.
- log("File " + current + " does not exist.",
- Project.MSG_ERR);
+ 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;
}
-
- inputFileNames.addElement(current.getAbsolutePath());
+ if (destinationFile != null
+ && fileUtils.fileNameEquals(destinationFile, file)) {
+ throw new BuildException("Input file \""
+ + file + "\" "
+ + "is the same as the output
file.");
+ }
+ sourceFiles.addElement(file);
}
+ }
+
+ /** perform the concatenation */
+ private void cat() {
+ OutputStream os = null;
+ Reader reader = null;
+ char[] buffer = new char[8192];
- final int len = inputFileNames.size();
- if (len == 0) {
- log("Warning: Could not find any of the files specified " +
- "in concat task.", Project.MSG_WARN);
- return;
- }
+ try {
- String[] input = new String[len];
- inputFileNames.copyInto(input);
+ if (destinationFile == null) {
+ // Log using WARN so it displays in 'quiet' mode.
+ os = new LogOutputStream(this, Project.MSG_WARN);
+ } else {
+ // ensure that the parent dir of dest file exists
+ File parent = fileUtils.getParentFile(destinationFile);
+ if (!parent.exists()) {
+ parent.mkdirs();
+ }
- // Next, perform the concatenation.
- if (encoding == null) {
- OutputStream os = null;
- InputStream is = null;
+ os = new FileOutputStream(destinationFile.getAbsolutePath(),
+ append);
+ }
- try {
+ PrintWriter writer = null;
+ if (encoding == null) {
+ writer = new PrintWriter(
+ new BufferedWriter(
+ new OutputStreamWriter(os)));
+ } else {
+ writer = new PrintWriter(
+ new BufferedWriter(
+ new OutputStreamWriter(os, encoding)));
+ }
- if (destinationFile == null) {
- // Log using WARN so it displays in 'quiet' mode.
- os = new LogOutputStream(this, Project.MSG_WARN);
+
+ if (header != null) {
+ if (header.getFiltering()) {
+ concatenate(
+ buffer, writer, new StringReader(header.getValue()));
} else {
- os =
- new
FileOutputStream(destinationFile.getAbsolutePath(),
- append);
-
- // This flag should only be recognized for the first
- // file. In the context of a single 'cat', we always
- // want to append.
- append = true;
+ writer.print(header.getValue());
}
-
- for (int i = 0; i < len; i++) {
+ }
- // Make sure input != output.
- if (destinationFile != null &&
- destinationFile.getAbsolutePath().equals(input[i])) {
- throw new BuildException("Input file \""
- + destinationFile.getName()
- + "\" is the same as the output file.");
- }
+ if (textBuffer != null) {
+ reader = new StringReader(
+ getProject().replaceProperties(textBuffer.toString()));
+ } else {
+ reader = new MultiReader();
+ }
+
+ concatenate(buffer, writer, reader);
- is = new FileInputStream(input[i]);
- byte[] buffer = new byte[8192];
- while (true) {
- int bytesRead = is.read(buffer);
- if (bytesRead == -1) { // EOF
- break;
- }
-
- // Write the read data.
- os.write(buffer, 0, bytesRead);
- }
- os.flush();
- is.close();
- is = null;
- }
- } catch (IOException ioex) {
- throw new BuildException("Error while concatenating: "
- + ioex.getMessage(), ioex);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (Exception ignore) {}
- }
- if (os != null) {
- try {
- os.close();
- } catch (Exception ignore) {}
+ if (footer != null) {
+ if (footer.getFiltering()) {
+ concatenate(
+ buffer, writer, new StringReader(footer.getValue()));
+ } else {
+ writer.print(footer.getValue());
}
}
- } else { // user specified encoding
+ writer.flush();
+ os.flush();
- Writer out = null;
- BufferedReader in = null;
+ } catch (IOException ioex) {
+ throw new BuildException("Error while concatenating: "
+ + ioex.getMessage(), ioex);
+ } finally {
+ if (reader != null) {
+ try {reader.close();} catch (IOException ignore) {}
+ }
+ if (os != null) {
+ try {os.close();} catch (IOException ignore) {}
+ }
+ }
+ }
- try {
- if (destinationFile == null) {
- // Log using WARN so it displays in 'quiet' mode.
- out = new OutputStreamWriter(
- new LogOutputStream(this, Project.MSG_WARN));
- } else {
- out = new OutputStreamWriter(
- new FileOutputStream(destinationFile
- .getAbsolutePath(),
- append),
- encoding);
-
- // This flag should only be recognized for the first
- // file. In the context of a single 'cat', we always
- // want to append.
- append = true;
- }
- for (int i = 0; i < len; i++) {
- in = new BufferedReader(
- new InputStreamReader(new
FileInputStream(input[i]),
- encoding));
-
- String line;
- char[] buffer = new char[4096];
- while (true) {
- int charsRead = in.read(buffer);
- if (charsRead == -1) { // EOF
- break;
- }
-
- // Write the read data.
- out.write(buffer, 0, charsRead);
- }
- out.flush();
- in.close();
- in = null;
- }
- } catch (IOException ioe) {
- throw new BuildException("Error while concatenating: "
- + ioe.getMessage(), ioe);
- } finally {
- // Close resources.
- if (in != null) {
- try {
- in.close();
- } catch (Exception ignore) {}
- }
- if (out != null) {
- try {
- out.close();
- } catch (Exception ignore) {}
- }
+ /** Concatenate a single reader to the writer using buffer */
+ private void concatenate(char[] buffer, Writer writer, Reader in)
+ throws IOException {
+ if (filterChains != null) {
+ ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setBufferSize(8192);
+ helper.setPrimaryReader(in);
+ helper.setFilterChains(filterChains);
+ helper.setProject(getProject());
+ in = new BufferedReader(helper.getAssembledReader());
+ }
+
+ while (true) {
+ int nRead = in.read(buffer, 0, buffer.length);
+ if (nRead == -1) {
+ break;
}
+ writer.write(buffer, 0, nRead);
}
+
+ writer.flush();
}
/**
- * This method concatenates the text which was added inside the
- * 'concat' tags. If the text between the tags consists only of
- * whitespace characters, it is ignored.
+ * Treat empty nested text as no text.
+ *
+ * <p>Depending on the XML parser, addText may have been called
+ * for "ignorable whitespace" as well.</p>
*/
- private void catText() {
+ private void sanitizeText() {
+ if (textBuffer != null) {
+ if (textBuffer.toString().trim().length() == 0) {
+ textBuffer = null;
+ }
+ }
+ }
- // Check the buffer.
- if (textBuffer == null) {
- // No text to write.
- return;
+ /**
+ * sub element points to a file or contains text
+ */
+ public static class TextElement {
+ private String value;
+ private boolean trimLeading = false;
+ private boolean trim = false;
+ private boolean filtering = true;
+
+ /**
+ * whether to filter the text in this element
+ * or not.
+ *
+ * @param filtering true if the text should be filtered.
+ * the default value is true.
+ */
+ public void setFiltering(boolean filtering) {
+ this.filtering = filtering;
+ }
+
+ /** return the filtering attribute */
+ private boolean getFiltering() {
+ return filtering;
}
+
+ /**
+ * set the text using a file
+ * @param file the file to use
+ * @throws BuildException if the file does not exist, or cannot be
+ * read
+ */
+ public void setFile(File file) {
+ // non-existing files are not allowed
+ if (!file.exists()) {
+ throw new BuildException("File " + file + " does not
exist.");
+ }
- String text = textBuffer.toString();
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ value = fileUtils.readFully(reader);
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ } finally {
+ if (reader != null) {
+ try {reader.close();} catch (Throwable t) {}
+ }
+ }
+ }
- // Replace ${property} strings.
- text = getProject().replaceProperties(text);
+ /**
+ * set the text using inline
+ */
+ public void addText(String value) {
+ if (value.trim().length() == 0) {
+ return;
+ }
+ this.value = value;
+ }
- // Set up a writer if necessary.
- FileWriter writer = null;
- if (destinationFile != null) {
- try {
- writer = new FileWriter(destinationFile.getAbsolutePath(),
- append);
- } catch (IOException ioe) {
- throw new BuildException("Error creating destination " +
- "file.", ioe);
+ /**
+ * s:^\s*:: on each line of input
+ * @param strip if true do the trim
+ */
+ public void setTrimLeading(boolean strip) {
+ this.trimLeading = strip;
+ }
+
+ /**
+ * whether to call text.trim()
+ */
+ public void setTrim(boolean trim) {
+ this.trim = trim;
+ }
+
+ /**
+ * return the text, after possible trimming
+ */
+ public String getValue() {
+ if (value == null) {
+ value = "";
}
+ if (value.trim().length() == 0) {
+ value = "";
+ }
+ if (trimLeading) {
+ char[] current = value.toCharArray();
+ StringBuffer b = new StringBuffer(current.length);
+ boolean startOfLine = true;
+ int pos = 0;
+ while (pos < current.length) {
+ char ch = current[pos++];
+ if (startOfLine) {
+ if (ch == ' ' || ch == '\t') {
+ continue;
+ }
+ startOfLine = false;
+ }
+ b.append(ch);
+ if (ch == '\n' || ch == '\r') {
+ startOfLine = true;
+ }
+ }
+ value = b.toString();
+ }
+ if (trim) {
+ value = value.trim();
+ }
+ return value;
}
+ }
- // Reads the text, line by line.
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new StringReader(text));
+ /**
+ * This class reads from each of the source files in turn.
+ * The concatentated result can then be filtered as
+ * a single stream.
+ */
+ private class MultiReader extends Reader {
+ private int pos = 0;
+ private Reader reader = null;
- String line;
- while ((line = reader.readLine()) != null) {
- if (destinationFile == null) {
- // Log the line, using WARN so it displays in
- // 'quiet' mode.
- log(line, Project.MSG_WARN);
+ private Reader getReader() throws IOException {
+ if (reader == null) {
+ if (encoding == null) {
+ reader = new BufferedReader(
+ new FileReader((File) sourceFiles.elementAt(pos)));
} else {
- writer.write(line);
- writer.write(StringUtils.LINE_SEP);
- writer.flush();
- }
+ // invoke the zoo of io readers
+ reader = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(
+ (File) sourceFiles.elementAt(pos)),
+ encoding));
+ }
}
+ return reader;
+ }
- } catch (IOException ioe) {
- throw new BuildException("Error while concatenating " +
- "text.", ioe);
- } finally {
- // Close resources.
- if (reader != null) {
- try {
+ /**
+ * Read a character from the current reader object. Advance
+ * to the next if the reader is finished.
+ * @return the character read, -1 for EOF on the last reader.
+ * @exception IOException - possiblly thrown by the read for a reader
+ * object.
+ */
+ public int read() throws IOException {
+ while (pos < sourceFiles.size()) {
+ int ch = getReader().read();
+ if (ch == -1) {
reader.close();
- } catch (Exception ignore) {}
+ reader = null;
+ } else {
+ return ch;
+ }
+ pos++;
}
+ return -1;
+ }
- if (writer != null) {
- try {
- writer.close();
- } catch (Exception ignore) {}
+ /**
+ * Read into the buffer <code>cbuf</code>.
+ * @param cbuf The array to be read into.
+ * @param off The offset.
+ * @param len The length to read.
+ * @exception IOException - possiblely thrown by the reads to the
+ * reader objects.
+ */
+ public int read(char cbuf[], int off, int len)
+ throws IOException {
+ int amountRead = 0;
+ int iOff = off;
+ while (pos < sourceFiles.size()) {
+ int nRead = getReader().read(cbuf, off, len);
+ if (nRead == -1 || nRead == 0) {
+ reader.close();
+ reader = null;
+ pos++;
+ } else {
+ len -= nRead;
+ off += nRead;
+ amountRead += nRead;
+ if (len == 0) {
+ return amountRead;
+ }
+ }
+ }
+ if (amountRead == 0) {
+ return -1;
+ } else {
+ return amountRead;
}
}
- }
- /**
- * Treat empty nested text as no text.
- *
- * <p>Depending on the XML parser, addText may have been called
- * for "ignorable whitespace" as well.</p>
- */
- private void sanitizeText() {
- if (textBuffer != null) {
- if (textBuffer.toString().trim().length() == 0) {
- textBuffer = null;
+ public void close() throws IOException {
+ if (reader != null) {
+ reader.close();
}
}
}
+ }
-}
1.6 +78 -10
ant/src/testcases/org/apache/tools/ant/taskdefs/ConcatTest.java
Index: ConcatTest.java
===================================================================
RCS file:
/home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/ConcatTest.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- ConcatTest.java 10 Feb 2003 14:14:45 -0000 1.5
+++ ConcatTest.java 28 Mar 2003 12:41:01 -0000 1.6
@@ -1,7 +1,7 @@
/*
* The Apache Software License, Version 1.1
*
- * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -97,14 +97,7 @@
* test.
*/
public void tearDown() {
- // Remove temporary files.
- String[] rm = new String[] { tempFile, tempFile2 };
- for (int i = 0; i < rm.length; i++) {
- File f = new File(getProjectDir(), rm[i]);
- if (f.exists()) {
- f.delete();
- }
- }
+ executeTarget("cleanup");
}
/**
@@ -140,7 +133,6 @@
* Cats the file created in test3 three times.
*/
public void test4() {
-
test3();
File file = new File(getProjectDir(), tempFile);
@@ -174,4 +166,80 @@
public void testConcatNoNewlineEncoding() {
expectLog("testConcatNoNewlineEncoding", "ab");
}
+
+ public void testPath() {
+ test3();
+
+ File file = new File(getProjectDir(), tempFile);
+ final long origSize = file.length();
+
+ executeTarget("testPath");
+
+ File file2 = new File(getProjectDir(), tempFile2);
+ final long newSize = file2.length();
+
+ assertEquals(origSize, newSize);
+
+ }
+ public void testAppend() {
+ test3();
+
+ File file = new File(getProjectDir(), tempFile);
+ final long origSize = file.length();
+
+ executeTarget("testAppend");
+
+ File file2 = new File(getProjectDir(), tempFile2);
+ final long newSize = file2.length();
+
+ assertEquals(origSize*2, newSize);
+
+ }
+
+ public void testFilter() {
+ executeTarget("testfilter");
+ assertTrue(getLog().indexOf("REPLACED") > -1);
+ }
+
+ public void testNoOverwrite() {
+ executeTarget("testnooverwrite");
+ File file2 = new File(getProjectDir(), tempFile2);
+ long size = file2.length();
+ assertEquals(size, 0);
+ }
+
+ public void testheaderfooter() {
+ test3();
+ expectLog("testheaderfooter", "headerHello, World!footer");
+ }
+
+ public void testfileheader() {
+ test3();
+ expectLog("testfileheader", "Hello, World!Hello, World!");
+ }
+
+ /**
+ * Expect an exception when attempting to cat an file to itself
+ */
+ public void testsame() {
+ expectBuildException("samefile", "output file same as input");
+ }
+
+ /**
+ * Check if filter inline works
+ */
+ public void testfilterinline() {
+ executeTarget("testfilterinline");
+ assertTrue(getLog().indexOf("REPLACED") > -1);
+ }
+
+ /**
+ * Check if multireader works
+ */
+ public void testmultireader() {
+ executeTarget("testmultireader");
+ assertTrue(getLog().indexOf("Bye") > -1);
+ assertTrue(getLog().indexOf("Hello") == -1);
+ }
+
}