git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@292247 13f79535-47bb-0310-9956-ffa450edef68master
@@ -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><fileset></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><fileset></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><fileset></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><fileset></code>, or if the | specified in the <code><fileset></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><fileset></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><fileset></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><filterset></code> elements. | |||||
</copy> | </copy> | ||||
</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> | |||||
</copy> | </copy> | ||||
</pre> | </pre> | ||||
<p><b>Collect all items from the current CLASSPATH setting into a | |||||
destination directory, flattening the directory structure.</b></p> | |||||
<pre> | |||||
<copy todir="dest" flatten="true"> | |||||
<path> | |||||
<pathelement path="${java.class.path}"/> | |||||
</path> | |||||
</copy> | |||||
</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 | ||||
@@ -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><fileset></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><move></code> is the <a | <code><move></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><fileset></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><filterset></code> elements. | |||||
</fileset> | </fileset> | ||||
</move> | </move> | ||||
</pre> | </pre> | ||||
<p><b>Move a list of files to a new directory</b></p> | |||||
<pre> | |||||
<move todir="some/new/dir"> | |||||
<filelist dir="my/src/dir"> | |||||
<file name="file1.txt"/> | |||||
<file name="file2.txt"/> | |||||
</filelist> | |||||
</move> | |||||
</pre> | |||||
<p><b>Append <code>".bak"</code> to the names of all files | <p><b>Append <code>".bak"</code> to the names of all files | ||||
in a directory.</b></p> | in a directory.</b></p> | ||||
<pre> | <pre> | ||||
@@ -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><fileset></code> has been | |||||
supported as a nested element.</p> | |||||
<h4>preserveInTarget</h4> | <h4>preserveInTarget</h4> | ||||
@@ -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> |
@@ -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> | ||||
@@ -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"/> | ||||
@@ -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; | |||||
} | |||||
} | } |
@@ -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); | |||||
} | } | ||||
/** | /** | ||||
@@ -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()); | ||||
@@ -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() { | ||||
@@ -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"; | ||||