You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Copy.java 20 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "Ant" and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.io.File;
  56. import java.io.IOException;
  57. import java.util.Enumeration;
  58. import java.util.Hashtable;
  59. import java.util.Vector;
  60. import org.apache.tools.ant.BuildException;
  61. import org.apache.tools.ant.DirectoryScanner;
  62. import org.apache.tools.ant.Project;
  63. import org.apache.tools.ant.Task;
  64. import org.apache.tools.ant.types.FileSet;
  65. import org.apache.tools.ant.types.FilterChain;
  66. import org.apache.tools.ant.types.FilterSet;
  67. import org.apache.tools.ant.types.FilterSetCollection;
  68. import org.apache.tools.ant.types.Mapper;
  69. import org.apache.tools.ant.util.FileNameMapper;
  70. import org.apache.tools.ant.util.FileUtils;
  71. import org.apache.tools.ant.util.FlatFileNameMapper;
  72. import org.apache.tools.ant.util.IdentityMapper;
  73. import org.apache.tools.ant.util.SourceFileScanner;
  74. /**
  75. * Copies a file or directory to a new file
  76. * or directory. Files are only copied if the source file is newer
  77. * than the destination file, or when the destination file does not
  78. * exist. It is possible to explicitly overwrite existing files.</p>
  79. *
  80. * <p>This implementation is based on Arnout Kuiper's initial design
  81. * document, the following mailing list discussions, and the
  82. * copyfile/copydir tasks.</p>
  83. *
  84. * @author Glenn McAllister
  85. * <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com</a>
  86. * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  87. * @author <A href="gholam@xtra.co.nz">Michael McCallum</A>
  88. * @author Magesh Umasankar
  89. *
  90. * @version $Revision$
  91. *
  92. * @since Ant 1.2
  93. *
  94. * @ant.task category="filesystem"
  95. */
  96. public class Copy extends Task {
  97. protected File file = null; // the source file
  98. protected File destFile = null; // the destination file
  99. protected File destDir = null; // the destination directory
  100. protected Vector filesets = new Vector();
  101. protected boolean filtering = false;
  102. protected boolean preserveLastModified = false;
  103. protected boolean forceOverwrite = false;
  104. protected boolean flatten = false;
  105. protected int verbosity = Project.MSG_VERBOSE;
  106. protected boolean includeEmpty = true;
  107. private boolean failonerror = true;
  108. protected Hashtable fileCopyMap = new Hashtable();
  109. protected Hashtable dirCopyMap = new Hashtable();
  110. protected Hashtable completeDirMap = new Hashtable();
  111. protected Mapper mapperElement = null;
  112. private Vector filterChains = new Vector();
  113. private Vector filterSets = new Vector();
  114. private FileUtils fileUtils;
  115. private String encoding = null;
  116. /**
  117. * Copy task constructor.
  118. */
  119. public Copy() {
  120. fileUtils = FileUtils.newFileUtils();
  121. }
  122. protected FileUtils getFileUtils() {
  123. return fileUtils;
  124. }
  125. /**
  126. * Sets a single source file to copy.
  127. */
  128. public void setFile(File file) {
  129. this.file = file;
  130. }
  131. /**
  132. * Sets the destination file.
  133. */
  134. public void setTofile(File destFile) {
  135. this.destFile = destFile;
  136. }
  137. /**
  138. * Sets the destination directory.
  139. */
  140. public void setTodir(File destDir) {
  141. this.destDir = destDir;
  142. }
  143. /**
  144. * Adds a FilterChain.
  145. */
  146. public FilterChain createFilterChain() {
  147. FilterChain filterChain = new FilterChain();
  148. filterChains.addElement(filterChain);
  149. return filterChain;
  150. }
  151. /**
  152. * Adds a filterset.
  153. */
  154. public FilterSet createFilterSet() {
  155. FilterSet filterSet = new FilterSet();
  156. filterSets.addElement(filterSet);
  157. return filterSet;
  158. }
  159. /**
  160. * Give the copied files the same last modified time as the original files.
  161. * @deprecated setPreserveLastModified(String) has been deprecated and
  162. * replaced with setPreserveLastModified(boolean) to
  163. * consistently let the Introspection mechanism work.
  164. */
  165. public void setPreserveLastModified(String preserve) {
  166. setPreserveLastModified(Project.toBoolean(preserve));
  167. }
  168. /**
  169. * Give the copied files the same last modified time as the original files.
  170. */
  171. public void setPreserveLastModified(boolean preserve) {
  172. preserveLastModified = preserve;
  173. }
  174. /**
  175. * Whether to give the copied files the same last modified time as
  176. * the original files.
  177. *
  178. * @since 1.32, Ant 1.5
  179. */
  180. public boolean getPreserveLastModified() {
  181. return preserveLastModified;
  182. }
  183. /**
  184. * Get the filtersets being applied to this operation.
  185. *
  186. * @return a vector of FilterSet objects
  187. */
  188. protected Vector getFilterSets() {
  189. return filterSets;
  190. }
  191. /**
  192. * Get the filterchains being applied to this operation.
  193. *
  194. * @return a vector of FilterChain objects
  195. */
  196. protected Vector getFilterChains() {
  197. return filterChains;
  198. }
  199. /**
  200. * If true, enables filtering.
  201. */
  202. public void setFiltering(boolean filtering) {
  203. this.filtering = filtering;
  204. }
  205. /**
  206. * Overwrite any existing destination file(s).
  207. */
  208. public void setOverwrite(boolean overwrite) {
  209. this.forceOverwrite = overwrite;
  210. }
  211. /**
  212. * When copying directory trees, the files can be "flattened"
  213. * into a single directory. If there are multiple files with
  214. * the same name in the source directory tree, only the first
  215. * file will be copied into the "flattened" directory, unless
  216. * the forceoverwrite attribute is true.
  217. */
  218. public void setFlatten(boolean flatten) {
  219. this.flatten = flatten;
  220. }
  221. /**
  222. * Used to force listing of all names of copied files.
  223. */
  224. public void setVerbose(boolean verbose) {
  225. if (verbose) {
  226. this.verbosity = Project.MSG_INFO;
  227. } else {
  228. this.verbosity = Project.MSG_VERBOSE;
  229. }
  230. }
  231. /**
  232. * Used to copy empty directories.
  233. */
  234. public void setIncludeEmptyDirs(boolean includeEmpty) {
  235. this.includeEmpty = includeEmpty;
  236. }
  237. /**
  238. * If false, note errors to the output but keep going.
  239. * @param failonerror true or false
  240. */
  241. public void setFailOnError(boolean failonerror) {
  242. this.failonerror = failonerror;
  243. }
  244. /**
  245. * Adds a set of files to copy.
  246. */
  247. public void addFileset(FileSet set) {
  248. filesets.addElement(set);
  249. }
  250. /**
  251. * Defines the mapper to map source to destination files.
  252. */
  253. public Mapper createMapper() throws BuildException {
  254. if (mapperElement != null) {
  255. throw new BuildException("Cannot define more than one mapper",
  256. getLocation());
  257. }
  258. mapperElement = new Mapper(getProject());
  259. return mapperElement;
  260. }
  261. /**
  262. * Sets the character encoding
  263. *
  264. * @since 1.32, Ant 1.5
  265. */
  266. public void setEncoding (String encoding) {
  267. this.encoding = encoding;
  268. }
  269. /**
  270. * @return the character encoding, <code>null</code> if not set.
  271. *
  272. * @since 1.32, Ant 1.5
  273. */
  274. public String getEncoding() {
  275. return encoding;
  276. }
  277. /**
  278. * Performs the copy operation.
  279. */
  280. public void execute() throws BuildException {
  281. File savedFile = file; // may be altered in validateAttributes
  282. File savedDestFile = destFile;
  283. File savedDestDir = destDir;
  284. FileSet savedFileSet = null;
  285. if (file == null && destFile != null && filesets.size() == 1) {
  286. // will be removed in validateAttributes
  287. savedFileSet = (FileSet) filesets.elementAt(0);
  288. }
  289. // make sure we don't have an illegal set of options
  290. validateAttributes();
  291. try {
  292. // deal with the single file
  293. if (file != null) {
  294. if (file.exists()) {
  295. if (destFile == null) {
  296. destFile = new File(destDir, file.getName());
  297. }
  298. if (forceOverwrite ||
  299. !destFile.exists() ||
  300. (file.lastModified() > destFile.lastModified())) {
  301. fileCopyMap.put(file.getAbsolutePath(),
  302. destFile.getAbsolutePath());
  303. } else {
  304. log(file + " omitted as " + destFile
  305. + " is up to date.", Project.MSG_VERBOSE);
  306. }
  307. } else {
  308. String message = "Warning: Could not find file "
  309. + file.getAbsolutePath() + " to copy.";
  310. if (!failonerror) {
  311. log(message);
  312. } else {
  313. throw new BuildException(message);
  314. }
  315. }
  316. }
  317. // deal with the filesets
  318. for (int i = 0; i < filesets.size(); i++) {
  319. FileSet fs = (FileSet) filesets.elementAt(i);
  320. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  321. File fromDir = fs.getDir(getProject());
  322. String[] srcFiles = ds.getIncludedFiles();
  323. String[] srcDirs = ds.getIncludedDirectories();
  324. boolean isEverythingIncluded = ds.isEverythingIncluded();
  325. if (isEverythingIncluded
  326. && !flatten && mapperElement == null) {
  327. completeDirMap.put(fromDir, destDir);
  328. }
  329. scan(fromDir, destDir, srcFiles, srcDirs);
  330. }
  331. // do all the copy operations now...
  332. doFileOperations();
  333. } finally {
  334. // clean up again, so this instance can be used a second
  335. // time
  336. file = savedFile;
  337. destFile = savedDestFile;
  338. destDir = savedDestDir;
  339. if (savedFileSet != null) {
  340. filesets.insertElementAt(savedFileSet, 0);
  341. }
  342. fileCopyMap.clear();
  343. dirCopyMap.clear();
  344. completeDirMap.clear();
  345. }
  346. }
  347. //************************************************************************
  348. // protected and private methods
  349. //************************************************************************
  350. /**
  351. * Ensure we have a consistent and legal set of attributes, and set
  352. * any internal flags necessary based on different combinations
  353. * of attributes.
  354. */
  355. protected void validateAttributes() throws BuildException {
  356. if (file == null && filesets.size() == 0) {
  357. throw new BuildException("Specify at least one source "
  358. + "- a file or a fileset.");
  359. }
  360. if (destFile != null && destDir != null) {
  361. throw new BuildException("Only one of tofile and todir "
  362. + "may be set.");
  363. }
  364. if (destFile == null && destDir == null) {
  365. throw new BuildException("One of tofile or todir must be set.");
  366. }
  367. if (file != null && file.exists() && file.isDirectory()) {
  368. throw new BuildException("Use a fileset to copy directories.");
  369. }
  370. if (destFile != null && filesets.size() > 0) {
  371. if (filesets.size() > 1) {
  372. throw new BuildException(
  373. "Cannot concatenate multiple files into a single file.");
  374. } else {
  375. FileSet fs = (FileSet) filesets.elementAt(0);
  376. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  377. String[] srcFiles = ds.getIncludedFiles();
  378. if (srcFiles.length == 0) {
  379. throw new BuildException(
  380. "Cannot perform operation from directory to file.");
  381. } else if (srcFiles.length == 1) {
  382. if (file == null) {
  383. file = new File(ds.getBasedir(), srcFiles[0]);
  384. filesets.removeElementAt(0);
  385. } else {
  386. throw new BuildException("Cannot concatenate multiple "
  387. + "files into a single file.");
  388. }
  389. } else {
  390. throw new BuildException("Cannot concatenate multiple "
  391. + "files into a single file.");
  392. }
  393. }
  394. }
  395. if (destFile != null) {
  396. destDir = fileUtils.getParentFile(destFile);
  397. }
  398. }
  399. /**
  400. * Compares source files to destination files to see if they should be
  401. * copied.
  402. */
  403. protected void scan(File fromDir, File toDir, String[] files,
  404. String[] dirs) {
  405. FileNameMapper mapper = null;
  406. if (mapperElement != null) {
  407. mapper = mapperElement.getImplementation();
  408. } else if (flatten) {
  409. mapper = new FlatFileNameMapper();
  410. } else {
  411. mapper = new IdentityMapper();
  412. }
  413. buildMap(fromDir, toDir, files, mapper, fileCopyMap);
  414. if (includeEmpty) {
  415. buildMap(fromDir, toDir, dirs, mapper, dirCopyMap);
  416. }
  417. }
  418. protected void buildMap(File fromDir, File toDir, String[] names,
  419. FileNameMapper mapper, Hashtable map) {
  420. String[] toCopy = null;
  421. if (forceOverwrite) {
  422. Vector v = new Vector();
  423. for (int i = 0; i < names.length; i++) {
  424. if (mapper.mapFileName(names[i]) != null) {
  425. v.addElement(names[i]);
  426. }
  427. }
  428. toCopy = new String[v.size()];
  429. v.copyInto(toCopy);
  430. } else {
  431. SourceFileScanner ds = new SourceFileScanner(this);
  432. toCopy = ds.restrict(names, fromDir, toDir, mapper);
  433. }
  434. for (int i = 0; i < toCopy.length; i++) {
  435. File src = new File(fromDir, toCopy[i]);
  436. File dest = new File(toDir, mapper.mapFileName(toCopy[i])[0]);
  437. map.put(src.getAbsolutePath(), dest.getAbsolutePath());
  438. }
  439. }
  440. /**
  441. * Actually does the file (and possibly empty directory) copies.
  442. * This is a good method for subclasses to override.
  443. */
  444. protected void doFileOperations() {
  445. if (fileCopyMap.size() > 0) {
  446. log("Copying " + fileCopyMap.size()
  447. + " file" + (fileCopyMap.size() == 1 ? "" : "s")
  448. + " to " + destDir.getAbsolutePath());
  449. Enumeration e = fileCopyMap.keys();
  450. while (e.hasMoreElements()) {
  451. String fromFile = (String) e.nextElement();
  452. String toFile = (String) fileCopyMap.get(fromFile);
  453. if (fromFile.equals(toFile)) {
  454. log("Skipping self-copy of " + fromFile, verbosity);
  455. continue;
  456. }
  457. try {
  458. log("Copying " + fromFile + " to " + toFile, verbosity);
  459. FilterSetCollection executionFilters =
  460. new FilterSetCollection();
  461. if (filtering) {
  462. executionFilters
  463. .addFilterSet(getProject().getGlobalFilterSet());
  464. }
  465. for (Enumeration filterEnum = filterSets.elements();
  466. filterEnum.hasMoreElements();) {
  467. executionFilters
  468. .addFilterSet((FilterSet) filterEnum.nextElement());
  469. }
  470. fileUtils.copyFile(fromFile, toFile, executionFilters,
  471. filterChains, forceOverwrite,
  472. preserveLastModified, encoding,
  473. getProject());
  474. } catch (IOException ioe) {
  475. String msg = "Failed to copy " + fromFile + " to " + toFile
  476. + " due to " + ioe.getMessage();
  477. File targetFile = new File(toFile);
  478. if (targetFile.exists() && !targetFile.delete()) {
  479. msg += " and I couldn't delete the corrupt " + toFile;
  480. }
  481. throw new BuildException(msg, ioe, getLocation());
  482. }
  483. }
  484. }
  485. if (includeEmpty) {
  486. Enumeration e = dirCopyMap.elements();
  487. int count = 0;
  488. while (e.hasMoreElements()) {
  489. File d = new File((String) e.nextElement());
  490. if (!d.exists()) {
  491. if (!d.mkdirs()) {
  492. log("Unable to create directory "
  493. + d.getAbsolutePath(), Project.MSG_ERR);
  494. } else {
  495. count++;
  496. }
  497. }
  498. }
  499. if (count > 0) {
  500. log("Copied " + count +
  501. " empty director" +
  502. (count == 1 ? "y" : "ies") +
  503. " to " + destDir.getAbsolutePath());
  504. }
  505. }
  506. }
  507. }