Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 718   Methods: 24
NCLOC: 393   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
Jar.java 56.9% 68.4% 70.8% 65.7%
 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.ByteArrayInputStream;
 58   
 import java.io.ByteArrayOutputStream;
 59   
 import java.io.File;
 60   
 import java.io.FileOutputStream;
 61   
 import java.io.FileInputStream;
 62   
 import java.io.IOException;
 63   
 import java.io.InputStream;
 64   
 import java.io.UnsupportedEncodingException;
 65   
 import java.io.InputStreamReader;
 66   
 import java.io.OutputStreamWriter;
 67   
 import java.io.PrintWriter;
 68   
 import java.io.Reader;
 69   
 import java.util.Enumeration;
 70   
 import java.util.Vector;
 71   
 import java.util.zip.ZipEntry;
 72   
 import java.util.zip.ZipFile;
 73   
 import org.apache.tools.ant.BuildException;
 74   
 import org.apache.tools.ant.Project;
 75   
 import org.apache.tools.ant.types.EnumeratedAttribute;
 76   
 import org.apache.tools.ant.types.FileSet;
 77   
 import org.apache.tools.ant.types.Resource;
 78   
 import org.apache.tools.ant.types.ZipFileSet;
 79   
 import org.apache.tools.zip.ZipOutputStream;
 80   
 
 81   
 
 82   
 /**
 83   
  * Creates a JAR archive.
 84   
  *
 85   
  * @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
 86   
  * @author Brian Deitte
 87   
  *         <a href="mailto:bdeitte@macromedia.com">bdeitte@macromedia.com</a>
 88   
  *
 89   
  * @since Ant 1.1
 90   
  *
 91   
  * @ant.task category="packaging"
 92   
  */
 93   
 public class Jar extends Zip {
 94   
     /** The index file name. */
 95   
     private static final String INDEX_NAME = "META-INF/INDEX.LIST";
 96   
 
 97   
     /** The mainfest file name. */
 98   
     private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
 99   
 
 100   
     /** merged manifests added through addConfiguredManifest */
 101   
     private Manifest configuredManifest;
 102   
     /** shadow of the above if upToDate check alters the value */
 103   
     private Manifest savedConfiguredManifest;
 104   
 
 105   
     /**  merged manifests added through filesets */
 106   
     private Manifest filesetManifest;
 107   
 
 108   
     /** 
 109   
      * Manifest of original archive, will be set to null if not in
 110   
      * update mode.
 111   
      */
 112   
     private Manifest originalManifest;
 113   
 
 114   
     /**
 115   
      *  whether to merge fileset manifests;
 116   
      *  value is true if filesetmanifest is 'merge' or 'mergewithoutmain'
 117   
      */
 118   
     private FilesetManifestConfig filesetManifestConfig;
 119   
 
 120   
     /**
 121   
      * whether to merge the main section of fileset manifests;
 122   
      * value is true if filesetmanifest is 'merge'
 123   
      */
 124   
     private boolean mergeManifestsMain = true;
 125   
 
 126   
     /** the manifest specified by the 'manifest' attribute **/
 127   
     private Manifest manifest;
 128   
 
 129   
     /** The encoding to use when reading in a manifest file */
 130   
     private String manifestEncoding;
 131   
     
 132   
     /**
 133   
      * The file found from the 'manifest' attribute.  This can be
 134   
      * either the location of a manifest, or the name of a jar added
 135   
      * through a fileset.  If its the name of an added jar, the
 136   
      * manifest is looked for in META-INF/MANIFEST.MF
 137   
      */
 138   
     private File manifestFile;
 139   
 
 140   
     /** jar index is JDK 1.3+ only */
 141   
     private boolean index = false;
 142   
 
 143   
     /** 
 144   
      * whether to really create the archive in createEmptyZip, will
 145   
      * get set in getResourcesToAdd.
 146   
      */
 147   
     private boolean createEmpty = false;
 148   
 
 149   
     /**
 150   
      * Stores all files that are in the root of the archive (i.e. that
 151   
      * have a name that doesn't contain a slash) so they can get
 152   
      * listed in the index.
 153   
      *
 154   
      * Will not be filled unless the user has asked for an index.
 155   
      *
 156   
      * @since Ant 1.6
 157   
      */
 158   
     private Vector rootEntries;
 159   
 
 160   
     /** constructor */
 161  63
     public Jar() {
 162  63
         super();
 163  63
         archiveType = "jar";
 164  63
         emptyBehavior = "create";
 165  63
         setEncoding("UTF8");
 166  63
         rootEntries = new Vector();
 167   
     }
 168   
 
 169   
     /**
 170   
      * @ant.attribute ignore="true"
 171   
      */
 172  0
     public void setWhenempty(WhenEmpty we) {
 173  0
         log("JARs are never empty, they contain at least a manifest file",
 174   
             Project.MSG_WARN);
 175   
     }
 176   
 
 177   
     /**
 178   
      * @deprecated Use setDestFile(File) instead
 179   
      */
 180  6
     public void setJarfile(File jarFile) {
 181  6
         setDestFile(jarFile);
 182   
     }
 183   
 
 184   
     /**
 185   
      * Set whether or not to create an index list for classes.
 186   
      * This may speed up classloading in some cases.
 187   
      */
 188  2
     public void setIndex(boolean flag){
 189  2
         index = flag;
 190   
     }
 191   
 
 192   
     /**
 193   
      * Set whether or not to create an index list for classes.
 194   
      * This may speed up classloading in some cases.
 195   
      */
 196  0
     public void setManifestEncoding(String manifestEncoding) {
 197  0
         this.manifestEncoding = manifestEncoding;
 198   
     }
 199   
 
 200   
     /**
 201   
      * Allows the manifest for the archive file to be provided inline
 202   
      * in the build file rather than in an external file.
 203   
      *
 204   
      * @param newManifest
 205   
      * @throws ManifestException
 206   
      */
 207  6
     public void addConfiguredManifest(Manifest newManifest)
 208   
         throws ManifestException {
 209  6
         if (configuredManifest == null) {
 210  6
             configuredManifest = newManifest;
 211   
         } else {
 212  0
             configuredManifest.merge(newManifest);
 213   
         }
 214  6
         savedConfiguredManifest = configuredManifest;
 215   
     }
 216   
 
 217   
     /**
 218   
      * The manifest file to use. This can be either the location of a manifest,
 219   
      * or the name of a jar added through a fileset. If its the name of an added
 220   
      * jar, the task expects the manifest to be in the jar at META-INF/MANIFEST.MF.
 221   
      *
 222   
      * @param manifestFile the manifest file to use.
 223   
      */
 224  9
     public void setManifest(File manifestFile) {
 225  9
         if (!manifestFile.exists()) {
 226  1
             throw new BuildException("Manifest file: " + manifestFile +
 227   
                                      " does not exist.", getLocation());
 228   
         }
 229   
 
 230  8
         this.manifestFile = manifestFile;
 231   
     }
 232   
 
 233  8
     private Manifest getManifest(File manifestFile) {
 234   
 
 235  8
         Manifest newManifest = null;
 236  8
         FileInputStream fis = null;
 237  8
         InputStreamReader isr = null;
 238  8
         try {
 239  8
             fis = new FileInputStream(manifestFile);
 240  8
             if (manifestEncoding == null) {
 241  8
                 isr = new InputStreamReader(fis);
 242   
             } else {
 243  0
                 isr = new InputStreamReader(fis, manifestEncoding);
 244   
             }
 245  8
             newManifest = getManifest(isr);
 246   
         } catch (UnsupportedEncodingException e) {
 247  0
             throw new BuildException("Unsupported encoding while reading manifest: "
 248   
                                      + e.getMessage(), e);
 249   
         } catch (IOException e) {
 250  0
             throw new BuildException("Unable to read manifest file: "
 251   
                                      + manifestFile
 252   
                                      + " (" + e.getMessage() + ")", e);
 253   
         } finally {
 254  8
             if (isr != null) {
 255  8
                 try {
 256  8
                     isr.close();
 257   
                 } catch (IOException e) {
 258   
                     // do nothing
 259   
                 }
 260   
             }
 261   
         }
 262  5
         return newManifest;
 263   
     }
 264   
 
 265   
     /**
 266   
      * @return null if jarFile doesn't contain a manifest, the
 267   
      * manifest otherwise.
 268   
      * @since Ant 1.5.2
 269   
      */
 270  17
     private Manifest getManifestFromJar(File jarFile) throws IOException {
 271  17
         ZipFile zf = null;
 272  17
         try {
 273  17
             zf = new ZipFile(jarFile);
 274   
             
 275   
             // must not use getEntry as "well behaving" applications
 276   
             // must accept the manifest in any capitalization
 277  17
             Enumeration enum = zf.entries();
 278  34
             while (enum.hasMoreElements()) {
 279  34
                 ZipEntry ze = (ZipEntry) enum.nextElement();
 280  34
                 if (ze.getName().equalsIgnoreCase(MANIFEST_NAME)) {
 281  17
                     InputStreamReader isr =
 282   
                         new InputStreamReader(zf.getInputStream(ze), "UTF-8");
 283  17
                     return getManifest(isr);
 284   
                 }
 285   
             }
 286  0
             return null;
 287   
         } finally {
 288  17
             if (zf != null) {
 289  17
                 try {
 290  17
                     zf.close();
 291   
                 } catch (IOException e) {
 292   
                     // XXX - log an error?  throw an exception?
 293   
                 }
 294   
             }
 295   
         }
 296   
     }
 297   
 
 298  25
     private Manifest getManifest(Reader r) {
 299   
 
 300  25
         Manifest newManifest = null;
 301  25
         try {
 302  25
             newManifest = new Manifest(r);
 303   
         } catch (ManifestException e) {
 304  3
             log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
 305  3
             throw new BuildException("Invalid Manifest: " + manifestFile,
 306   
                                      e, getLocation());
 307   
         } catch (IOException e) {
 308  0
             throw new BuildException("Unable to read manifest file"
 309   
                                      + " (" + e.getMessage() + ")", e);
 310   
         }
 311  22
         return newManifest;
 312   
     }
 313   
 
 314   
     /**
 315   
      * Behavior when a Manifest is found in a zipfileset or zipgroupfileset file.
 316   
      * Valid values are "skip", "merge", and "mergewithoutmain".
 317   
      * "merge" will merge all of manifests together, and merge this into any
 318   
      * other specified manifests.
 319   
      * "mergewithoutmain" merges everything but the Main section of the manifests.
 320   
      * Default value is "skip".
 321   
      *
 322   
      * Note: if this attribute's value is not "skip", the created jar will not
 323   
      * be readable by using java.util.jar.JarInputStream
 324   
      *
 325   
      * @param config setting for found manifest behavior.
 326   
      */
 327  0
     public void setFilesetmanifest(FilesetManifestConfig config) {
 328  0
         filesetManifestConfig = config;
 329  0
         mergeManifestsMain = "merge".equals(config.getValue());
 330   
 
 331  0
         if (filesetManifestConfig != null
 332   
             && ! filesetManifestConfig.getValue().equals("skip")) {
 333   
 
 334  0
             doubleFilePass = true;
 335   
         }
 336   
     }
 337   
 
 338   
     /**
 339   
      * Adds a zipfileset to include in the META-INF directory.
 340   
      *
 341   
      * @param fs zipfileset to add
 342   
      */
 343  0
     public void addMetainf(ZipFileSet fs) {
 344   
         // We just set the prefix for this fileset, and pass it up.
 345  0
         fs.setPrefix("META-INF/");
 346  0
         super.addFileset(fs);
 347   
     }
 348   
 
 349  59
     protected void initZipOutputStream(ZipOutputStream zOut)
 350   
         throws IOException, BuildException {
 351   
 
 352  59
         if (! skipWriting) {
 353  59
             Manifest jarManifest = createManifest();
 354  56
             writeManifest(zOut, jarManifest);
 355   
         }
 356   
     }
 357   
 
 358  76
     private Manifest createManifest()
 359   
         throws BuildException {
 360  76
         try {
 361  76
             Manifest finalManifest = Manifest.getDefaultManifest();
 362   
 
 363  76
             if (manifest == null) {
 364  72
                 if (manifestFile != null) {
 365   
                     // if we haven't got the manifest yet, attempt to
 366   
                     // get it now and have manifest be the final merge
 367  8
                     manifest = getManifest(manifestFile);
 368   
                 }
 369   
             }
 370   
 
 371   
             /*
 372   
              * Precedence: manifestFile wins over inline manifest,
 373   
              * over manifests read from the filesets over the original
 374   
              * manifest.
 375   
              *
 376   
              * merge with null argument is a no-op
 377   
              */
 378   
 
 379  73
             if (isInUpdateMode()) {
 380  15
                 finalManifest.merge(originalManifest);
 381   
             }
 382  73
             finalManifest.merge(filesetManifest);
 383  73
             finalManifest.merge(configuredManifest);
 384  73
             finalManifest.merge(manifest, !mergeManifestsMain);
 385   
 
 386  73
             return finalManifest;
 387   
 
 388   
         } catch (ManifestException e) {
 389  0
             log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
 390  0
             throw new BuildException("Invalid Manifest", e, getLocation());
 391   
         }
 392   
     }
 393   
 
 394  56
     private void writeManifest(ZipOutputStream zOut, Manifest manifest)
 395   
         throws IOException {
 396  56
         for (Enumeration e = manifest.getWarnings();
 397  60
              e.hasMoreElements();) {
 398  4
             log("Manifest warning: " + (String) e.nextElement(),
 399   
                 Project.MSG_WARN);
 400   
         }
 401   
 
 402  56
         zipDir(null, zOut, "META-INF/", ZipFileSet.DEFAULT_DIR_MODE);
 403   
         // time to write the manifest
 404  56
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 405  56
         OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");
 406  56
         PrintWriter writer = new PrintWriter(osw);
 407  56
         manifest.write(writer);
 408  56
         writer.flush();
 409   
 
 410  56
         ByteArrayInputStream bais =
 411   
             new ByteArrayInputStream(baos.toByteArray());
 412  56
         super.zipFile(bais, zOut, MANIFEST_NAME,
 413   
                       System.currentTimeMillis(), null,
 414   
                       ZipFileSet.DEFAULT_FILE_MODE);
 415  56
         super.initZipOutputStream(zOut);
 416   
     }
 417   
 
 418  56
     protected void finalizeZipOutputStream(ZipOutputStream zOut)
 419   
         throws IOException, BuildException {
 420   
 
 421  56
         if (index) {
 422  2
             createIndexList(zOut);
 423   
         }
 424   
     }
 425   
 
 426   
     /**
 427   
      * Create the index list to speed up classloading.
 428   
      * This is a JDK 1.3+ specific feature and is enabled by default. See
 429   
      * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR+Index">
 430   
      * the JAR index specification</a> for more details.
 431   
      *
 432   
      * @param zOut the zip stream representing the jar being built.
 433   
      * @throws IOException thrown if there is an error while creating the
 434   
      * index and adding it to the zip stream.
 435   
      */
 436  2
     private void createIndexList(ZipOutputStream zOut) throws IOException {
 437  2
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 438   
         // encoding must be UTF8 as specified in the specs.
 439  2
         PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos,
 440   
                                                                     "UTF8"));
 441   
 
 442   
         // version-info blankline
 443  2
         writer.println("JarIndex-Version: 1.0");
 444  2
         writer.println();
 445   
 
 446   
         // header newline
 447  2
         writer.println(zipFile.getName());
 448   
 
 449   
         // JarIndex is sorting the directories by ascending order.
 450   
         // it's painful to do in JDK 1.1 and it has no value but cosmetic
 451   
         // since it will be read into a hashtable by the classloader.
 452  2
         Enumeration enum = addedDirs.keys();
 453  2
         while (enum.hasMoreElements()) {
 454  4
             String dir = (String) enum.nextElement();
 455   
 
 456   
             // try to be smart, not to be fooled by a weird directory name
 457   
             // @fixme do we need to check for directories starting by ./ ?
 458  4
             dir = dir.replace('\\', '/');
 459  4
             int pos = dir.lastIndexOf('/');
 460  4
             if (pos != -1){
 461  4
                 dir = dir.substring(0, pos);
 462   
             }
 463   
 
 464   
             // looks like nothing from META-INF should be added
 465   
             // and the check is not case insensitive.
 466   
             // see sun.misc.JarIndex
 467  4
             if (dir.startsWith("META-INF")) {
 468  2
                 continue;
 469   
             }
 470   
             // name newline
 471  2
             writer.println(dir);
 472   
         }
 473   
 
 474  2
         enum = rootEntries.elements();
 475  2
         while (enum.hasMoreElements()) {
 476  2
             writer.println(enum.nextElement());
 477   
         }
 478   
 
 479  2
         writer.flush();
 480  2
         ByteArrayInputStream bais =
 481   
             new ByteArrayInputStream(baos.toByteArray());
 482  2
         super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(), null,
 483   
                       ZipFileSet.DEFAULT_FILE_MODE);
 484   
     }
 485   
 
 486   
     /**
 487   
      * Overriden from Zip class to deal with manifests and index lists.
 488   
      */
 489  144
     protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath,
 490   
                            long lastModified, File fromArchive, int mode)
 491   
         throws IOException {
 492  144
         if (MANIFEST_NAME.equalsIgnoreCase(vPath))  {
 493  0
             if (! doubleFilePass || (doubleFilePass && skipWriting)) {
 494  0
                 filesetManifest(fromArchive, is);
 495   
             }
 496  144
         } else if (INDEX_NAME.equalsIgnoreCase(vPath) && index) {
 497  2
             log("Warning: selected " + archiveType
 498   
                 + " files include a META-INF/INDEX.LIST which will"
 499   
                 + " be replaced by a newly generated one.", Project.MSG_WARN);
 500   
         } else {
 501  142
             if (index && vPath.indexOf("/") == -1) {
 502  2
                 rootEntries.addElement(vPath);
 503   
             }
 504  142
             super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
 505   
         }
 506   
     }
 507   
 
 508  0
     private void filesetManifest(File file, InputStream is) throws IOException {
 509  0
         if (manifestFile != null && manifestFile.equals(file)) {
 510   
             // If this is the same name specified in 'manifest', this
 511   
             // is the manifest to use
 512  0
             log("Found manifest " + file, Project.MSG_VERBOSE);
 513  0
             try {
 514  0
                 if (is != null) {
 515  0
                     InputStreamReader isr;
 516  0
                     if (manifestEncoding == null) {
 517  0
                         isr = new InputStreamReader(is);
 518   
                     } else {
 519  0
                         isr = new InputStreamReader(is, manifestEncoding);
 520   
                     }
 521  0
                     manifest = getManifest(isr);
 522   
                 } else {
 523  0
                     manifest = getManifest(file);
 524   
                 }
 525   
             } catch (UnsupportedEncodingException e) {
 526  0
                 throw new BuildException("Unsupported encoding while reading " 
 527   
                     + "manifest: " + e.getMessage(), e);
 528   
             }
 529  0
         } else if (filesetManifestConfig != null &&
 530   
                    !filesetManifestConfig.getValue().equals("skip")) {
 531   
             // we add this to our group of fileset manifests
 532  0
             log("Found manifest to merge in file " + file,
 533   
                 Project.MSG_VERBOSE);
 534   
 
 535  0
             try {
 536  0
                 Manifest newManifest = null;
 537  0
                 if (is != null) {
 538  0
                     InputStreamReader isr;
 539  0
                     if (manifestEncoding == null) {
 540  0
                         isr = new InputStreamReader(is);
 541   
                     } else {
 542  0
                         isr = new InputStreamReader(is, manifestEncoding);
 543   
                     }
 544  0
                     newManifest = getManifest(isr);
 545   
                 } else {
 546  0
                     newManifest = getManifest(file);
 547   
                 }
 548   
 
 549  0
                 if (filesetManifest == null) {
 550  0
                     filesetManifest = newManifest;
 551   
                 } else {
 552  0
                     filesetManifest.merge(newManifest);
 553   
                 }
 554   
             } catch (UnsupportedEncodingException e) {
 555  0
                 throw new BuildException("Unsupported encoding while reading " 
 556   
                     + "manifest: " + e.getMessage(), e);
 557   
             } catch (ManifestException e) {
 558  0
                 log("Manifest in file " + file + " is invalid: "
 559   
                     + e.getMessage(), Project.MSG_ERR);
 560  0
                 throw new BuildException("Invalid Manifest", e, getLocation());
 561   
             }
 562   
         } else {
 563   
             // assuming 'skip' otherwise
 564   
             // don't warn if skip has been requested explicitly, warn if user
 565   
             // didn't set the attribute
 566   
 
 567   
             // Hide warning also as it makes no sense since
 568   
             // the filesetmanifest attribute itself has been
 569   
             // hidden
 570   
 
 571   
             //int logLevel = filesetManifestConfig == null ?
 572   
             //    Project.MSG_WARN : Project.MSG_VERBOSE;
 573   
             //log("File " + file
 574   
             //    + " includes a META-INF/MANIFEST.MF which will be ignored. "
 575   
             //    + "To include this file, set filesetManifest to a value other "
 576   
             //    + "than 'skip'.", logLevel);
 577   
         }
 578   
     }
 579   
 
 580   
     /**
 581   
      * Collect the resources that are newer than the corresponding
 582   
      * entries (or missing) in the original archive.
 583   
      *
 584   
      * <p>If we are going to recreate the archive instead of updating
 585   
      * it, all resources should be considered as new, if a single one
 586   
      * is.  Because of this, subclasses overriding this method must
 587   
      * call <code>super.getResourcesToAdd</code> and indicate with the
 588   
      * third arg if they already know that the archive is
 589   
      * out-of-date.</p>
 590   
      *
 591   
      * @param filesets The filesets to grab resources from
 592   
      * @param zipFile intended archive file (may or may not exist)
 593   
      * @param needsUpdate whether we already know that the archive is
 594   
      * out-of-date.  Subclasses overriding this method are supposed to
 595   
      * set this value correctly in their call to
 596   
      * super.getResourcesToAdd.
 597   
      * @return an array of resources to add for each fileset passed in as well
 598   
      *         as a flag that indicates whether the archive is uptodate.
 599   
      *
 600   
      * @exception BuildException if it likes
 601   
      */
 602  57
     protected ArchiveState getResourcesToAdd(FileSet[] filesets,
 603   
                                              File zipFile,
 604   
                                              boolean needsUpdate)
 605   
         throws BuildException {
 606   
 
 607   
         // need to handle manifest as a special check
 608  57
         if (zipFile.exists()) {
 609   
             // if it doesn't exist, it will get created anyway, don't
 610   
             // bother with any up-to-date checks.
 611   
 
 612  17
             try {
 613  17
                 originalManifest = getManifestFromJar(zipFile);
 614  17
                 if (originalManifest == null) {
 615  0
                     log("Updating jar since the current jar has no manifest",
 616   
                         Project.MSG_VERBOSE);
 617  0
                     needsUpdate = true;
 618   
                 } else {
 619  17
                     Manifest mf = createManifest();
 620  17
                     if (!mf.equals(originalManifest)) {
 621  1
                         log("Updating jar since jar manifest has changed", 
 622   
                             Project.MSG_VERBOSE);
 623  1
                         needsUpdate = true;
 624   
                     }
 625   
                 }
 626   
             } catch (Throwable t) {
 627  0
                 log("error while reading original manifest: " + t.getMessage(),
 628   
                     Project.MSG_WARN);
 629  0
                 needsUpdate = true;
 630   
             }
 631   
 
 632   
         } else {
 633   
             // no existing archive
 634  40
             needsUpdate = true;
 635   
         }
 636   
 
 637  57
         createEmpty = needsUpdate;
 638  57
         return super.getResourcesToAdd(filesets, zipFile, needsUpdate);
 639   
     }
 640   
 
 641  12
     protected boolean createEmptyZip(File zipFile) throws BuildException {
 642  12
         if (!createEmpty) {
 643  0
             return true;
 644   
         }
 645   
         
 646  12
         ZipOutputStream zOut = null;
 647  12
         try {
 648  12
             log("Building MANIFEST-only jar: " 
 649   
                 + getDestFile().getAbsolutePath());
 650  12
             zOut = new ZipOutputStream(new FileOutputStream(getDestFile()));
 651   
 
 652  12
             zOut.setEncoding(getEncoding());
 653  12
             if (isCompress()) {
 654  12
                 zOut.setMethod(ZipOutputStream.DEFLATED);
 655   
             } else {
 656  0
                 zOut.setMethod(ZipOutputStream.STORED);
 657   
             }
 658  12
             initZipOutputStream(zOut);
 659  9
             finalizeZipOutputStream(zOut);
 660   
         } catch (IOException ioe) {
 661  0
             throw new BuildException("Could not create almost empty JAR archive"
 662   
                                      + " (" + ioe.getMessage() + ")", ioe,
 663   
                                      getLocation());
 664   
         } finally {
 665   
             // Close the output stream.
 666  12
             try {
 667  12
                 if (zOut != null) {
 668  12
                     zOut.close();
 669   
                 }
 670   
             } catch (IOException ex) {
 671   
             }
 672  12
             createEmpty = false;
 673   
         }
 674  9
         return true;
 675   
     }
 676   
 
 677   
     /**
 678   
      * Make sure we don't think we already have a MANIFEST next time this task
 679   
      * gets executed.
 680   
      *
 681   
      * @see Zip#cleanUp
 682   
      */
 683  57
     protected void cleanUp() {
 684  57
         super.cleanUp();
 685   
 
 686   
         // we want to save this info if we are going to make another pass
 687  57
         if (! doubleFilePass || (doubleFilePass && ! skipWriting)) {
 688  57
             manifest = null;
 689  57
             configuredManifest = savedConfiguredManifest;
 690  57
             filesetManifest = null;
 691  57
             originalManifest = null;
 692   
         }
 693  57
         rootEntries.removeAllElements();
 694   
     }
 695   
 
 696   
     /**
 697   
      * reset to default values.
 698   
      *
 699   
      * @see Zip#reset
 700   
      *
 701   
      * @since 1.44, Ant 1.5
 702   
      */
 703  0
     public void reset() {
 704  0
         super.reset();
 705  0
         configuredManifest = null;
 706  0
         filesetManifestConfig = null;
 707  0
         mergeManifestsMain = false;
 708  0
         manifestFile = null;
 709  0
         index = false;
 710   
     }
 711   
 
 712   
     public static class FilesetManifestConfig extends EnumeratedAttribute {
 713  0
         public String[] getValues() {
 714  0
             return new String[] {"skip", "merge", "mergewithoutmain"};
 715   
         }
 716   
     }
 717   
 }
 718