|
- /*
- * 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.Arrays;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Hashtable;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- 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.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<>();
- // 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<>();
- protected Hashtable<String, String[]> dirCopyMap = new LinkedHashtable<>();
- protected Hashtable<File, File> completeDirMap = new LinkedHashtable<>();
-
- protected Mapper mapperElement = null;
- protected FileUtils fileUtils;
- //CheckStyle:VisibilityModifier ON
- private final Vector<FilterChain> filterChains = new Vector<>();
- private final Vector<FilterSet> filterSets = new Vector<>();
- 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>
- *
- * @param f boolean
- * @since Ant 1.8.2
- */
- public void setForce(final boolean f) {
- force = f;
- }
-
- /**
- * Whether read-only destinations will be overwritten.
- *
- * @return boolean
- * @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 semantic (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 Map<File, List<String>> filesByBasedir = new HashMap<>();
- final Map<File, List<String>> dirsByBasedir = new HashMap<>();
- final Set<File> baseDirs = new HashSet<>();
- final List<Resource> nonFileResources = new ArrayList<>();
-
- for (ResourceCollection rc : rcs) {
-
- // Step (1) - beware of the ZipFileSet
- if (rc instanceof FileSet && rc.isFilesystemOnly()) {
- final FileSet fs = (FileSet) rc;
- DirectoryScanner ds;
- try {
- ds = fs.getDirectoryScanner(getProject());
- } catch (final BuildException e) {
- if (failonerror
- || !getMessage(e).endsWith(DirectoryScanner
- .DOES_NOT_EXIST_POSTFIX)) {
- throw e;
- }
- 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.isEmpty() || 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 Set<File> baseDirs,
- final Map<File, List<String>> dirsByBasedir,
- final Map<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.isEmpty()) {
- 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.isEmpty()) {
- if (rcs.size() > 1) {
- throw new BuildException(
- "Cannot concatenate multiple files into a single file.");
- }
- final ResourceCollection rc = rcs.elementAt(0);
- if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
- throw new BuildException(
- "Only FileSystem resources are supported.");
- }
- if (rc.isEmpty()) {
- throw new BuildException(MSG_WHEN_COPYING_EMPTY_RC_TO_FILE);
- }
- 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 (String name : names) {
- if (mapper.mapFileName(name) != null) {
- v.addElement(name);
- }
- }
- toCopy = new String[v.size()];
- v.copyInto(toCopy);
- } else {
- final SourceFileScanner ds = new SourceFileScanner(this);
- toCopy = ds.restrict(names, fromDir, toDir, mapper, granularity);
- }
- for (String name : toCopy) {
- final File src = new File(fromDir, name);
- final String[] mappedFiles = mapper.mapFileName(name);
- if (mappedFiles == null || mappedFiles.length == 0) {
- continue;
- }
-
- 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 Map<Resource, String[]> map = new HashMap<>();
- Resource[] toCopy;
- if (forceOverwrite) {
- final List<Resource> v = new ArrayList<>();
- for (Resource rc : fromResources) {
- if (mapper.mapFileName(rc.getName()) != null) {
- v.add(rc);
- }
- }
- toCopy = v.toArray(new Resource[v.size()]);
- } else {
- toCopy = ResourceUtils.selectOutOfDateSources(this, fromResources, mapper,
- name -> new FileResource(toDir, name), granularity);
- }
- for (Resource rc : toCopy) {
- final String[] mappedFiles = mapper.mapFileName(rc.getName());
- if (mappedFiles == null || mappedFiles.length == 0) {
- throw new BuildException("Can't copy a resource without a"
- + " name if the mapper doesn't"
- + " provide one.");
- }
- if (!enableMultipleMappings) {
- map.put(rc, 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(rc, 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.isEmpty()) {
- 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();
-
- for (final String toFile : e.getValue()) {
- 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 (String dir : dirs) {
- final File d = new File(dir);
- 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.isEmpty()) {
- 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><copy> can while <move> can't since we don't
- * know how to remove non-file resources.</p>
- *
- * <p>This implementation returns true only if this task is
- * <copy>. 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<>(names.length);
- m.put(baseDir, l);
- }
- l.addAll(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 placeholder 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 StringBuilder message = new StringBuilder();
- 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().contains("MalformedInput")) {
- 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();
- }
- }
|