Coverage Report - org.codehaus.plexus.archiver.jar.Manifest
 
Classes in this File Line Coverage Branch Coverage Complexity
Manifest
69%
57/82
57%
15/26
2.27
Manifest$Attribute
67%
47/70
35%
15/42
2.27
Manifest$BaseAttribute
30%
3/10
0%
0/12
2.27
Manifest$ExistingAttribute
33%
5/15
0%
0/2
2.27
Manifest$ExistingSection
75%
12/16
16%
1/6
2.27
Manifest$Section
50%
27/54
26%
9/34
2.27
 
 1  
 /**
 2  
  *
 3  
  * Copyright 2004 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.codehaus.plexus.archiver.jar;
 18  
 
 19  
 import java.io.ByteArrayInputStream;
 20  
 import java.io.ByteArrayOutputStream;
 21  
 import java.io.IOException;
 22  
 import java.io.InputStream;
 23  
 import java.io.Reader;
 24  
 import java.io.StringWriter;
 25  
 import java.io.Writer;
 26  
 import java.util.ArrayList;
 27  
 import java.util.Collection;
 28  
 import java.util.Enumeration;
 29  
 import java.util.Hashtable;
 30  
 import java.util.Iterator;
 31  
 import java.util.Locale;
 32  
 import java.util.StringTokenizer;
 33  
 import java.util.Vector;
 34  
 import java.util.jar.Attributes;
 35  
 import org.codehaus.plexus.archiver.ArchiverException;
 36  
 
 37  
 /**
 38  
  * Holds the data of a jar manifest.
 39  
  * <p>
 40  
  * Manifests are processed according to the
 41  
  * <a href="http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html">Jar
 42  
  * file specification.</a>
 43  
  * Specifically, a manifest element consists of
 44  
  * a set of attributes and sections. These sections in turn may contain
 45  
  * attributes. Note in particular that this may result in manifest lines
 46  
  * greater than 72 bytes (including line break) being wrapped and continued
 47  
  * on the next line. If an application can not handle the continuation
 48  
  * mechanism, it is a defect in the application, not this task.</p>
 49  
  *
 50  
  * @since Ant 1.4
 51  
  */
 52  0
 public class Manifest
 53  
     extends java.util.jar.Manifest implements Iterable<String>
 54  
 {
 55  
 
 56  
     /**
 57  
      * The Name Attribute is the first in a named section
 58  
      */
 59  
     private static final String ATTRIBUTE_NAME = ManifestConstants.ATTRIBUTE_NAME;
 60  
 
 61  
     /**
 62  
      * The From Header is disallowed in a Manifest
 63  
      */
 64  
     private static final String ATTRIBUTE_FROM = ManifestConstants.ATTRIBUTE_FROM;
 65  
 
 66  
     /**
 67  
      * Default Manifest version if one is not specified
 68  
      */
 69  
     private static final String DEFAULT_MANIFEST_VERSION = ManifestConstants.DEFAULT_MANIFEST_VERSION;
 70  
 
 71  
     /**
 72  
      * The max length of a line in a Manifest
 73  
      */
 74  
     private static final int MAX_LINE_LENGTH = 72;
 75  
 
 76  
     /**
 77  
      * Max length of a line section which is continued. Need to allow
 78  
      * for the CRLF.
 79  
      */
 80  
     private static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
 81  
 
 82  
     /**
 83  
      * The End-Of-Line marker in manifests
 84  
      */
 85  
     static final String EOL = "\r\n";
 86  
 
 87  22
     public static class BaseAttribute
 88  
     {
 89  
 
 90  
         /**
 91  
          * The attribute's name
 92  
          */
 93  22
         protected String name = null;
 94  
 
 95  
         /**
 96  
          * Get the Attribute's name
 97  
          *
 98  
          * @return the attribute's name.
 99  
          */
 100  
         public String getName()
 101  
         {
 102  17
             return name;
 103  
         }
 104  
 
 105  
         @Override
 106  
         public boolean equals( Object o )
 107  
         {
 108  0
             if ( this == o )
 109  
             {
 110  0
                 return true;
 111  
             }
 112  0
             if ( !( o instanceof BaseAttribute ) )
 113  
             {
 114  0
                 return false;
 115  
             }
 116  
 
 117  0
             BaseAttribute that = (BaseAttribute) o;
 118  
 
 119  0
             return !( name != null ? !name.equals( that.name ) : that.name != null );
 120  
 
 121  
         }
 122  
 
 123  
         @Override
 124  
         public int hashCode()
 125  
         {
 126  0
             return name != null ? name.hashCode() : 0;
 127  
         }
 128  
 
 129  
     }
 130  
 
 131  
     /**
 132  
      * An attribute for the manifest.
 133  
      * Those attributes that are not nested into a section will be added to the "Main" section.
 134  
      */
 135  4
     public static class Attribute
 136  
         extends BaseAttribute implements Iterable<String>
 137  
     {
 138  
 
 139  
         /**
 140  
          * The attribute's value
 141  
          */
 142  22
         private Vector<String> values = new Vector<String>();
 143  
 
 144  
         /**
 145  
          * For multivalued attributes, this is the index of the attribute
 146  
          * currently being defined.
 147  
          */
 148  22
         private int currentIndex = 0;
 149  
 
 150  
         /**
 151  
          * Construct an empty attribute
 152  
          */
 153  
         public Attribute()
 154  5
         {
 155  5
         }
 156  
 
 157  
         /**
 158  
          * Construct a manifest by specifying its name and value
 159  
          *
 160  
          * @param name the attribute's name
 161  
          * @param value the Attribute's value
 162  
          */
 163  
         public Attribute( String name, String value )
 164  17
         {
 165  17
             this.name = name;
 166  17
             setValue( value );
 167  17
         }
 168  
 
 169  
         @Override
 170  
         public Iterator<String> iterator()
 171  
         {
 172  0
             return values.iterator();
 173  
         }
 174  
 
 175  
         /**
 176  
          * @see java.lang.Object#hashCode
 177  
          */
 178  
         @Override
 179  
         public int hashCode()
 180  
         {
 181  0
             int hashCode = super.hashCode();
 182  0
             hashCode += values.hashCode();
 183  0
             return hashCode;
 184  
         }
 185  
 
 186  
         /**
 187  
          * @see java.lang.Object#equals
 188  
          */
 189  
         @Override
 190  
         public boolean equals( Object rhs )
 191  
         {
 192  0
             if ( super.equals( rhs ) )
 193  
             {
 194  0
                 return false;
 195  
             }
 196  0
             if ( rhs == null || rhs.getClass() != getClass() )
 197  
             {
 198  0
                 return false;
 199  
             }
 200  
 
 201  0
             if ( rhs == this )
 202  
             {
 203  0
                 return true;
 204  
             }
 205  
 
 206  0
             Attribute rhsAttribute = (Attribute) rhs;
 207  0
             String lhsKey = getKey();
 208  0
             String rhsKey = rhsAttribute.getKey();
 209  
             //noinspection SimplifiableIfStatement,ConstantConditions
 210  0
             if ( ( lhsKey == null && rhsKey != null ) || ( lhsKey != null && rhsKey == null ) || !lhsKey.equals(
 211  
                 rhsKey ) )
 212  
             {
 213  0
                 return false;
 214  
             }
 215  
 
 216  0
             return rhsAttribute.values != null && values.equals( rhsAttribute.values );
 217  
         }
 218  
 
 219  
         /**
 220  
          * Set the Attribute's name; required
 221  
          *
 222  
          * @param name the attribute's name
 223  
          */
 224  
         public void setName( String name )
 225  
         {
 226  4
             this.name = name;
 227  4
         }
 228  
 
 229  
         /**
 230  
          * Get the attribute's Key - its name in lower case.
 231  
          *
 232  
          * @return the attribute's key.
 233  
          */
 234  
         public String getKey()
 235  
         {
 236  33
             return getKey( name );
 237  
         }
 238  
 
 239  
         /**
 240  
          * Get the key for the specified attribute name - its name in lower case.
 241  
          *
 242  
          * @return the attribute's key.
 243  
          */
 244  
         private static String getKey( String name )
 245  
         {
 246  37
             if ( name == null )
 247  
             {
 248  0
                 return null;
 249  
             }
 250  37
             return name.toLowerCase( Locale.ENGLISH );
 251  
         }
 252  
 
 253  
         /**
 254  
          * Set the Attribute's value; required
 255  
          *
 256  
          * @param value the attribute's value
 257  
          */
 258  
         public void setValue( String value )
 259  
         {
 260  21
             if ( currentIndex >= values.size() )
 261  
             {
 262  21
                 values.addElement( value );
 263  21
                 currentIndex = values.size() - 1;
 264  
             }
 265  
             else
 266  
             {
 267  0
                 values.setElementAt( value, currentIndex );
 268  
             }
 269  21
         }
 270  
 
 271  
         /**
 272  
          * Get the Attribute's value.
 273  
          *
 274  
          * @return the attribute's value.
 275  
          */
 276  
         public String getValue()
 277  
         {
 278  28
             if ( values.size() == 0 )
 279  
             {
 280  0
                 return null;
 281  
             }
 282  
 
 283  28
             String fullValue = "";
 284  28
             for ( String value : values )
 285  
             {
 286  28
                 fullValue += value + " ";
 287  28
             }
 288  28
             return fullValue.trim();
 289  
         }
 290  
 
 291  
         /**
 292  
          * Add a new value to this attribute - making it multivalued.
 293  
          *
 294  
          * @param value the attribute's additional value
 295  
          */
 296  
         public void addValue( String value )
 297  
         {
 298  0
             currentIndex++;
 299  0
             setValue( value );
 300  0
         }
 301  
 
 302  
         /**
 303  
          * Writes the attribute out to a writer.
 304  
          *
 305  
          * @param writer the Writer to which the attribute is written
 306  
          *
 307  
          * @throws IOException if the attribute value cannot be written
 308  
          */
 309  
         void write( Writer writer )
 310  
             throws IOException
 311  
         {
 312  4
             for ( String value : values )
 313  
             {
 314  4
                 writeValue( writer, value );
 315  4
             }
 316  4
         }
 317  
 
 318  
         /**
 319  
          * Write a single attribute value out. Should handle multiple lines of attribute value.
 320  
          *
 321  
          * @param writer the Writer to which the attribute is written
 322  
          * @param value the attribute value
 323  
          *
 324  
          * @throws IOException if the attribute value cannot be written
 325  
          */
 326  
         private void writeValue( Writer writer, String value )
 327  
             throws IOException
 328  
         {
 329  4
             String nameValue = name + ": " + value;
 330  
 
 331  4
             StringTokenizer tokenizer = new StringTokenizer( nameValue, "\n\r" );
 332  
 
 333  4
             String prefix = "";
 334  
 
 335  12
             while ( tokenizer.hasMoreTokens() )
 336  
             {
 337  8
                 writeLine( writer, prefix + tokenizer.nextToken() );
 338  8
                 prefix = " ";
 339  
             }
 340  4
         }
 341  
 
 342  
         /**
 343  
          * Write a single Manifest line. Should handle more than 72 bytes of line
 344  
          *
 345  
          * @param writer the Writer to which the attribute is written
 346  
          * @param line the manifest line to be written
 347  
          *
 348  
          * @throws java.io.IOException when Io excepts
 349  
          */
 350  
         private void writeLine( Writer writer, String line )
 351  
             throws IOException
 352  
         {
 353  
             // Make sure we have at most 70 bytes in UTF-8 as specified excluding line break
 354  17
             while ( line.getBytes( "UTF-8" ).length > MAX_SECTION_LENGTH )
 355  
             {
 356  
                 // Try to find a MAX_SECTION_LENGTH
 357  
                 // Use the minimum because we operate on at most chars and not bytes here otherwise
 358  
                 // if we have more bytes than chars we will die in an IndexOutOfBoundsException.
 359  9
                 int breakIndex = Math.min( line.length(), MAX_SECTION_LENGTH );
 360  9
                 String section = line.substring( 0, breakIndex );
 361  248
                 while ( section.getBytes( "UTF-8" ).length > MAX_SECTION_LENGTH && breakIndex > 0 )
 362  
                 {
 363  239
                     breakIndex--;
 364  239
                     section = line.substring( 0, breakIndex );
 365  
                 }
 366  9
                 if ( breakIndex == 0 )
 367  
                 {
 368  0
                     throw new IOException( "Unable to write manifest line " + line );
 369  
                 }
 370  9
                 writer.write( section + EOL );
 371  9
                 line = " " + line.substring( breakIndex );
 372  9
             }
 373  8
             writer.write( line + EOL );
 374  8
         }
 375  
 
 376  
     }
 377  
 
 378  
     public class ExistingAttribute
 379  
         extends Attribute implements Iterable<String>
 380  
     {
 381  
 
 382  
         private final Attributes attributes;
 383  
 
 384  
         public ExistingAttribute( Attributes attributes, String name )
 385  1
         {
 386  1
             this.attributes = attributes;
 387  1
             this.name = name;
 388  1
         }
 389  
 
 390  
         @Override
 391  
         public Iterator<String> iterator()
 392  
         {
 393  0
             return getKeys( attributes ).iterator();
 394  
         }
 395  
 
 396  
         @Override
 397  
         public void setName( String name )
 398  
         {
 399  0
             throw new UnsupportedOperationException( "Cant do this" );
 400  
         }
 401  
 
 402  
         @Override
 403  
         public String getKey()
 404  
         {
 405  0
             return name;
 406  
         }
 407  
 
 408  
         @Override
 409  
         public void setValue( String value )
 410  
         {
 411  0
             attributes.putValue( name, value );
 412  0
         }
 413  
 
 414  
         @Override
 415  
         public String getValue()
 416  
         {
 417  1
             return attributes.getValue( name );
 418  
         }
 419  
 
 420  
         @Override
 421  
         public void addValue( String value )
 422  
         {
 423  0
             String value1 = getValue();
 424  0
             value1 = ( value1 != null ) ? " " + value : value;
 425  0
             setValue( value1 );
 426  0
         }
 427  
 
 428  
         @Override
 429  
         void write( Writer writer )
 430  
             throws IOException
 431  
         {
 432  0
             throw new UnsupportedOperationException( "Cant do this" );
 433  
         }
 434  
 
 435  
     }
 436  
 
 437  
     private static Collection<String> getKeys( Attributes attributes )
 438  
     {
 439  0
         Collection<String> result = new ArrayList<String>();
 440  0
         for ( Object objectObjectEntry : attributes.keySet() )
 441  
         {
 442  0
             result.add( objectObjectEntry.toString() );
 443  0
         }
 444  0
         return result;
 445  
     }
 446  
 
 447  
     /**
 448  
      * A manifest section - you can nest attribute elements into sections.
 449  
      * A section consists of a set of attribute values,
 450  
      * separated from other sections by a blank line.
 451  
      */
 452  41
     public static class Section implements Iterable<String>
 453  
     {
 454  
 
 455  
         /**
 456  
          * Warnings for this section
 457  
          */
 458  38
         private Vector<String> warnings = new Vector<String>();
 459  
 
 460  
         /**
 461  
          * The section's name if any. The main section in a
 462  
          * manifest is unnamed.
 463  
          */
 464  38
         private String name = null;
 465  
 
 466  
         /**
 467  
          * The section's attributes.
 468  
          */
 469  38
         private Hashtable<String, Attribute> attributes = new Hashtable<String, Attribute>();
 470  
 
 471  
         /**
 472  
          * Index used to retain the attribute ordering
 473  
          */
 474  38
         private Vector<String> attributeIndex = new Vector<String>();
 475  
 
 476  
         /**
 477  
          * The name of the section; optional -default is the main section.
 478  
          *
 479  
          * @param name the section's name
 480  
          */
 481  
         public void setName( String name )
 482  
         {
 483  3
             this.name = name;
 484  3
         }
 485  
 
 486  
         /**
 487  
          * Get the Section's name.
 488  
          *
 489  
          * @return the section's name.
 490  
          */
 491  
         public String getName()
 492  
         {
 493  3
             return name;
 494  
         }
 495  
 
 496  
         @Override
 497  
         public Iterator<String> iterator()
 498  
         {
 499  0
             return attributes.keySet().iterator();
 500  
         }
 501  
 
 502  
         /**
 503  
          * Get a attribute of the section
 504  
          *
 505  
          * @param attributeName the name of the attribute
 506  
          *
 507  
          * @return a Manifest.Attribute instance if the attribute is
 508  
          * single-valued, otherwise a Vector of Manifest.Attribute
 509  
          * instances.
 510  
          */
 511  
         public Attribute getAttribute( String attributeName )
 512  
         {
 513  4
             return attributes.get( attributeName.toLowerCase() );
 514  
         }
 515  
 
 516  
         /**
 517  
          * Add an attribute to the section.
 518  
          *
 519  
          * @param attribute the attribute to be added to the section
 520  
          *
 521  
          * @throws ManifestException if the attribute is not valid.
 522  
          */
 523  
         public void addConfiguredAttribute( Attribute attribute )
 524  
             throws ManifestException
 525  
         {
 526  4
             String check = addAttributeAndCheck( attribute );
 527  4
             if ( check != null )
 528  
             {
 529  0
                 throw new ManifestException(
 530  
                     "Specify the section name using " + "the \"name\" attribute of the <section> element rather "
 531  
                         + "than using a \"Name\" manifest attribute" );
 532  
             }
 533  4
         }
 534  
 
 535  
         /**
 536  
          * Add an attribute to the section
 537  
          *
 538  
          * @param attribute the attribute to be added.
 539  
          *
 540  
          * @return the value of the attribute if it is a name
 541  
          * attribute - null other wise
 542  
          *
 543  
          * @throws ManifestException if the attribute already
 544  
          * exists in this section.
 545  
          */
 546  
         public String addAttributeAndCheck( Attribute attribute )
 547  
             throws ManifestException
 548  
         {
 549  4
             if ( attribute.getName() == null || attribute.getValue() == null )
 550  
             {
 551  0
                 throw new ManifestException( "Attributes must have name and value" );
 552  
             }
 553  4
             if ( attribute.getKey().equalsIgnoreCase( ATTRIBUTE_NAME ) )
 554  
             {
 555  0
                 warnings.addElement(
 556  
                     "\"" + ATTRIBUTE_NAME + "\" attributes " + "should not occur in the main section and must be the "
 557  
                         + "first element in all other sections: \"" + attribute.getName() + ": " + attribute.getValue()
 558  
                         + "\"" );
 559  0
                 return attribute.getValue();
 560  
             }
 561  
 
 562  4
             if ( attribute.getKey().startsWith( Attribute.getKey( ATTRIBUTE_FROM ) ) )
 563  
             {
 564  0
                 warnings.addElement( "Manifest attributes should not start " + "with \"" + ATTRIBUTE_FROM + "\" in \""
 565  
                                          + attribute.getName() + ": " + attribute.getValue() + "\"" );
 566  
             }
 567  
             else
 568  
             {
 569  
                 // classpath attributes go into a vector
 570  4
                 String attributeKey = attribute.getKey();
 571  4
                 if ( attributeKey.equalsIgnoreCase( ManifestConstants.ATTRIBUTE_CLASSPATH ) )
 572  
                 {
 573  0
                     Attribute classpathAttribute = attributes.get( attributeKey );
 574  
 
 575  0
                     if ( classpathAttribute == null )
 576  
                     {
 577  0
                         storeAttribute( attribute );
 578  
                     }
 579  
                     else
 580  
                     {
 581  0
                         warnings.addElement( "Multiple Class-Path attributes " + "are supported but violate the Jar "
 582  
                                                  + "specification and may not be correctly "
 583  
                                                  + "processed in all environments" );
 584  
 
 585  0
                         for ( String value : attribute )
 586  
                         {
 587  0
                             classpathAttribute.addValue( value );
 588  0
                         }
 589  
                     }
 590  0
                 }
 591  4
                 else if ( attributes.containsKey( attributeKey ) )
 592  
                 {
 593  0
                     throw new ManifestException( "The attribute \"" + attribute.getName() + "\" may not occur more "
 594  
                                                      + "than once in the same section" );
 595  
                 }
 596  
                 else
 597  
                 {
 598  4
                     storeAttribute( attribute );
 599  
                 }
 600  
             }
 601  4
             return null;
 602  
         }
 603  
 
 604  
         /**
 605  
          * Store an attribute and update the index.
 606  
          *
 607  
          * @param attribute the attribute to be stored
 608  
          */
 609  
         protected void storeAttribute( Attribute attribute )
 610  
         {
 611  4
             if ( attribute == null )
 612  
             {
 613  0
                 return;
 614  
             }
 615  
 
 616  4
             String attributeKey = attribute.getKey();
 617  4
             attributes.put( attributeKey, attribute );
 618  4
             if ( !attributeIndex.contains( attributeKey ) )
 619  
             {
 620  4
                 attributeIndex.addElement( attributeKey );
 621  
             }
 622  4
         }
 623  
 
 624  
         /**
 625  
          * Get the warnings for this section.
 626  
          *
 627  
          * @return an Enumeration of warning strings.
 628  
          */
 629  
         public Enumeration<String> getWarnings()
 630  
         {
 631  17
             return warnings.elements();
 632  
         }
 633  
 
 634  
         /**
 635  
          * @see java.lang.Object#hashCode
 636  
          */
 637  
         @Override
 638  
         public int hashCode()
 639  
         {
 640  0
             int hashCode = 0;
 641  
 
 642  0
             if ( name != null )
 643  
             {
 644  0
                 hashCode += name.hashCode();
 645  
             }
 646  
 
 647  0
             hashCode += attributes.hashCode();
 648  0
             return hashCode;
 649  
         }
 650  
 
 651  
         /**
 652  
          * @see java.lang.Object#equals
 653  
          */
 654  
         @Override
 655  
         public boolean equals( Object rhs )
 656  
         {
 657  0
             if ( rhs == null || rhs.getClass() != getClass() )
 658  
             {
 659  0
                 return false;
 660  
             }
 661  
 
 662  0
             if ( rhs == this )
 663  
             {
 664  0
                 return true;
 665  
             }
 666  
 
 667  0
             Section rhsSection = (Section) rhs;
 668  
 
 669  0
             return rhsSection.attributes != null && attributes.equals( rhsSection.attributes );
 670  
         }
 671  
 
 672  
     }
 673  
 
 674  
     public class ExistingSection implements Iterable<String>
 675  
     {
 676  
 
 677  
         private final Attributes backingAttributes;
 678  
 
 679  
         private final String sectionName;
 680  
 
 681  
         public ExistingSection( Attributes backingAttributes, String sectionName )
 682  14
         {
 683  14
             this.backingAttributes = backingAttributes;
 684  14
             this.sectionName = sectionName;
 685  14
         }
 686  
 
 687  
         @Override
 688  
         public Iterator<String> iterator()
 689  
         {
 690  0
             return getKeys( backingAttributes ).iterator();
 691  
         }
 692  
 
 693  
         public ExistingAttribute getAttribute( String attributeName )
 694  
         {
 695  1
             Attributes.Name name = new Attributes.Name( attributeName );
 696  1
             return backingAttributes.containsKey( name )
 697  
                        ? new ExistingAttribute( backingAttributes, attributeName )
 698  
                        : null;
 699  
 
 700  
         }
 701  
 
 702  
         public String getName()
 703  
         {
 704  0
             return sectionName;
 705  
         }
 706  
 
 707  
         public String getAttributeValue( String attributeName )
 708  
         {
 709  10
             return backingAttributes.getValue( attributeName );
 710  
         }
 711  
 
 712  
         public void removeAttribute( String attributeName )
 713  
         {
 714  2
             backingAttributes.remove( new Attributes.Name( attributeName ) );
 715  2
         }
 716  
 
 717  
         public void addConfiguredAttribute( Attribute attribute )
 718  
             throws ManifestException
 719  
         {
 720  6
             backingAttributes.putValue( attribute.getName(), attribute.getValue() );
 721  6
         }
 722  
 
 723  
         public String addAttributeAndCheck( Attribute attribute )
 724  
             throws ManifestException
 725  
         {
 726  2
             return remap( backingAttributes, attribute );
 727  
         }
 728  
 
 729  
         @Override
 730  
         public int hashCode()
 731  
         {
 732  0
             return backingAttributes.hashCode();
 733  
         }
 734  
 
 735  
         @Override
 736  
         public boolean equals( Object rhs )
 737  
         {
 738  0
             return rhs instanceof ExistingSection && backingAttributes.equals(
 739  
                 ( (ExistingSection) rhs ).backingAttributes );
 740  
         }
 741  
 
 742  
     }
 743  
 
 744  
     @Override
 745  
     public Iterator<String> iterator()
 746  
     {
 747  0
         return getEntries().keySet().iterator();
 748  
     }
 749  
 
 750  
     /**
 751  
      * The main section of this manifest
 752  
      */
 753  35
     private Section mainSection = new Section();
 754  
 
 755  
     /**
 756  
      * Construct a manifest from Ant's default manifest file.
 757  
      *
 758  
      * @return the default manifest.
 759  
      *
 760  
      * @throws ArchiverException if there is a problem loading the
 761  
      * default manifest
 762  
      */
 763  
     public static Manifest getDefaultManifest()
 764  
         throws ArchiverException
 765  
     {
 766  20
         final Manifest defaultManifest = new Manifest();
 767  20
         defaultManifest.getMainAttributes().putValue( "Manifest-Version", "1.0" );
 768  
 
 769  20
         String createdBy = "Plexus Archiver";
 770  
 
 771  20
         final String plexusArchiverVersion = JdkManifestFactory.getArchiverVersion();
 772  
 
 773  20
         if ( plexusArchiverVersion != null )
 774  
         {
 775  0
             createdBy += " " + plexusArchiverVersion;
 776  
         }
 777  
 
 778  20
         defaultManifest.getMainAttributes().putValue( "Created-By", createdBy );
 779  
 
 780  20
         return defaultManifest;
 781  
     }
 782  
 
 783  
     /**
 784  
      * Construct an empty manifest
 785  
      */
 786  
     public Manifest()
 787  29
     {
 788  29
         setManifestVersion();
 789  29
     }
 790  
 
 791  
     private void setManifestVersion()
 792  
     {
 793  35
         getMainAttributes().put( Attributes.Name.MANIFEST_VERSION, "1.0" );
 794  35
     }
 795  
 
 796  
     /**
 797  
      * Read a manifest file from the given reader
 798  
      *
 799  
      * @param r is the reader from which the Manifest is read
 800  
      *
 801  
      * @throws ManifestException if the manifest is not valid according
 802  
      * to the JAR spec
 803  
      * @throws IOException if the manifest cannot be read from the reader.
 804  
      * @deprecated This constructor does not properly map characters to bytes. Use
 805  
      * {@link #Manifest(InputStream)}. Will be removed in 4.0.
 806  
      */
 807  
     @Deprecated
 808  
     public Manifest( Reader r )
 809  
         throws ManifestException, IOException
 810  
     {
 811  0
         super( getInputStream( r ) );
 812  0
         setManifestVersion();
 813  0
     }
 814  
 
 815  
     public Manifest( InputStream is )
 816  
         throws IOException
 817  
     {
 818  9
         super( is );
 819  6
         setManifestVersion();
 820  6
     }
 821  
 
 822  
     /**
 823  
      * Add a section to the manifest
 824  
      *
 825  
      * @param section the manifest section to be added
 826  
      *
 827  
      * @throws ManifestException if the secti0on is not valid.
 828  
      */
 829  
     public void addConfiguredSection( Section section )
 830  
         throws ManifestException
 831  
     {
 832  3
         String sectionName = section.getName();
 833  3
         if ( sectionName == null )
 834  
         {
 835  0
             throw new ManifestException( "Sections must have a name" );
 836  
         }
 837  3
         Attributes attributes = getOrCreateAttributes( sectionName );
 838  3
         for ( String s : section.attributes.keySet() )
 839  
         {
 840  
 
 841  4
             Attribute attribute = section.getAttribute( s );
 842  4
             attributes.putValue( attribute.getName(), attribute.getValue() );
 843  4
         }
 844  3
     }
 845  
 
 846  
     private Attributes getOrCreateAttributes( String name )
 847  
     {
 848  3
         Attributes attributes = getAttributes( name );
 849  3
         if ( attributes == null )
 850  
         {
 851  3
             attributes = new Attributes();
 852  3
             getEntries().put( name, attributes );
 853  
         }
 854  3
         return attributes;
 855  
     }
 856  
 
 857  
     /**
 858  
      * Add an attribute to the manifest - it is added to the main section.
 859  
      *
 860  
      * @param attribute the attribute to be added.
 861  
      *
 862  
      * @throws ManifestException if the attribute is not valid.
 863  
      */
 864  
     public void addConfiguredAttribute( Attribute attribute )
 865  
         throws ManifestException
 866  
     {
 867  5
         remap( getMainAttributes(), attribute );
 868  5
     }
 869  
 
 870  
     /**
 871  
      * Writes the manifest out to a writer.
 872  
      *
 873  
      * @param writer the Writer to which the manifest is written
 874  
      *
 875  
      * @throws IOException if the manifest cannot be written
 876  
      */
 877  
     public void write( Writer writer )
 878  
         throws IOException
 879  
     {
 880  2
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 881  2
         super.write( byteArrayOutputStream );
 882  
         // We know that UTF-8 is the encoding of the JAR file specification
 883  2
         writer.write( byteArrayOutputStream.toString( "UTF-8" ) );
 884  2
     }
 885  
 
 886  
     /**
 887  
      * Convert the manifest to its string representation
 888  
      *
 889  
      * @return a multiline string with the Manifest as it
 890  
      * appears in a Manifest file.
 891  
      */
 892  
     @Override
 893  
     public String toString()
 894  
     {
 895  0
         StringWriter sw = new StringWriter();
 896  
         try
 897  
         {
 898  0
             write( sw );
 899  
         }
 900  0
         catch ( IOException e )
 901  
         {
 902  0
             return null;
 903  0
         }
 904  0
         return sw.toString();
 905  
     }
 906  
 
 907  
     /**
 908  
      * Get the warnings for this manifest.
 909  
      *
 910  
      * @return an enumeration of warning strings
 911  
      */
 912  
     Enumeration<String> getWarnings()
 913  
     {
 914  17
         Vector<String> warnings = new Vector<String>();
 915  
 
 916  17
         Enumeration<String> warnEnum = mainSection.getWarnings();
 917  17
         while ( warnEnum.hasMoreElements() )
 918  
         {
 919  0
             warnings.addElement( warnEnum.nextElement() );
 920  
         }
 921  
 
 922  17
         return warnings.elements();
 923  
     }
 924  
 
 925  
     /**
 926  
      * Get the version of the manifest
 927  
      *
 928  
      * @return the manifest's version string
 929  
      */
 930  
     public String getManifestVersion()
 931  
     {
 932  
         /*
 933  
          The version of this manifest
 934  
          */
 935  1
         return DEFAULT_MANIFEST_VERSION;
 936  
     }
 937  
 
 938  
     /**
 939  
      * Get the main section of the manifest
 940  
      *
 941  
      * @return the main section of the manifest
 942  
      */
 943  
     public ExistingSection getMainSection()
 944  
     {
 945  6
         return new ExistingSection( getMainAttributes(), null );
 946  
     }
 947  
 
 948  
     /**
 949  
      * Get a particular section from the manifest
 950  
      *
 951  
      * @param name the name of the section desired.
 952  
      *
 953  
      * @return the specified section or null if that section
 954  
      * does not exist in the manifest
 955  
      */
 956  
     public ExistingSection getSection( String name )
 957  
     {
 958  9
         Attributes attributes = getAttributes( name );
 959  9
         if ( attributes != null )
 960  
         {
 961  8
             return new ExistingSection( attributes, name );
 962  
         }
 963  1
         return null;
 964  
     }
 965  
 
 966  
     @Deprecated
 967  
     private static InputStream getInputStream( Reader r )
 968  
         throws IOException
 969  
     {
 970  0
         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 971  
         int read;
 972  0
         while ( ( read = r.read() ) != -1 )
 973  
         {
 974  0
             byteArrayOutputStream.write( read );
 975  
         }
 976  0
         return new ByteArrayInputStream( byteArrayOutputStream.toByteArray() );
 977  
     }
 978  
 
 979  
     public static String remap( Attributes backingAttributes, Attribute attribute )
 980  
         throws ManifestException
 981  
     {
 982  7
         if ( attribute.getKey() == null || attribute.getValue() == null )
 983  
         {
 984  0
             throw new ManifestException( "Attributes must have name and value" );
 985  
         }
 986  
 
 987  7
         String attributeKey = attribute.getKey();
 988  7
         if ( attributeKey.equalsIgnoreCase( ManifestConstants.ATTRIBUTE_CLASSPATH ) )
 989  
         {
 990  4
             String classpathAttribute = backingAttributes.getValue( attributeKey );
 991  
 
 992  4
             if ( classpathAttribute == null )
 993  
             {
 994  3
                 classpathAttribute = attribute.getValue();
 995  
             }
 996  
             else
 997  
             {
 998  1
                 classpathAttribute += " " + attribute.getValue();
 999  
             }
 1000  4
             backingAttributes.putValue( ManifestConstants.ATTRIBUTE_CLASSPATH, classpathAttribute );
 1001  4
         }
 1002  
         else
 1003  
         {
 1004  3
             backingAttributes.putValue( attribute.getName(), attribute.getValue() );
 1005  3
             if ( attribute.getKey().equalsIgnoreCase( ATTRIBUTE_NAME ) )
 1006  
             {
 1007  0
                 return attribute.getValue();
 1008  
             }
 1009  
         }
 1010  7
         return null;
 1011  
 
 1012  
     }
 1013  
 
 1014  
 }