Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 449   Methods: 14
NCLOC: 265   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Checksum.java 66.2% 72% 64.3% 69.6%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2001-2002 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   
 
 56   
 import java.io.BufferedReader;
 57   
 import java.io.File;
 58   
 import java.io.FileInputStream;
 59   
 import java.io.FileOutputStream;
 60   
 import java.io.IOException;
 61   
 import java.io.InputStreamReader;
 62   
 import java.security.DigestInputStream;
 63   
 import java.security.MessageDigest;
 64   
 import java.security.NoSuchAlgorithmException;
 65   
 import java.security.NoSuchProviderException;
 66   
 import java.util.Enumeration;
 67   
 import java.util.Hashtable;
 68   
 import java.util.Vector;
 69   
 import org.apache.tools.ant.BuildException;
 70   
 import org.apache.tools.ant.DirectoryScanner;
 71   
 import org.apache.tools.ant.Project;
 72   
 import org.apache.tools.ant.taskdefs.condition.Condition;
 73   
 import org.apache.tools.ant.types.FileSet;
 74   
 
 75   
 /**
 76   
  * Used to create or verify file checksums.
 77   
  *
 78   
  * @author Magesh Umasankar
 79   
  *
 80   
  * @since Ant 1.5
 81   
  *
 82   
  * @ant.task category="control"
 83   
  */
 84   
 public class Checksum extends MatchingTask implements Condition {
 85   
     /**
 86   
      * File for which checksum is to be calculated.
 87   
      */
 88   
     private File file = null;
 89   
     /**
 90   
      * MessageDigest algorithm to be used.
 91   
      */
 92   
     private String algorithm = "MD5";
 93   
     /**
 94   
      * MessageDigest Algorithm provider
 95   
      */
 96   
     private String provider = null;
 97   
     /**
 98   
      * File Extension that is be to used to create or identify
 99   
      * destination file
 100   
      */
 101   
     private String fileext;
 102   
     /**
 103   
      * Holds generated checksum and gets set as a Project Property.
 104   
      */
 105   
     private String property;
 106   
     /**
 107   
      * Whether or not to create a new file.
 108   
      * Defaults to <code>false</code>.
 109   
      */
 110   
     private boolean forceOverwrite;
 111   
     /**
 112   
      * Contains the result of a checksum verification. ("true" or "false")
 113   
      */
 114   
     private String verifyProperty;
 115   
     /**
 116   
      * Vector to hold source file sets.
 117   
      */
 118   
     private Vector filesets = new Vector();
 119   
     /**
 120   
      * Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
 121   
      */
 122   
     private Hashtable includeFileMap = new Hashtable();
 123   
     /**
 124   
      * Message Digest instance
 125   
      */
 126   
     private MessageDigest messageDigest;
 127   
     /**
 128   
      * is this task being used as a nested condition element?
 129   
      */
 130   
     private boolean isCondition;
 131   
     /**
 132   
      * Size of the read buffer to use.
 133   
      */
 134   
     private int readBufferSize = 8 * 1024;
 135   
 
 136   
     /**
 137   
      * Sets the file for which the checksum is to be calculated.
 138   
      */
 139  7
     public void setFile(File file) {
 140  7
         this.file = file;
 141   
     }
 142   
 
 143   
     /**
 144   
      * Specifies the algorithm to be used to compute the checksum.
 145   
      * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
 146   
      */
 147  0
     public void setAlgorithm(String algorithm) {
 148  0
         this.algorithm = algorithm;
 149   
     }
 150   
 
 151   
     /**
 152   
      * Sets the MessageDigest algorithm provider to be used
 153   
      * to calculate the checksum.
 154   
      */
 155  0
     public void setProvider(String provider) {
 156  0
         this.provider = provider;
 157   
     }
 158   
 
 159   
     /**
 160   
      * Sets the file extension that is be to used to
 161   
      * create or identify destination file.
 162   
      */
 163  5
     public void setFileext(String fileext) {
 164  5
         this.fileext = fileext;
 165   
     }
 166   
 
 167   
     /**
 168   
      * Sets the property to hold the generated checksum.
 169   
      */
 170  1
     public void setProperty(String property) {
 171  1
         this.property = property;
 172   
     }
 173   
 
 174   
     /**
 175   
      * Sets the verify property.  This project property holds
 176   
      * the result of a checksum verification - "true" or "false"
 177   
      */
 178  3
     public void setVerifyproperty(String verifyProperty) {
 179  3
         this.verifyProperty = verifyProperty;
 180   
     }
 181   
 
 182   
     /**
 183   
      * Whether or not to overwrite existing file irrespective of
 184   
      * whether it is newer than
 185   
      * the source file.  Defaults to false.
 186   
      */
 187  0
     public void setForceOverwrite(boolean forceOverwrite) {
 188  0
         this.forceOverwrite = forceOverwrite;
 189   
     }
 190   
 
 191   
     /**
 192   
      * The size of the read buffer to use.
 193   
      */
 194  0
     public void setReadBufferSize(int size) {
 195  0
         this.readBufferSize = size;
 196   
     }
 197   
 
 198   
     /**
 199   
      * Files to generate checksums for.
 200   
      */
 201  0
     public void addFileset(FileSet set) {
 202  0
         filesets.addElement(set);
 203   
     }
 204   
 
 205   
     /**
 206   
      * Calculate the checksum(s).
 207   
      */
 208  5
     public void execute() throws BuildException {
 209  5
         isCondition = false;
 210  5
         boolean value = validateAndExecute();
 211  5
         if (verifyProperty != null) {
 212  3
             getProject().setNewProperty(verifyProperty,
 213   
                                 new Boolean(value).toString());
 214   
         }
 215   
     }
 216   
 
 217   
     /**
 218   
      * Calculate the checksum(s)
 219   
      *
 220   
      * @return Returns true if the checksum verification test passed,
 221   
      * false otherwise.
 222   
      */
 223  2
     public boolean eval() throws BuildException {
 224  2
         isCondition = true;
 225  2
         return validateAndExecute();
 226   
     }
 227   
 
 228   
     /**
 229   
      * Validate attributes and get down to business.
 230   
      */
 231  7
     private boolean validateAndExecute() throws BuildException {
 232  7
         String savedFileExt = fileext;
 233   
 
 234  7
         if (file == null && filesets.size() == 0) {
 235  0
             throw new BuildException(
 236   
                 "Specify at least one source - a file or a fileset.");
 237   
         }
 238   
 
 239  7
         if (file != null && file.exists() && file.isDirectory()) {
 240  0
             throw new BuildException(
 241   
                 "Checksum cannot be generated for directories");
 242   
         }
 243   
 
 244  7
         if (property != null && fileext != null) {
 245  0
             throw new BuildException(
 246   
                 "Property and FileExt cannot co-exist.");
 247   
         }
 248   
 
 249  7
         if (property != null) {
 250  1
             if (forceOverwrite) {
 251  0
                 throw new BuildException(
 252   
                     "ForceOverwrite cannot be used when Property is specified");
 253   
             }
 254   
 
 255  1
             if (file != null) {
 256  1
                 if (filesets.size() > 0) {
 257  0
                     throw new BuildException("Multiple files cannot be used "
 258   
                         + "when Property is specified");
 259   
                 }
 260   
             } else {
 261  0
                 if (filesets.size() > 1) {
 262  0
                     throw new BuildException("Multiple files cannot be used "
 263   
                         + "when Property is specified");
 264   
                 }
 265   
             }
 266   
         }
 267   
 
 268  7
         if (verifyProperty != null) {
 269  3
             isCondition = true;
 270   
         }
 271   
 
 272  7
         if (verifyProperty != null && forceOverwrite) {
 273  0
             throw new BuildException(
 274   
                 "VerifyProperty and ForceOverwrite cannot co-exist.");
 275   
         }
 276   
 
 277  7
         if (isCondition && forceOverwrite) {
 278  0
             throw new BuildException("ForceOverwrite cannot be used when "
 279   
                 + "conditions are being used.");
 280   
         }
 281   
 
 282  7
         messageDigest = null;
 283  7
         if (provider != null) {
 284  0
             try {
 285  0
                 messageDigest = MessageDigest.getInstance(algorithm, provider);
 286   
             } catch (NoSuchAlgorithmException noalgo) {
 287  0
                 throw new BuildException(noalgo, getLocation());
 288   
             } catch (NoSuchProviderException noprovider) {
 289  0
                 throw new BuildException(noprovider, getLocation());
 290   
             }
 291   
         } else {
 292  7
             try {
 293  7
                 messageDigest = MessageDigest.getInstance(algorithm);
 294   
             } catch (NoSuchAlgorithmException noalgo) {
 295  0
                 throw new BuildException(noalgo, getLocation());
 296   
             }
 297   
         }
 298   
 
 299  7
         if (messageDigest == null) {
 300  0
             throw new BuildException("Unable to create Message Digest",
 301   
                                      getLocation());
 302   
         }
 303   
 
 304  7
         if (fileext == null) {
 305  2
             fileext = "." + algorithm;
 306  5
         } else if (fileext.trim().length() == 0) {
 307  0
             throw new BuildException(
 308   
                 "File extension when specified must not be an empty string");
 309   
         }
 310   
 
 311  7
         try {
 312  7
             addToIncludeFileMap(file);
 313   
 
 314  7
             int sizeofFileSet = filesets.size();
 315  7
             for (int i = 0; i < sizeofFileSet; i++) {
 316  0
                 FileSet fs = (FileSet) filesets.elementAt(i);
 317  0
                 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
 318  0
                 String[] srcFiles = ds.getIncludedFiles();
 319  0
                 for (int j = 0; j < srcFiles.length; j++) {
 320  0
                     File src = new File(fs.getDir(getProject()), srcFiles[j]);
 321  0
                     addToIncludeFileMap(src);
 322   
                 }
 323   
             }
 324   
 
 325  7
             return generateChecksums();
 326   
         } finally {
 327  7
             fileext = savedFileExt;
 328  7
             includeFileMap.clear();
 329   
         }
 330   
     }
 331   
 
 332   
     /**
 333   
      * Add key-value pair to the hashtable upon which
 334   
      * to later operate upon.
 335   
      */
 336  7
     private void addToIncludeFileMap(File file) throws BuildException {
 337  7
         if (file != null) {
 338  7
             if (file.exists()) {
 339  7
                 if (property == null) {
 340  6
                     File dest
 341   
                         = new File(file.getParent(), file.getName() + fileext);
 342  6
                     if (forceOverwrite || isCondition ||
 343   
                         (file.lastModified() > dest.lastModified())) {
 344  6
                         includeFileMap.put(file, dest);
 345   
                     } else {
 346  0
                         log(file + " omitted as " + dest + " is up to date.",
 347   
                             Project.MSG_VERBOSE);
 348   
                     }
 349   
                 } else {
 350  1
                     includeFileMap.put(file, property);
 351   
                 }
 352   
             } else {
 353  0
                 String message = "Could not find file "
 354   
                                  + file.getAbsolutePath()
 355   
                                  + " to generate checksum for.";
 356  0
                 log(message);
 357  0
                 throw new BuildException(message, getLocation());
 358   
             }
 359   
         }
 360   
     }
 361   
 
 362   
     /**
 363   
      * Generate checksum(s) using the message digest created earlier.
 364   
      */
 365  7
     private boolean generateChecksums() throws BuildException {
 366  7
         boolean checksumMatches = true;
 367  7
         FileInputStream fis = null;
 368  7
         FileOutputStream fos = null;
 369  7
         byte[] buf = new byte[readBufferSize];
 370  7
         try {
 371  7
             for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) {
 372  7
                 messageDigest.reset();
 373  7
                 File src = (File) e.nextElement();
 374  7
                 if (!isCondition) {
 375  2
                     log("Calculating " + algorithm + " checksum for " + src);
 376   
                 }
 377  7
                 fis = new FileInputStream(src);
 378  7
                 DigestInputStream dis = new DigestInputStream(fis,
 379   
                                                               messageDigest);
 380  7
                 while (dis.read(buf, 0, readBufferSize) != -1) {
 381   
                     ;
 382   
                 }
 383  7
                 dis.close();
 384  7
                 fis.close();
 385  7
                 fis = null;
 386  7
                 byte[] fileDigest = messageDigest.digest ();
 387  7
                 StringBuffer checksumSb = new StringBuffer();
 388  7
                 for (int i = 0; i < fileDigest.length; i++) {
 389  112
                     String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
 390  112
                     if (hexStr.length() < 2) {
 391  12
                         checksumSb.append("0");
 392   
                     }
 393  112
                     checksumSb.append(hexStr);
 394   
                 }
 395  7
                 String checksum = checksumSb.toString();
 396   
                 //can either be a property name string or a file
 397  7
                 Object destination = includeFileMap.get(src);
 398  7
                 if (destination instanceof java.lang.String) {
 399  1
                     String prop = (String) destination;
 400  1
                     if (isCondition) {
 401  0
                         checksumMatches = checksumMatches &&
 402   
                             checksum.equals(property);
 403   
                     } else {
 404  1
                         getProject().setNewProperty(prop, checksum);
 405   
                     }
 406  6
                 } else if (destination instanceof java.io.File) {
 407  6
                     if (isCondition) {
 408  5
                         File existingFile = (File) destination;
 409  5
                         if (existingFile.exists()) {
 410  4
                             fis = new FileInputStream(existingFile);
 411  4
                             InputStreamReader isr = new InputStreamReader(fis);
 412  4
                             BufferedReader br = new BufferedReader(isr);
 413  4
                             String suppliedChecksum = br.readLine();
 414  4
                             fis.close();
 415  4
                             fis = null;
 416  4
                             br.close();
 417  4
                             isr.close();
 418  4
                             checksumMatches = checksumMatches &&
 419   
                                 checksum.equals(suppliedChecksum);
 420   
                         } else {
 421  1
                             checksumMatches = false;
 422   
                         }
 423   
                     } else {
 424  1
                         File dest = (File) destination;
 425  1
                         fos = new FileOutputStream(dest);
 426  1
                         fos.write(checksum.getBytes());
 427  1
                         fos.close();
 428  1
                         fos = null;
 429   
                     }
 430   
                 }
 431   
             }
 432   
         } catch (Exception e) {
 433  0
             throw new BuildException(e, getLocation());
 434   
         } finally {
 435  7
             if (fis != null) {
 436  0
                 try {
 437  0
                     fis.close();
 438   
                 } catch (IOException e) {}
 439   
             }
 440  7
             if (fos != null) {
 441  0
                 try {
 442  0
                     fos.close();
 443   
                 } catch (IOException e) {}
 444   
             }
 445   
         }
 446  7
         return checksumMatches;
 447   
     }
 448   
 }
 449