diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/Copy.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/Copy.java new file mode 100644 index 000000000..0a3baf5f6 --- /dev/null +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/Copy.java @@ -0,0 +1,514 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.taskdefs; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Mapper; +import org.apache.tools.ant.types.FilterChain; +import org.apache.tools.ant.types.FilterSet; +import org.apache.tools.ant.types.FilterSetCollection; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.FileNameMapper; +import org.apache.tools.ant.util.FlatFileNameMapper; +import org.apache.tools.ant.util.IdentityMapper; +import org.apache.tools.ant.util.SourceFileScanner; + +import java.io.File; +import java.io.IOException; +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; + +/** + * A consolidated copy task. Copies a file or directory to a new file + * or directory. Files are only copied if the source file is newer + * than the destination file, or when the destination file does not + * exist. It is possible to explicitly overwrite existing files.

+ * + *

This implementation is based on Arnout Kuiper's initial design + * document, the following mailing list discussions, and the + * copyfile/copydir tasks.

+ * + * @author Glenn McAllister glennm@ca.ibm.com + * @author Stefan Bodewig + * @author Michael McCallum + * @author Magesh Umasankar + * + * @version $Revision$ + * + * @ant:task category="filesystem" + */ +public class Copy extends Task { + protected File file = null; // the source file + protected File destFile = null; // the destination file + protected File destDir = null; // the destination directory + protected Vector filesets = new Vector(); + + protected boolean filtering = false; + protected boolean preserveLastModified = false; + protected boolean forceOverwrite = false; + protected boolean flatten = false; + protected int verbosity = Project.MSG_VERBOSE; + protected boolean includeEmpty = true; + + protected Hashtable fileCopyMap = new Hashtable(); + protected Hashtable dirCopyMap = new Hashtable(); + protected Hashtable completeDirMap = new Hashtable(); + + protected Mapper mapperElement = null; + private Vector filterChains = new Vector(); + private Vector filterSets = new Vector(); + private FileUtils fileUtils; + private String encoding = null; + + public Copy() { + fileUtils = FileUtils.newFileUtils(); + } + + protected FileUtils getFileUtils() {return fileUtils;} + + /** + * Sets a single source file to copy. + */ + public void setFile(File file) { + this.file = file; + } + + /** + * Sets the destination file. + */ + public void setTofile(File destFile) { + this.destFile = destFile; + } + + /** + * Sets the destination directory. + */ + public void setTodir(File destDir) { + this.destDir = destDir; + } + + /** + * Create a nested filterchain + */ + public FilterChain createFilterChain() { + FilterChain filterChain = new FilterChain(); + filterChains.addElement(filterChain); + return filterChain; + } + + /** + * Create a nested filterset + */ + public FilterSet createFilterSet() { + FilterSet filterSet = new FilterSet(); + filterSets.addElement(filterSet); + return filterSet; + } + + /** + * Give the copied files the same last modified time as the original files. + * @deprecated setPreserveLastModified(String) has been deprecated and + * replaced with setPreserveLastModified(boolean) to + * consistently let the Introspection mechanism work. + */ + public void setPreserveLastModified(String preserve) { + setPreserveLastModified(Project.toBoolean(preserve)); + } + + /** + * Give the copied files the same last modified time as the original files. + */ + public void setPreserveLastModified(boolean preserve) { + preserveLastModified = preserve; + } + + /** + * Whether to give the copied files the same last modified time as + * the original files. + * + * @since 1.32, Ant 1.5 + */ + public boolean getPreserveLastModified() { + return preserveLastModified; + } + + /** + * Get the filtersets being applied to this operation. + * + * @return a vector of FilterSet objects + */ + protected Vector getFilterSets() { + return filterSets; + } + + /** + * Get the filterchains being applied to this operation. + * + * @return a vector of FilterChain objects + */ + protected Vector getFilterChains() { + return filterChains; + } + + /** + * Sets filtering. + */ + public void setFiltering(boolean filtering) { + this.filtering = filtering; + } + + /** + * Overwrite any existing destination file(s). + */ + public void setOverwrite(boolean overwrite) { + this.forceOverwrite = overwrite; + } + + /** + * When copying directory trees, the files can be "flattened" + * into a single directory. If there are multiple files with + * the same name in the source directory tree, only the first + * file will be copied into the "flattened" directory, unless + * the forceoverwrite attribute is true. + */ + public void setFlatten(boolean flatten) { + this.flatten = flatten; + } + + /** + * Used to force listing of all names of copied files. + */ + public void setVerbose(boolean verbose) { + if (verbose) { + this.verbosity = Project.MSG_INFO; + } else { + this.verbosity = Project.MSG_VERBOSE; + } + } + + /** + * Used to copy empty directories. + */ + public void setIncludeEmptyDirs(boolean includeEmpty) { + this.includeEmpty = includeEmpty; + } + + /** + * Adds a set of files (nested fileset attribute). + */ + public void addFileset(FileSet set) { + filesets.addElement(set); + } + + /** + * Defines the FileNameMapper to use (nested mapper element). + */ + public Mapper createMapper() throws BuildException { + if (mapperElement != null) { + throw new BuildException("Cannot define more than one mapper", + location); + } + mapperElement = new Mapper(project); + return mapperElement; + } + + /** + * Sets the character encoding + * + * @since 1.32, Ant 1.5 + */ + public void setEncoding (String encoding) { + this.encoding = encoding; + } + + /** + * @return the character encoding, null if not set. + * + * @since 1.32, Ant 1.5 + */ + public String getEncoding() { + return encoding; + } + + /** + * Performs the copy operation. + */ + public void execute() throws BuildException { + // make sure we don't have an illegal set of options + validateAttributes(); + + // deal with the single file + if (file != null) { + if (file.exists()) { + if (destFile == null) { + destFile = new File(destDir, file.getName()); + } + + if (forceOverwrite || + (file.lastModified() > destFile.lastModified())) { + fileCopyMap.put(file.getAbsolutePath(), destFile.getAbsolutePath()); + } else { + log(file + " omitted as " + destFile + " is up to date.", + Project.MSG_VERBOSE); + } + } else { + String message = "Could not find file " + + file.getAbsolutePath() + " to copy."; + log(message); + throw new BuildException(message); + } + } + + // deal with the filesets + for (int i=0; i 0) { + if (filesets.size() > 1) { + throw new BuildException( + "Cannot concatenate multiple files into a single file."); + } else { + FileSet fs = (FileSet) filesets.elementAt(0); + DirectoryScanner ds = fs.getDirectoryScanner(project); + String[] srcFiles = ds.getIncludedFiles(); + + if (srcFiles.length > 0) { + if (file == null) { + file = new File(ds.getBasedir(), srcFiles[0]); + filesets.removeElementAt(0); + } else { + throw new BuildException( + "Cannot concatenate multiple files into a single file."); + } + } else { + throw new BuildException( + "Cannot perform operation from directory to file."); + } + } + } + + if (destFile != null) { + destDir = new File(destFile.getParent()); // be 1.1 friendly + } + + } + + /** + * Compares source files to destination files to see if they should be + * copied. + */ + protected void scan(File fromDir, File toDir, String[] files, String[] dirs) { + FileNameMapper mapper = null; + if (mapperElement != null) { + mapper = mapperElement.getImplementation(); + } else if (flatten) { + mapper = new FlatFileNameMapper(); + } else { + mapper = new IdentityMapper(); + } + + buildMap(fromDir, toDir, files, mapper, fileCopyMap); + + if (includeEmpty) { + buildMap(fromDir, toDir, dirs, mapper, dirCopyMap); + } + } + + protected void buildMap(File fromDir, File toDir, String[] names, + FileNameMapper mapper, Hashtable map) { + + String[] toCopy = null; + if (forceOverwrite) { + Vector v = new Vector(); + for (int i=0; i 0) { + log("Copying " + fileCopyMap.size() + + " file" + (fileCopyMap.size() == 1 ? "" : "s") + + " to " + destDir.getAbsolutePath() ); + + Enumeration e = fileCopyMap.keys(); + while (e.hasMoreElements()) { + String fromFile = (String) e.nextElement(); + String toFile = (String) fileCopyMap.get(fromFile); + + if( fromFile.equals( toFile ) ) { + log("Skipping self-copy of " + fromFile, verbosity); + continue; + } + + try { + log("Copying " + fromFile + " to " + toFile, verbosity); + + FilterSetCollection executionFilters = new FilterSetCollection(); + if (filtering) { + executionFilters.addFilterSet(project.getGlobalFilterSet()); + } + for (Enumeration filterEnum = filterSets.elements(); filterEnum.hasMoreElements();) { + executionFilters.addFilterSet((FilterSet)filterEnum.nextElement()); + } + fileUtils.copyFile(fromFile, toFile, executionFilters, filterChains, + forceOverwrite, preserveLastModified, + encoding, project); + } catch (IOException ioe) { + String msg = "Failed to copy " + fromFile + " to " + toFile + + " due to " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } + } + } + + if (includeEmpty) { + Enumeration e = dirCopyMap.elements(); + int count = 0; + while (e.hasMoreElements()) { + File d = new File((String)e.nextElement()); + if (!d.exists()) { + if (!d.mkdirs()) { + log("Unable to create directory " + d.getAbsolutePath(), Project.MSG_ERR); + } else { + count++; + } + } + } + + if (count > 0) { + log("Copied " + count + + " empty director" + + (count==1?"y":"ies") + + " to " + destDir.getAbsolutePath()); + } + } + } + +} diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/Move.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/Move.java new file mode 100644 index 000000000..269e4b64b --- /dev/null +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/taskdefs/Move.java @@ -0,0 +1,312 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FilterSetCollection; +import org.apache.tools.ant.types.FilterSet; +import org.apache.tools.ant.types.FileSet; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +/** + * Moves a file or directory to a new file or directory. By default, + * the destination is overwriten when existing. When overwrite is + * turned off, then files are only moved if the source file is + * newer than the destination file, or when the destination file does + * not exist.

+ * + *

Source files and directories are only deleted when the file or + * directory has been copied to the destination successfully. Filtering + * also works.

+ * + *

This implementation is based on Arnout Kuiper's initial design + * document, the following mailing list discussions, and the + * copyfile/copydir tasks.

+ * + * @author Glenn McAllister glennm@ca.ibm.com + * @author Magesh Umasankar + * @version $Revision$ + * + * @ant:task category="filesystem" + */ +public class Move extends Copy { + + private Vector filterSets = null; + private Vector filterChains = null; + + public Move() { + super(); + forceOverwrite = true; + } + +//************************************************************************ +// protected and private methods +//************************************************************************ + + protected void doFileOperations() { + filterSets = getFilterSets(); + filterChains = getFilterChains(); + + //Attempt complete directory renames, if any, first. + if (completeDirMap.size() > 0) { + Enumeration e = completeDirMap.keys(); + while (e.hasMoreElements()) { + File fromDir = (File) e.nextElement(); + File toDir = (File) completeDirMap.get(fromDir); + try { + log("Attempting to rename dir: " + fromDir + + " to " + toDir, verbosity); + renameFile(fromDir, toDir, filtering, forceOverwrite); + } catch (IOException ioe) { + String msg = "Failed to rename dir " + fromDir + + " to " + toDir + + " due to " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } + } + } + if (fileCopyMap.size() > 0) { // files to move + log("Moving " + fileCopyMap.size() + " files to " + + destDir.getAbsolutePath() ); + + Enumeration e = fileCopyMap.keys(); + while (e.hasMoreElements()) { + String fromFile = (String) e.nextElement(); + String toFile = (String) fileCopyMap.get(fromFile); + + if( fromFile.equals( toFile ) ) { + log("Skipping self-move of " + fromFile, verbosity); + continue; + } + + boolean moved = false; + File f = new File(fromFile); + + if (f.exists()) { //Is this file still available to be moved? + File d = new File(toFile); + + try { + log("Attempting to rename: " + fromFile + + " to " + toFile, verbosity); + moved = renameFile(f, d, filtering, forceOverwrite); + } catch (IOException ioe) { + String msg = "Failed to rename " + fromFile + + " to " + toFile + + " due to " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } + + if (!moved) { + try { + log("Moving " + fromFile + " to " + toFile, verbosity); + + FilterSetCollection executionFilters = new FilterSetCollection(); + if (filtering) { + executionFilters.addFilterSet(project.getGlobalFilterSet()); + } + for (Enumeration filterEnum = getFilterSets().elements(); filterEnum.hasMoreElements();) { + executionFilters.addFilterSet((FilterSet)filterEnum.nextElement()); + } + getFileUtils().copyFile(f, d, executionFilters, filterChains, + forceOverwrite, + getPreserveLastModified(), + getEncoding(), project); + + f = new File(fromFile); + if (!f.delete()) { + throw new BuildException("Unable to delete file " + + f.getAbsolutePath()); + } + } catch (IOException ioe) { + String msg = "Failed to copy " + fromFile + " to " + + toFile + + " due to " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } + } + } + } + } + + if (includeEmpty) { + Enumeration e = dirCopyMap.elements(); + int count = 0; + while (e.hasMoreElements()) { + File d = new File((String)e.nextElement()); + if (!d.exists()) { + if (!d.mkdirs()) { + log("Unable to create directory " + d.getAbsolutePath(), Project.MSG_ERR); + } else { + count++; + } + } + } + + if (count > 0) { + log("Moved " + count + " empty directories to " + destDir.getAbsolutePath()); + } + } + + if (filesets.size() > 0) { + Enumeration e = filesets.elements(); + while (e.hasMoreElements()) { + FileSet fs = (FileSet)e.nextElement(); + File dir = fs.getDir(project); + + if (okToDelete(dir)) { + deleteDir(dir); + } + } + } + } + + /** + * Its only ok to delete a directory tree if there are + * no files in it. + */ + protected boolean okToDelete(File d) { + String[] list = d.list(); + if (list == null) { + return false; + } // maybe io error? + + for (int i = 0; i < list.length; i++) { + String s = list[i]; + File f = new File(d, s); + if (f.isDirectory()) { + if (!okToDelete(f)) { + return false; + } + } else { + return false; // found a file + } + } + + return true; + } + + /** + * Go and delete the directory tree. + */ + protected void deleteDir(File d) { + String[] list = d.list(); + if (list == null) { + return; + } // on an io error list() can return null + + for (int i = 0; i < list.length; i++) { + String s = list[i]; + File f = new File(d, s); + if (f.isDirectory()) { + deleteDir(f); + } else { + throw new BuildException("UNEXPECTED ERROR - The file " + f.getAbsolutePath() + " should not exist!"); + } + } + log("Deleting directory " + d.getAbsolutePath(), verbosity); + if (!d.delete()) { + throw new BuildException("Unable to delete directory " + d.getAbsolutePath()); + } + } + + /** + * Attempts to rename a file from a source to a destination. + * If overwrite is set to true, this method overwrites existing file + * even if the destination file is newer. Otherwise, the source file is + * renamed only if the destination file is older than it. + * Method then checks if token filtering is used. If it is, this method + * returns false assuming it is the responsibility to the copyFile method. + * + * @throws IOException + */ + protected boolean renameFile(File sourceFile, File destFile, + boolean filtering, boolean overwrite) + throws IOException, BuildException { + + boolean renamed = true; + if ((filterSets != null && filterSets.size() > 0) || + (filterChains != null && filterChains.size() > 0)) { + renamed = false; + } else { + if (!filtering) { + // ensure that parent dir of dest file exists! + // not using getParentFile method to stay 1.1 compat + String parentPath = destFile.getParent(); + if (parentPath != null) { + File parent = new File(parentPath); + if (!parent.exists()) { + parent.mkdirs(); + } + } + + if (destFile.exists()) { + if (!destFile.delete()) { + throw new BuildException("Unable to remove existing file " + + destFile); + } + } + renamed = sourceFile.renameTo(destFile); + } else { + renamed = false; + } + } + return renamed; + } +} diff --git a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java index 6d4916d37..9febac2b4 100644 --- a/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java +++ b/proposal/sandbox/filterreaders/src/main/org/apache/tools/ant/util/FileUtils.java @@ -73,9 +73,11 @@ import java.text.DecimalFormat; import java.util.Random; import java.util.Stack; import java.util.StringTokenizer; +import java.util.Vector; 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.types.FilterSetCollection; /** @@ -87,6 +89,7 @@ import org.apache.tools.ant.types.FilterSetCollection; * @author duncan@x180.com * @author Conor MacNeill * @author Stefan Bodewig + * @author Magesh Umasankar * * @version $Revision$ */ @@ -178,6 +181,28 @@ public class FileUtils { overwrite, preserveLastModified, encoding); } + /** + * Convienence method to copy a file from a source to a + * destination specifying if token filtering must be used, if + * filter chains must be used, if source files may overwrite + * newer destination files and the last modified time of + * destFile file should be made equal + * to the last modified time of sourceFile. + * + * @throws IOException + * + * @since 1.15, Ant 1.5 + */ + public void copyFile(String sourceFile, String destFile, + FilterSetCollection filters, Vector filterChains, + boolean overwrite, boolean preserveLastModified, + String encoding, Project project) + throws IOException { + copyFile(new File(sourceFile), new File(destFile), filters, + filterChains, overwrite, preserveLastModified, + encoding, project); + } + /** * Convienence method to copy a file from a source to a destination. * No filtering is performed. @@ -243,6 +268,27 @@ public class FileUtils { FilterSetCollection filters, boolean overwrite, boolean preserveLastModified, String encoding) throws IOException { + copyFile(sourceFile, destFile, filters, null, overwrite, + preserveLastModified, encoding, null); + } + + /** + * Convienence method to copy a file from a source to a + * destination specifying if token filtering must be used, if + * filter chains must be used, if source files may overwrite + * newer destination files and the last modified time of + * destFile file should be made equal + * to the last modified time of sourceFile. + * + * @throws IOException + * + * @since 1.15, Ant 1.5 + */ + public void copyFile(File sourceFile, File destFile, + FilterSetCollection filters, Vector filterChains, + boolean overwrite, boolean preserveLastModified, + String encoding, Project project) + throws IOException { if (overwrite || !destFile.exists() || destFile.lastModified() < sourceFile.lastModified()) { @@ -258,7 +304,12 @@ public class FileUtils { parent.mkdirs(); } - if (filters != null && filters.hasFilters()) { + final boolean filterSetsAvailable = (filters != null + && filters.hasFilters()); + final boolean filterChainsAvailable = (filterChains != null + && filterChains.size() > 0); + + if (filterSetsAvailable || filterChainsAvailable) { BufferedReader in = null; BufferedWriter out = null; @@ -266,8 +317,20 @@ public class FileUtils { in = new BufferedReader(new FileReader(sourceFile)); out = new BufferedWriter(new FileWriter(destFile)); } else { - in = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), encoding)); - out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destFile), encoding)); + in = new BufferedReader(new InputStreamReader( + new FileInputStream(sourceFile), encoding)); + out = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(destFile), encoding)); + } + + if (filterChainsAvailable) { + ChainReaderHelper crh = new ChainReaderHelper(); + crh.setBufferSize(8192); + crh.setPrimaryReader(in); + crh.setFilterChains(filterChains); + crh.setProject(project); + Reader rdr = crh.getAssembledReader(); + in = new BufferedReader(rdr); } int length; @@ -277,7 +340,11 @@ public class FileUtils { if (line.length() == 0) { out.newLine(); } else { - newline = filters.replaceTokens(line); + if (filterSetsAvailable) { + newline = filters.replaceTokens(line); + } else { + newline = line; + } out.write(newline); out.newLine(); }