From 5a381558aef54280f925781bed3d4c9ba7ced360 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Thu, 6 Oct 2005 03:51:48 +0000 Subject: [PATCH] and can now work on non-filesystem resources as well git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@306521 13f79535-47bb-0310-9956-ffa450edef68 --- docs/manual/CoreTasks/copy.html | 10 +- docs/manual/CoreTasks/sync.html | 6 +- src/etc/testcases/taskdefs/copy.xml | 9 +- src/etc/testcases/taskdefs/sync.xml | 13 + .../org/apache/tools/ant/taskdefs/Copy.java | 322 ++++++++++++++---- .../org/apache/tools/ant/taskdefs/Sync.java | 25 ++ .../apache/tools/ant/taskdefs/CopyTest.java | 10 + .../apache/tools/ant/taskdefs/SyncTest.java | 4 + 8 files changed, 322 insertions(+), 77 deletions(-) diff --git a/docs/manual/CoreTasks/copy.html b/docs/manual/CoreTasks/copy.html index 25b65cda6..65f4bc728 100644 --- a/docs/manual/CoreTasks/copy.html +++ b/docs/manual/CoreTasks/copy.html @@ -16,12 +16,8 @@ or when the destination file does not exist. However, you can explicitly overwrite files with the overwrite attribute.

Resource -Collections are used to select a group of files to copy. Only -file system based resource collections are supported, this includes filesets, filelist and path. To use a resource collection, the -todir attribute must be set.

+Collections are used to select a group of files to copy. To use a +resource collection, the todir attribute must be set.

Note: If you employ filters in your copy operation, you should @@ -144,7 +140,7 @@ operation as filtersets

Parameters specified as nested elements

-

fileset or any other filesystem based resource collection

+

fileset or any other resource collection

Resource Collections are used to select groups of files to copy. To use a resource collection, the todir attribute must be set.

diff --git a/docs/manual/CoreTasks/sync.html b/docs/manual/CoreTasks/sync.html index b6aa00479..200eff670 100644 --- a/docs/manual/CoreTasks/sync.html +++ b/docs/manual/CoreTasks/sync.html @@ -13,10 +13,10 @@

Description

Synchronize a target directory from the files defined in one or -more filesystem based Resource Collections.

+more Resource Collections.

Any file in the target directory that has not been matched by at -least one of the nested resource collection gets removed. I.e. if you exclude a +least one of the nested resource collections gets removed. I.e. if you exclude a file in your sources and a file of that name is present in the target dir, it will get removed from the target.

@@ -72,7 +72,7 @@ dir, it will get removed from the target.

Parameters specified as nested elements

-

fileset or any other filesystem based resource collection

+

fileset or any other resource collection

Resource Collections are used to select groups of files to copy. To use a resource collection, the todir attribute must be set.

diff --git a/src/etc/testcases/taskdefs/copy.xml b/src/etc/testcases/taskdefs/copy.xml index 4691ec110..2ef1f24c2 100644 --- a/src/etc/testcases/taskdefs/copy.xml +++ b/src/etc/testcases/taskdefs/copy.xml @@ -197,7 +197,14 @@ a=b=
- + + + + + + + + diff --git a/src/etc/testcases/taskdefs/sync.xml b/src/etc/testcases/taskdefs/sync.xml index 53cb13a5b..87b0846c4 100644 --- a/src/etc/testcases/taskdefs/sync.xml +++ b/src/etc/testcases/taskdefs/sync.xml @@ -48,6 +48,19 @@ + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/Copy.java b/src/main/org/apache/tools/ant/taskdefs/Copy.java index d32632f20..a9b86b775 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Copy.java +++ b/src/main/org/apache/tools/ant/taskdefs/Copy.java @@ -39,10 +39,12 @@ import org.apache.tools.ant.types.FilterChain; import org.apache.tools.ant.types.FilterSetCollection; 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.FileResource; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.FileNameMapper; import org.apache.tools.ant.util.IdentityMapper; +import org.apache.tools.ant.util.ResourceUtils; import org.apache.tools.ant.util.SourceFileScanner; import org.apache.tools.ant.util.FlatFileNameMapper; @@ -435,76 +437,94 @@ public class Copy extends Task { HashMap filesByBasedir = new HashMap(); HashMap dirsByBasedir = new HashMap(); HashSet baseDirs = new HashSet(); + ArrayList nonFileResources = new ArrayList(); for (int i = 0; i < rcs.size(); i++) { ResourceCollection rc = (ResourceCollection) rcs.elementAt(i); - if (rc.isFilesystemOnly()) { - - // Step (1) - if (rc instanceof FileSet) { - FileSet fs = (FileSet) rc; - DirectoryScanner ds = null; - try { - ds = fs.getDirectoryScanner(getProject()); - } catch (BuildException e) { - if (failonerror - || !e.getMessage().endsWith(" not found.")) { - throw e; - } else { - log("Warning: " + e.getMessage()); - continue; - } + // Step (1) - beware of the ZipFileSet + if (rc instanceof FileSet && rc.isFilesystemOnly()) { + FileSet fs = (FileSet) rc; + DirectoryScanner ds = null; + try { + ds = fs.getDirectoryScanner(getProject()); + } catch (BuildException e) { + if (failonerror + || !e.getMessage().endsWith(" not found.")) { + throw e; + } else { + log("Warning: " + e.getMessage()); + continue; } - File fromDir = fs.getDir(getProject()); + } + File fromDir = fs.getDir(getProject()); + + String[] srcFiles = ds.getIncludedFiles(); + 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."); + } - String[] srcFiles = ds.getIncludedFiles(); - String[] srcDirs = ds.getIncludedDirectories(); - if (!flatten && mapperElement == null - && ds.isEverythingIncluded() && !fs.hasPatterns()) { - completeDirMap.put(fromDir, destDir); + Iterator resources = rc.iterator(); + while (resources.hasNext()) { + Resource r = (Resource) resources.next(); + if (!r.isExists()) { + continue; } - add(fromDir, srcFiles, filesByBasedir); - add(fromDir, srcDirs, dirsByBasedir); - baseDirs.add(fromDir); - } else { // not a fileset - - Iterator resources = rc.iterator(); - while (resources.hasNext()) { - FileResource fr = (FileResource) resources.next(); - if (!fr.isExists()) { - continue; + + File baseDir = NULL_FILE_PLACEHOLDER; + String name = r.getName(); + if (r instanceof FileResource) { + FileResource fr = (FileResource) r; + baseDir = getKeyFile(fr.getBaseDir()); + if (fr.getBaseDir() == null) { + name = fr.getFile().getAbsolutePath(); } - File baseDir = getKeyFile(fr.getBaseDir()); - add(baseDir, baseDir == NULL_FILE_PLACEHOLDER - ? fr.getFile().getAbsolutePath() : fr.getName(), - fr.isDirectory() ? dirsByBasedir - : filesByBasedir); + } + + // copying of dirs is trivial and can be done + // for non-file resources as well as for real + // files. + if (r.isDirectory() || r instanceof FileResource) { + add(baseDir, name, + r.isDirectory() ? dirsByBasedir + : filesByBasedir); baseDirs.add(baseDir); + } else { // a not-directory file resource + // needs special treatment + nonFileResources.add(r); } } + } + } - Iterator iter = baseDirs.iterator(); - while (iter.hasNext()) { - File f = (File) iter.next(); - List files = (List) filesByBasedir.get(f); - List dirs = (List) dirsByBasedir.get(f); + Iterator iter = baseDirs.iterator(); + while (iter.hasNext()) { + File f = (File) iter.next(); + List files = (List) filesByBasedir.get(f); + List dirs = (List) dirsByBasedir.get(f); - String[] srcFiles = new String[0]; - if (files != null) { - srcFiles = (String[]) files.toArray(srcFiles); - } - String[] srcDirs = new String[0]; - if (dirs != null) { - srcDirs = (String[]) dirs.toArray(srcDirs); - } - scan(f == NULL_FILE_PLACEHOLDER ? null : f, destDir, - srcFiles, srcDirs); - } - } else { // not a File resource collection - throw new BuildException( - "Only FileSystem resources are supported."); + String[] srcFiles = new String[0]; + if (files != null) { + srcFiles = (String[]) files.toArray(srcFiles); } + String[] srcDirs = new String[0]; + if (dirs != null) { + srcDirs = (String[]) dirs.toArray(srcDirs); + } + scan(f == NULL_FILE_PLACEHOLDER ? null : f, destDir, srcFiles, + srcDirs); } + // do all the copy operations now... try { doFileOperations(); @@ -515,6 +535,22 @@ public class Copy extends Task { throw e; } } + + if (nonFileResources.size() > 0) { + Resource[] nonFiles = + (Resource[]) nonFileResources.toArray(new Resource[0]); + // restrict to out-of-date resources + Map map = scan(nonFiles, destDir); + try { + doResourceOperations(map); + } catch (BuildException e) { + if (!failonerror) { + log("Warning: " + e.getMessage(), Project.MSG_ERR); + } else { + throw e; + } + } + } } finally { // clean up again, so this instance can be used a second // time @@ -563,7 +599,8 @@ public class Copy extends Task { ResourceCollection rc = (ResourceCollection) rcs.elementAt(0); if (!rc.isFilesystemOnly()) { throw new BuildException("Only FileSystem resources are" - + " supported."); + + " supported when concatenating" + + " files."); } if (rc.size() == 0) { throw new BuildException( @@ -599,14 +636,7 @@ public class Copy extends Task { */ 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(); - } + FileNameMapper mapper = getMapper(); buildMap(fromDir, toDir, files, mapper, fileCopyMap); if (includeEmpty) { @@ -614,6 +644,22 @@ public class Copy extends Task { } } + /** + * 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 scan(Resource[] fromResources, File toDir) { + return buildMap(fromResources, toDir, getMapper()); + } + /** * Add to a map of files/directories to copy. * @@ -641,7 +687,6 @@ public class Copy extends Task { } for (int i = 0; i < toCopy.length; i++) { File src = new File(fromDir, toCopy[i]); - String[] mappedFiles = mapper.mapFileName(toCopy[i]); if (!enableMultipleMappings) { @@ -657,6 +702,56 @@ public class Copy extends Task { } } + /** + * Create a map of resources to copy. + * + * @param fromResources The source resources. + * @param toDir the destination directory. + * @param mapper a FileNameMapper value. + * @return a map of source resource to array of destination files. + * @since Ant 1.7 + */ + protected Map buildMap(Resource[] fromResources, final File toDir, + FileNameMapper mapper) { + HashMap map = new HashMap(); + Resource[] toCopy = null; + if (forceOverwrite) { + Vector v = new Vector(); + 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(String name) { + return new FileResource(toDir, name); + } + }, + granularity); + } + for (int i = 0; i < toCopy.length; i++) { + String[] mappedFiles = mapper.mapFileName(toCopy[i].getName()); + + 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. @@ -738,6 +833,84 @@ public class Copy extends Task { } } + /** + * 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(Map map) { + if (map.size() > 0) { + log("Copying " + map.size() + + " resource" + (map.size() == 1 ? "" : "s") + + " to " + destDir.getAbsolutePath()); + + Iterator iter = map.keySet().iterator(); + while (iter.hasNext()) { + Resource fromResource = (Resource) iter.next(); + String[] toFiles = (String[]) map.get(fromResource); + + for (int i = 0; i < toFiles.length; i++) { + String toFile = toFiles[i]; + + try { + log("Copying " + fromResource + " to " + toFile, + verbosity); + + FilterSetCollection executionFilters = + new FilterSetCollection(); + if (filtering) { + executionFilters + .addFilterSet(getProject().getGlobalFilterSet()); + } + for (Enumeration filterEnum = filterSets.elements(); + filterEnum.hasMoreElements();) { + executionFilters + .addFilterSet((FilterSet) filterEnum.nextElement()); + } + ResourceUtils.copyResource(fromResource, + new FileResource(destDir, + toFile), + executionFilters, + filterChains, + forceOverwrite, + preserveLastModified, + inputEncoding, + outputEncoding, + getProject()); + } catch (IOException ioe) { + String msg = "Failed to copy " + fromResource + + " to " + toFile + + " due to " + ioe.getMessage(); + File targetFile = new File(toFile); + if (targetFile.exists() && !targetFile.delete()) { + msg += " and I couldn't delete the corrupt " + toFile; + } + throw new BuildException(msg, ioe, getLocation()); + } + } + } + } + } + + /** + * Whether this task can deal with non-file resources. + * + *

<copy> can while <move> can't since we don't + * know how to remove non-file resources.

+ * + *

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.

+ * + * @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. @@ -770,4 +943,21 @@ public class Copy extends Task { private static File getKeyFile(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; + } + } diff --git a/src/main/org/apache/tools/ant/taskdefs/Sync.java b/src/main/org/apache/tools/ant/taskdefs/Sync.java index 17cc14836..15e3b87c7 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Sync.java +++ b/src/main/org/apache/tools/ant/taskdefs/Sync.java @@ -26,6 +26,8 @@ package org.apache.tools.ant.taskdefs; import java.io.File; import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; import java.util.Set; import org.apache.tools.ant.BuildException; @@ -35,6 +37,7 @@ import org.apache.tools.ant.Task; import org.apache.tools.ant.types.AbstractFileSet; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.PatternSet; +import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.selectors.FileSelector; import org.apache.tools.ant.types.selectors.NoneSelector; @@ -384,6 +387,21 @@ public class Sync extends Task { } } + /** + * @see Copy#scan(Resource[], File) + */ + protected Map scan(Resource[] resources, File toDir) { + assertTrue("No mapper", mapperElement == null); + + Map m = super.scan(resources, toDir); + + Iterator iter = m.keySet().iterator(); + while (iter.hasNext()) { + nonOrphans.add(((Resource) iter.next()).getName()); + } + return m; + } + /** * Get the destination directory. * @return the destination directory @@ -400,6 +418,13 @@ public class Sync extends Task { return includeEmpty; } + /** + * Yes, we can. + * @since Ant 1.7 + */ + protected boolean supportsNonFileResources() { + return true; + } } /** diff --git a/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java b/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java index 16e648a96..687fe6a38 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java @@ -197,6 +197,16 @@ public class CopyTest extends BuildFileTest { assertTrue(file3.exists()); } + public void testZipfileset() { + executeTarget("testZipfileset"); + File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file1.txt"); + File file2 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file2.txt"); + File file3 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file3.txt"); + assertTrue(file1.exists()); + assertTrue(file2.exists()); + assertTrue(file3.exists()); + } + public void _testResourcePlain() { executeTarget("testResourcePlain"); } diff --git a/src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java b/src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java index 30aa99b20..5fffdacc2 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java @@ -67,6 +67,10 @@ public class SyncTest extends BuildFileTest { testCopyAndRemove("copyandremove-with-filelist"); } + public void testCopyAndRemoveWithZipfileset() { + testCopyAndRemove("copyandremove-with-zipfileset"); + } + private void testCopyAndRemove(String target) { executeTarget(target); String d = getProject().getProperty("dest") + "/a/b/c/d";