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
+ *
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; iSource 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();
}