Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 1,054   Methods: 48
NCLOC: 542   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
Manifest.java 73.9% 81% 93.8% 80.2%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2001-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.BufferedReader;
 58   
 import java.io.IOException;
 59   
 import java.io.InputStream;
 60   
 import java.io.InputStreamReader;
 61   
 import java.io.PrintWriter;
 62   
 import java.io.Reader;
 63   
 import java.io.StringWriter;
 64   
 import java.io.UnsupportedEncodingException;
 65   
 import java.util.Enumeration;
 66   
 import java.util.Hashtable;
 67   
 import java.util.Vector;
 68   
 import org.apache.tools.ant.BuildException;
 69   
 import org.apache.tools.ant.util.CollectionUtils;
 70   
 
 71   
 /**
 72   
  * Holds the data of a jar manifest.
 73   
  *
 74   
  * Manifests are processed according to the
 75   
  * {@link <a href="http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html">Jar
 76   
  * file specification.</a>}.
 77   
  * Specifically, a manifest element consists of
 78   
  * a set of attributes and sections. These sections in turn may contain
 79   
  * attributes. Note in particular that this may result in manifest lines
 80   
  * greater than 72 bytes being wrapped and continued on the next
 81   
  * line. If an application can not handle the continuation mechanism, it
 82   
  * is a defect in the application, not this task.
 83   
  * @author Conor MacNeill
 84   
  * @author Stefan Bodewig
 85   
  * @author <a href="mailto:j_a_fernandez@yahoo.com">Jose Alberto Fernandez</a>
 86   
  *
 87   
  * @since Ant 1.4
 88   
  */
 89   
 public class Manifest {
 90   
     /** The standard manifest version header */
 91   
     public static final String ATTRIBUTE_MANIFEST_VERSION
 92   
         = "Manifest-Version";
 93   
 
 94   
     /** The standard Signature Version header */
 95   
     public static final String ATTRIBUTE_SIGNATURE_VERSION
 96   
         = "Signature-Version";
 97   
 
 98   
     /** The Name Attribute is the first in a named section */
 99   
     public static final String ATTRIBUTE_NAME = "Name";
 100   
 
 101   
     /** The From Header is disallowed in a Manifest */
 102   
     public static final String ATTRIBUTE_FROM = "From";
 103   
 
 104   
     /** The Class-Path Header is special - it can be duplicated */
 105   
     public static final String ATTRIBUTE_CLASSPATH = "class-path";
 106   
 
 107   
     /** Default Manifest version if one is not specified */
 108   
     public static final  String DEFAULT_MANIFEST_VERSION = "1.0";
 109   
 
 110   
     /** The max length of a line in a Manifest */
 111   
     public static final int MAX_LINE_LENGTH = 72;
 112   
 
 113   
     /**
 114   
      * Max length of a line section which is continued. Need to allow
 115   
      * for the CRLF.
 116   
      */
 117   
     public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
 118   
 
 119   
     /** The End-Of-Line marker in manifests */
 120   
     public static final String EOL = "\r\n";
 121   
 
 122   
     /**
 123   
      * An attribute for the manifest.
 124   
      * Those attributes that are not nested into a section will be added to the "Main" section.
 125   
      */
 126   
     public static class Attribute {
 127   
         /** The attribute's name */
 128   
         private String name = null;
 129   
 
 130   
         /** The attribute's value */
 131   
         private Vector values = new Vector();
 132   
 
 133   
         /**
 134   
          * For multivalued attributes, this is the index of the attribute
 135   
          * currently being defined.
 136   
          */
 137   
         private int currentIndex = 0;
 138   
 
 139   
         /**
 140   
          * Construct an empty attribute */
 141  25
         public Attribute() {
 142   
         }
 143   
 
 144   
         /**
 145   
          * Construct an attribute by parsing a line from the Manifest
 146   
          *
 147   
          * @param line the line containing the attribute name and value
 148   
          *
 149   
          * @throws ManifestException if the line is not valid
 150   
          */
 151  228599
         public Attribute(String line) throws ManifestException {
 152  228599
             parse(line);
 153   
         }
 154   
 
 155   
         /**
 156   
          * Construct a manifest by specifying its name and value
 157   
          *
 158   
          * @param name the attribute's name
 159   
          * @param value the Attribute's value
 160   
          */
 161  112
         public Attribute(String name, String value) {
 162  112
             this.name = name;
 163  112
             setValue(value);
 164   
         }
 165   
 
 166   
         /**
 167   
          * @see java.lang.Object#hashCode
 168   
          */
 169  0
         public int hashCode() {
 170  0
             int hashCode = 0;
 171   
 
 172  0
             if (name != null) {
 173  0
                 hashCode += name.hashCode();
 174   
             }
 175   
 
 176  0
             hashCode += values.hashCode();
 177  0
             return hashCode;
 178   
         }
 179   
 
 180   
         /**
 181   
          * @see java.lang.Object#equals
 182   
          */
 183  41
         public boolean equals(Object rhs) {
 184  41
             if (rhs == null || rhs.getClass() != getClass()) {
 185  0
                 return false;
 186   
             }
 187   
 
 188  41
             if (rhs == this) {
 189  19
                 return true;
 190   
             }
 191   
 
 192  22
             Attribute rhsAttribute = (Attribute) rhs;
 193  22
             String lhsKey = getKey();
 194  22
             String rhsKey = rhsAttribute.getKey();
 195  22
             if ((lhsKey == null && rhsKey != null)
 196   
                  || (lhsKey != null && rhsKey == null)
 197   
                  || !lhsKey.equals(rhsKey)) {
 198  0
                 return false;
 199   
             }
 200   
 
 201  22
             return CollectionUtils.equals(values, rhsAttribute.values);
 202   
         }
 203   
 
 204   
         /**
 205   
          * Parse a line into name and value pairs
 206   
          *
 207   
          * @param line the line to be parsed
 208   
          *
 209   
          * @throws ManifestException if the line does not contain a colon
 210   
          * separating the name and value
 211   
          */
 212  228599
         public void parse(String line) throws ManifestException {
 213  228599
             int index = line.indexOf(": ");
 214  228599
             if (index == -1) {
 215  2
                 throw new ManifestException("Manifest line \"" + line
 216   
                     + "\" is not valid as it does not "
 217   
                     + "contain a name and a value separated by ': ' ");
 218   
             }
 219  228597
             name = line.substring(0, index);
 220  228597
             setValue(line.substring(index + 2));
 221   
         }
 222   
 
 223   
         /**
 224   
          * Set the Attribute's name; required
 225   
          *
 226   
          * @param name the attribute's name
 227   
          */
 228  24
         public void setName(String name) {
 229  24
             this.name = name;
 230   
         }
 231   
 
 232   
         /**
 233   
          * Get the Attribute's name
 234   
          *
 235   
          * @return the attribute's name.
 236   
          */
 237  228645
         public String getName() {
 238  228645
             return name;
 239   
         }
 240   
 
 241   
         /**
 242   
          * Get the attribute's Key - its name in lower case.
 243   
          *
 244   
          * @return the attribute's key.
 245   
          */
 246  684112
         public String getKey() {
 247  684112
             if (name == null) {
 248  1
                 return null;
 249   
             }
 250  684111
             return name.toLowerCase();
 251   
         }
 252   
 
 253   
         /**
 254   
          * Set the Attribute's value; required
 255   
          *
 256   
          * @param value the attribute's value
 257   
          */
 258  228742
         public void setValue(String value) {
 259  228742
             if (currentIndex >= values.size()) {
 260  228739
                 values.addElement(value);
 261  228739
                 currentIndex = values.size() - 1;
 262   
             } else {
 263  3
                 values.setElementAt(value, currentIndex);
 264   
             }
 265   
         }
 266   
 
 267   
         /**
 268   
          * Get the Attribute's value.
 269   
          *
 270   
          * @return the attribute's value.
 271   
          */
 272  232789
         public String getValue() {
 273  232789
             if (values.size() == 0) {
 274  1
                 return null;
 275   
             }
 276   
 
 277  232788
             String fullValue = "";
 278  232788
             for (Enumeration e = getValues(); e.hasMoreElements();) {
 279  232791
                 String value = (String) e.nextElement();
 280  232791
                 fullValue += value + " ";
 281   
             }
 282  232788
             return fullValue.trim();
 283   
         }
 284   
 
 285   
         /**
 286   
          * Add a new value to this attribute - making it multivalued.
 287   
          *
 288   
          * @param value the attribute's additional value
 289   
          */
 290  6
         public void addValue(String value) {
 291  6
             currentIndex++;
 292  6
             setValue(value);
 293   
         }
 294   
 
 295   
         /**
 296   
          * Get all the attribute's values.
 297   
          *
 298   
          * @return an enumeration of the attributes values
 299   
          */
 300  232964
         public Enumeration getValues() {
 301  232964
             return values.elements();
 302   
         }
 303   
 
 304   
         /**
 305   
          * Add a continuation line from the Manifest file.
 306   
          *
 307   
          * When lines are too long in a manifest, they are continued on the
 308   
          * next line by starting with a space. This method adds the continuation
 309   
          * data to the attribute value by skipping the first character.
 310   
          *
 311   
          * @param line the continuation line.
 312   
          */
 313  3
         public void addContinuation(String line) {
 314  3
             String currentValue = (String) values.elementAt(currentIndex);
 315  3
             setValue(currentValue + line.substring(1));
 316   
         }
 317   
 
 318   
         /**
 319   
          * Write the attribute out to a print writer.
 320   
          *
 321   
          * @param writer the Writer to which the attribute is written
 322   
          *
 323   
          * @throws IOException if the attribte value cannot be written
 324   
          */
 325  170
         public void write(PrintWriter writer) throws IOException {
 326  170
             for (Enumeration e = getValues(); e.hasMoreElements();) {
 327  176
                 writeValue(writer, (String) e.nextElement());
 328   
             }
 329   
         }
 330   
 
 331   
         /**
 332   
          * Write a single attribute value out
 333   
          *
 334   
          * @param writer the Writer to which the attribute is written
 335   
          * @param value the attribute value
 336   
          *
 337   
          * @throws IOException if the attribte value cannot be written
 338   
          */
 339  176
         private void writeValue(PrintWriter writer, String value)
 340   
              throws IOException {
 341  176
             String line = name + ": " + value;
 342  176
             while (line.getBytes().length > MAX_LINE_LENGTH) {
 343   
                 // try to find a MAX_LINE_LENGTH byte section
 344  6
                 int breakIndex = MAX_SECTION_LENGTH;
 345  6
                 String section = line.substring(0, breakIndex);
 346  6
                 while (section.getBytes().length > MAX_SECTION_LENGTH
 347   
                      && breakIndex > 0) {
 348  0
                     breakIndex--;
 349  0
                     section = line.substring(0, breakIndex);
 350   
                 }
 351  6
                 if (breakIndex == 0) {
 352  0
                     throw new IOException("Unable to write manifest line "
 353   
                         + name + ": " + value);
 354   
                 }
 355  6
                 writer.print(section + EOL);
 356  6
                 line = " " + line.substring(breakIndex);
 357   
             }
 358  176
             writer.print(line + EOL);
 359   
         }
 360   
     }
 361   
 
 362   
     /**
 363   
      * A manifest section - you can nest attribute elements into sections.
 364   
      * A section consists of a set of attribute values,
 365   
      * separated from other sections by a blank line.
 366   
      */
 367   
     public static class Section {
 368   
         /** Warnings for this section */
 369   
         private Vector warnings = new Vector();
 370   
 
 371   
         /**
 372   
          * The section's name if any. The main section in a
 373   
          * manifest is unnamed.
 374   
          */
 375   
         private String name = null;
 376   
 
 377   
         /** The section's attributes.*/
 378   
         private Hashtable attributes = new Hashtable();
 379   
 
 380   
         /** Index used to retain the attribute ordering */
 381   
         private Vector attributeIndex = new Vector();
 382   
 
 383   
         /**
 384   
          * The name of the section; optional -default is the main section.
 385   
          * @param name the section's name
 386   
          */
 387  57662
         public void setName(String name) {
 388  57662
             this.name = name;
 389   
         }
 390   
 
 391   
         /**
 392   
          * Get the Section's name.
 393   
          *
 394   
          * @return the section's name.
 395   
          */
 396  57707
         public String getName() {
 397  57707
             return name;
 398   
         }
 399   
 
 400   
         /**
 401   
          * Read a section through a reader.
 402   
          *
 403   
          * @param reader the reader from which the section is read
 404   
          *
 405   
          * @return the name of the next section if it has been read as
 406   
          *         part of this section - This only happens if the
 407   
          *         Manifest is malformed.
 408   
          *
 409   
          * @throws ManifestException if the section is not valid according
 410   
          *         to the JAR spec
 411   
          * @throws IOException if the section cannot be read from the reader.
 412   
          */
 413  61658
         public String read(BufferedReader reader)
 414   
              throws ManifestException, IOException {
 415  61658
             Attribute attribute = null;
 416  61658
             while (true) {
 417  232614
                 String line = reader.readLine();
 418  232614
                 if (line == null || line.length() == 0) {
 419  61656
                     return null;
 420   
                 }
 421  170958
                 if (line.charAt(0) == ' ') {
 422   
                     // continuation line
 423  3
                     if (attribute == null) {
 424  0
                         if (name != null) {
 425   
                             // a continuation on the first line is a
 426   
                             // continuation of the name - concatenate this
 427   
                             // line and the name
 428  0
                             name += line.substring(1);
 429   
                         } else {
 430  0
                             throw new ManifestException("Can't start an "
 431   
                                 + "attribute with a continuation line " + line);
 432   
                         }
 433   
                     } else {
 434  3
                         attribute.addContinuation(line);
 435   
                     }
 436   
                 } else {
 437  170955
                     attribute = new Attribute(line);
 438  170954
                     String nameReadAhead = addAttributeAndCheck(attribute);
 439  170954
                     if (nameReadAhead != null) {
 440  1
                         return nameReadAhead;
 441   
                     }
 442   
                 }
 443   
             }
 444   
         }
 445   
 
 446   
         /**
 447   
          * Merge in another section
 448   
          *
 449   
          * @param section the section to be merged with this one.
 450   
          *
 451   
          * @throws ManifestException if the sections cannot be merged.
 452   
          */
 453  45
         public void merge(Section section) throws ManifestException {
 454  45
             if (name == null && section.getName() != null
 455   
                 || name != null
 456   
                 && !(name.equalsIgnoreCase(section.getName()))) {
 457  0
                 throw new ManifestException("Unable to merge sections "
 458   
                     + "with different names");
 459   
             }
 460   
 
 461  45
             Enumeration e = section.getAttributeKeys();
 462  45
             while (e.hasMoreElements()) {
 463  50
                 String attributeName = (String) e.nextElement();
 464  50
                 Attribute attribute = section.getAttribute(attributeName);
 465  50
                 if (attributeName.equals(ATTRIBUTE_CLASSPATH) &&
 466   
                         attributes.containsKey(attributeName)) {
 467  0
                     Attribute ourClassPath = getAttribute(attributeName);
 468  0
                     Enumeration cpe = attribute.getValues();
 469  0
                     while (cpe.hasMoreElements()) {
 470  0
                         String value = (String) cpe.nextElement();
 471  0
                         ourClassPath.addValue(value);
 472   
                     }
 473   
                 } else {
 474   
                     // the merge file always wins
 475  50
                     storeAttribute(attribute);
 476   
                 }
 477   
             }
 478   
 
 479   
             // add in the warnings
 480  45
             Enumeration warnEnum = section.warnings.elements();
 481  45
             while (warnEnum.hasMoreElements()) {
 482  4
                 warnings.addElement(warnEnum.nextElement());
 483   
             }
 484   
         }
 485   
 
 486   
         /**
 487   
          * Write the section out to a print writer.
 488   
          *
 489   
          * @param writer the Writer to which the section is written
 490   
          *
 491   
          * @throws IOException if the section cannot be written
 492   
          */
 493  76
         public void write(PrintWriter writer) throws IOException {
 494  76
             if (name != null) {
 495  13
                 Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
 496  13
                 nameAttr.write(writer);
 497   
             }
 498  76
             Enumeration e = getAttributeKeys();
 499  76
             while (e.hasMoreElements()) {
 500  157
                 String key = (String) e.nextElement();
 501  157
                 Attribute attribute = getAttribute(key);
 502  157
                 attribute.write(writer);
 503   
             }
 504  76
             writer.print(EOL);
 505   
         }
 506   
 
 507   
         /**
 508   
          * Get a attribute of the section
 509   
          *
 510   
          * @param attributeName the name of the attribute
 511   
          * @return a Manifest.Attribute instance if the attribute is
 512   
          *         single-valued, otherwise a Vector of Manifest.Attribute
 513   
          *         instances.
 514   
          */
 515  8201
         public Attribute getAttribute(String attributeName) {
 516  8201
             return (Attribute) attributes.get(attributeName.toLowerCase());
 517   
         }
 518   
 
 519   
         /**
 520   
          * Get the attribute keys.
 521   
          *
 522   
          * @return an Enumeration of Strings, each string being the lower case
 523   
          *         key of an attribute of the section.
 524   
          */
 525  135
         public Enumeration getAttributeKeys() {
 526  135
             return attributeIndex.elements();
 527   
         }
 528   
 
 529   
         /**
 530   
          * Get the value of the attribute with the name given.
 531   
          *
 532   
          * @param attributeName the name of the attribute to be returned.
 533   
          *
 534   
          * @return the attribute's value or null if the attribute does not exist
 535   
          *         in the section
 536   
          */
 537  7974
         public String getAttributeValue(String attributeName) {
 538  7974
             Attribute attribute = getAttribute(attributeName.toLowerCase());
 539  7974
             if (attribute == null) {
 540  3838
                 return null;
 541   
             }
 542  4136
             return attribute.getValue();
 543   
         }
 544   
 
 545   
         /**
 546   
          * Remove tge given attribute from the section
 547   
          *
 548   
          * @param attributeName the name of the attribute to be removed.
 549   
          */
 550  4014
         public void removeAttribute(String attributeName) {
 551  4014
             String key = attributeName.toLowerCase();
 552  4014
             attributes.remove(key);
 553  4014
             attributeIndex.removeElement(key);
 554   
         }
 555   
 
 556   
         /**
 557   
          * Add an attribute to the section.
 558   
          *
 559   
          * @param attribute the attribute to be added to the section
 560   
          *
 561   
          * @exception ManifestException if the attribute is not valid.
 562   
          */
 563  23
         public void addConfiguredAttribute(Attribute attribute)
 564   
              throws ManifestException {
 565  23
             String check = addAttributeAndCheck(attribute);
 566  22
             if (check != null) {
 567  1
                 throw new BuildException("Specify the section name using "
 568   
                     + "the \"name\" attribute of the <section> element rather "
 569   
                     + "than using a \"Name\" manifest attribute");
 570   
             }
 571   
         }
 572   
 
 573   
         /**
 574   
          * Add an attribute to the section
 575   
          *
 576   
          * @param attribute the attribute to be added.
 577   
          *
 578   
          * @return the value of the attribute if it is a name
 579   
          *         attribute - null other wise
 580   
          *
 581   
          * @exception ManifestException if the attribute already
 582   
          *            exists in this section.
 583   
          */
 584  170977
         public String addAttributeAndCheck(Attribute attribute)
 585   
              throws ManifestException {
 586  170977
             if (attribute.getName() == null || attribute.getValue() == null) {
 587  0
                 throw new BuildException("Attributes must have name and value");
 588   
             }
 589  170977
             if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
 590  2
                 warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
 591   
                     + "should not occur in the main section and must be the "
 592   
                     + "first element in all other sections: \""
 593   
                     + attribute.getName() + ": " + attribute.getValue() + "\"");
 594  2
                 return attribute.getValue();
 595   
             }
 596   
 
 597  170975
             if (attribute.getKey().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
 598  1
                 warnings.addElement("Manifest attributes should not start "
 599   
                     + "with \"" + ATTRIBUTE_FROM + "\" in \""
 600   
                     + attribute.getName() + ": " + attribute.getValue() + "\"");
 601   
             } else {
 602   
                 // classpath attributes go into a vector
 603  170974
                 String attributeKey = attribute.getKey();
 604  170974
                 if (attributeKey.equals(ATTRIBUTE_CLASSPATH)) {
 605  132
                     Attribute classpathAttribute =
 606   
                         (Attribute) attributes.get(attributeKey);
 607   
 
 608  132
                     if (classpathAttribute == null) {
 609  126
                         storeAttribute(attribute);
 610   
                     } else {
 611  6
                         Enumeration e = attribute.getValues();
 612  6
                         while (e.hasMoreElements()) {
 613  6
                             String value = (String) e.nextElement();
 614  6
                             classpathAttribute.addValue(value);
 615   
                         }
 616   
                     }
 617  170842
                 } else if (attributes.containsKey(attributeKey)) {
 618  1
                     throw new ManifestException("The attribute \""
 619   
                         + attribute.getName() + "\" may not occur more "
 620   
                         + "than once in the same section");
 621   
                 } else {
 622  170841
                     storeAttribute(attribute);
 623   
                 }
 624   
             }
 625  170974
             return null;
 626   
         }
 627   
 
 628   
         /**
 629   
          * Clone this section
 630   
          *
 631   
          * @since Ant 1.5.2
 632   
          */
 633  12
         public Object clone() {
 634  12
             Section cloned = new Section();
 635  12
             cloned.setName(name);
 636  12
             Enumeration e = getAttributeKeys();
 637  12
             while (e.hasMoreElements()) {
 638  16
                 String key = (String) e.nextElement();
 639  16
                 Attribute attribute = getAttribute(key);
 640  16
                 cloned.storeAttribute(new Attribute(attribute.getName(), 
 641   
                                                     attribute.getValue()));
 642   
             }
 643  12
             return cloned;
 644   
         }
 645   
 
 646   
         /**
 647   
          * Store an attribute and update the index.
 648   
          *
 649   
          * @param attribute the attribute to be stored
 650   
          */
 651  171116
         private void storeAttribute(Attribute attribute) {
 652  171116
             if (attribute == null) {
 653  0
                 return;
 654   
             }
 655  171116
             String attributeKey = attribute.getKey();
 656  171116
             attributes.put(attributeKey, attribute);
 657  171116
             if (!attributeIndex.contains(attributeKey)) {
 658  171081
                 attributeIndex.addElement(attributeKey);
 659   
             }
 660   
         }
 661   
 
 662   
         /**
 663   
          * Get the warnings for this section.
 664   
          *
 665   
          * @return an Enumeration of warning strings.
 666   
          */
 667  66
         public Enumeration getWarnings() {
 668  66
             return warnings.elements();
 669   
         }
 670   
 
 671   
         /**
 672   
          * @see java.lang.Object#hashCode
 673   
          */
 674  0
         public int hashCode() {
 675  0
             int hashCode = 0;
 676   
 
 677  0
             if (name != null) {
 678  0
                 hashCode += name.hashCode();
 679   
             }
 680   
 
 681  0
             hashCode += attributes.hashCode();
 682  0
             return hashCode;
 683   
         }
 684   
 
 685   
         /**
 686   
          * @see java.lang.Object#equals
 687   
          */
 688  23
         public boolean equals(Object rhs) {
 689  23
             if (rhs == null || rhs.getClass() != getClass()) {
 690  0
                 return false;
 691   
             }
 692   
 
 693  23
             if (rhs == this) {
 694  0
                 return true;
 695   
             }
 696   
 
 697  23
             Section rhsSection = (Section) rhs;
 698   
 
 699  23
             return CollectionUtils.equals(attributes, rhsSection.attributes);
 700   
         }
 701   
     }
 702   
 
 703   
 
 704   
     /** The version of this manifest */
 705   
     private String manifestVersion = DEFAULT_MANIFEST_VERSION;
 706   
 
 707   
     /** The main section of this manifest */
 708   
     private Section mainSection = new Section();
 709   
 
 710   
     /** The named sections of this manifest */
 711   
     private Hashtable sections = new Hashtable();
 712   
 
 713   
     /** Index of sections - used to retain order of sections in manifest */
 714   
     private Vector sectionIndex = new Vector();
 715   
 
 716   
     /**
 717   
      * Construct a manifest from Ant's default manifest file.
 718   
      *
 719   
      * @return the default manifest.
 720   
      * @exception BuildException if there is a problem loading the
 721   
      *            default manifest
 722   
      */
 723  83
     public static Manifest getDefaultManifest() throws BuildException {
 724  83
         try {
 725  83
             String defManifest = "/org/apache/tools/ant/defaultManifest.mf";
 726  83
             InputStream in = Manifest.class.getResourceAsStream(defManifest);
 727  83
             if (in == null) {
 728  0
                 throw new BuildException("Could not find default manifest: "
 729   
                     + defManifest);
 730   
             }
 731  83
             try {
 732  83
                 Manifest defaultManifest 
 733   
                     = new Manifest(new InputStreamReader(in, "UTF-8"));
 734  83
                 Attribute createdBy = new Attribute("Created-By", 
 735   
                     System.getProperty("java.vm.version") + " ("
 736   
                     + System.getProperty("java.vm.vendor") + ")" );
 737  83
                 defaultManifest.getMainSection().storeAttribute(createdBy);
 738  83
                 return defaultManifest;
 739   
             } catch (UnsupportedEncodingException e) {
 740  0
                 return new Manifest(new InputStreamReader(in));
 741   
             }
 742   
         } catch (ManifestException e) {
 743  0
             throw new BuildException("Default manifest is invalid !!", e);
 744   
         } catch (IOException e) {
 745  0
             throw new BuildException("Unable to read default manifest", e);
 746   
         }
 747   
     }
 748   
 
 749   
     /** Construct an empty manifest */
 750  17
     public Manifest() {
 751  17
         manifestVersion = null;
 752   
     }
 753   
 
 754   
     /**
 755   
      * Read a manifest file from the given reader
 756   
      *
 757   
      * @param r is the reader from which the Manifest is read
 758   
      *
 759   
      * @throws ManifestException if the manifest is not valid according
 760   
      *         to the JAR spec
 761   
      * @throws IOException if the manifest cannot be read from the reader.
 762   
      */
 763  4016
     public Manifest(Reader r) throws ManifestException, IOException {
 764  4016
         BufferedReader reader = new BufferedReader(r);
 765   
         // This should be the manifest version
 766  4016
         String nextSectionName = mainSection.read(reader);
 767  4015
         String readManifestVersion
 768   
             = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
 769  4015
         if (readManifestVersion != null) {
 770  4014
             manifestVersion = readManifestVersion;
 771  4014
             mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
 772   
         }
 773   
 
 774  4015
         String line = null;
 775  ?
         while ((line = reader.readLine()) != null) {
 776  57645
             if (line.length() == 0) {
 777  1
                 continue;
 778   
             }
 779   
 
 780  57644
             Section section = new Section();
 781  57644
             if (nextSectionName == null) {
 782  57644
                 Attribute sectionName = new Attribute(line);
 783  57643
                 if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
 784  1
                     throw new ManifestException("Manifest sections should "
 785   
                         + "start with a \"" + ATTRIBUTE_NAME
 786   
                         + "\" attribute and not \""
 787   
                         + sectionName.getName() + "\"");
 788   
                 }
 789  57642
                 nextSectionName = sectionName.getValue();
 790   
             } else {
 791   
                 // we have already started reading this section
 792   
                 // this line is the first attribute. set it and then
 793   
                 // let the normal read handle the rest
 794  0
                 Attribute firstAttribute = new Attribute(line);
 795  0
                 section.addAttributeAndCheck(firstAttribute);
 796   
             }
 797   
 
 798  57642
             section.setName(nextSectionName);
 799  57642
             nextSectionName = section.read(reader);
 800  57642
             addConfiguredSection(section);
 801   
         }
 802   
     }
 803   
 
 804   
     /**
 805   
      * Add a section to the manifest
 806   
      *
 807   
      * @param section the manifest section to be added
 808   
      *
 809   
      * @exception ManifestException if the secti0on is not valid.
 810   
      */
 811  57662
     public void addConfiguredSection(Section section)
 812   
          throws ManifestException {
 813  57662
         String sectionName = section.getName();
 814  57662
         if (sectionName == null) {
 815  1
             throw new BuildException("Sections must have a name");
 816   
         }
 817  57661
         sections.put(sectionName, section);
 818  57661
         if (!sectionIndex.contains(sectionName)) {
 819  57661
             sectionIndex.addElement(sectionName);
 820   
         }
 821   
     }
 822   
 
 823   
     /**
 824   
      * Add an attribute to the manifest - it is added to the main section.
 825   
      *
 826   
      * @param attribute the attribute to be added.
 827   
      *
 828   
      * @exception ManifestException if the attribute is not valid.
 829   
      */
 830  14
     public void addConfiguredAttribute(Attribute attribute)
 831   
          throws ManifestException {
 832  14
         if (attribute.getKey() == null || attribute.getValue() == null) {
 833  2
             throw new BuildException("Attributes must have name and value");
 834   
         }
 835  12
         if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_MANIFEST_VERSION)) {
 836  0
             manifestVersion = attribute.getValue();
 837   
         } else {
 838  12
             mainSection.addConfiguredAttribute(attribute);
 839   
         }
 840   
     }
 841   
 
 842   
     /**
 843   
      * Merge the contents of the given manifest into this manifest
 844   
      *
 845   
      * @param other the Manifest to be merged with this one.
 846   
      *
 847   
      * @throws ManifestException if there is a problem merging the
 848   
      *         manfest according to the Manifest spec.
 849   
      */
 850  169
     public void merge(Manifest other) throws ManifestException {
 851  169
         merge(other, false);
 852   
     }
 853   
 
 854   
     /**
 855   
      * Merge the contents of the given manifest into this manifest
 856   
      *
 857   
      * @param other the Manifest to be merged with this one.
 858   
      * @param overwriteMain whether to overwrite the main section
 859   
      *        of the current manifest
 860   
      *
 861   
      * @throws ManifestException if there is a problem merging the
 862   
      *         manfest according to the Manifest spec.
 863   
      */
 864  242
     public void merge(Manifest other, boolean overwriteMain)
 865   
          throws ManifestException {
 866  242
         if (other != null) {
 867  44
              if (overwriteMain) {
 868  0
                  mainSection = (Section) other.mainSection.clone();
 869   
              } else {
 870  44
                  mainSection.merge(other.mainSection);
 871   
              }
 872   
 
 873  44
              if (other.manifestVersion != null) {
 874  27
                  manifestVersion = other.manifestVersion;
 875   
              }
 876   
 
 877  44
              Enumeration e = other.getSectionNames();
 878  44
              while (e.hasMoreElements()) {
 879  13
                  String sectionName = (String) e.nextElement();
 880  13
                  Section ourSection = (Section) sections.get(sectionName);
 881  13
                  Section otherSection
 882   
                     = (Section) other.sections.get(sectionName);
 883  13
                  if (ourSection == null) {
 884  12
                      if (otherSection != null) {
 885  12
                          addConfiguredSection((Section) otherSection.clone());
 886   
                      }
 887   
                  } else {
 888  1
                      ourSection.merge(otherSection);
 889   
                  }
 890   
              }
 891   
          }
 892   
     }
 893   
 
 894   
     /**
 895   
     * Write the manifest out to a print writer.
 896   
     *
 897   
     * @param writer the Writer to which the manifest is written
 898   
     *
 899   
     * @throws IOException if the manifest cannot be written
 900   
     */
 901  63
     public void write(PrintWriter writer) throws IOException {
 902  63
         writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
 903  63
         String signatureVersion
 904   
             = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
 905  63
         if (signatureVersion != null) {
 906  0
             writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": "
 907   
                 + signatureVersion + EOL);
 908  0
             mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
 909   
         }
 910  63
         mainSection.write(writer);
 911   
 
 912   
         // add it back
 913  63
         if (signatureVersion != null) {
 914  0
             try {
 915  0
                 Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
 916   
                     signatureVersion);
 917  0
                 mainSection.addConfiguredAttribute(svAttr);
 918   
             } catch (ManifestException e) {
 919   
                 // shouldn't happen - ignore
 920   
             }
 921   
         }
 922   
 
 923  63
         Enumeration e = sectionIndex.elements();
 924  63
         while (e.hasMoreElements()) {
 925  13
             String sectionName = (String) e.nextElement();
 926  13
             Section section = getSection(sectionName);
 927  13
             section.write(writer);
 928   
         }
 929   
     }
 930   
 
 931   
     /**
 932   
      * Convert the manifest to its string representation
 933   
      *
 934   
      * @return a multiline string with the Manifest as it
 935   
      *         appears in a Manifest file.
 936   
      */
 937  2
     public String toString() {
 938  2
         StringWriter sw = new StringWriter();
 939  2
         try {
 940  2
             write(new PrintWriter(sw));
 941   
         } catch (IOException e) {
 942  0
             return null;
 943   
         }
 944  2
         return sw.toString();
 945   
     }
 946   
 
 947   
     /**
 948   
      * Get the warnings for this manifest.
 949   
      *
 950   
      * @return an enumeration of warning strings
 951   
      */
 952  56
     public Enumeration getWarnings() {
 953  56
         Vector warnings = new Vector();
 954   
 
 955  56
         Enumeration warnEnum = mainSection.getWarnings();
 956  56
         while (warnEnum.hasMoreElements()) {
 957  4
             warnings.addElement(warnEnum.nextElement());
 958   
         }
 959   
 
 960   
         // create a vector and add in the warnings for all the sections
 961  56
         Enumeration e = sections.elements();
 962  56
         while (e.hasMoreElements()) {
 963  10
             Section section = (Section) e.nextElement();
 964  10
             Enumeration e2 = section.getWarnings();
 965  10
             while (e2.hasMoreElements()) {
 966  0
                 warnings.addElement(e2.nextElement());
 967   
             }
 968   
         }
 969   
 
 970  56
         return warnings.elements();
 971   
     }
 972   
 
 973   
     /**
 974   
      * @see java.lang.Object#hashCode
 975   
      */
 976  0
     public int hashCode() {
 977  0
         int hashCode = 0;
 978   
 
 979  0
         if (manifestVersion != null) {
 980  0
             hashCode += manifestVersion.hashCode();
 981   
         }
 982  0
         hashCode += mainSection.hashCode();
 983  0
         hashCode += sections.hashCode();
 984   
 
 985  0
         return hashCode;
 986   
     }
 987   
 
 988   
     /**
 989   
      * @see java.lang.Object#equals
 990   
      */
 991  25
     public boolean equals(Object rhs) {
 992  25
         if (rhs == null || rhs.getClass() != getClass()) {
 993  1
             return false;
 994   
         }
 995   
 
 996  24
         if (rhs == this) {
 997  0
             return true;
 998   
         }
 999   
 
 1000  24
         Manifest rhsManifest = (Manifest) rhs;
 1001  24
         if (manifestVersion == null) {
 1002  0
             if (rhsManifest.manifestVersion != null) {
 1003  0
                 return false;
 1004   
             }
 1005  24
         } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) {
 1006  2
             return false;
 1007   
         }
 1008   
 
 1009  22
         if (!mainSection.equals(rhsManifest.mainSection)) {
 1010  3
             return false;
 1011   
         }
 1012   
 
 1013  19
         return CollectionUtils.equals(sections, rhsManifest.sections);
 1014   
     }
 1015   
 
 1016   
     /**
 1017   
      * Get the version of the manifest
 1018   
      *
 1019   
      * @return the manifest's version string
 1020   
      */
 1021  2
     public String getManifestVersion() {
 1022  2
         return manifestVersion;
 1023   
     }
 1024   
 
 1025   
     /**
 1026   
      * Get the main section of the manifest
 1027   
      *
 1028   
      * @return the main section of the manifest
 1029   
      */
 1030  3978
     public Section getMainSection() {
 1031  3978
         return mainSection;
 1032   
     }
 1033   
 
 1034   
     /**
 1035   
      * Get a particular section from the manifest
 1036   
      *
 1037   
      * @param name the name of the section desired.
 1038   
      * @return the specified section or null if that section
 1039   
      * does not exist in the manifest
 1040   
      */
 1041  16
     public Section getSection(String name) {
 1042  16
         return (Section) sections.get(name);
 1043   
     }
 1044   
 
 1045   
     /**
 1046   
      * Get the section names in this manifest.
 1047   
      *
 1048   
      * @return an Enumeration of section names
 1049   
      */
 1050  46
     public Enumeration getSectionNames() {
 1051  46
         return sectionIndex.elements();
 1052   
     }
 1053   
 }
 1054