From d93d7b09161b9fed366062f9fc095e46e772a69e Mon Sep 17 00:00:00 2001
From: Stefan Bodewig Copies a file or FileSet to a new file or directory. By default, files are
+ Copies a file or resource collection to a new file or directory. By default, files are
only copied if the source file is newer than the destination file,
or when the destination file does not exist. However, you can explicitly
overwrite files with the FileSets are used to select a
-set of files to copy.
-To use a 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
+
Note: If you employ filters in your copy operation, you should
@@ -39,7 +42,7 @@ operation as filtersets
FileSets are used to select
-sets of files to copy.
- To use a fileset, the Resource
+Collections are used to select groups of files to copy. To use a
+resource collection, the Prior to Ant 1.7 only You can define filename transformations by using a nested
+ Note that the source name handed to the mapper depends on the
+resource collection you use. If you use FilterSets are used to replace
tokens in files that are copied.
@@ -207,7 +219,6 @@ followed by Copy a set of files to a directory, replacing @TITLE@ with Foo Bar
in all files. Collect all items from the current CLASSPATH setting into a
+destination directory, flattening the directory structure. Unix Note: File permissions are not retained when files
are copied; they end up with the default Moves a file to a new file or directory, or sets of files to
+ Moves a file to a new file or directory, or collections of files to
a new directory. By default, the
destination file is overwritten if it already exists. When overwrite is
turned off, then files are only moved if the source file is newer than
the destination file, or when the destination file does not exist. FileSets are used to select sets of files
-to move to the todir directory. Resource
+Collections are used to select a group of files to move. Only
+file system based resource collections are supported, this includes filesets, filelist and path. Prior to Ant 1.7 only
+ Since Ant 1.6.3, the file attribute may be used to move
(rename) an entire directory. If tofile denotes an existing file, or
there is a directory by the same name in todir, the action will fail.
@@ -32,7 +41,7 @@ there is a directory by the same name in todir, the action will fail.
Note that the source name handed to the mapper depends on the
+resource collection you use. If you use The Move task supports nested
FilterChains. Move a list of files to a new directory Append Synchronize a target directory from the files defined in one or
-more filesets. Any file in the target directory that has not been matched by at
-least one of the nested filesets gets removed. I.e. if you exclude a
+least one of the nested resource collection 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.Copy
Description
-overwrite
attribute.<fileset>
, the todir
attribute
-must be set.todir
attribute must be set.file
The file to copy.
Yes, unless a nested
-
+ resource collection element is used.
<fileset>
element is used.
preservelastmodified
@@ -52,7 +55,7 @@ operation as filtersets
The file to copy to.
With the file
attribute, either tofile
or todir
can be used.
- With nested <fileset>
elements, if the set of files
+ With nested resource collection elements, if the number of included files
is greater than 1, or if only the dir
attribute is
specified in the <fileset>
, or if the
file
attribute is also specified, then only
@@ -141,10 +144,12 @@ operation as filtersets
Parameters specified as nested elements
-fileset
- todir
attribute must be set.fileset or any other filesystem based resource collection
+todir
attribute must be set.<fileset>
has been
+supported as a nested element.mapper
<fileset>
+or any other collection that provides a base directory, the name
+passed to the mapper will be a relative filename, relative to the base
+directory. In any other case the absolute filename of the source will
+be used.filterset
<filterset>
elements.
</copy>
-
@@ -219,6 +230,16 @@ in all files.
+ <copy todir="dest" flatten="true">
+ <path>
+ <pathelement path="${java.class.path}"/>
+ </path>
+ </copy>
+
+
UMASK
permissions
instead. This
diff --git a/docs/manual/CoreTasks/move.html b/docs/manual/CoreTasks/move.html
index b991edc4e..30f896ed7 100644
--- a/docs/manual/CoreTasks/move.html
+++ b/docs/manual/CoreTasks/move.html
@@ -10,13 +10,22 @@
Move
Description
-<fileset>
has been supported as a nested element.
+To use a resource collection, the todir
attribute must be
+set.file
the file or directory to move
One of file or
- at least one nested fileset element
+ at least one nested resource collection element
preservelastmodified
@@ -135,6 +144,12 @@ there is a directory by the same name in todir, the action will fail.
href="../CoreTypes/mapper.html">mapper element. The default mapper used by
<move>
is the identity.
+<fileset>
+or any other collection that provides a base directory, the name
+passed to the mapper will be a relative filename, relative to the base
+directory. In any other case the absolute filename of the source will
+be used.filterchain
<filterset>
elements.
</fileset>
</move>
+
+ <move todir="some/new/dir">
+ <filelist dir="my/src/dir">
+ <file name="file1.txt"/>
+ <file name="file2.txt"/>
+ </filelist>
+ </move>
+
".bak"
to the names of all files
in a directory.
diff --git a/docs/manual/CoreTasks/sync.html b/docs/manual/CoreTasks/sync.html
index d6e01d221..b6aa00479 100644
--- a/docs/manual/CoreTasks/sync.html
+++ b/docs/manual/CoreTasks/sync.html
@@ -13,10 +13,10 @@
Description
todir
- the target directory to sync with the filesets
+ the target directory to sync with the resource collections
Yes
@@ -40,7 +40,7 @@ dir, it will get removed from the target.
@@ -72,9 +72,12 @@ dir, it will get removed from the target.includeEmptyDirs
- Copy any empty directories included in the FileSet(s).
+ Copy any empty directories included in the resource collection(s).
No; defaults to true.
FileSets are used to select -sets of files and directories.
+Resource
+Collections are used to select groups of files to copy. To use a
+resource collection, the todir
attribute must be set.
Prior to Ant 1.7 only <fileset>
has been
+supported as a nested element.
path
element as a nested ResourceCollection.
- * @param path
+ * Add a collection of files to copy.
+ * @param res a resource collection to copy.
+ * @since Ant 1.7
*/
- public void addPath(Path path) {
- //add((ResourceCollection)path);
- }
- /**
- * Adds a Resource element as a nested ResourceCollection.
- * @param path
- * /
- public void add(Resource res) {
- add((ResourceCollection)res);
- }
- /**
- * Adds a resources
element as a nested ResourceCollection.
- * @param path
- * /
- public void add(Resources res) {
- add((ResourceCollection)res);
+ public void add(ResourceCollection res) {
+ rcs.add(res);
}
- */
/**
* Define the mapper to map source to destination files.
@@ -400,10 +381,10 @@ public class Copy extends Task {
File savedFile = file; // may be altered in validateAttributes
File savedDestFile = destFile;
File savedDestDir = destDir;
- FileSet savedFileSet = null;
- if (file == null && destFile != null && filesets.size() == 1) {
+ ResourceCollection savedRc = null;
+ if (file == null && destFile != null && rcs.size() == 1) {
// will be removed in validateAttributes
- savedFileSet = (FileSet) filesets.elementAt(0);
+ savedRc = (ResourceCollection) rcs.elementAt(0);
}
// make sure we don't have an illegal set of options
validateAttributes();
@@ -434,30 +415,96 @@ public class Copy extends Task {
}
}
}
- // deal with the filesets
- for (int i = 0; i < filesets.size(); i++) {
- FileSet fs = (FileSet) filesets.elementAt(i);
- 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());
-
- String[] srcFiles = ds.getIncludedFiles();
- String[] srcDirs = ds.getIncludedDirectories();
- if (!flatten && mapperElement == null
- && ds.isEverythingIncluded() && !fs.hasPatterns()) {
- completeDirMap.put(fromDir, destDir);
- }
- scan(fromDir, destDir, srcFiles, srcDirs);
+ // 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.
+ */
+
+ HashMap filesByBasedir = new HashMap();
+ HashMap dirsByBasedir = new HashMap();
+ HashSet baseDirs = new HashSet();
+ 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;
+ }
+ }
+ 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
+
+ Iterator resources = rc.iterator();
+ while (resources.hasNext()) {
+ FileResource fr = (FileResource) resources.next();
+ if (!fr.isExists()) {
+ continue;
+ }
+ File baseDir = getKeyFile(fr.getBaseDir());
+ add(baseDir,
+ baseDir == NULL_FILE_PLACEHOLDER
+ ? fr.getFile().getAbsolutePath() : fr.getName(),
+ fr.isDirectory() ? dirsByBasedir
+ : filesByBasedir);
+ baseDirs.add(baseDir);
+ }
+ }
+
+ 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.");
+ }
}
// do all the copy operations now...
try {
@@ -475,8 +522,8 @@ public class Copy extends Task {
file = savedFile;
destFile = savedDestFile;
destDir = savedDestDir;
- if (savedFileSet != null) {
- filesets.insertElementAt(savedFileSet, 0);
+ if (savedRc != null) {
+ rcs.insertElementAt(savedRc, 0);
}
fileCopyMap.clear();
dirCopyMap.clear();
@@ -495,9 +542,9 @@ public class Copy extends Task {
* @exception BuildException if an error occurs.
*/
protected void validateAttributes() throws BuildException {
- if (file == null && filesets.size() == 0) {
+ if (file == null && rcs.size() == 0) {
throw new BuildException(
- "Specify at least one source--a file or a fileset.");
+ "Specify at least one source--a file or a resource collection.");
}
if (destFile != null && destDir != null) {
throw new BuildException(
@@ -507,24 +554,26 @@ public class Copy extends Task {
throw new BuildException("One of tofile or todir must be set.");
}
if (file != null && file.isDirectory()) {
- throw new BuildException("Use a fileset to copy directories.");
+ throw new BuildException("Use a resource collection to copy directories.");
}
- if (destFile != null && filesets.size() > 0) {
- if (filesets.size() > 1) {
+ if (destFile != null && rcs.size() > 0) {
+ if (rcs.size() > 1) {
throw new BuildException(
"Cannot concatenate multiple files into a single file.");
} else {
- FileSet fs = (FileSet) filesets.elementAt(0);
- DirectoryScanner ds = fs.getDirectoryScanner(getProject());
- String[] srcFiles = ds.getIncludedFiles();
-
- if (srcFiles.length == 0) {
+ ResourceCollection rc = (ResourceCollection) rcs.elementAt(0);
+ if (!rc.isFilesystemOnly()) {
+ throw new BuildException("Only FileSystem resources are"
+ + " supported.");
+ }
+ if (rc.size() == 0) {
throw new BuildException(
"Cannot perform operation from directory to file.");
- } else if (srcFiles.length == 1) {
+ } else if (rc.size() == 1) {
+ FileResource r = (FileResource) rc.iterator().next();
if (file == null) {
- file = new File(ds.getBasedir(), srcFiles[0]);
- filesets.removeElementAt(0);
+ file = r.getFile();
+ rcs.removeElementAt(0);
} else {
throw new BuildException(
"Cannot concatenate multiple files into a single file.");
@@ -689,4 +738,37 @@ public class Copy extends Task {
}
}
}
+
+ /**
+ * 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, String[] names, Map m) {
+ if (names != null) {
+ baseDir = getKeyFile(baseDir);
+ List l = (List) m.get(baseDir);
+ if (l == null) {
+ l = new ArrayList(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(File baseDir, String name, Map 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(File f) {
+ return f == null ? NULL_FILE_PLACEHOLDER : f;
+ }
}
diff --git a/src/main/org/apache/tools/ant/taskdefs/Sync.java b/src/main/org/apache/tools/ant/taskdefs/Sync.java
index e532a2d91..14cb8714c 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Sync.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Sync.java
@@ -35,6 +35,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.ResourceCollection;
import org.apache.tools.ant.types.selectors.FileSelector;
import org.apache.tools.ant.types.selectors.NoneSelector;
@@ -212,9 +213,11 @@ public class Sync extends Task {
// delete them.
for (int i = dirs.length - 1; i >= 0; --i) {
File f = new File(toDir, dirs[i]);
+ if (f.list().length < 1) {
log("Removing orphan directory: " + f, Project.MSG_DEBUG);
f.delete();
++removedCount[0];
+ }
}
return removedCount;
}
@@ -310,7 +313,16 @@ public class Sync extends Task {
* @param set a fileset
*/
public void addFileset(FileSet set) {
- myCopy.addFileset(set);
+ add(set);
+ }
+
+ /**
+ * Adds a collection of filesystem resources to copy.
+ * @param rc a resource collection
+ * @since Ant 1.7
+ */
+ public void add(ResourceCollection rc) {
+ myCopy.add(rc);
}
/**
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java b/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java
index 8be9c8d13..efa1a642e 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java
@@ -121,6 +121,13 @@ public class CopyTest extends BuildFileTest {
assertTrue(file.exists());
}
+ public void testSingleFilePath() {
+ executeTarget("test_single_file_path");
+ File file = new File(getProjectDir(),
+ "copytest_single_file_path.tmp");
+ assertTrue(file.exists());
+ }
+
public void testTranscoding() throws IOException {
executeTarget("testTranscoding");
File f1 = getProject().resolveFile("copy/expected/utf-8");
@@ -148,7 +155,7 @@ public class CopyTest extends BuildFileTest {
assertTrue(getBuildException().getMessage().endsWith(" not found."));
}
- public void _testFileResourcePlain() {
+ public void testFileResourcePlain() {
executeTarget("testFileResourcePlain");
File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file1.txt");
File file2 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file2.txt");
@@ -168,23 +175,23 @@ public class CopyTest extends BuildFileTest {
assertTrue(file3.exists());
}
- public void _testFileResourceWithFilter() {
+ public void testFileResourceWithFilter() {
executeTarget("testFileResourceWithFilter");
File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/fileNR.txt");
assertTrue(file1.exists());
try {
- String file1Content = FILE_UTILS.readFully(new FileReader(file1));
- assertEquals(file1Content, "This is file 42");
- } catch (IOException e) {
- // no-op: not a real business error
- }
+ String file1Content = FILE_UTILS.readFully(new FileReader(file1));
+ assertEquals("This is file 42", file1Content);
+ } catch (IOException e) {
+ // no-op: not a real business error
+ }
}
- public void _testPathAsResource() {
+ public void testPathAsResource() {
executeTarget("testPathAsResource");
- File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file1.txt.bak");
- File file2 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file2.txt.bak");
- File file3 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file3.txt.bak");
+ 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());
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java b/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java
index 1c4c657da..90c45e631 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java
@@ -82,11 +82,28 @@ public class MoveTest extends BuildFileTest {
}
public void testCompleteDirectoryMove() throws IOException {
- executeTarget("testCompleteDirectoryMove");
+ testCompleteDirectoryMove("testCompleteDirectoryMove");
+ }
+
+ public void testCompleteDirectoryMove2() throws IOException {
+ testCompleteDirectoryMove("testCompleteDirectoryMove2");
+ }
+
+ private void testCompleteDirectoryMove(String target) throws IOException {
+ executeTarget(target);
+ assertTrue(getProject().resolveFile("E").exists());
+ assertTrue(getProject().resolveFile("E/1").exists());
+ assertTrue(!getProject().resolveFile("A/1").exists());
+ //