source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Copy.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 23.3 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs;
19
20import java.io.File;
21import java.io.IOException;
22import java.util.Enumeration;
23import java.util.Hashtable;
24import java.util.Vector;
25import org.apache.tools.ant.BuildException;
26import org.apache.tools.ant.DirectoryScanner;
27import org.apache.tools.ant.Project;
28import org.apache.tools.ant.Task;
29import org.apache.tools.ant.types.FileSet;
30import org.apache.tools.ant.types.FilterChain;
31import org.apache.tools.ant.types.FilterSet;
32import org.apache.tools.ant.types.FilterSetCollection;
33import org.apache.tools.ant.types.Mapper;
34import org.apache.tools.ant.util.FileNameMapper;
35import org.apache.tools.ant.util.FileUtils;
36import org.apache.tools.ant.util.FlatFileNameMapper;
37import org.apache.tools.ant.util.IdentityMapper;
38import org.apache.tools.ant.util.SourceFileScanner;
39
40/**
41 * Copies a file or directory to a new file
42 * or directory. Files are only copied if the source file is newer
43 * than the destination file, or when the destination file does not
44 * exist. It is possible to explicitly overwrite existing files.</p>
45 *
46 * <p>This implementation is based on Arnout Kuiper's initial design
47 * document, the following mailing list discussions, and the
48 * copyfile/copydir tasks.</p>
49 *
50 *
51 * @since Ant 1.2
52 *
53 * @ant.task category="filesystem"
54 */
55public class Copy extends Task {
56 protected File file = null; // the source file
57 protected File destFile = null; // the destination file
58 protected File destDir = null; // the destination directory
59 protected Vector filesets = new Vector();
60
61 private boolean enableMultipleMappings = false;
62 protected boolean filtering = false;
63 protected boolean preserveLastModified = false;
64 protected boolean forceOverwrite = false;
65 protected boolean flatten = false;
66 protected int verbosity = Project.MSG_VERBOSE;
67 protected boolean includeEmpty = true;
68 protected boolean failonerror = true;
69
70 protected Hashtable fileCopyMap = new Hashtable();
71 protected Hashtable dirCopyMap = new Hashtable();
72 protected Hashtable completeDirMap = new Hashtable();
73
74 protected Mapper mapperElement = null;
75 protected FileUtils fileUtils;
76 private Vector filterChains = new Vector();
77 private Vector filterSets = new Vector();
78 private String inputEncoding = null;
79 private String outputEncoding = null;
80 private long granularity = 0;
81
82 /**
83 * Copy task constructor.
84 */
85 public Copy() {
86 fileUtils = FileUtils.newFileUtils();
87 granularity = fileUtils.getFileTimestampGranularity();
88 }
89
90 /**
91 * @return the fileutils object
92 */
93 protected FileUtils getFileUtils() {
94 return fileUtils;
95 }
96
97 /**
98 * Sets a single source file to copy.
99 * @param file the file to copy
100 */
101 public void setFile(File file) {
102 this.file = file;
103 }
104
105 /**
106 * Sets the destination file.
107 * @param destFile the file to copy to
108 */
109 public void setTofile(File destFile) {
110 this.destFile = destFile;
111 }
112
113 /**
114 * Sets the destination directory.
115 * @param destDir the destination directory
116 */
117 public void setTodir(File destDir) {
118 this.destDir = destDir;
119 }
120
121 /**
122 * Adds a FilterChain.
123 * @return a filter chain object
124 */
125 public FilterChain createFilterChain() {
126 FilterChain filterChain = new FilterChain();
127 filterChains.addElement(filterChain);
128 return filterChain;
129 }
130
131 /**
132 * Adds a filterset.
133 * @return a filter set object
134 */
135 public FilterSet createFilterSet() {
136 FilterSet filterSet = new FilterSet();
137 filterSets.addElement(filterSet);
138 return filterSet;
139 }
140
141 /**
142 * Give the copied files the same last modified time as the original files.
143 * @param preserve a boolean string
144 * @deprecated setPreserveLastModified(String) has been deprecated and
145 * replaced with setPreserveLastModified(boolean) to
146 * consistently let the Introspection mechanism work.
147 */
148 public void setPreserveLastModified(String preserve) {
149 setPreserveLastModified(Project.toBoolean(preserve));
150 }
151
152 /**
153 * Give the copied files the same last modified time as the original files.
154 * @param preserve if true perverse the modified time, default is false
155 */
156 public void setPreserveLastModified(boolean preserve) {
157 preserveLastModified = preserve;
158 }
159
160 /**
161 * Whether to give the copied files the same last modified time as
162 * the original files.
163 * @return the preserveLastModified attribute
164 * @since 1.32, Ant 1.5
165 */
166 public boolean getPreserveLastModified() {
167 return preserveLastModified;
168 }
169
170 /**
171 * Get the filtersets being applied to this operation.
172 *
173 * @return a vector of FilterSet objects
174 */
175 protected Vector getFilterSets() {
176 return filterSets;
177 }
178
179 /**
180 * Get the filterchains being applied to this operation.
181 *
182 * @return a vector of FilterChain objects
183 */
184 protected Vector getFilterChains() {
185 return filterChains;
186 }
187
188 /**
189 * If true, enables filtering.
190 * @param filtering if true enable filtering, default is false
191 */
192 public void setFiltering(boolean filtering) {
193 this.filtering = filtering;
194 }
195
196 /**
197 * Overwrite any existing destination file(s).
198 * @param overwrite if true force overwriting of destination file(s)
199 * even if the destination file(s) are younger than
200 * the corresponding source file. Default is false.
201 */
202 public void setOverwrite(boolean overwrite) {
203 this.forceOverwrite = overwrite;
204 }
205
206 /**
207 * When copying directory trees, the files can be "flattened"
208 * into a single directory. If there are multiple files with
209 * the same name in the source directory tree, only the first
210 * file will be copied into the "flattened" directory, unless
211 * the forceoverwrite attribute is true.
212 * @param flatten if true flatten the destination directory. Default
213 * is false.
214 */
215 public void setFlatten(boolean flatten) {
216 this.flatten = flatten;
217 }
218
219 /**
220 * Used to force listing of all names of copied files.
221 * @param verbose output the names of copied files. Default is false.
222 */
223 public void setVerbose(boolean verbose) {
224 if (verbose) {
225 this.verbosity = Project.MSG_INFO;
226 } else {
227 this.verbosity = Project.MSG_VERBOSE;
228 }
229 }
230
231 /**
232 * Used to copy empty directories.
233 * @param includeEmpty if true copy empty directories. Default is true.
234 */
235 public void setIncludeEmptyDirs(boolean includeEmpty) {
236 this.includeEmpty = includeEmpty;
237 }
238
239 /**
240 * Attribute to handle mappers that return multiple
241 * mappings for a given source path.
242 * @param enableMultipleMappings If true the task will
243 * copy to all the mappings for a given source path, if
244 * false, only the first file or directory is
245 * processed.
246 * By default, this setting is false to provide backward
247 * compatibility with earlier releases.
248 * @since 1.6
249 */
250 public void setEnableMultipleMappings(boolean enableMultipleMappings) {
251 this.enableMultipleMappings = enableMultipleMappings;
252 }
253
254 /**
255 * @return the value of the enableMultipleMapping attribute
256 */
257 public boolean isEnableMultipleMapping() {
258 return enableMultipleMappings;
259 }
260
261 /**
262 * If false, note errors to the output but keep going.
263 * @param failonerror true or false
264 */
265 public void setFailOnError(boolean failonerror) {
266 this.failonerror = failonerror;
267 }
268
269 /**
270 * Adds a set of files to copy.
271 * @param set a set of files to copy
272 */
273 public void addFileset(FileSet set) {
274 filesets.addElement(set);
275 }
276
277 /**
278 * Defines the mapper to map source to destination files.
279 * @return a mapper to be configured
280 * @exception BuildException if more than one mapper is defined
281 */
282 public Mapper createMapper() throws BuildException {
283 if (mapperElement != null) {
284 throw new BuildException("Cannot define more than one mapper",
285 getLocation());
286 }
287 mapperElement = new Mapper(getProject());
288 return mapperElement;
289 }
290
291 /**
292 * A nested filenamemapper
293 * @param fileNameMapper the mapper to add
294 * @since Ant 1.6.3
295 */
296 public void add(FileNameMapper fileNameMapper) {
297 createMapper().add(fileNameMapper);
298 }
299
300
301 /**
302 * Sets the character encoding
303 * @param encoding the character encoding
304 * @since 1.32, Ant 1.5
305 */
306 public void setEncoding(String encoding) {
307 this.inputEncoding = encoding;
308 if (outputEncoding == null) {
309 outputEncoding = encoding;
310 }
311 }
312
313 /**
314 * @return the character encoding, <code>null</code> if not set.
315 *
316 * @since 1.32, Ant 1.5
317 */
318 public String getEncoding() {
319 return inputEncoding;
320 }
321
322 /**
323 * Sets the character encoding for output files.
324 * @param encoding the character encoding
325 * @since Ant 1.6
326 */
327 public void setOutputEncoding(String encoding) {
328 this.outputEncoding = encoding;
329 }
330
331 /**
332 * @return the character encoding for output files,
333 * <code>null</code> if not set.
334 *
335 * @since Ant 1.6
336 */
337 public String getOutputEncoding() {
338 return outputEncoding;
339 }
340
341 /**
342 * The number of milliseconds leeway to give before deciding a
343 * target is out of date.
344 *
345 * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p>
346 *
347 * @since Ant 1.6.2
348 */
349 public void setGranularity(long granularity) {
350 this.granularity = granularity;
351 }
352
353 /**
354 * Performs the copy operation.
355 * @exception BuildException if an error occurs
356 */
357 public void execute() throws BuildException {
358 File savedFile = file; // may be altered in validateAttributes
359 File savedDestFile = destFile;
360 File savedDestDir = destDir;
361 FileSet savedFileSet = null;
362 if (file == null && destFile != null && filesets.size() == 1) {
363 // will be removed in validateAttributes
364 savedFileSet = (FileSet) filesets.elementAt(0);
365 }
366
367 // make sure we don't have an illegal set of options
368 validateAttributes();
369
370 try {
371
372 // deal with the single file
373 if (file != null) {
374 if (file.exists()) {
375 if (destFile == null) {
376 destFile = new File(destDir, file.getName());
377 }
378
379 if (forceOverwrite || !destFile.exists()
380 || (file.lastModified() - granularity
381 > destFile.lastModified())) {
382 fileCopyMap.put(file.getAbsolutePath(),
383 new String[] {destFile.getAbsolutePath()});
384 } else {
385 log(file + " omitted as " + destFile
386 + " is up to date.", Project.MSG_VERBOSE);
387 }
388 } else {
389 String message = "Warning: Could not find file "
390 + file.getAbsolutePath() + " to copy.";
391 if (!failonerror) {
392 log(message);
393 } else {
394 throw new BuildException(message);
395 }
396 }
397 }
398
399 // deal with the filesets
400 for (int i = 0; i < filesets.size(); i++) {
401 FileSet fs = (FileSet) filesets.elementAt(i);
402 DirectoryScanner ds = null;
403 try {
404 ds = fs.getDirectoryScanner(getProject());
405 } catch (BuildException e) {
406 if (failonerror
407 || !e.getMessage().endsWith(" not found.")) {
408 throw e;
409 } else {
410 log("Warning: " + e.getMessage());
411 continue;
412 }
413 }
414
415 File fromDir = fs.getDir(getProject());
416
417 String[] srcFiles = ds.getIncludedFiles();
418 String[] srcDirs = ds.getIncludedDirectories();
419 boolean isEverythingIncluded = ds.isEverythingIncluded()
420 && (!fs.hasSelectors() && !fs.hasPatterns());
421 if (isEverythingIncluded
422 && !flatten && mapperElement == null) {
423 completeDirMap.put(fromDir, destDir);
424 }
425 scan(fromDir, destDir, srcFiles, srcDirs);
426 }
427
428 // do all the copy operations now...
429 try {
430 doFileOperations();
431 } catch (BuildException e) {
432 if (!failonerror) {
433 log("Warning: " + e.getMessage(), Project.MSG_ERR);
434 } else {
435 throw e;
436 }
437 }
438 } finally {
439 // clean up again, so this instance can be used a second
440 // time
441 file = savedFile;
442 destFile = savedDestFile;
443 destDir = savedDestDir;
444 if (savedFileSet != null) {
445 filesets.insertElementAt(savedFileSet, 0);
446 }
447
448 fileCopyMap.clear();
449 dirCopyMap.clear();
450 completeDirMap.clear();
451 }
452 }
453
454 /************************************************************************
455 ** protected and private methods
456 ************************************************************************/
457
458 /**
459 * Ensure we have a consistent and legal set of attributes, and set
460 * any internal flags necessary based on different combinations
461 * of attributes.
462 * @exception BuildException if an error occurs
463 */
464 protected void validateAttributes() throws BuildException {
465 if (file == null && filesets.size() == 0) {
466 throw new BuildException("Specify at least one source "
467 + "- a file or a fileset.");
468 }
469
470 if (destFile != null && destDir != null) {
471 throw new BuildException("Only one of tofile and todir "
472 + "may be set.");
473 }
474
475 if (destFile == null && destDir == null) {
476 throw new BuildException("One of tofile or todir must be set.");
477 }
478
479 if (file != null && file.exists() && file.isDirectory()) {
480 throw new BuildException("Use a fileset to copy directories.");
481 }
482
483 if (destFile != null && filesets.size() > 0) {
484 if (filesets.size() > 1) {
485 throw new BuildException(
486 "Cannot concatenate multiple files into a single file.");
487 } else {
488 FileSet fs = (FileSet) filesets.elementAt(0);
489 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
490 String[] srcFiles = ds.getIncludedFiles();
491
492 if (srcFiles.length == 0) {
493 throw new BuildException(
494 "Cannot perform operation from directory to file.");
495 } else if (srcFiles.length == 1) {
496 if (file == null) {
497 file = new File(ds.getBasedir(), srcFiles[0]);
498 filesets.removeElementAt(0);
499 } else {
500 throw new BuildException("Cannot concatenate multiple "
501 + "files into a single file.");
502 }
503 } else {
504 throw new BuildException("Cannot concatenate multiple "
505 + "files into a single file.");
506 }
507 }
508 }
509
510 if (destFile != null) {
511 destDir = fileUtils.getParentFile(destFile);
512 }
513
514 }
515
516 /**
517 * Compares source files to destination files to see if they should be
518 * copied.
519 *
520 * @param fromDir The source directory
521 * @param toDir The destination directory
522 * @param files A list of files to copy
523 * @param dirs A list of directories to copy
524 */
525 protected void scan(File fromDir, File toDir, String[] files,
526 String[] dirs) {
527 FileNameMapper mapper = null;
528 if (mapperElement != null) {
529 mapper = mapperElement.getImplementation();
530 } else if (flatten) {
531 mapper = new FlatFileNameMapper();
532 } else {
533 mapper = new IdentityMapper();
534 }
535
536 buildMap(fromDir, toDir, files, mapper, fileCopyMap);
537
538 if (includeEmpty) {
539 buildMap(fromDir, toDir, dirs, mapper, dirCopyMap);
540 }
541 }
542
543 /**
544 * Add to a map of files/directories to copy
545 *
546 * @param fromDir the source directory
547 * @param toDir the destination directory
548 * @param names a list of filenames
549 * @param mapper a <code>FileNameMapper</code> value
550 * @param map a map of source file to array of destination files
551 */
552 protected void buildMap(File fromDir, File toDir, String[] names,
553 FileNameMapper mapper, Hashtable map) {
554
555 String[] toCopy = null;
556 if (forceOverwrite) {
557 Vector v = new Vector();
558 for (int i = 0; i < names.length; i++) {
559 if (mapper.mapFileName(names[i]) != null) {
560 v.addElement(names[i]);
561 }
562 }
563 toCopy = new String[v.size()];
564 v.copyInto(toCopy);
565 } else {
566 SourceFileScanner ds = new SourceFileScanner(this);
567 toCopy = ds.restrict(names, fromDir, toDir, mapper, granularity);
568 }
569
570 for (int i = 0; i < toCopy.length; i++) {
571 File src = new File(fromDir, toCopy[i]);
572
573 String[] mappedFiles = mapper.mapFileName(toCopy[i]);
574
575 if (!enableMultipleMappings) {
576 map.put(src.getAbsolutePath(),
577 new String[] {new File(toDir, mappedFiles[0]).getAbsolutePath()});
578 } else {
579 // reuse the array created by the mapper
580 for (int k = 0; k < mappedFiles.length; k++) {
581 mappedFiles[k] = new File(toDir, mappedFiles[k]).getAbsolutePath();
582 }
583
584 map.put(src.getAbsolutePath(), mappedFiles);
585 }
586 }
587 }
588
589 /**
590 * Actually does the file (and possibly empty directory) copies.
591 * This is a good method for subclasses to override.
592 */
593 protected void doFileOperations() {
594 if (fileCopyMap.size() > 0) {
595 log("Copying " + fileCopyMap.size()
596 + " file" + (fileCopyMap.size() == 1 ? "" : "s")
597 + " to " + destDir.getAbsolutePath());
598
599 Enumeration e = fileCopyMap.keys();
600 while (e.hasMoreElements()) {
601 String fromFile = (String) e.nextElement();
602 String[] toFiles = (String[]) fileCopyMap.get(fromFile);
603
604 for (int i = 0; i < toFiles.length; i++) {
605 String toFile = toFiles[i];
606
607 if (fromFile.equals(toFile)) {
608 log("Skipping self-copy of " + fromFile, verbosity);
609 continue;
610 }
611
612 try {
613 log("Copying " + fromFile + " to " + toFile, verbosity);
614
615 FilterSetCollection executionFilters =
616 new FilterSetCollection();
617 if (filtering) {
618 executionFilters
619 .addFilterSet(getProject().getGlobalFilterSet());
620 }
621 for (Enumeration filterEnum = filterSets.elements();
622 filterEnum.hasMoreElements();) {
623 executionFilters
624 .addFilterSet((FilterSet) filterEnum.nextElement());
625 }
626 fileUtils.copyFile(fromFile, toFile, executionFilters,
627 filterChains, forceOverwrite,
628 preserveLastModified, inputEncoding,
629 outputEncoding, getProject());
630 } catch (IOException ioe) {
631 String msg = "Failed to copy " + fromFile + " to " + toFile
632 + " due to " + ioe.getMessage();
633 File targetFile = new File(toFile);
634 if (targetFile.exists() && !targetFile.delete()) {
635 msg += " and I couldn't delete the corrupt " + toFile;
636 }
637 throw new BuildException(msg, ioe, getLocation());
638 }
639 }
640 }
641 }
642
643 if (includeEmpty) {
644 Enumeration e = dirCopyMap.elements();
645 int createCount = 0;
646 while (e.hasMoreElements()) {
647 String[] dirs = (String[]) e.nextElement();
648 for (int i = 0; i < dirs.length; i++) {
649 File d = new File(dirs[i]);
650 if (!d.exists()) {
651 if (!d.mkdirs()) {
652 log("Unable to create directory "
653 + d.getAbsolutePath(), Project.MSG_ERR);
654 } else {
655 createCount++;
656 }
657 }
658 }
659 }
660 if (createCount > 0) {
661 log("Copied " + dirCopyMap.size()
662 + " empty director"
663 + (dirCopyMap.size() == 1 ? "y" : "ies")
664 + " to " + createCount
665 + " empty director"
666 + (createCount == 1 ? "y" : "ies") + " under "
667 + destDir.getAbsolutePath());
668 }
669 }
670 }
671}
672
Note: See TracBrowser for help on using the repository browser.