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