Browse Source

<copy> and <sync> 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
master
Stefan Bodewig 19 years ago
parent
commit
5a381558ae
8 changed files with 322 additions and 77 deletions
  1. +3
    -7
      docs/manual/CoreTasks/copy.html
  2. +3
    -3
      docs/manual/CoreTasks/sync.html
  3. +8
    -1
      src/etc/testcases/taskdefs/copy.xml
  4. +13
    -0
      src/etc/testcases/taskdefs/sync.xml
  5. +256
    -66
      src/main/org/apache/tools/ant/taskdefs/Copy.java
  6. +25
    -0
      src/main/org/apache/tools/ant/taskdefs/Sync.java
  7. +10
    -0
      src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java
  8. +4
    -0
      src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java

+ 3
- 7
docs/manual/CoreTasks/copy.html View File

@@ -16,12 +16,8 @@ or when the destination file does not exist. However, you can explicitly
overwrite files with the <code>overwrite</code> attribute.</p>

<p><a href="../CoreTypes/resources.html#collection">Resource
Collection</a>s are used to select a group of files to copy. Only
file system based resource collections are supported, this includes <a
href="../CoreTypes/fileset.html">fileset</a>s, <a
href="../CoreTypes/filelist.html">filelist</a> and <a
href="../using.html#path">path</a>. To use a resource collection, the
<code>todir</code> attribute must be set.</p>
Collection</a>s are used to select a group of files to copy. To use a
resource collection, the <code>todir</code> attribute must be set.</p>

<p>
<strong>Note: </strong>If you employ filters in your copy operation, you should
@@ -144,7 +140,7 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a>
</table>
<h3>Parameters specified as nested elements</h3>

<h4>fileset or any other filesystem based resource collection</h4>
<h4>fileset or any other resource collection</h4>
<p><a href="../CoreTypes/resources.html#collection">Resource
Collection</a>s are used to select groups of files to copy. To use a
resource collection, the <code>todir</code> attribute must be set.</p>


+ 3
- 3
docs/manual/CoreTasks/sync.html View File

@@ -13,10 +13,10 @@
<h3>Description</h3>

<p>Synchronize a target directory from the files defined in one or
more filesystem based <a href="../CoreTypes/resources.html#collection">Resource Collection</a>s.</p>
more <a href="../CoreTypes/resources.html#collection">Resource Collection</a>s.</p>

<p>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.</p>

@@ -72,7 +72,7 @@ dir, it will get removed from the target.</p>

<h3>Parameters specified as nested elements</h3>

<h4>fileset or any other filesystem based resource collection</h4>
<h4>fileset or any other resource collection</h4>
<p><a href="../CoreTypes/resources.html#collection">Resource
Collection</a>s are used to select groups of files to copy. To use a
resource collection, the <code>todir</code> attribute must be set.</p>


+ 8
- 1
src/etc/testcases/taskdefs/copy.xml View File

@@ -197,7 +197,14 @@ a=b=
</copy>
</target>
<target name="testZipfileset" depends="testResource.prepare">
<zip destfile="${from.dir}/test.zip" roundup="false">
<fileset dir="${from.dir}" excludes="*.zip"/>
</zip>
<copy todir="${to.dir}">
<zipfileset src="${from.dir}/test.zip"/>
</copy>
</target>
<target name="cleanup">
<delete file="copytest1.tmp"/>


+ 13
- 0
src/etc/testcases/taskdefs/sync.xml View File

@@ -48,6 +48,19 @@
</sync>
</target>

<target name="copyandremove-with-zipfileset" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<mkdir dir="${dest}/e"/>
<touch file="${dest}/e/f"/>
<zip destfile="${src}/test.zip">
<fileset dir="${src}" excludes="*.zip"/>
</zip>
<sync todir="${dest}">
<zipfileset src="${src}/test.zip"/>
</sync>
</target>

<target name="copyandremove-emptypreserve" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>


+ 256
- 66
src/main/org/apache/tools/ant/taskdefs/Copy.java View File

@@ -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 <code>FileNameMapper</code> 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.
*
* <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>
*
* @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;
}

}

+ 25
- 0
src/main/org/apache/tools/ant/taskdefs/Sync.java View File

@@ -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;
}
}

/**


+ 10
- 0
src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java View File

@@ -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");
}


+ 4
- 0
src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java View File

@@ -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";


Loading…
Cancel
Save