Browse Source

Add resource collection support to copy and friends

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@292247 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 19 years ago
parent
commit
d93d7b0916
11 changed files with 367 additions and 152 deletions
  1. +33
    -12
      docs/manual/CoreTasks/copy.html
  2. +28
    -4
      docs/manual/CoreTasks/move.html
  3. +10
    -7
      docs/manual/CoreTasks/sync.html
  4. +43
    -35
      src/etc/testcases/taskdefs/copy.xml
  5. +20
    -0
      src/etc/testcases/taskdefs/move.xml
  6. +13
    -0
      src/etc/testcases/taskdefs/sync.xml
  7. +161
    -79
      src/main/org/apache/tools/ant/taskdefs/Copy.java
  8. +13
    -1
      src/main/org/apache/tools/ant/taskdefs/Sync.java
  9. +18
    -11
      src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java
  10. +19
    -2
      src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java
  11. +9
    -1
      src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java

+ 33
- 12
docs/manual/CoreTasks/copy.html View File

@@ -10,15 +10,18 @@


<h2><a name="copy">Copy</a></h2> <h2><a name="copy">Copy</a></h2>
<h3>Description</h3> <h3>Description</h3>
<p>Copies a file or FileSet to a new file or directory. By default, files are
<p>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, only copied if the source file is newer than the destination file,
or when the destination file does not exist. However, you can explicitly or when the destination file does not exist. However, you can explicitly
overwrite files with the <code>overwrite</code> attribute.</p> overwrite files with the <code>overwrite</code> attribute.</p>


<p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select a
set of files to copy.
To use a <code>&lt;fileset&gt;</code>, the <code>todir</code> attribute
must be set.</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>


<p> <p>
<strong>Note: </strong>If you employ filters in your copy operation, you should <strong>Note: </strong>If you employ filters in your copy operation, you should
@@ -39,7 +42,7 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a>
<td valign="top">file</td> <td valign="top">file</td>
<td valign="top">The file to copy.</td> <td valign="top">The file to copy.</td>
<td valign="top" align="center">Yes, unless a nested <td valign="top" align="center">Yes, unless a nested
<code>&lt;fileset&gt;</code> element is used.</td>
resource collection element is used.</td>
</tr> </tr>
<tr> <tr>
<td valign="top">preservelastmodified</td> <td valign="top">preservelastmodified</td>
@@ -52,7 +55,7 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a>
<td valign="top">The file to copy to.</td> <td valign="top">The file to copy to.</td>
<td valign="top" align="center" rowspan="2">With the <code>file</code> <td valign="top" align="center" rowspan="2">With the <code>file</code>
attribute, either <code>tofile</code> or <code>todir</code> can be used. attribute, either <code>tofile</code> or <code>todir</code> can be used.
With nested <code>&lt;fileset&gt;</code> 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 <code>dir</code> attribute is is greater than 1, or if only the <code>dir</code> attribute is
specified in the <code>&lt;fileset&gt;</code>, or if the specified in the <code>&lt;fileset&gt;</code>, or if the
<code>file</code> attribute is also specified, then only <code>file</code> attribute is also specified, then only
@@ -141,10 +144,12 @@ operation as <a href="../CoreTypes/filterset.html">filtersets</a>
</table> </table>
<h3>Parameters specified as nested elements</h3> <h3>Parameters specified as nested elements</h3>


<h4>fileset</h4>
<p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select
sets of files to copy.
To use a fileset, the <code>todir</code> attribute must be set.</p>
<h4>fileset or any other filesystem based 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>
<p>Prior to Ant 1.7 only <code>&lt;fileset&gt;</code> has been
supported as a nested element.</p>


<h4>mapper</h4> <h4>mapper</h4>
<p>You can define filename transformations by using a nested <a <p>You can define filename transformations by using a nested <a
@@ -156,6 +161,13 @@ sets of files to copy.
one can use a filenamemapper type in place of the mapper element. one can use a filenamemapper type in place of the mapper element.
</p> </p>


<p>Note that the source name handed to the mapper depends on the
resource collection you use. If you use <code>&lt;fileset&gt;</code>
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.</p>

<h4>filterset</h4> <h4>filterset</h4>
<p><a href="../CoreTypes/filterset.html">FilterSet</a>s are used to replace <p><a href="../CoreTypes/filterset.html">FilterSet</a>s are used to replace
tokens in files that are copied. tokens in files that are copied.
@@ -207,7 +219,6 @@ followed by <code>&lt;filterset&gt;</code> elements.
&lt;/copy&gt; &lt;/copy&gt;
</pre> </pre>



<p><b>Copy a set of files to a directory, replacing @TITLE@ with Foo Bar <p><b>Copy a set of files to a directory, replacing @TITLE@ with Foo Bar
in all files.</b></p> in all files.</b></p>
<pre> <pre>
@@ -219,6 +230,16 @@ in all files.</b></p>
&lt;/copy&gt; &lt;/copy&gt;
</pre> </pre>


<p><b>Collect all items from the current CLASSPATH setting into a
destination directory, flattening the directory structure.</b></p>
<pre>
&lt;copy todir="dest" flatten="true"&gt;
&lt;path&gt;
&lt;pathelement path="${java.class.path}"/&gt;
&lt;/path&gt;
&lt;/copy&gt;
</pre>

<p><strong>Unix Note:</strong> File permissions are not retained when files <p><strong>Unix Note:</strong> File permissions are not retained when files
are copied; they end up with the default <code>UMASK</code> permissions are copied; they end up with the default <code>UMASK</code> permissions
instead. This instead. This


+ 28
- 4
docs/manual/CoreTasks/move.html View File

@@ -10,13 +10,22 @@


<h2><a name="move">Move</a></h2> <h2><a name="move">Move</a></h2>
<h3>Description</h3> <h3>Description</h3>
<p>Moves a file to a new file or directory, or sets of files to
<p>Moves a file to a new file or directory, or collections of files to
a new directory. By default, the a new directory. By default, the
destination file is overwritten if it already exists. When <var>overwrite</var> is destination file is overwritten if it already exists. When <var>overwrite</var> is
turned off, then files are only moved if the source file is newer than 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.</p> the destination file, or when the destination file does not exist.</p>
<p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select sets of files
to move to the <var>todir</var> directory.</p>

<p><a href="../CoreTypes/resources.html#collection">Resource
Collection</a>s are used to select a group of files to move. 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>. Prior to Ant 1.7 only
<code>&lt;fileset&gt;</code> has been supported as a nested element.
To use a resource collection, the <code>todir</code> attribute must be
set.</p>

<p><b>Since Ant 1.6.3</b>, the <i>file</i> attribute may be used to move <p><b>Since Ant 1.6.3</b>, the <i>file</i> attribute may be used to move
(rename) an entire directory. If <i>tofile</i> denotes an existing file, or (rename) an entire directory. If <i>tofile</i> denotes an existing file, or
there is a directory by the same name in <i>todir</i>, the action will fail. there is a directory by the same name in <i>todir</i>, the action will fail.
@@ -32,7 +41,7 @@ there is a directory by the same name in <i>todir</i>, the action will fail.
<td valign="top">file</td> <td valign="top">file</td>
<td valign="top">the file or directory to move</td> <td valign="top">the file or directory to move</td>
<td valign="top" align="center">One of <var>file</var> or <td valign="top" align="center">One of <var>file</var> or
at least one nested fileset element</td>
at least one nested resource collection element</td>
</tr> </tr>
<tr> <tr>
<td valign="top">preservelastmodified</td> <td valign="top">preservelastmodified</td>
@@ -135,6 +144,12 @@ there is a directory by the same name in <i>todir</i>, the action will fail.
href="../CoreTypes/mapper.html">mapper</a> element. The default mapper used by href="../CoreTypes/mapper.html">mapper</a> element. The default mapper used by
<code>&lt;move&gt;</code> is the <a <code>&lt;move&gt;</code> is the <a
href="../CoreTypes/mapper.html#identity-mapper">identity</a>.</p> href="../CoreTypes/mapper.html#identity-mapper">identity</a>.</p>
<p>Note that the source name handed to the mapper depends on the
resource collection you use. If you use <code>&lt;fileset&gt;</code>
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.</p>
<h4>filterchain</h4> <h4>filterchain</h4>
<p>The Move task supports nested <a href="../CoreTypes/filterchain.html"> <p>The Move task supports nested <a href="../CoreTypes/filterchain.html">
FilterChain</a>s.</p> FilterChain</a>s.</p>
@@ -173,6 +188,15 @@ followed by <code>&lt;filterset&gt;</code> elements.
&lt;/fileset&gt; &lt;/fileset&gt;
&lt;/move&gt; &lt;/move&gt;
</pre> </pre>
<p><b>Move a list of files to a new directory</b></p>
<pre>
&lt;move todir=&quot;some/new/dir&quot;&gt;
&lt;filelist dir=&quot;my/src/dir&quot;&gt;
&lt;file name="file1.txt"/&gt;
&lt;file name="file2.txt"/&gt;
&lt;/filelist&gt;
&lt;/move&gt;
</pre>
<p><b>Append <code>&quot;.bak&quot;</code> to the names of all files <p><b>Append <code>&quot;.bak&quot;</code> to the names of all files
in a directory.</b></p> in a directory.</b></p>
<pre> <pre>


+ 10
- 7
docs/manual/CoreTasks/sync.html View File

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


<p>Synchronize a target directory from the files defined in one or <p>Synchronize a target directory from the files defined in one or
more filesets.</p>
more filesystem based <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 <p>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 file in your sources and a file of that name is present in the target
dir, it will get removed from the target.</p> dir, it will get removed from the target.</p>


@@ -29,7 +29,7 @@ dir, it will get removed from the target.</p>
</tr> </tr>
<tr> <tr>
<td valign="top">todir</td> <td valign="top">todir</td>
<td valign="top">the target directory to sync with the filesets</td>
<td valign="top">the target directory to sync with the resource collections</td>
<td align="center" valign="top">Yes</td> <td align="center" valign="top">Yes</td>
</tr> </tr>
<tr> <tr>
@@ -40,7 +40,7 @@ dir, it will get removed from the target.</p>
</tr> </tr>
<tr> <tr>
<td valign="top">includeEmptyDirs</td> <td valign="top">includeEmptyDirs</td>
<td valign="top">Copy any empty directories included in the FileSet(s).
<td valign="top">Copy any empty directories included in the resource collection(s).
</td> </td>
<td valign="top" align="center">No; defaults to true.</td> <td valign="top" align="center">No; defaults to true.</td>
</tr> </tr>
@@ -72,9 +72,12 @@ dir, it will get removed from the target.</p>


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


<h4>fileset</h4>
<p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select
sets of files and directories.</p>
<h4>fileset or any other filesystem based 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>
<p>Prior to Ant 1.7 only <code>&lt;fileset&gt;</code> has been
supported as a nested element.</p>


<h4>preserveInTarget</h4> <h4>preserveInTarget</h4>




+ 43
- 35
src/etc/testcases/taskdefs/copy.xml View File

@@ -76,6 +76,14 @@ a=b=
</copy> </copy>
</target> </target>
<target name="test_single_file_path">
<copy tofile="copytest_single_file_path.tmp">
<path>
<pathelement location="copy.xml"/>
</path>
</copy>
</target>
<target name="testFilterSet"> <target name="testFilterSet">
<copy file="copy.filterset" tofile="copy.filterset.tmp"> <copy file="copy.filterset" tofile="copy.filterset.tmp">
<filterset> <filterset>
@@ -121,76 +129,75 @@ a=b=
</copy> </copy>
</target> </target>


<!--
<!--
<typedef name="resource" classname="org.apache.tools.ant.types.Resource"/> <typedef name="resource" classname="org.apache.tools.ant.types.Resource"/>
<typedef name="resources" classname="org.apache.tools.ant.types.resources.Resources"/> <typedef name="resources" classname="org.apache.tools.ant.types.resources.Resources"/>
--> -->
<property name="to.dir" value="copy-todir-tmp"/>
<property name="from.dir" value="copy-fromdir-tmp"/>
<target name="testResource.prepare">
<mkdir dir="${from.dir}"/>
<concat destfile="${from.dir}/file1.txt">This is file 1</concat>
<concat destfile="${from.dir}/file2.txt">This is file 2</concat>
<concat destfile="${from.dir}/file3.txt">This is file 3</concat>
<concat destfile="${from.dir}/fileNR.txt">This is file @NR@</concat>
</target>
<target name="testFileResourcePlain" depends="testResource.prepare"> <target name="testFileResourcePlain" depends="testResource.prepare">
<copy todir="${to.dir}">
<copy todir="${to.dir}" flatten="true">
<resources> <resources>
<file file="${from.dir}/file1.txt"/> <file file="${from.dir}/file1.txt"/>
<file file="${from.dir}/file2.txt"/> <file file="${from.dir}/file2.txt"/>
<file file="${from.dir}/file3.txt"/> <file file="${from.dir}/file3.txt"/>
</resources>
</copy>
</resources>
</copy>
</target> </target>


<target name="testFileResourceWithMapper" depends="testResource.prepare"> <target name="testFileResourceWithMapper" depends="testResource.prepare">
<copy todir="${to.dir}">
<copy todir="${to.dir}" flatten="true">
<resources> <resources>
<file file="${from.dir}/file1.txt"/> <file file="${from.dir}/file1.txt"/>
<file file="${from.dir}/file2.txt"/> <file file="${from.dir}/file2.txt"/>
<file file="${from.dir}/file3.txt"/> <file file="${from.dir}/file3.txt"/>
</resources>
</resources>
<regexpmapper from="^(.*)\.txt$$" to="\1.txt.bak"/> <regexpmapper from="^(.*)\.txt$$" to="\1.txt.bak"/>
</copy>
</copy>
</target> </target>



<property name="to.dir" value="copy-todir-tmp"/>
<property name="from.dir" value="copy-todir-tmp"/>
<target name="testResource.prepare">
<mkdir dir="${from.dir}"/>
<concat destfile="${to.dir}/file1.txt">This is file 1</concat>
<concat destfile="${to.dir}/file2.txt">This is file 2</concat>
<concat destfile="${to.dir}/file3.txt">This is file 3</concat>
<concat destfile="${to.dir}/fileNR.txt">This is file @nr@</concat>
</target>
<target name="testFileResourceWithFilter" depends="testResource.prepare"> <target name="testFileResourceWithFilter" depends="testResource.prepare">
<copy todir="${to.dir}">
<copy todir="${to.dir}" flatten="true">
<resources> <resources>
<file file="${from.dir}/fileNR.txt"/> <file file="${from.dir}/fileNR.txt"/>
</resources>
</resources>
<filterset> <filterset>
<filter token="NR" value="42"/>
</filterset>
</copy>
<filter token="NR" value="42"/>
</filterset>
</copy>
</target> </target>
<target name="testResourcePlain"> <target name="testResourcePlain">
</target> </target>
<target name="testResourcePlainWithMapper"> <target name="testResourcePlainWithMapper">
</target> </target>
<target name="testResourcePlainWithFilter"> <target name="testResourcePlainWithFilter">
</target> </target>
<target name="testOnlineResources"> <target name="testOnlineResources">
</target> </target>
<target name="testPathAsResource">
<target name="testPathAsResource" depends="testResource.prepare">
<copy todir="${to.dir}"> <copy todir="${to.dir}">
<path> <path>
<fileset dir="${from.dir}"/> <fileset dir="${from.dir}"/>
</path>
</copy>
</path>
</copy>
</target> </target>
<target name="cleanup"> <target name="cleanup">
<delete file="copytest1.tmp"/> <delete file="copytest1.tmp"/>
@@ -204,7 +211,8 @@ a=b=
<delete dir="copytest1dir"/> <delete dir="copytest1dir"/>
<delete quiet="yes" file="copy.filter.out"/> <delete quiet="yes" file="copy.filter.out"/>
<delete quiet="yes" file="copy.filter.inp"/> <delete quiet="yes" file="copy.filter.inp"/>
<delete dir="${to.dir}"/>
<delete dir="${from.dir}"/>
<delete dir="${to.dir}"/>
</target> </target>


</project> </project>

+ 20
- 0
src/etc/testcases/taskdefs/move.xml View File

@@ -59,6 +59,26 @@
</move> </move>
</target> </target>


<target name="testCompleteDirectoryMove2">
<mkdir dir="A"/>
<touch file="A/1"/>
<move todir="E">
<path>
<fileset dir="A"/>
</path>
</move>
</target>

<target name="testPathElementMove">
<mkdir dir="A"/>
<touch file="A/1"/>
<move todir="E" flatten="true">
<path>
<pathelement location="A/1"/>
</path>
</move>
</target>

<target name="testMoveFileAndFileset"> <target name="testMoveFileAndFileset">
<mkdir dir="A" /> <mkdir dir="A" />
<touch> <touch>


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

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


<target name="copyandremove-with-filelist" depends="setup">
<mkdir dir="${src}/a/b/c"/>
<touch file="${src}/a/b/c/d"/>
<mkdir dir="${dest}/e"/>
<touch file="${dest}/e/f"/>
<sync todir="${dest}">
<filelist dir="${src}">
<file name="a/b/c/d"/>
<file name="not-there"/>
</filelist>
</sync>
</target>

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


+ 161
- 79
src/main/org/apache/tools/ant/taskdefs/Copy.java View File

@@ -19,9 +19,15 @@ package org.apache.tools.ant.taskdefs;


import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Vector;
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.apache.tools.ant.Task; import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
@@ -32,9 +38,8 @@ import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterChain; import org.apache.tools.ant.types.FilterChain;
import org.apache.tools.ant.types.FilterSetCollection; import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.Resources;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.FileNameMapper; import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.IdentityMapper; import org.apache.tools.ant.util.IdentityMapper;
@@ -57,10 +62,12 @@ import org.apache.tools.ant.util.FlatFileNameMapper;
* @ant.task category="filesystem" * @ant.task category="filesystem"
*/ */
public class Copy extends Task { public class Copy extends Task {
private static final File NULL_FILE_PLACEHOLDER = new File("/NULL_FILE");

protected File file = null; // the source file protected File file = null; // the source file
protected File destFile = null; // the destination file protected File destFile = null; // the destination file
protected File destDir = null; // the destination directory protected File destDir = null; // the destination directory
protected Vector filesets = new Vector();
protected Vector rcs = new Vector();


private boolean enableMultipleMappings = false; private boolean enableMultipleMappings = false;
protected boolean filtering = false; protected boolean filtering = false;
@@ -276,43 +283,17 @@ public class Copy extends Task {
* @param set a set of files to copy. * @param set a set of files to copy.
*/ */
public void addFileset(FileSet set) { public void addFileset(FileSet set) {
filesets.addElement(set);
add(set);
} }
/* JHM: It would be the finest solution to use this method directly.
* But if I understood the IntrospectionHelper(final Class bean)
* right - especially line 258ff (the last "else if" statement),
* I must have a <b>class</b> with an no-arg constructor. But I only
* have an interface. :-(
* So I have to add the three methods ... But I can reuse this
* method :-)
*
*/
public void add(ResourceCollection res) {
//TODO: implement resources
}
/** /**
* Adds a <code>path</code> 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 <code>resources</code> 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. * 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 savedFile = file; // may be altered in validateAttributes
File savedDestFile = destFile; File savedDestFile = destFile;
File savedDestDir = destDir; 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 // 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 // make sure we don't have an illegal set of options
validateAttributes(); 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... // do all the copy operations now...
try { try {
@@ -475,8 +522,8 @@ public class Copy extends Task {
file = savedFile; file = savedFile;
destFile = savedDestFile; destFile = savedDestFile;
destDir = savedDestDir; destDir = savedDestDir;
if (savedFileSet != null) {
filesets.insertElementAt(savedFileSet, 0);
if (savedRc != null) {
rcs.insertElementAt(savedRc, 0);
} }
fileCopyMap.clear(); fileCopyMap.clear();
dirCopyMap.clear(); dirCopyMap.clear();
@@ -495,9 +542,9 @@ public class Copy extends Task {
* @exception BuildException if an error occurs. * @exception BuildException if an error occurs.
*/ */
protected void validateAttributes() throws BuildException { protected void validateAttributes() throws BuildException {
if (file == null && filesets.size() == 0) {
if (file == null && rcs.size() == 0) {
throw new BuildException( 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) { if (destFile != null && destDir != null) {
throw new BuildException( throw new BuildException(
@@ -507,24 +554,26 @@ public class Copy extends Task {
throw new BuildException("One of tofile or todir must be set."); throw new BuildException("One of tofile or todir must be set.");
} }
if (file != null && file.isDirectory()) { 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( throw new BuildException(
"Cannot concatenate multiple files into a single file."); "Cannot concatenate multiple files into a single file.");
} else { } 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( throw new BuildException(
"Cannot perform operation from directory to file."); "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) { if (file == null) {
file = new File(ds.getBasedir(), srcFiles[0]);
filesets.removeElementAt(0);
file = r.getFile();
rcs.removeElementAt(0);
} else { } else {
throw new BuildException( throw new BuildException(
"Cannot concatenate multiple files into a single file."); "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;
}
} }

+ 13
- 1
src/main/org/apache/tools/ant/taskdefs/Sync.java View File

@@ -35,6 +35,7 @@ import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.AbstractFileSet; import org.apache.tools.ant.types.AbstractFileSet;
import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.PatternSet; 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.FileSelector;
import org.apache.tools.ant.types.selectors.NoneSelector; import org.apache.tools.ant.types.selectors.NoneSelector;


@@ -212,9 +213,11 @@ public class Sync extends Task {
// delete them. // delete them.
for (int i = dirs.length - 1; i >= 0; --i) { for (int i = dirs.length - 1; i >= 0; --i) {
File f = new File(toDir, dirs[i]); File f = new File(toDir, dirs[i]);
if (f.list().length < 1) {
log("Removing orphan directory: " + f, Project.MSG_DEBUG); log("Removing orphan directory: " + f, Project.MSG_DEBUG);
f.delete(); f.delete();
++removedCount[0]; ++removedCount[0];
}
} }
return removedCount; return removedCount;
} }
@@ -310,7 +313,16 @@ public class Sync extends Task {
* @param set a fileset * @param set a fileset
*/ */
public void addFileset(FileSet set) { 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);
} }


/** /**


+ 18
- 11
src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java View File

@@ -121,6 +121,13 @@ public class CopyTest extends BuildFileTest {
assertTrue(file.exists()); 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 { public void testTranscoding() throws IOException {
executeTarget("testTranscoding"); executeTarget("testTranscoding");
File f1 = getProject().resolveFile("copy/expected/utf-8"); File f1 = getProject().resolveFile("copy/expected/utf-8");
@@ -148,7 +155,7 @@ public class CopyTest extends BuildFileTest {
assertTrue(getBuildException().getMessage().endsWith(" not found.")); assertTrue(getBuildException().getMessage().endsWith(" not found."));
} }
public void _testFileResourcePlain() {
public void testFileResourcePlain() {
executeTarget("testFileResourcePlain"); executeTarget("testFileResourcePlain");
File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file1.txt"); File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file1.txt");
File file2 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file2.txt"); File file2 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/file2.txt");
@@ -168,23 +175,23 @@ public class CopyTest extends BuildFileTest {
assertTrue(file3.exists()); assertTrue(file3.exists());
} }
public void _testFileResourceWithFilter() {
public void testFileResourceWithFilter() {
executeTarget("testFileResourceWithFilter"); executeTarget("testFileResourceWithFilter");
File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/fileNR.txt"); File file1 = new File(getProjectDir(), getProject().getProperty("to.dir")+"/fileNR.txt");
assertTrue(file1.exists()); assertTrue(file1.exists());
try { 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"); 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(file1.exists());
assertTrue(file2.exists()); assertTrue(file2.exists());
assertTrue(file3.exists()); assertTrue(file3.exists());


+ 19
- 2
src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java View File

@@ -82,11 +82,28 @@ public class MoveTest extends BuildFileTest {
} }


public void testCompleteDirectoryMove() throws IOException { 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());
// <path> swallows the basedir, it seems
//assertTrue(!getProject().resolveFile("A").exists());
}

public void testPathElementMove() throws IOException {
executeTarget("testPathElementMove");
assertTrue(getProject().resolveFile("E").exists()); assertTrue(getProject().resolveFile("E").exists());
assertTrue(getProject().resolveFile("E/1").exists()); assertTrue(getProject().resolveFile("E/1").exists());
assertTrue(!getProject().resolveFile("A/1").exists()); assertTrue(!getProject().resolveFile("A/1").exists());
assertTrue(!getProject().resolveFile("A").exists());
assertTrue(getProject().resolveFile("A").exists());
} }


public void testMoveFileAndFileset() { public void testMoveFileAndFileset() {


+ 9
- 1
src/testcases/org/apache/tools/ant/taskdefs/SyncTest.java View File

@@ -60,7 +60,15 @@ public class SyncTest extends BuildFileTest {
} }


public void testCopyAndRemove() { public void testCopyAndRemove() {
executeTarget("copyandremove");
testCopyAndRemove("copyandremove");
}

public void testCopyAndRemoveWithFileList() {
testCopyAndRemove("copyandremove-with-filelist");
}

private void testCopyAndRemove(String target) {
executeTarget(target);
String d = getProject().getProperty("dest") + "/a/b/c/d"; String d = getProject().getProperty("dest") + "/a/b/c/d";
assertFileIsPresent(d); assertFileIsPresent(d);
String f = getProject().getProperty("dest") + "/e/f"; String f = getProject().getProperty("dest") + "/e/f";


Loading…
Cancel
Save