Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 603   Methods: 29
NCLOC: 345   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Copy.java 70.7% 82.4% 86.2% 79.3%
 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   
 
 55   
 package org.apache.tools.ant.taskdefs;
 56   
 
 57   
 import java.io.File;
 58   
 import java.io.IOException;
 59   
 import java.util.Enumeration;
 60   
 import java.util.Hashtable;
 61   
 import java.util.Vector;
 62   
 import org.apache.tools.ant.BuildException;
 63   
 import org.apache.tools.ant.DirectoryScanner;
 64   
 import org.apache.tools.ant.Project;
 65   
 import org.apache.tools.ant.Task;
 66   
 import org.apache.tools.ant.types.FileSet;
 67   
 import org.apache.tools.ant.types.FilterChain;
 68   
 import org.apache.tools.ant.types.FilterSet;
 69   
 import org.apache.tools.ant.types.FilterSetCollection;
 70   
 import org.apache.tools.ant.types.Mapper;
 71   
 import org.apache.tools.ant.util.FileNameMapper;
 72   
 import org.apache.tools.ant.util.FileUtils;
 73   
 import org.apache.tools.ant.util.FlatFileNameMapper;
 74   
 import org.apache.tools.ant.util.IdentityMapper;
 75   
 import org.apache.tools.ant.util.SourceFileScanner;
 76   
 
 77   
 /**
 78   
  * Copies a file or directory to a new file
 79   
  * or directory.  Files are only copied if the source file is newer
 80   
  * than the destination file, or when the destination file does not
 81   
  * exist.  It is possible to explicitly overwrite existing files.</p>
 82   
  *
 83   
  * <p>This implementation is based on Arnout Kuiper's initial design
 84   
  * document, the following mailing list discussions, and the
 85   
  * copyfile/copydir tasks.</p>
 86   
  *
 87   
  * @author Glenn McAllister
 88   
  *         <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com</a>
 89   
  * @author Stefan Bodewig
 90   
  * @author <A href="gholam@xtra.co.nz">Michael McCallum</A>
 91   
  * @author Magesh Umasankar
 92   
  *
 93   
  * @version $Revision: 1.55 $
 94   
  *
 95   
  * @since Ant 1.2
 96   
  *
 97   
  * @ant.task category="filesystem"
 98   
  */
 99   
 public class Copy extends Task {
 100   
     protected File file = null;     // the source file
 101   
     protected File destFile = null; // the destination file
 102   
     protected File destDir = null;  // the destination directory
 103   
     protected Vector filesets = new Vector();
 104   
 
 105   
     protected boolean filtering = false;
 106   
     protected boolean preserveLastModified = false;
 107   
     protected boolean forceOverwrite = false;
 108   
     protected boolean flatten = false;
 109   
     protected int verbosity = Project.MSG_VERBOSE;
 110   
     protected boolean includeEmpty = true;
 111   
     private boolean failonerror = true;
 112   
 
 113   
     protected Hashtable fileCopyMap = new Hashtable();
 114   
     protected Hashtable dirCopyMap = new Hashtable();
 115   
     protected Hashtable completeDirMap = new Hashtable();
 116   
 
 117   
     protected Mapper mapperElement = null;
 118   
     private Vector filterChains = new Vector();
 119   
     private Vector filterSets = new Vector();
 120   
     private FileUtils fileUtils;
 121   
     private String inputEncoding = null;
 122   
     private String outputEncoding = null;
 123   
 
 124   
     /**
 125   
      * Copy task constructor.
 126   
      */
 127  135
     public Copy() {
 128  135
         fileUtils = FileUtils.newFileUtils();
 129   
     }
 130   
 
 131  2
     protected FileUtils getFileUtils() {
 132  2
         return fileUtils;
 133   
     }
 134   
 
 135   
     /**
 136   
      * Sets a single source file to copy.
 137   
      */
 138  108
     public void setFile(File file) {
 139  108
         this.file = file;
 140   
     }
 141   
 
 142   
     /**
 143   
      * Sets the destination file.
 144   
      */
 145  104
     public void setTofile(File destFile) {
 146  104
         this.destFile = destFile;
 147   
     }
 148   
 
 149   
     /**
 150   
      * Sets the destination directory.
 151   
      */
 152  31
     public void setTodir(File destDir) {
 153  31
         this.destDir = destDir;
 154   
     }
 155   
 
 156   
     /**
 157   
      * Adds a FilterChain.
 158   
      */
 159  6
     public FilterChain createFilterChain() {
 160  6
         FilterChain filterChain = new FilterChain();
 161  6
         filterChains.addElement(filterChain);
 162  6
         return filterChain;
 163   
     }
 164   
 
 165   
     /**
 166   
      * Adds a filterset.
 167   
      */
 168  6
     public FilterSet createFilterSet() {
 169  6
         FilterSet filterSet = new FilterSet();
 170  6
         filterSets.addElement(filterSet);
 171  6
         return filterSet;
 172   
     }
 173   
 
 174   
     /**
 175   
      * Give the copied files the same last modified time as the original files.
 176   
      * @deprecated setPreserveLastModified(String) has been deprecated and
 177   
      *             replaced with setPreserveLastModified(boolean) to
 178   
      *             consistently let the Introspection mechanism work.
 179   
      */
 180  0
     public void setPreserveLastModified(String preserve) {
 181  0
         setPreserveLastModified(Project.toBoolean(preserve));
 182   
     }
 183   
 
 184   
     /**
 185   
      * Give the copied files the same last modified time as the original files.
 186   
      */
 187  2
     public void setPreserveLastModified(boolean preserve) {
 188  2
         preserveLastModified = preserve;
 189   
     }
 190   
 
 191   
     /**
 192   
      * Whether to give the copied files the same last modified time as
 193   
      * the original files.
 194   
      *
 195   
      * @since 1.32, Ant 1.5
 196   
      */
 197  2
     public boolean getPreserveLastModified() {
 198  2
         return preserveLastModified;
 199   
     }
 200   
 
 201   
     /**
 202   
      * Get the filtersets being applied to this operation.
 203   
      *
 204   
      * @return a vector of FilterSet objects
 205   
      */
 206  6
     protected Vector getFilterSets() {
 207  6
         return filterSets;
 208   
     }
 209   
 
 210   
     /**
 211   
      * Get the filterchains being applied to this operation.
 212   
      *
 213   
      * @return a vector of FilterChain objects
 214   
      */
 215  4
     protected Vector getFilterChains() {
 216  4
         return filterChains;
 217   
     }
 218   
 
 219   
     /**
 220   
      * If true, enables filtering.
 221   
      */
 222  5
     public void setFiltering(boolean filtering) {
 223  5
         this.filtering = filtering;
 224   
     }
 225   
 
 226   
     /**
 227   
      * Overwrite any existing destination file(s).
 228   
      */
 229  12
     public void setOverwrite(boolean overwrite) {
 230  12
         this.forceOverwrite = overwrite;
 231   
     }
 232   
 
 233   
     /**
 234   
      * When copying directory trees, the files can be "flattened"
 235   
      * into a single directory.  If there are multiple files with
 236   
      * the same name in the source directory tree, only the first
 237   
      * file will be copied into the "flattened" directory, unless
 238   
      * the forceoverwrite attribute is true.
 239   
      */
 240  0
     public void setFlatten(boolean flatten) {
 241  0
         this.flatten = flatten;
 242   
     }
 243   
 
 244   
     /**
 245   
      * Used to force listing of all names of copied files.
 246   
      */
 247  0
     public void setVerbose(boolean verbose) {
 248  0
         if (verbose) {
 249  0
             this.verbosity = Project.MSG_INFO;
 250   
         } else {
 251  0
             this.verbosity = Project.MSG_VERBOSE;
 252   
         }
 253   
     }
 254   
 
 255   
     /**
 256   
      * Used to copy empty directories.
 257   
      */
 258  0
     public void setIncludeEmptyDirs(boolean includeEmpty) {
 259  0
         this.includeEmpty = includeEmpty;
 260   
     }
 261   
 
 262   
     /**
 263   
      * If false, note errors to the output but keep going.
 264   
      * @param failonerror true or false
 265   
      */
 266  4
     public void setFailOnError(boolean failonerror) {
 267  4
         this.failonerror = failonerror;
 268   
     }
 269   
 
 270   
     /**
 271   
      * Adds a set of files to copy.
 272   
      */
 273  28
     public void addFileset(FileSet set) {
 274  28
         filesets.addElement(set);
 275   
     }
 276   
 
 277   
     /**
 278   
      * Defines the mapper to map source to destination files.
 279   
      */
 280  1
     public Mapper createMapper() throws BuildException {
 281  1
         if (mapperElement != null) {
 282  0
             throw new BuildException("Cannot define more than one mapper",
 283   
                                      getLocation());
 284   
         }
 285  1
         mapperElement = new Mapper(getProject());
 286  1
         return mapperElement;
 287   
     }
 288   
 
 289   
     /**
 290   
      * Sets the character encoding
 291   
      *
 292   
      * @since 1.32, Ant 1.5
 293   
      */
 294  3
     public void setEncoding (String encoding) {
 295  3
         this.inputEncoding = encoding;
 296  3
         if (outputEncoding == null) {
 297  3
             outputEncoding = encoding;
 298   
         }
 299   
     }
 300   
 
 301   
     /**
 302   
      * @return the character encoding, <code>null</code> if not set.
 303   
      *
 304   
      * @since 1.32, Ant 1.5
 305   
      */
 306  2
     public String getEncoding() {
 307  2
         return inputEncoding;
 308   
     }
 309   
 
 310   
     /**
 311   
      * Sets the character encoding for output files.
 312   
      *
 313   
      * @since Ant 1.6
 314   
      */
 315  1
     public void setOutputEncoding(String encoding) {
 316  1
         this.outputEncoding = encoding;
 317   
     }
 318   
 
 319   
     /**
 320   
      * @return the character encoding for output files,
 321   
      * <code>null</code> if not set.
 322   
      *
 323   
      * @since Ant 1.6
 324   
      */
 325  2
     public String getOutputEncoding() {
 326  2
         return outputEncoding;
 327   
     }
 328   
 
 329   
     /**
 330   
      * Performs the copy operation.
 331   
      */
 332  135
     public void execute() throws BuildException {
 333  135
         File savedFile = file; // may be altered in validateAttributes
 334  135
         File savedDestFile = destFile;
 335  135
         File savedDestDir = destDir;
 336  135
         FileSet savedFileSet = null;
 337  135
         if (file == null && destFile != null && filesets.size() == 1) {
 338   
             // will be removed in validateAttributes
 339  1
             savedFileSet = (FileSet) filesets.elementAt(0);
 340   
         }
 341   
 
 342   
         // make sure we don't have an illegal set of options
 343  135
         validateAttributes();
 344   
 
 345  135
         try {
 346   
 
 347   
             // deal with the single file
 348  135
             if (file != null) {
 349  109
                 if (file.exists()) {
 350  105
                     if (destFile == null) {
 351  3
                         destFile = new File(destDir, file.getName());
 352   
                     }
 353   
 
 354  105
                     if (forceOverwrite ||
 355   
                         !destFile.exists() ||
 356   
                         (file.lastModified() > destFile.lastModified())) {
 357  92
                         fileCopyMap.put(file.getAbsolutePath(),
 358   
                                         destFile.getAbsolutePath());
 359   
                     } else {
 360  13
                         log(file + " omitted as " + destFile
 361   
                             + " is up to date.", Project.MSG_VERBOSE);
 362   
                     }
 363   
                 } else {
 364  4
                     String message = "Warning: Could not find file "
 365   
                         + file.getAbsolutePath() + " to copy.";
 366  4
                     if (!failonerror) {
 367  1
                         log(message);
 368   
                     } else {
 369  3
                         throw new BuildException(message);
 370   
                     }
 371   
                 }
 372   
             }
 373   
 
 374   
             // deal with the filesets
 375  132
             for (int i = 0; i < filesets.size(); i++) {
 376  27
                 FileSet fs = (FileSet) filesets.elementAt(i);
 377  27
                 DirectoryScanner ds = null;
 378  27
                 try {
 379  27
                     ds = fs.getDirectoryScanner(getProject());
 380   
                 } catch (BuildException e) {
 381  2
                     if (failonerror 
 382   
                         || !e.getMessage().endsWith(" not found.")) {
 383  1
                         throw e;
 384   
                     } else {
 385  1
                         log("Warning: " + e.getMessage());
 386  1
                         continue;
 387   
                     }
 388   
                 }
 389   
                 
 390  25
                 File fromDir = fs.getDir(getProject());
 391   
 
 392  25
                 String[] srcFiles = ds.getIncludedFiles();
 393  25
                 String[] srcDirs = ds.getIncludedDirectories();
 394  25
                 boolean isEverythingIncluded = ds.isEverythingIncluded();
 395  25
                 if (isEverythingIncluded
 396   
                     && !flatten && mapperElement == null) {
 397  0
                     completeDirMap.put(fromDir, destDir);
 398   
                 }
 399  25
                 scan(fromDir, destDir, srcFiles, srcDirs);
 400   
             }
 401   
 
 402   
             // do all the copy operations now...
 403  131
             doFileOperations();
 404   
         } finally {
 405   
             // clean up again, so this instance can be used a second
 406   
             // time
 407  135
             file = savedFile;
 408  135
             destFile = savedDestFile;
 409  135
             destDir = savedDestDir;
 410  135
             if (savedFileSet != null) {
 411  1
                 filesets.insertElementAt(savedFileSet, 0);
 412   
             }
 413   
 
 414  135
             fileCopyMap.clear();
 415  135
             dirCopyMap.clear();
 416  135
             completeDirMap.clear();
 417   
         }
 418   
     }
 419   
 
 420   
 //************************************************************************
 421   
 //  protected and private methods
 422   
 //************************************************************************
 423   
 
 424   
     /**
 425   
      * Ensure we have a consistent and legal set of attributes, and set
 426   
      * any internal flags necessary based on different combinations
 427   
      * of attributes.
 428   
      */
 429  135
     protected void validateAttributes() throws BuildException {
 430  135
         if (file == null && filesets.size() == 0) {
 431  0
             throw new BuildException("Specify at least one source "
 432   
                                      + "- a file or a fileset.");
 433   
         }
 434   
 
 435  135
         if (destFile != null && destDir != null) {
 436  0
             throw new BuildException("Only one of tofile and todir "
 437   
                                      + "may be set.");
 438   
         }
 439   
 
 440  135
         if (destFile == null && destDir == null) {
 441  0
             throw new BuildException("One of tofile or todir must be set.");
 442   
         }
 443   
 
 444  135
         if (file != null && file.exists() && file.isDirectory()) {
 445  0
             throw new BuildException("Use a fileset to copy directories.");
 446   
         }
 447   
 
 448  135
         if (destFile != null && filesets.size() > 0) {
 449  1
             if (filesets.size() > 1) {
 450  0
                 throw new BuildException(
 451   
                     "Cannot concatenate multiple files into a single file.");
 452   
             } else {
 453  1
                 FileSet fs = (FileSet) filesets.elementAt(0);
 454  1
                 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
 455  1
                 String[] srcFiles = ds.getIncludedFiles();
 456   
 
 457  1
                 if (srcFiles.length == 0) {
 458  0
                     throw new BuildException(
 459   
                         "Cannot perform operation from directory to file.");
 460  1
                 } else if (srcFiles.length == 1) {
 461  1
                     if (file == null) {
 462  1
                         file = new File(ds.getBasedir(), srcFiles[0]);
 463  1
                         filesets.removeElementAt(0);
 464   
                     } else {
 465  0
                         throw new BuildException("Cannot concatenate multiple "
 466   
                                                  + "files into a single file.");
 467   
                     }
 468   
                 } else {
 469  0
                     throw new BuildException("Cannot concatenate multiple "
 470   
                                              + "files into a single file.");
 471   
                 }
 472   
             }
 473   
         }
 474   
 
 475  135
         if (destFile != null) {
 476  104
             destDir = fileUtils.getParentFile(destFile);
 477   
         }
 478   
 
 479   
     }
 480   
 
 481   
     /**
 482   
      * Compares source files to destination files to see if they should be
 483   
      * copied.
 484   
      */
 485  25
     protected void scan(File fromDir, File toDir, String[] files,
 486   
                         String[] dirs) {
 487  25
         FileNameMapper mapper = null;
 488  25
         if (mapperElement != null) {
 489  2
             mapper = mapperElement.getImplementation();
 490  23
         } else if (flatten) {
 491  0
             mapper = new FlatFileNameMapper();
 492   
         } else {
 493  23
             mapper = new IdentityMapper();
 494   
         }
 495   
 
 496  25
         buildMap(fromDir, toDir, files, mapper, fileCopyMap);
 497   
 
 498  25
         if (includeEmpty) {
 499  25
             buildMap(fromDir, toDir, dirs, mapper, dirCopyMap);
 500   
         }
 501   
     }
 502   
 
 503  50
     protected void buildMap(File fromDir, File toDir, String[] names,
 504   
                             FileNameMapper mapper, Hashtable map) {
 505   
 
 506  50
         String[] toCopy = null;
 507  50
         if (forceOverwrite) {
 508  8
             Vector v = new Vector();
 509  8
             for (int i = 0; i < names.length; i++) {
 510  5
                 if (mapper.mapFileName(names[i]) != null) {
 511  5
                     v.addElement(names[i]);
 512   
                 }
 513   
             }
 514  8
             toCopy = new String[v.size()];
 515  8
             v.copyInto(toCopy);
 516   
         } else {
 517  42
             SourceFileScanner ds = new SourceFileScanner(this);
 518  42
             toCopy = ds.restrict(names, fromDir, toDir, mapper);
 519   
         }
 520   
 
 521  50
         for (int i = 0; i < toCopy.length; i++) {
 522  230
             File src = new File(fromDir, toCopy[i]);
 523  230
             File dest = new File(toDir, mapper.mapFileName(toCopy[i])[0]);
 524  230
             map.put(src.getAbsolutePath(), dest.getAbsolutePath());
 525   
         }
 526   
     }
 527   
 
 528   
     /**
 529   
      * Actually does the file (and possibly empty directory) copies.
 530   
      * This is a good method for subclasses to override.
 531   
      */
 532  129
     protected void doFileOperations() {
 533  129
         if (fileCopyMap.size() > 0) {
 534  112
             log("Copying " + fileCopyMap.size()
 535   
                 + " file" + (fileCopyMap.size() == 1 ? "" : "s")
 536   
                 + " to " + destDir.getAbsolutePath());
 537   
 
 538  112
             Enumeration e = fileCopyMap.keys();
 539  112
             while (e.hasMoreElements()) {
 540  319
                 String fromFile = (String) e.nextElement();
 541  319
                 String toFile = (String) fileCopyMap.get(fromFile);
 542   
 
 543  319
                 if (fromFile.equals(toFile)) {
 544  0
                     log("Skipping self-copy of " + fromFile, verbosity);
 545  0
                     continue;
 546   
                 }
 547   
 
 548  319
                 try {
 549  319
                     log("Copying " + fromFile + " to " + toFile, verbosity);
 550   
 
 551  319
                     FilterSetCollection executionFilters =
 552   
                         new FilterSetCollection();
 553  319
                     if (filtering) {
 554  5
                         executionFilters
 555   
                             .addFilterSet(getProject().getGlobalFilterSet());
 556   
                     }
 557  319
                     for (Enumeration filterEnum = filterSets.elements();
 558  324
                          filterEnum.hasMoreElements();) {
 559  5
                         executionFilters
 560   
                             .addFilterSet((FilterSet) filterEnum.nextElement());
 561   
                     }
 562  319
                     fileUtils.copyFile(fromFile, toFile, executionFilters,
 563   
                                        filterChains, forceOverwrite,
 564   
                                        preserveLastModified, inputEncoding,
 565   
                                        outputEncoding, getProject());
 566   
                 } catch (IOException ioe) {
 567  0
                     String msg = "Failed to copy " + fromFile + " to " + toFile
 568   
                         + " due to " + ioe.getMessage();
 569  0
                     File targetFile = new File(toFile);
 570  0
                     if (targetFile.exists() && !targetFile.delete()) {
 571  0
                         msg += " and I couldn't delete the corrupt " + toFile;
 572   
                     }
 573  0
                     throw new BuildException(msg, ioe, getLocation());
 574   
                 }
 575   
             }
 576   
         }
 577   
 
 578  129
         if (includeEmpty) {
 579  129
             Enumeration e = dirCopyMap.elements();
 580  129
             int count = 0;
 581  129
             while (e.hasMoreElements()) {
 582  1
                 File d = new File((String) e.nextElement());
 583  1
                 if (!d.exists()) {
 584  0
                     if (!d.mkdirs()) {
 585  0
                         log("Unable to create directory "
 586   
                             + d.getAbsolutePath(), Project.MSG_ERR);
 587   
                     } else {
 588  0
                         count++;
 589   
                     }
 590   
                 }
 591   
             }
 592   
 
 593  129
             if (count > 0) {
 594  0
                 log("Copied " + count +
 595   
                     " empty director" +
 596   
                     (count == 1 ? "y" : "ies") +
 597   
                     " to " + destDir.getAbsolutePath());
 598   
             }
 599   
         }
 600   
     }
 601   
 
 602   
 }
 603