aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java1111
1 files changed, 1111 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java
new file mode 100644
index 00000000..2394b8f4
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Copy.java
@@ -0,0 +1,1111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+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.types.Mapper;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.resources.FileProvider;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.FlatFileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.LinkedHashtable;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * <p>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>
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Copy extends Task {
+ private static final String MSG_WHEN_COPYING_EMPTY_RC_TO_FILE =
+ "Cannot perform operation from directory to file.";
+
+ static final File NULL_FILE_PLACEHOLDER = new File("/NULL_FILE");
+ static final String LINE_SEPARATOR = System.getProperty("line.separator");
+ // CheckStyle:VisibilityModifier OFF - bc
+ protected File file = null; // the source file
+ protected File destFile = null; // the destination file
+ protected File destDir = null; // the destination directory
+ protected Vector<ResourceCollection> rcs = new Vector<ResourceCollection>();
+ // here to provide API backwards compatibility
+ protected Vector<ResourceCollection> filesets = rcs;
+
+ private boolean enableMultipleMappings = false;
+ 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 boolean failonerror = true;
+
+ protected Hashtable<String, String[]> fileCopyMap = new LinkedHashtable<String, String[]>();
+ protected Hashtable<String, String[]> dirCopyMap = new LinkedHashtable<String, String[]>();
+ protected Hashtable<File, File> completeDirMap = new LinkedHashtable<File, File>();
+
+ protected Mapper mapperElement = null;
+ protected FileUtils fileUtils;
+ //CheckStyle:VisibilityModifier ON
+ private final Vector<FilterChain> filterChains = new Vector<FilterChain>();
+ private final Vector<FilterSet> filterSets = new Vector<FilterSet>();
+ private String inputEncoding = null;
+ private String outputEncoding = null;
+ private long granularity = 0;
+ private boolean force = false;
+ private boolean quiet = false;
+
+ // used to store the single non-file resource to copy when the
+ // tofile attribute has been used
+ private Resource singleResource = null;
+
+ /**
+ * Copy task constructor.
+ */
+ public Copy() {
+ fileUtils = FileUtils.getFileUtils();
+ granularity = fileUtils.getFileTimestampGranularity();
+ }
+
+ /**
+ * Get the FileUtils for this task.
+ * @return the fileutils object.
+ */
+ protected FileUtils getFileUtils() {
+ return fileUtils;
+ }
+
+ /**
+ * Set a single source file to copy.
+ * @param file the file to copy.
+ */
+ public void setFile(final File file) {
+ this.file = file;
+ }
+
+ /**
+ * Set the destination file.
+ * @param destFile the file to copy to.
+ */
+ public void setTofile(final File destFile) {
+ this.destFile = destFile;
+ }
+
+ /**
+ * Set the destination directory.
+ * @param destDir the destination directory.
+ */
+ public void setTodir(final File destDir) {
+ this.destDir = destDir;
+ }
+
+ /**
+ * Add a FilterChain.
+ * @return a filter chain object.
+ */
+ public FilterChain createFilterChain() {
+ final FilterChain filterChain = new FilterChain();
+ filterChains.addElement(filterChain);
+ return filterChain;
+ }
+
+ /**
+ * Add a filterset.
+ * @return a filter set object.
+ */
+ public FilterSet createFilterSet() {
+ final FilterSet filterSet = new FilterSet();
+ filterSets.addElement(filterSet);
+ return filterSet;
+ }
+
+ /**
+ * Give the copied files the same last modified time as the original files.
+ * @param preserve a boolean string.
+ * @deprecated since 1.5.x.
+ * setPreserveLastModified(String) has been deprecated and
+ * replaced with setPreserveLastModified(boolean) to
+ * consistently let the Introspection mechanism work.
+ */
+ @Deprecated
+ public void setPreserveLastModified(final String preserve) {
+ setPreserveLastModified(Project.toBoolean(preserve));
+ }
+
+ /**
+ * Give the copied files the same last modified time as the original files.
+ * @param preserve if true preserve the modified time; default is false.
+ */
+ public void setPreserveLastModified(final boolean preserve) {
+ preserveLastModified = preserve;
+ }
+
+ /**
+ * Get whether to give the copied files the same last modified time as
+ * the original files.
+ * @return the whether destination files will inherit the modification
+ * times of the corresponding source 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<FilterSet> getFilterSets() {
+ return filterSets;
+ }
+
+ /**
+ * Get the filterchains being applied to this operation.
+ *
+ * @return a vector of FilterChain objects.
+ */
+ protected Vector<FilterChain> getFilterChains() {
+ return filterChains;
+ }
+
+ /**
+ * Set filtering mode.
+ * @param filtering if true enable filtering; default is false.
+ */
+ public void setFiltering(final boolean filtering) {
+ this.filtering = filtering;
+ }
+
+ /**
+ * Set overwrite mode regarding existing destination file(s).
+ * @param overwrite if true force overwriting of destination file(s)
+ * even if the destination file(s) are younger than
+ * the corresponding source file. Default is false.
+ */
+ public void setOverwrite(final boolean overwrite) {
+ this.forceOverwrite = overwrite;
+ }
+
+ /**
+ * Whether read-only destinations will be overwritten.
+ *
+ * <p>Defaults to false</p>
+ *
+ * @since Ant 1.8.2
+ */
+ public void setForce(final boolean f) {
+ force = f;
+ }
+
+ /**
+ * Whether read-only destinations will be overwritten.
+ *
+ * @since Ant 1.8.2
+ */
+ public boolean getForce() {
+ return force;
+ }
+
+ /**
+ * Set whether files copied from directory trees will 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.
+ * @param flatten if true flatten the destination directory. Default
+ * is false.
+ */
+ public void setFlatten(final boolean flatten) {
+ this.flatten = flatten;
+ }
+
+ /**
+ * Set verbose mode. Used to force listing of all names of copied files.
+ * @param verbose whether to output the names of copied files.
+ * Default is false.
+ */
+ public void setVerbose(final boolean verbose) {
+ this.verbosity = verbose ? Project.MSG_INFO : Project.MSG_VERBOSE;
+ }
+
+ /**
+ * Set whether to copy empty directories.
+ * @param includeEmpty if true copy empty directories. Default is true.
+ */
+ public void setIncludeEmptyDirs(final boolean includeEmpty) {
+ this.includeEmpty = includeEmpty;
+ }
+
+ /**
+ * Set quiet mode. Used to hide messages when a file or directory to be
+ * copied does not exist.
+ *
+ * @param quiet
+ * whether or not to display error messages when a file or
+ * directory does not exist. Default is false.
+ */
+ public void setQuiet(final boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ /**
+ * Set method of handling mappers that return multiple
+ * mappings for a given source path.
+ * @param enableMultipleMappings If true the task will
+ * copy to all the mappings for a given source path, if
+ * false, only the first file or directory is
+ * processed.
+ * By default, this setting is false to provide backward
+ * compatibility with earlier releases.
+ * @since Ant 1.6
+ */
+ public void setEnableMultipleMappings(final boolean enableMultipleMappings) {
+ this.enableMultipleMappings = enableMultipleMappings;
+ }
+
+ /**
+ * Get whether multiple mapping is enabled.
+ * @return true if multiple mapping is enabled; false otherwise.
+ */
+ public boolean isEnableMultipleMapping() {
+ return enableMultipleMappings;
+ }
+
+ /**
+ * Set whether to fail when errors are encountered. If false, note errors
+ * to the output but keep going. Default is true.
+ * @param failonerror true or false.
+ */
+ public void setFailOnError(final boolean failonerror) {
+ this.failonerror = failonerror;
+ }
+
+ /**
+ * Add a set of files to copy.
+ * @param set a set of files to copy.
+ */
+ public void addFileset(final FileSet set) {
+ add(set);
+ }
+
+ /**
+ * Add a collection of files to copy.
+ * @param res a resource collection to copy.
+ * @since Ant 1.7
+ */
+ public void add(final ResourceCollection res) {
+ rcs.add(res);
+ }
+
+ /**
+ * Define the mapper to map source to destination files.
+ * @return a mapper to be configured.
+ * @exception BuildException if more than one mapper is defined.
+ */
+ public Mapper createMapper() throws BuildException {
+ if (mapperElement != null) {
+ throw new BuildException("Cannot define more than one mapper",
+ getLocation());
+ }
+ mapperElement = new Mapper(getProject());
+ return mapperElement;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ * @param fileNameMapper the mapper to add.
+ * @since Ant 1.6.3
+ */
+ public void add(final FileNameMapper fileNameMapper) {
+ createMapper().add(fileNameMapper);
+ }
+
+ /**
+ * Set the character encoding.
+ * @param encoding the character encoding.
+ * @since 1.32, Ant 1.5
+ */
+ public void setEncoding(final String encoding) {
+ this.inputEncoding = encoding;
+ if (outputEncoding == null) {
+ outputEncoding = encoding;
+ }
+ }
+
+ /**
+ * Get the character encoding to be used.
+ * @return the character encoding, <code>null</code> if not set.
+ *
+ * @since 1.32, Ant 1.5
+ */
+ public String getEncoding() {
+ return inputEncoding;
+ }
+
+ /**
+ * Set the character encoding for output files.
+ * @param encoding the output character encoding.
+ * @since Ant 1.6
+ */
+ public void setOutputEncoding(final String encoding) {
+ this.outputEncoding = encoding;
+ }
+
+ /**
+ * Get the character encoding for output files.
+ * @return the character encoding for output files,
+ * <code>null</code> if not set.
+ *
+ * @since Ant 1.6
+ */
+ public String getOutputEncoding() {
+ return outputEncoding;
+ }
+
+ /**
+ * Set the number of milliseconds leeway to give before deciding a
+ * target is out of date.
+ *
+ * <p>Default is 1 second, or 2 seconds on DOS systems.</p>
+ * @param granularity the granularity used to decide if a target is out of
+ * date.
+ * @since Ant 1.6.2
+ */
+ public void setGranularity(final long granularity) {
+ this.granularity = granularity;
+ }
+
+ /**
+ * Perform the copy operation.
+ * @exception BuildException if an error occurs.
+ */
+ @Override
+ public void execute() throws BuildException {
+ final File savedFile = file; // may be altered in validateAttributes
+ final File savedDestFile = destFile;
+ final File savedDestDir = destDir;
+ ResourceCollection savedRc = null;
+ if (file == null && destFile != null && rcs.size() == 1) {
+ // will be removed in validateAttributes
+ savedRc = rcs.elementAt(0);
+ }
+
+ try {
+ // make sure we don't have an illegal set of options
+ try {
+ validateAttributes();
+ } catch (final BuildException e) {
+ if (failonerror
+ || !getMessage(e)
+ .equals(MSG_WHEN_COPYING_EMPTY_RC_TO_FILE)) {
+ throw e;
+ } else {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ return;
+ }
+ }
+
+ // deal with the single file
+ copySingleFile();
+
+ // deal with the ResourceCollections
+
+ /* for historical and performance reasons we have to do
+ things in a rather complex way.
+
+ (1) Move is optimized to move directories if a fileset
+ has been included completely, therefore FileSets need a
+ special treatment. This is also required to support
+ the failOnError semantice (skip filesets with broken
+ basedir but handle the remaining collections).
+
+ (2) We carry around a few protected methods that work
+ on basedirs and arrays of names. To optimize stuff, all
+ resources with the same basedir get collected in
+ separate lists and then each list is handled in one go.
+ */
+
+ final HashMap<File, List<String>> filesByBasedir = new HashMap<File, List<String>>();
+ final HashMap<File, List<String>> dirsByBasedir = new HashMap<File, List<String>>();
+ final HashSet<File> baseDirs = new HashSet<File>();
+ final ArrayList<Resource> nonFileResources = new ArrayList<Resource>();
+ final int size = rcs.size();
+ for (int i = 0; i < size; i++) {
+ final ResourceCollection rc = rcs.elementAt(i);
+
+ // Step (1) - beware of the ZipFileSet
+ if (rc instanceof FileSet && rc.isFilesystemOnly()) {
+ final FileSet fs = (FileSet) rc;
+ DirectoryScanner ds = null;
+ try {
+ ds = fs.getDirectoryScanner(getProject());
+ } catch (final BuildException e) {
+ if (failonerror
+ || !getMessage(e).endsWith(DirectoryScanner
+ .DOES_NOT_EXIST_POSTFIX)) {
+ throw e;
+ } else {
+ if (!quiet) {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ }
+ continue;
+ }
+ }
+ final File fromDir = fs.getDir(getProject());
+
+ final String[] srcFiles = ds.getIncludedFiles();
+ final String[] srcDirs = ds.getIncludedDirectories();
+ if (!flatten && mapperElement == null
+ && ds.isEverythingIncluded() && !fs.hasPatterns()) {
+ completeDirMap.put(fromDir, destDir);
+ }
+ add(fromDir, srcFiles, filesByBasedir);
+ add(fromDir, srcDirs, dirsByBasedir);
+ baseDirs.add(fromDir);
+ } else { // not a fileset or contains non-file resources
+
+ if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
+ throw new BuildException(
+ "Only FileSystem resources are supported.");
+ }
+
+ for (final Resource r : rc) {
+ if (!r.isExists()) {
+ final String message = "Warning: Could not find resource "
+ + r.toLongString() + " to copy.";
+ if (!failonerror) {
+ if (!quiet) {
+ log(message, Project.MSG_ERR);
+ }
+ } else {
+ throw new BuildException(message);
+ }
+ continue;
+ }
+
+ File baseDir = NULL_FILE_PLACEHOLDER;
+ String name = r.getName();
+ final FileProvider fp = r.as(FileProvider.class);
+ if (fp != null) {
+ final FileResource fr = ResourceUtils.asFileResource(fp);
+ baseDir = getKeyFile(fr.getBaseDir());
+ if (fr.getBaseDir() == null) {
+ name = fr.getFile().getAbsolutePath();
+ }
+ }
+
+ // copying of dirs is trivial and can be done
+ // for non-file resources as well as for real
+ // files.
+ if (r.isDirectory() || fp != null) {
+ add(baseDir, name,
+ r.isDirectory() ? dirsByBasedir
+ : filesByBasedir);
+ baseDirs.add(baseDir);
+ } else { // a not-directory file resource
+ // needs special treatment
+ nonFileResources.add(r);
+ }
+ }
+ }
+ }
+
+ iterateOverBaseDirs(baseDirs, dirsByBasedir, filesByBasedir);
+
+ // do all the copy operations now...
+ try {
+ doFileOperations();
+ } catch (final BuildException e) {
+ if (!failonerror) {
+ if (!quiet) {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ }
+ } else {
+ throw e;
+ }
+ }
+
+ if (nonFileResources.size() > 0 || singleResource != null) {
+ final Resource[] nonFiles =
+ nonFileResources.toArray(new Resource[nonFileResources.size()]);
+ // restrict to out-of-date resources
+ final Map<Resource, String[]> map = scan(nonFiles, destDir);
+ if (singleResource != null) {
+ map.put(singleResource,
+ new String[] {destFile.getAbsolutePath()});
+ }
+ try {
+ doResourceOperations(map);
+ } catch (final BuildException e) {
+ if (!failonerror) {
+ if (!quiet) {
+ log("Warning: " + getMessage(e), Project.MSG_ERR);
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+ } finally {
+ // clean up again, so this instance can be used a second
+ // time
+ singleResource = null;
+ file = savedFile;
+ destFile = savedDestFile;
+ destDir = savedDestDir;
+ if (savedRc != null) {
+ rcs.insertElementAt(savedRc, 0);
+ }
+ fileCopyMap.clear();
+ dirCopyMap.clear();
+ completeDirMap.clear();
+ }
+ }
+
+ /************************************************************************
+ ** protected and private methods
+ ************************************************************************/
+
+ private void copySingleFile() {
+ // deal with the single file
+ if (file != null) {
+ if (file.exists()) {
+ if (destFile == null) {
+ destFile = new File(destDir, file.getName());
+ }
+ if (forceOverwrite || !destFile.exists()
+ || (file.lastModified() - granularity
+ > destFile.lastModified())) {
+ fileCopyMap.put(file.getAbsolutePath(),
+ new String[] {destFile.getAbsolutePath()});
+ } else {
+ log(file + " omitted as " + destFile
+ + " is up to date.", Project.MSG_VERBOSE);
+ }
+ } else {
+ final String message = "Warning: Could not find file "
+ + file.getAbsolutePath() + " to copy.";
+ if (!failonerror) {
+ if (!quiet) {
+ log(message, Project.MSG_ERR);
+ }
+ } else {
+ throw new BuildException(message);
+ }
+ }
+ }
+ }
+
+ private void iterateOverBaseDirs(
+ final HashSet<File> baseDirs, final HashMap<File, List<String>> dirsByBasedir, final HashMap<File, List<String>> filesByBasedir) {
+
+ for (final File f : baseDirs) {
+ final List<String> files = filesByBasedir.get(f);
+ final List<String> dirs = dirsByBasedir.get(f);
+
+ String[] srcFiles = new String[0];
+ if (files != null) {
+ srcFiles = files.toArray(srcFiles);
+ }
+ String[] srcDirs = new String[0];
+ if (dirs != null) {
+ srcDirs = dirs.toArray(srcDirs);
+ }
+ scan(f == NULL_FILE_PLACEHOLDER ? null : f, destDir, srcFiles,
+ srcDirs);
+ }
+ }
+
+ /**
+ * Ensure we have a consistent and legal set of attributes, and set
+ * any internal flags necessary based on different combinations
+ * of attributes.
+ * @exception BuildException if an error occurs.
+ */
+ protected void validateAttributes() throws BuildException {
+ if (file == null && rcs.size() == 0) {
+ throw new BuildException(
+ "Specify at least one source--a file or a resource collection.");
+ }
+ 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.isDirectory()) {
+ throw new BuildException("Use a resource collection to copy directories.");
+ }
+ if (destFile != null && rcs.size() > 0) {
+ if (rcs.size() > 1) {
+ throw new BuildException(
+ "Cannot concatenate multiple files into a single file.");
+ } else {
+ final ResourceCollection rc = rcs.elementAt(0);
+ if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
+ throw new BuildException("Only FileSystem resources are"
+ + " supported.");
+ }
+ if (rc.size() == 0) {
+ throw new BuildException(MSG_WHEN_COPYING_EMPTY_RC_TO_FILE);
+ } else if (rc.size() == 1) {
+ final Resource res = rc.iterator().next();
+ final FileProvider r = res.as(FileProvider.class);
+ if (file == null) {
+ if (r != null) {
+ file = r.getFile();
+ } else {
+ singleResource = res;
+ }
+ rcs.removeElementAt(0);
+ } else {
+ throw new BuildException(
+ "Cannot concatenate multiple files into a single file.");
+ }
+ } else {
+ throw new BuildException(
+ "Cannot concatenate multiple files into a single file.");
+ }
+ }
+ }
+ if (destFile != null) {
+ destDir = destFile.getParentFile();
+ }
+ }
+
+ /**
+ * Compares source files to destination files to see if they should be
+ * copied.
+ *
+ * @param fromDir The source directory.
+ * @param toDir The destination directory.
+ * @param files A list of files to copy.
+ * @param dirs A list of directories to copy.
+ */
+ protected void scan(final File fromDir, final File toDir, final String[] files,
+ final String[] dirs) {
+ final FileNameMapper mapper = getMapper();
+ buildMap(fromDir, toDir, files, mapper, fileCopyMap);
+
+ if (includeEmpty) {
+ buildMap(fromDir, toDir, dirs, mapper, dirCopyMap);
+ }
+ }
+
+ /**
+ * Compares source resources to destination files to see if they
+ * should be copied.
+ *
+ * @param fromResources The source resources.
+ * @param toDir The destination directory.
+ *
+ * @return a Map with the out-of-date resources as keys and an
+ * array of target file names as values.
+ *
+ * @since Ant 1.7
+ */
+ protected Map<Resource, String[]> scan(final Resource[] fromResources, final File toDir) {
+ return buildMap(fromResources, toDir, getMapper());
+ }
+
+ /**
+ * Add to a map of files/directories to copy.
+ *
+ * @param fromDir the source directory.
+ * @param toDir the destination directory.
+ * @param names a list of filenames.
+ * @param mapper a <code>FileNameMapper</code> value.
+ * @param map a map of source file to array of destination files.
+ */
+ protected void buildMap(final File fromDir, final File toDir, final String[] names,
+ final FileNameMapper mapper, final Hashtable<String, String[]> map) {
+ String[] toCopy = null;
+ if (forceOverwrite) {
+ final Vector<String> v = new Vector<String>();
+ 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 {
+ final SourceFileScanner ds = new SourceFileScanner(this);
+ toCopy = ds.restrict(names, fromDir, toDir, mapper, granularity);
+ }
+ for (int i = 0; i < toCopy.length; i++) {
+ final File src = new File(fromDir, toCopy[i]);
+ final String[] mappedFiles = mapper.mapFileName(toCopy[i]);
+
+ if (!enableMultipleMappings) {
+ map.put(src.getAbsolutePath(),
+ new String[] {new File(toDir, mappedFiles[0]).getAbsolutePath()});
+ } else {
+ // reuse the array created by the mapper
+ for (int k = 0; k < mappedFiles.length; k++) {
+ mappedFiles[k] = new File(toDir, mappedFiles[k]).getAbsolutePath();
+ }
+ map.put(src.getAbsolutePath(), mappedFiles);
+ }
+ }
+ }
+
+ /**
+ * Create a map of resources to copy.
+ *
+ * @param fromResources The source resources.
+ * @param toDir the destination directory.
+ * @param mapper a <code>FileNameMapper</code> value.
+ * @return a map of source resource to array of destination files.
+ * @since Ant 1.7
+ */
+ protected Map<Resource, String[]> buildMap(final Resource[] fromResources, final File toDir,
+ final FileNameMapper mapper) {
+ final HashMap<Resource, String[]> map = new HashMap<Resource, String[]>();
+ Resource[] toCopy = null;
+ if (forceOverwrite) {
+ final Vector<Resource> v = new Vector<Resource>();
+ for (int i = 0; i < fromResources.length; i++) {
+ if (mapper.mapFileName(fromResources[i].getName()) != null) {
+ v.addElement(fromResources[i]);
+ }
+ }
+ toCopy = new Resource[v.size()];
+ v.copyInto(toCopy);
+ } else {
+ toCopy = ResourceUtils.selectOutOfDateSources(this, fromResources,
+ mapper,
+ new ResourceFactory() {
+ public Resource getResource(final String name) {
+ return new FileResource(toDir, name);
+ }
+ },
+ granularity);
+ }
+ for (int i = 0; i < toCopy.length; i++) {
+ final String[] mappedFiles = mapper.mapFileName(toCopy[i].getName());
+ for (int j = 0; j < mappedFiles.length; j++) {
+ if (mappedFiles[j] == null) {
+ throw new BuildException("Can't copy a resource without a"
+ + " name if the mapper doesn't"
+ + " provide one.");
+ }
+ }
+
+ if (!enableMultipleMappings) {
+ map.put(toCopy[i],
+ new String[] {new File(toDir, mappedFiles[0]).getAbsolutePath()});
+ } else {
+ // reuse the array created by the mapper
+ for (int k = 0; k < mappedFiles.length; k++) {
+ mappedFiles[k] = new File(toDir, mappedFiles[k]).getAbsolutePath();
+ }
+ map.put(toCopy[i], mappedFiles);
+ }
+ }
+ return map;
+ }
+
+ /**
+ * 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());
+
+ for (final Map.Entry<String, String[]> e : fileCopyMap.entrySet()) {
+ final String fromFile = e.getKey();
+ final String[] toFiles = e.getValue();
+
+ for (int i = 0; i < toFiles.length; i++) {
+ final String toFile = toFiles[i];
+
+ if (fromFile.equals(toFile)) {
+ log("Skipping self-copy of " + fromFile, verbosity);
+ continue;
+ }
+ try {
+ log("Copying " + fromFile + " to " + toFile, verbosity);
+
+ final FilterSetCollection executionFilters =
+ new FilterSetCollection();
+ if (filtering) {
+ executionFilters
+ .addFilterSet(getProject().getGlobalFilterSet());
+ }
+ for (final FilterSet filterSet : filterSets) {
+ executionFilters.addFilterSet(filterSet);
+ }
+ fileUtils.copyFile(new File(fromFile), new File(toFile),
+ executionFilters,
+ filterChains, forceOverwrite,
+ preserveLastModified,
+ /* append: */ false, inputEncoding,
+ outputEncoding, getProject(),
+ getForce());
+ } catch (final IOException ioe) {
+ String msg = "Failed to copy " + fromFile + " to " + toFile
+ + " due to " + getDueTo(ioe);
+ final File targetFile = new File(toFile);
+ if (!(ioe instanceof
+ ResourceUtils.ReadOnlyTargetFileException)
+ && targetFile.exists() && !targetFile.delete()) {
+ msg += " and I couldn't delete the corrupt " + toFile;
+ }
+ if (failonerror) {
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ }
+ }
+ if (includeEmpty) {
+ int createCount = 0;
+ for (final String[] dirs : dirCopyMap.values()) {
+ for (int i = 0; i < dirs.length; i++) {
+ final File d = new File(dirs[i]);
+ if (!d.exists()) {
+ if (!(d.mkdirs() || d.isDirectory())) {
+ log("Unable to create directory "
+ + d.getAbsolutePath(), Project.MSG_ERR);
+ } else {
+ createCount++;
+ }
+ }
+ }
+ }
+ if (createCount > 0) {
+ log("Copied " + dirCopyMap.size()
+ + " empty director"
+ + (dirCopyMap.size() == 1 ? "y" : "ies")
+ + " to " + createCount
+ + " empty director"
+ + (createCount == 1 ? "y" : "ies") + " under "
+ + destDir.getAbsolutePath());
+ }
+ }
+ }
+
+ /**
+ * Actually does the resource copies.
+ * This is a good method for subclasses to override.
+ * @param map a map of source resource to array of destination files.
+ * @since Ant 1.7
+ */
+ protected void doResourceOperations(final Map<Resource, String[]> map) {
+ if (map.size() > 0) {
+ log("Copying " + map.size()
+ + " resource" + (map.size() == 1 ? "" : "s")
+ + " to " + destDir.getAbsolutePath());
+
+ for (final Map.Entry<Resource, String[]> e : map.entrySet()) {
+ final Resource fromResource = e.getKey();
+ for (final String toFile : e.getValue()) {
+ try {
+ log("Copying " + fromResource + " to " + toFile,
+ verbosity);
+
+ final FilterSetCollection executionFilters = new FilterSetCollection();
+ if (filtering) {
+ executionFilters
+ .addFilterSet(getProject().getGlobalFilterSet());
+ }
+ for (final FilterSet filterSet : filterSets) {
+ executionFilters.addFilterSet(filterSet);
+ }
+ ResourceUtils.copyResource(fromResource,
+ new FileResource(destDir,
+ toFile),
+ executionFilters,
+ filterChains,
+ forceOverwrite,
+ preserveLastModified,
+ /* append: */ false,
+ inputEncoding,
+ outputEncoding,
+ getProject(),
+ getForce());
+ } catch (final IOException ioe) {
+ String msg = "Failed to copy " + fromResource
+ + " to " + toFile
+ + " due to " + getDueTo(ioe);
+ final File targetFile = new File(toFile);
+ if (!(ioe instanceof
+ ResourceUtils.ReadOnlyTargetFileException)
+ && targetFile.exists() && !targetFile.delete()) {
+ msg += " and I couldn't delete the corrupt " + toFile;
+ }
+ if (failonerror) {
+ throw new BuildException(msg, ioe, getLocation());
+ }
+ log(msg, Project.MSG_ERR);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether this task can deal with non-file resources.
+ *
+ * <p>&lt;copy&gt; can while &lt;move&gt; can't since we don't
+ * know how to remove non-file resources.</p>
+ *
+ * <p>This implementation returns true only if this task is
+ * &lt;copy&gt;. Any subclass of this class that also wants to
+ * support non-file resources needs to override this method. We
+ * need to do so for backwards compatibility reasons since we
+ * can't expect subclasses to support resources.</p>
+ * @return true if this task supports non file resources.
+ * @since Ant 1.7
+ */
+ protected boolean supportsNonFileResources() {
+ return getClass().equals(Copy.class);
+ }
+
+ /**
+ * Adds the given strings to a list contained in the given map.
+ * The file is the key into the map.
+ */
+ private static void add(File baseDir, final String[] names, final Map<File, List<String>> m) {
+ if (names != null) {
+ baseDir = getKeyFile(baseDir);
+ List<String> l = m.get(baseDir);
+ if (l == null) {
+ l = new ArrayList<String>(names.length);
+ m.put(baseDir, l);
+ }
+ l.addAll(java.util.Arrays.asList(names));
+ }
+ }
+
+ /**
+ * Adds the given string to a list contained in the given map.
+ * The file is the key into the map.
+ */
+ private static void add(final File baseDir, final String name, final Map<File, List<String>> m) {
+ if (name != null) {
+ add(baseDir, new String[] {name}, m);
+ }
+ }
+
+ /**
+ * Either returns its argument or a plaeholder if the argument is null.
+ */
+ private static File getKeyFile(final File f) {
+ return f == null ? NULL_FILE_PLACEHOLDER : f;
+ }
+
+ /**
+ * returns the mapper to use based on nested elements or the
+ * flatten attribute.
+ */
+ private FileNameMapper getMapper() {
+ FileNameMapper mapper = null;
+ if (mapperElement != null) {
+ mapper = mapperElement.getImplementation();
+ } else if (flatten) {
+ mapper = new FlatFileNameMapper();
+ } else {
+ mapper = new IdentityMapper();
+ }
+ return mapper;
+ }
+
+ /**
+ * Handle getMessage() for exceptions.
+ * @param ex the exception to handle
+ * @return ex.getMessage() if ex.getMessage() is not null
+ * otherwise return ex.toString()
+ */
+ private String getMessage(final Exception ex) {
+ return ex.getMessage() == null ? ex.toString() : ex.getMessage();
+ }
+
+ /**
+ * Returns a reason for failure based on
+ * the exception thrown.
+ * If the exception is not IOException output the class name,
+ * output the message
+ * if the exception is MalformedInput add a little note.
+ */
+ private String getDueTo(final Exception ex) {
+ final boolean baseIOException = ex.getClass() == IOException.class;
+ final StringBuffer message = new StringBuffer();
+ if (!baseIOException || ex.getMessage() == null) {
+ message.append(ex.getClass().getName());
+ }
+ if (ex.getMessage() != null) {
+ if (!baseIOException) {
+ message.append(" ");
+ }
+ message.append(ex.getMessage());
+ }
+ if (ex.getClass().getName().indexOf("MalformedInput") != -1) {
+ message.append(LINE_SEPARATOR);
+ message.append(
+ "This is normally due to the input file containing invalid");
+ message.append(LINE_SEPARATOR);
+ message.append("bytes for the character encoding used : ");
+ message.append(
+ (inputEncoding == null
+ ? fileUtils.getDefaultEncoding() : inputEncoding));
+ message.append(LINE_SEPARATOR);
+ }
+ return message.toString();
+ }
+}