|
@@ -51,18 +51,23 @@ |
|
|
* information on the Apache Software Foundation, please see |
|
|
* information on the Apache Software Foundation, please see |
|
|
* <http://www.apache.org/>. |
|
|
* <http://www.apache.org/>. |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
package org.apache.tools.ant.taskdefs; |
|
|
package org.apache.tools.ant.taskdefs; |
|
|
|
|
|
|
|
|
import java.io.BufferedReader; |
|
|
import java.io.BufferedReader; |
|
|
|
|
|
import java.io.BufferedWriter; |
|
|
import java.io.File; |
|
|
import java.io.File; |
|
|
import java.io.FileInputStream; |
|
|
import java.io.FileInputStream; |
|
|
import java.io.FileOutputStream; |
|
|
import java.io.FileOutputStream; |
|
|
|
|
|
import java.io.FileReader; |
|
|
import java.io.FileWriter; |
|
|
import java.io.FileWriter; |
|
|
import java.io.IOException; |
|
|
import java.io.IOException; |
|
|
import java.io.InputStream; |
|
|
import java.io.InputStream; |
|
|
import java.io.InputStreamReader; |
|
|
import java.io.InputStreamReader; |
|
|
import java.io.OutputStream; |
|
|
import java.io.OutputStream; |
|
|
import java.io.OutputStreamWriter; |
|
|
import java.io.OutputStreamWriter; |
|
|
|
|
|
import java.io.PrintWriter; |
|
|
|
|
|
import java.io.Reader; |
|
|
import java.io.StringReader; |
|
|
import java.io.StringReader; |
|
|
import java.io.Writer; |
|
|
import java.io.Writer; |
|
|
import java.util.Enumeration; |
|
|
import java.util.Enumeration; |
|
@@ -72,8 +77,12 @@ import org.apache.tools.ant.DirectoryScanner; |
|
|
import org.apache.tools.ant.Project; |
|
|
import org.apache.tools.ant.Project; |
|
|
import org.apache.tools.ant.ProjectHelper; |
|
|
import org.apache.tools.ant.ProjectHelper; |
|
|
import org.apache.tools.ant.Task; |
|
|
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.FileList; |
|
|
import org.apache.tools.ant.types.FileSet; |
|
|
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 |
|
|
import org.apache.tools.ant.util.StringUtils; // 1.1 |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -93,6 +102,7 @@ import org.apache.tools.ant.util.StringUtils; // 1.1 |
|
|
* </pre> |
|
|
* </pre> |
|
|
* |
|
|
* |
|
|
* @author <a href="mailto:derek@activate.net">Derek Slager</a> |
|
|
* @author <a href="mailto:derek@activate.net">Derek Slager</a> |
|
|
|
|
|
* @author Peter Reilly |
|
|
*/ |
|
|
*/ |
|
|
public class Concat extends Task { |
|
|
public class Concat extends Task { |
|
|
|
|
|
|
|
@@ -127,7 +137,20 @@ public class Concat extends Task { |
|
|
* Stores a collection of file sets and/or file lists, used to |
|
|
* Stores a collection of file sets and/or file lists, used to |
|
|
* select multiple files for concatenation. |
|
|
* 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. |
|
|
// Constructors. |
|
|
|
|
|
|
|
@@ -156,27 +179,54 @@ public class Concat extends Task { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Sets the encoding for the input files, used when displaying the |
|
|
|
|
|
* data via the console. |
|
|
|
|
|
|
|
|
* Sets the character encoding |
|
|
*/ |
|
|
*/ |
|
|
public void setEncoding(String encoding) { |
|
|
public void setEncoding(String encoding) { |
|
|
this.encoding = encoding; |
|
|
this.encoding = encoding; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Force overwrite existing destination file |
|
|
|
|
|
* @since Ant 1.6 |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setForce(boolean force) { |
|
|
|
|
|
this.forceOverwrite = force; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Nested element creators. |
|
|
// 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. |
|
|
* Set of files to concatenate. |
|
|
*/ |
|
|
*/ |
|
|
public void addFileset(FileSet set) { |
|
|
public void addFileset(FileSet set) { |
|
|
fileSets.addElement(set); |
|
|
|
|
|
|
|
|
sources.addElement(set); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* List of files to concatenate. |
|
|
* List of files to concatenate. |
|
|
*/ |
|
|
*/ |
|
|
public void addFilelist(FileList list) { |
|
|
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); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -194,6 +244,22 @@ public class Concat extends Task { |
|
|
textBuffer.append(text); |
|
|
textBuffer.append(text); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 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. |
|
|
* This method performs the concatenation. |
|
|
*/ |
|
|
*/ |
|
@@ -204,7 +270,7 @@ public class Concat extends Task { |
|
|
sanitizeText(); |
|
|
sanitizeText(); |
|
|
|
|
|
|
|
|
// Sanity check our inputs. |
|
|
// Sanity check our inputs. |
|
|
if (fileSets.size() == 0 && textBuffer == null) { |
|
|
|
|
|
|
|
|
if (sources.size() == 0 && textBuffer == null) { |
|
|
// Nothing to concatenate! |
|
|
// Nothing to concatenate! |
|
|
throw new BuildException("At least one file " + |
|
|
throw new BuildException("At least one file " + |
|
|
"must be provided, or " + |
|
|
"must be provided, or " + |
|
@@ -214,69 +280,59 @@ public class Concat extends Task { |
|
|
// If using filesets, disallow inline text. This is similar to |
|
|
// If using filesets, disallow inline text. This is similar to |
|
|
// using GNU 'cat' with file arguments -- stdin is simply |
|
|
// using GNU 'cat' with file arguments -- stdin is simply |
|
|
// ignored. |
|
|
// ignored. |
|
|
if (fileSets.size() > 0 && textBuffer != null) { |
|
|
|
|
|
|
|
|
if (sources.size() > 0 && textBuffer != null) { |
|
|
throw new BuildException("Cannot include inline text " + |
|
|
throw new BuildException("Cannot include inline text " + |
|
|
"when using filesets."); |
|
|
"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 class Concat extends Task { |
|
|
*/ |
|
|
*/ |
|
|
public void reset() { |
|
|
public void reset() { |
|
|
append = false; |
|
|
append = false; |
|
|
|
|
|
forceOverwrite = true; |
|
|
destinationFile = null; |
|
|
destinationFile = null; |
|
|
encoding = 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; |
|
|
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 { |
|
|
} 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()); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (textBuffer != null) { |
|
|
|
|
|
reader = new StringReader( |
|
|
|
|
|
getProject().replaceProperties(textBuffer.toString())); |
|
|
|
|
|
} else { |
|
|
|
|
|
reader = new MultiReader(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
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."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
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."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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) {} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
String text = textBuffer.toString(); |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* set the text using inline |
|
|
|
|
|
*/ |
|
|
|
|
|
public void addText(String value) { |
|
|
|
|
|
if (value.trim().length() == 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
this.value = value; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Replace ${property} strings. |
|
|
|
|
|
text = getProject().replaceProperties(text); |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* s:^\s*:: on each line of input |
|
|
|
|
|
* @param strip if true do the trim |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setTrimLeading(boolean strip) { |
|
|
|
|
|
this.trimLeading = strip; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 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); |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 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)); |
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 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; |
|
|
|
|
|
|
|
|
|
|
|
private Reader getReader() throws IOException { |
|
|
|
|
|
if (reader == null) { |
|
|
|
|
|
if (encoding == null) { |
|
|
|
|
|
reader = new BufferedReader( |
|
|
|
|
|
new FileReader((File) sourceFiles.elementAt(pos))); |
|
|
} else { |
|
|
} 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(); |
|
|
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(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
|