git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271671 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -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 | |||||
| * <http://www.apache.org/>. | |||||
| */ | |||||
| 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.</p> | |||||
| * | |||||
| * <p>This implementation is based on Arnout Kuiper's initial design | |||||
| * document, the following mailing list discussions, and the | |||||
| * copyfile/copydir tasks.</p> | |||||
| * | |||||
| * @author Glenn McAllister <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com</a> | |||||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
| * @author <A href="gholam@xtra.co.nz">Michael McCallum</A> | |||||
| * @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a> | |||||
| * | |||||
| * @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, <code>null</code> 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<filesets.size(); i++) { | |||||
| FileSet fs = (FileSet) filesets.elementAt(i); | |||||
| DirectoryScanner ds = fs.getDirectoryScanner(project); | |||||
| File fromDir = fs.getDir(project); | |||||
| String[] srcFiles = ds.getIncludedFiles(); | |||||
| String[] srcDirs = ds.getIncludedDirectories(); | |||||
| boolean isEverythingIncluded = ds.isEverythingIncluded(); | |||||
| if (isEverythingIncluded | |||||
| && !flatten && mapperElement == null) { | |||||
| completeDirMap.put(fromDir, destDir); | |||||
| } | |||||
| scan(fromDir, destDir, srcFiles, srcDirs); | |||||
| } | |||||
| // do all the copy operations now... | |||||
| doFileOperations(); | |||||
| // clean up destDir again - so this instance can be used a second | |||||
| // time without throwing an exception | |||||
| if (destFile != null) { | |||||
| destDir = null; | |||||
| } | |||||
| fileCopyMap.clear(); | |||||
| dirCopyMap.clear(); | |||||
| completeDirMap.clear(); | |||||
| } | |||||
| //************************************************************************ | |||||
| // protected and private methods | |||||
| //************************************************************************ | |||||
| /** | |||||
| * Ensure we have a consistent and legal set of attributes, and set | |||||
| * any internal flags necessary based on different combinations | |||||
| * of attributes. | |||||
| */ | |||||
| protected void validateAttributes() throws BuildException { | |||||
| if (file == null && filesets.size() == 0) { | |||||
| throw new BuildException("Specify at least one source - a file or a fileset."); | |||||
| } | |||||
| if (destFile != null && destDir != null) { | |||||
| throw new BuildException("Only one of tofile and todir may be set."); | |||||
| } | |||||
| if (destFile == null && destDir == null) { | |||||
| throw new BuildException("One of tofile or todir must be set."); | |||||
| } | |||||
| if (file != null && file.exists() && file.isDirectory()) { | |||||
| throw new BuildException("Use a fileset to copy directories."); | |||||
| } | |||||
| if (destFile != null && filesets.size() > 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<names.length; i++) { | |||||
| if (mapper.mapFileName(names[i]) != null) { | |||||
| v.addElement(names[i]); | |||||
| } | |||||
| } | |||||
| toCopy = new String[v.size()]; | |||||
| v.copyInto(toCopy); | |||||
| } else { | |||||
| SourceFileScanner ds = new SourceFileScanner(this); | |||||
| toCopy = ds.restrict(names, fromDir, toDir, mapper); | |||||
| } | |||||
| for (int i = 0; i < toCopy.length; i++) { | |||||
| File src = new File(fromDir, toCopy[i]); | |||||
| File dest = new File(toDir, mapper.mapFileName(toCopy[i])[0]); | |||||
| map.put( src.getAbsolutePath(), dest.getAbsolutePath() ); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Actually does the file (and possibly empty directory) copies. | |||||
| * This is a good method for subclasses to override. | |||||
| */ | |||||
| protected void doFileOperations() { | |||||
| if (fileCopyMap.size() > 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()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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 | |||||
| * <http://www.apache.org/>. | |||||
| */ | |||||
| 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.</p> | |||||
| * | |||||
| * <p>Source files and directories are only deleted when the file or | |||||
| * directory has been copied to the destination successfully. Filtering | |||||
| * also works.</p> | |||||
| * | |||||
| * <p>This implementation is based on Arnout Kuiper's initial design | |||||
| * document, the following mailing list discussions, and the | |||||
| * copyfile/copydir tasks.</p> | |||||
| * | |||||
| * @author Glenn McAllister <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com</a> | |||||
| * @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a> | |||||
| * @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; | |||||
| } | |||||
| } | |||||
| @@ -73,9 +73,11 @@ import java.text.DecimalFormat; | |||||
| import java.util.Random; | import java.util.Random; | ||||
| import java.util.Stack; | import java.util.Stack; | ||||
| import java.util.StringTokenizer; | import java.util.StringTokenizer; | ||||
| import java.util.Vector; | |||||
| import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.filters.util.ChainReaderHelper; | |||||
| import org.apache.tools.ant.types.FilterSetCollection; | import org.apache.tools.ant.types.FilterSetCollection; | ||||
| /** | /** | ||||
| @@ -87,6 +89,7 @@ import org.apache.tools.ant.types.FilterSetCollection; | |||||
| * @author duncan@x180.com | * @author duncan@x180.com | ||||
| * @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | * @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | ||||
| * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | ||||
| * @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a> | |||||
| * | * | ||||
| * @version $Revision$ | * @version $Revision$ | ||||
| */ | */ | ||||
| @@ -178,6 +181,28 @@ public class FileUtils { | |||||
| overwrite, preserveLastModified, encoding); | 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 | |||||
| * <code>destFile</code> file should be made equal | |||||
| * to the last modified time of <code>sourceFile</code>. | |||||
| * | |||||
| * @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. | * Convienence method to copy a file from a source to a destination. | ||||
| * No filtering is performed. | * No filtering is performed. | ||||
| @@ -243,6 +268,27 @@ public class FileUtils { | |||||
| FilterSetCollection filters, boolean overwrite, | FilterSetCollection filters, boolean overwrite, | ||||
| boolean preserveLastModified, String encoding) | boolean preserveLastModified, String encoding) | ||||
| throws IOException { | 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 | |||||
| * <code>destFile</code> file should be made equal | |||||
| * to the last modified time of <code>sourceFile</code>. | |||||
| * | |||||
| * @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() || | if (overwrite || !destFile.exists() || | ||||
| destFile.lastModified() < sourceFile.lastModified()) { | destFile.lastModified() < sourceFile.lastModified()) { | ||||
| @@ -258,7 +304,12 @@ public class FileUtils { | |||||
| parent.mkdirs(); | 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; | BufferedReader in = null; | ||||
| BufferedWriter out = null; | BufferedWriter out = null; | ||||
| @@ -266,8 +317,20 @@ public class FileUtils { | |||||
| in = new BufferedReader(new FileReader(sourceFile)); | in = new BufferedReader(new FileReader(sourceFile)); | ||||
| out = new BufferedWriter(new FileWriter(destFile)); | out = new BufferedWriter(new FileWriter(destFile)); | ||||
| } else { | } 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; | int length; | ||||
| @@ -277,7 +340,11 @@ public class FileUtils { | |||||
| if (line.length() == 0) { | if (line.length() == 0) { | ||||
| out.newLine(); | out.newLine(); | ||||
| } else { | } else { | ||||
| newline = filters.replaceTokens(line); | |||||
| if (filterSetsAvailable) { | |||||
| newline = filters.replaceTokens(line); | |||||
| } else { | |||||
| newline = line; | |||||
| } | |||||
| out.write(newline); | out.write(newline); | ||||
| out.newLine(); | out.newLine(); | ||||
| } | } | ||||