KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > Manifest


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. 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  */

18
19 package org.apache.tools.ant.taskdefs;
20
21 import java.io.BufferedReader JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.InputStreamReader JavaDoc;
25 import java.io.PrintWriter JavaDoc;
26 import java.io.Reader JavaDoc;
27 import java.io.StringWriter JavaDoc;
28 import java.io.UnsupportedEncodingException JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.Hashtable JavaDoc;
31 import java.util.Vector JavaDoc;
32 import org.apache.tools.ant.BuildException;
33 import org.apache.tools.ant.util.FileUtils;
34
35 /**
36  * Holds the data of a jar manifest.
37  *
38  * Manifests are processed according to the
39  * {@link <a HREF="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html">Jar
40  * file specification.</a>}.
41  * Specifically, a manifest element consists of
42  * a set of attributes and sections. These sections in turn may contain
43  * attributes. Note in particular that this may result in manifest lines
44  * greater than 72 bytes being wrapped and continued on the next
45  * line. If an application can not handle the continuation mechanism, it
46  * is a defect in the application, not this task.
47  *
48  *
49  * @since Ant 1.4
50  */

51 public class Manifest {
52     /** The standard manifest version header */
53     public static final String JavaDoc ATTRIBUTE_MANIFEST_VERSION
54         = "Manifest-Version";
55
56     /** The standard Signature Version header */
57     public static final String JavaDoc ATTRIBUTE_SIGNATURE_VERSION
58         = "Signature-Version";
59
60     /** The Name Attribute is the first in a named section */
61     public static final String JavaDoc ATTRIBUTE_NAME = "Name";
62
63     /** The From Header is disallowed in a Manifest */
64     public static final String JavaDoc ATTRIBUTE_FROM = "From";
65
66     /** The Class-Path Header is special - it can be duplicated */
67     public static final String JavaDoc ATTRIBUTE_CLASSPATH = "Class-Path";
68
69     /** Default Manifest version if one is not specified */
70     public static final String JavaDoc DEFAULT_MANIFEST_VERSION = "1.0";
71
72     /** The max length of a line in a Manifest */
73     public static final int MAX_LINE_LENGTH = 72;
74
75     /**
76      * Max length of a line section which is continued. Need to allow
77      * for the CRLF.
78      */

79     public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
80
81     /** The End-Of-Line marker in manifests */
82     public static final String JavaDoc EOL = "\r\n";
83     /** Error for attributes */
84     public static final String JavaDoc ERROR_FROM_FORBIDDEN = "Manifest attributes should not start "
85                         + "with \"" + ATTRIBUTE_FROM + "\" in \"";
86
87     /** Encoding to be used for JAR files. */
88     public static final String JavaDoc JAR_ENCODING = "UTF-8";
89
90     /**
91      * An attribute for the manifest.
92      * Those attributes that are not nested into a section will be added to the "Main" section.
93      */

94     public static class Attribute {
95
96         /**
97          * Maximum length of the name to have the value starting on the same
98          * line as the name. This to stay under 72 bytes total line length
99          * (including CRLF).
100          */

101         private static final int MAX_NAME_VALUE_LENGTH = 68;
102
103         /**
104          * Maximum length of the name according to the jar specification.
105          * In this case the first line will have 74 bytes total line length
106          * (including CRLF). This conflicts with the 72 bytes total line length
107          * max, but is the only possible conclusion from the manifest specification, if
108          * names with 70 bytes length are allowed, have to be on the first line, and
109          * have to be followed by ": ".
110          */

111         private static final int MAX_NAME_LENGTH = 70;
112
113         /** The attribute's name */
114         private String JavaDoc name = null;
115
116         /** The attribute's value */
117         private Vector JavaDoc values = new Vector JavaDoc();
118
119         /**
120          * For multivalued attributes, this is the index of the attribute
121          * currently being defined.
122          */

123         private int currentIndex = 0;
124
125         /**
126          * Construct an empty attribute */

127         public Attribute() {
128         }
129
130         /**
131          * Construct an attribute by parsing a line from the Manifest
132          *
133          * @param line the line containing the attribute name and value
134          *
135          * @throws ManifestException if the line is not valid
136          */

137         public Attribute(String JavaDoc line) throws ManifestException {
138             parse(line);
139         }
140
141         /**
142          * Construct a manifest by specifying its name and value
143          *
144          * @param name the attribute's name
145          * @param value the Attribute's value
146          */

147         public Attribute(String JavaDoc name, String JavaDoc value) {
148             this.name = name;
149             setValue(value);
150         }
151
152         /**
153          * @see java.lang.Object#hashCode
154          * @return a hashcode based on the key and values.
155          */

156         public int hashCode() {
157             int hashCode = 0;
158
159             if (name != null) {
160                 hashCode += getKey().hashCode();
161             }
162
163             hashCode += values.hashCode();
164             return hashCode;
165         }
166
167         /**
168          * @param rhs the object to check for equality.
169          * @see java.lang.Object#equals
170          * @return true if the key and values are the same.
171          */

172         public boolean equals(Object JavaDoc rhs) {
173             if (rhs == null || rhs.getClass() != getClass()) {
174                 return false;
175             }
176
177             if (rhs == this) {
178                 return true;
179             }
180
181             Attribute rhsAttribute = (Attribute) rhs;
182             String JavaDoc lhsKey = getKey();
183             String JavaDoc rhsKey = rhsAttribute.getKey();
184             if ((lhsKey == null && rhsKey != null)
185                  || (lhsKey != null && rhsKey == null)
186                  || !lhsKey.equals(rhsKey)) {
187                 return false;
188             }
189
190             return values.equals(rhsAttribute.values);
191         }
192
193         /**
194          * Parse a line into name and value pairs
195          *
196          * @param line the line to be parsed
197          *
198          * @throws ManifestException if the line does not contain a colon
199          * separating the name and value
200          */

201         public void parse(String JavaDoc line) throws ManifestException {
202             int index = line.indexOf(": ");
203             if (index == -1) {
204                 throw new ManifestException("Manifest line \"" + line
205                     + "\" is not valid as it does not "
206                     + "contain a name and a value separated by ': ' ");
207             }
208             name = line.substring(0, index);
209             setValue(line.substring(index + 2));
210         }
211
212         /**
213          * Set the Attribute's name; required
214          *
215          * @param name the attribute's name
216          */

217         public void setName(String JavaDoc name) {
218             this.name = name;
219         }
220
221         /**
222          * Get the Attribute's name
223          *
224          * @return the attribute's name.
225          */

226         public String JavaDoc getName() {
227             return name;
228         }
229
230         /**
231          * Get the attribute's Key - its name in lower case.
232          *
233          * @return the attribute's key.
234          */

235         public String JavaDoc getKey() {
236             if (name == null) {
237                 return null;
238             }
239             return name.toLowerCase();
240         }
241
242         /**
243          * Set the Attribute's value; required
244          *
245          * @param value the attribute's value
246          */

247         public void setValue(String JavaDoc value) {
248             if (currentIndex >= values.size()) {
249                 values.addElement(value);
250                 currentIndex = values.size() - 1;
251             } else {
252                 values.setElementAt(value, currentIndex);
253             }
254         }
255
256         /**
257          * Get the Attribute's value.
258          *
259          * @return the attribute's value.
260          */

261         public String JavaDoc getValue() {
262             if (values.size() == 0) {
263                 return null;
264             }
265
266             String JavaDoc fullValue = "";
267             for (Enumeration JavaDoc e = getValues(); e.hasMoreElements();) {
268                 String JavaDoc value = (String JavaDoc) e.nextElement();
269                 fullValue += value + " ";
270             }
271             return fullValue.trim();
272         }
273
274         /**
275          * Add a new value to this attribute - making it multivalued.
276          *
277          * @param value the attribute's additional value
278          */

279         public void addValue(String JavaDoc value) {
280             currentIndex++;
281             setValue(value);
282         }
283
284         /**
285          * Get all the attribute's values.
286          *
287          * @return an enumeration of the attributes values
288          */

289         public Enumeration JavaDoc getValues() {
290             return values.elements();
291         }
292
293         /**
294          * Add a continuation line from the Manifest file.
295          *
296          * When lines are too long in a manifest, they are continued on the
297          * next line by starting with a space. This method adds the continuation
298          * data to the attribute value by skipping the first character.
299          *
300          * @param line the continuation line.
301          */

302         public void addContinuation(String JavaDoc line) {
303             String JavaDoc currentValue = (String JavaDoc) values.elementAt(currentIndex);
304             setValue(currentValue + line.substring(1));
305         }
306
307         /**
308          * Write the attribute out to a print writer.
309          *
310          * @param writer the Writer to which the attribute is written
311          *
312          * @throws IOException if the attribute value cannot be written
313          */

314         public void write(PrintWriter JavaDoc writer) throws IOException JavaDoc {
315             for (Enumeration JavaDoc e = getValues(); e.hasMoreElements();) {
316                 writeValue(writer, (String JavaDoc) e.nextElement());
317             }
318         }
319
320         /**
321          * Write a single attribute value out
322          *
323          * @param writer the Writer to which the attribute is written
324          * @param value the attribute value
325          *
326          * @throws IOException if the attribute value cannot be written
327          */

328         private void writeValue(PrintWriter JavaDoc writer, String JavaDoc value)
329              throws IOException JavaDoc {
330             String JavaDoc line = null;
331             int nameLength = name.getBytes(JAR_ENCODING).length;
332             if (nameLength > MAX_NAME_VALUE_LENGTH) {
333                 if (nameLength > MAX_NAME_LENGTH) {
334                     throw new IOException JavaDoc("Unable to write manifest line "
335                             + name + ": " + value);
336                 }
337                 writer.print(name + ": " + EOL);
338                 line = " " + value;
339             } else {
340                 line = name + ": " + value;
341             }
342             while (line.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH) {
343                 // try to find a MAX_LINE_LENGTH byte section
344
int breakIndex = MAX_SECTION_LENGTH;
345                 if (breakIndex >= line.length()) {
346                     breakIndex = line.length() - 1;
347                 }
348                 String JavaDoc section = line.substring(0, breakIndex);
349                 while (section.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH
350                      && breakIndex > 0) {
351                     breakIndex--;
352                     section = line.substring(0, breakIndex);
353                 }
354                 if (breakIndex == 0) {
355                     throw new IOException JavaDoc("Unable to write manifest line "
356                         + name + ": " + value);
357                 }
358                 writer.print(section + EOL);
359                 line = " " + line.substring(breakIndex);
360             }
361             writer.print(line + EOL);
362         }
363     }
364
365     /**
366      * A manifest section - you can nest attribute elements into sections.
367      * A section consists of a set of attribute values,
368      * separated from other sections by a blank line.
369      */

370     public static class Section {
371         /** Warnings for this section */
372         private Vector JavaDoc warnings = new Vector JavaDoc();
373
374         /**
375          * The section's name if any. The main section in a
376          * manifest is unnamed.
377          */

378         private String JavaDoc name = null;
379
380         /** The section's attributes.*/
381         private Hashtable JavaDoc attributes = new Hashtable JavaDoc();
382
383         /** Index used to retain the attribute ordering */
384         private Vector JavaDoc attributeIndex = new Vector JavaDoc();
385
386         /**
387          * The name of the section; optional -default is the main section.
388          * @param name the section's name
389          */

390         public void setName(String JavaDoc name) {
391             this.name = name;
392         }
393
394         /**
395          * Get the Section's name.
396          *
397          * @return the section's name.
398          */

399         public String JavaDoc getName() {
400             return name;
401         }
402
403         /**
404          * Read a section through a reader.
405          *
406          * @param reader the reader from which the section is read
407          *
408          * @return the name of the next section if it has been read as
409          * part of this section - This only happens if the
410          * Manifest is malformed.
411          *
412          * @throws ManifestException if the section is not valid according
413          * to the JAR spec
414          * @throws IOException if the section cannot be read from the reader.
415          */

416         public String JavaDoc read(BufferedReader JavaDoc reader)
417              throws ManifestException, IOException JavaDoc {
418             Attribute attribute = null;
419             while (true) {
420                 String JavaDoc line = reader.readLine();
421                 if (line == null || line.length() == 0) {
422                     return null;
423                 }
424                 if (line.charAt(0) == ' ') {
425                     // continuation line
426
if (attribute == null) {
427                         if (name != null) {
428                             // a continuation on the first line is a
429
// continuation of the name - concatenate this
430
// line and the name
431
name += line.substring(1);
432                         } else {
433                             throw new ManifestException("Can't start an "
434                                 + "attribute with a continuation line " + line);
435                         }
436                     } else {
437                         attribute.addContinuation(line);
438                     }
439                 } else {
440                     attribute = new Attribute(line);
441                     String JavaDoc nameReadAhead = addAttributeAndCheck(attribute);
442                     // refresh attribute in case of multivalued attributes.
443
attribute = getAttribute(attribute.getKey());
444                     if (nameReadAhead != null) {
445                         return nameReadAhead;
446                     }
447                 }
448             }
449         }
450
451         /**
452          * Merge in another section
453          *
454          * @param section the section to be merged with this one.
455          *
456          * @throws ManifestException if the sections cannot be merged.
457          */

458         public void merge(Section section) throws ManifestException {
459             if (name == null && section.getName() != null
460                 || name != null
461                 && !(name.equalsIgnoreCase(section.getName()))) {
462                 throw new ManifestException("Unable to merge sections "
463                     + "with different names");
464             }
465
466             Enumeration JavaDoc e = section.getAttributeKeys();
467             Attribute classpathAttribute = null;
468             while (e.hasMoreElements()) {
469                 String JavaDoc attributeName = (String JavaDoc) e.nextElement();
470                 Attribute attribute = section.getAttribute(attributeName);
471                 if (attributeName.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
472                     if (classpathAttribute == null) {
473                         classpathAttribute = new Attribute();
474                         classpathAttribute.setName(ATTRIBUTE_CLASSPATH);
475                     }
476                     Enumeration JavaDoc cpe = attribute.getValues();
477                     while (cpe.hasMoreElements()) {
478                         String JavaDoc value = (String JavaDoc) cpe.nextElement();
479                         classpathAttribute.addValue(value);
480                     }
481                 } else {
482                     // the merge file always wins
483
storeAttribute(attribute);
484                 }
485             }
486
487             if (classpathAttribute != null) {
488                 // the merge file *always* wins, even for Class-Path
489
storeAttribute(classpathAttribute);
490             }
491
492             // add in the warnings
493
Enumeration JavaDoc warnEnum = section.warnings.elements();
494             while (warnEnum.hasMoreElements()) {
495                 warnings.addElement(warnEnum.nextElement());
496             }
497         }
498
499         /**
500          * Write the section out to a print writer.
501          *
502          * @param writer the Writer to which the section is written
503          *
504          * @throws IOException if the section cannot be written
505          */

506         public void write(PrintWriter JavaDoc writer) throws IOException JavaDoc {
507             if (name != null) {
508                 Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
509                 nameAttr.write(writer);
510             }
511             Enumeration JavaDoc e = getAttributeKeys();
512             while (e.hasMoreElements()) {
513                 String JavaDoc key = (String JavaDoc) e.nextElement();
514                 Attribute attribute = getAttribute(key);
515                 attribute.write(writer);
516             }
517             writer.print(EOL);
518         }
519
520         /**
521          * Get a attribute of the section
522          *
523          * @param attributeName the name of the attribute
524          * @return a Manifest.Attribute instance if the attribute is
525          * single-valued, otherwise a Vector of Manifest.Attribute
526          * instances.
527          */

528         public Attribute getAttribute(String JavaDoc attributeName) {
529             return (Attribute) attributes.get(attributeName.toLowerCase());
530         }
531
532         /**
533          * Get the attribute keys.
534          *
535          * @return an Enumeration of Strings, each string being the lower case
536          * key of an attribute of the section.
537          */

538         public Enumeration JavaDoc getAttributeKeys() {
539             return attributeIndex.elements();
540         }
541
542         /**
543          * Get the value of the attribute with the name given.
544          *
545          * @param attributeName the name of the attribute to be returned.
546          *
547          * @return the attribute's value or null if the attribute does not exist
548          * in the section
549          */

550         public String JavaDoc getAttributeValue(String JavaDoc attributeName) {
551             Attribute attribute = getAttribute(attributeName.toLowerCase());
552             if (attribute == null) {
553                 return null;
554             }
555             return attribute.getValue();
556         }
557
558         /**
559          * Remove the given attribute from the section
560          *
561          * @param attributeName the name of the attribute to be removed.
562          */

563         public void removeAttribute(String JavaDoc attributeName) {
564             String JavaDoc key = attributeName.toLowerCase();
565             attributes.remove(key);
566             attributeIndex.removeElement(key);
567         }
568
569         /**
570          * Add an attribute to the section.
571          *
572          * @param attribute the attribute to be added to the section
573          *
574          * @exception ManifestException if the attribute is not valid.
575          */

576         public void addConfiguredAttribute(Attribute attribute)
577              throws ManifestException {
578             String JavaDoc check = addAttributeAndCheck(attribute);
579             if (check != null) {
580                 throw new BuildException("Specify the section name using "
581                     + "the \"name\" attribute of the <section> element rather "
582                     + "than using a \"Name\" manifest attribute");
583             }
584         }
585
586         /**
587          * Add an attribute to the section
588          *
589          * @param attribute the attribute to be added.
590          *
591          * @return the value of the attribute if it is a name
592          * attribute - null other wise
593          *
594          * @exception ManifestException if the attribute already
595          * exists in this section.
596          */

597         public String JavaDoc addAttributeAndCheck(Attribute attribute)
598              throws ManifestException {
599             if (attribute.getName() == null || attribute.getValue() == null) {
600                 throw new BuildException("Attributes must have name and value");
601             }
602             if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
603                 warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
604                     + "should not occur in the main section and must be the "
605                     + "first element in all other sections: \""
606                     + attribute.getName() + ": " + attribute.getValue() + "\"");
607                 return attribute.getValue();
608             }
609
610             if (attribute.getKey().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
611                 warnings.addElement(ERROR_FROM_FORBIDDEN
612                     + attribute.getName() + ": " + attribute.getValue() + "\"");
613             } else {
614                 // classpath attributes go into a vector
615
String JavaDoc attributeKey = attribute.getKey();
616                 if (attributeKey.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
617                     Attribute classpathAttribute =
618                         (Attribute) attributes.get(attributeKey);
619
620                     if (classpathAttribute == null) {
621                         storeAttribute(attribute);
622                     } else {
623                         warnings.addElement("Multiple Class-Path attributes "
624                             + "are supported but violate the Jar "
625                             + "specification and may not be correctly "
626                             + "processed in all environments");
627                         Enumeration JavaDoc e = attribute.getValues();
628                         while (e.hasMoreElements()) {
629                             String JavaDoc value = (String JavaDoc) e.nextElement();
630                             classpathAttribute.addValue(value);
631                         }
632                     }
633                 } else if (attributes.containsKey(attributeKey)) {
634                     throw new ManifestException("The attribute \""
635                         + attribute.getName() + "\" may not occur more "
636                         + "than once in the same section");
637                 } else {
638                     storeAttribute(attribute);
639                 }
640             }
641             return null;
642         }
643
644         /**
645          * Clone this section
646          *
647          * @return the cloned Section
648          * @since Ant 1.5.2
649          */

650         public Object JavaDoc clone() {
651             Section cloned = new Section();
652             cloned.setName(name);
653             Enumeration JavaDoc e = getAttributeKeys();
654             while (e.hasMoreElements()) {
655                 String JavaDoc key = (String JavaDoc) e.nextElement();
656                 Attribute attribute = getAttribute(key);
657                 cloned.storeAttribute(new Attribute(attribute.getName(),
658                                                     attribute.getValue()));
659             }
660             return cloned;
661         }
662
663         /**
664          * Store an attribute and update the index.
665          *
666          * @param attribute the attribute to be stored
667          */

668         private void storeAttribute(Attribute attribute) {
669             if (attribute == null) {
670                 return;
671             }
672             String JavaDoc attributeKey = attribute.getKey();
673             attributes.put(attributeKey, attribute);
674             if (!attributeIndex.contains(attributeKey)) {
675                 attributeIndex.addElement(attributeKey);
676             }
677         }
678
679         /**
680          * Get the warnings for this section.
681          *
682          * @return an Enumeration of warning strings.
683          */

684         public Enumeration JavaDoc getWarnings() {
685             return warnings.elements();
686         }
687
688         /**
689          * @see java.lang.Object#hashCode
690          * @return a hash value based on the attributes.
691          */

692         public int hashCode() {
693             return attributes.hashCode();
694         }
695
696         /**
697          * @see java.lang.Object#equals
698          * @param rhs the object to check for equality.
699          * @return true if the attributes are the same.
700          */

701         public boolean equals(Object JavaDoc rhs) {
702             if (rhs == null || rhs.getClass() != getClass()) {
703                 return false;
704             }
705
706             if (rhs == this) {
707                 return true;
708             }
709
710             Section rhsSection = (Section) rhs;
711
712             return attributes.equals(rhsSection.attributes);
713         }
714     }
715
716
717     /** The version of this manifest */
718     private String JavaDoc manifestVersion = DEFAULT_MANIFEST_VERSION;
719
720     /** The main section of this manifest */
721     private Section mainSection = new Section();
722
723     /** The named sections of this manifest */
724     private Hashtable JavaDoc sections = new Hashtable JavaDoc();
725
726     /** Index of sections - used to retain order of sections in manifest */
727     private Vector JavaDoc sectionIndex = new Vector JavaDoc();
728
729     /**
730      * Construct a manifest from Ant's default manifest file.
731      *
732      * @return the default manifest.
733      * @exception BuildException if there is a problem loading the
734      * default manifest
735      */

736     public static Manifest getDefaultManifest() throws BuildException {
737         InputStream JavaDoc in = null;
738         InputStreamReader JavaDoc insr = null;
739         try {
740             String JavaDoc defManifest = "/org/apache/tools/ant/defaultManifest.mf";
741             in = Manifest.class.getResourceAsStream(defManifest);
742             if (in == null) {
743                 throw new BuildException("Could not find default manifest: "
744                     + defManifest);
745             }
746             try {
747                 insr = new InputStreamReader JavaDoc(in, "UTF-8");
748                 Manifest defaultManifest = new Manifest(insr);
749                 Attribute createdBy = new Attribute("Created-By",
750                     System.getProperty("java.vm.version") + " ("
751                     + System.getProperty("java.vm.vendor") + ")");
752                 defaultManifest.getMainSection().storeAttribute(createdBy);
753                 return defaultManifest;
754             } catch (UnsupportedEncodingException JavaDoc e) {
755                 insr = new InputStreamReader JavaDoc(in);
756                 return new Manifest(insr);
757             }
758         } catch (ManifestException e) {
759             throw new BuildException("Default manifest is invalid !!", e);
760         } catch (IOException JavaDoc e) {
761             throw new BuildException("Unable to read default manifest", e);
762         } finally {
763             FileUtils.close(insr);
764             FileUtils.close(in);
765         }
766     }
767
768     /** Construct an empty manifest */
769     public Manifest() {
770         manifestVersion = null;
771     }
772
773     /**
774      * Read a manifest file from the given reader
775      *
776      * @param r is the reader from which the Manifest is read
777      *
778      * @throws ManifestException if the manifest is not valid according
779      * to the JAR spec
780      * @throws IOException if the manifest cannot be read from the reader.
781      */

782     public Manifest(Reader JavaDoc r) throws ManifestException, IOException JavaDoc {
783         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(r);
784         // This should be the manifest version
785
String JavaDoc nextSectionName = mainSection.read(reader);
786         String JavaDoc readManifestVersion
787             = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
788         if (readManifestVersion != null) {
789             manifestVersion = readManifestVersion;
790             mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
791         }
792
793         String JavaDoc line = null;
794         while ((line = reader.readLine()) != null) {
795             if (line.length() == 0) {
796                 continue;
797             }
798
799             Section section = new Section();
800             if (nextSectionName == null) {
801                 Attribute sectionName = new Attribute(line);
802                 if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
803                     throw new ManifestException("Manifest sections should "
804                         + "start with a \"" + ATTRIBUTE_NAME
805                         + "\" attribute and not \""
806                         + sectionName.getName() + "\"");
807                 }
808                 nextSectionName = sectionName.getValue();
809             } else {
810                 // we have already started reading this section
811
// this line is the first attribute. set it and then
812
// let the normal read handle the rest
813
Attribute firstAttribute = new Attribute(line);
814                 section.addAttributeAndCheck(firstAttribute);
815             }
816
817             section.setName(nextSectionName);
818             nextSectionName = section.read(reader);
819             addConfiguredSection(section);
820         }
821     }
822
823     /**
824      * Add a section to the manifest
825      *
826      * @param section the manifest section to be added
827      *
828      * @exception ManifestException if the secti0on is not valid.
829      */

830     public void addConfiguredSection(Section section)
831          throws ManifestException {
832         String JavaDoc sectionName = section.getName();
833         if (sectionName == null) {
834             throw new BuildException("Sections must have a name");
835         }
836         sections.put(sectionName, section);
837         if (!sectionIndex.contains(sectionName)) {
838             sectionIndex.addElement(sectionName);
839         }
840     }
841
842     /**
843      * Add an attribute to the manifest - it is added to the main section.
844      *
845      * @param attribute the attribute to be added.
846      *
847      * @exception ManifestException if the attribute is not valid.
848      */

849     public void addConfiguredAttribute(Attribute attribute)
850          throws ManifestException {
851         if (attribute.getKey() == null || attribute.getValue() == null) {
852             throw new BuildException("Attributes must have name and value");
853         }
854         if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_MANIFEST_VERSION)) {
855             manifestVersion = attribute.getValue();
856         } else {
857             mainSection.addConfiguredAttribute(attribute);
858         }
859     }
860
861     /**
862      * Merge the contents of the given manifest into this manifest
863      *
864      * @param other the Manifest to be merged with this one.
865      *
866      * @throws ManifestException if there is a problem merging the
867      * manifest according to the Manifest spec.
868      */

869     public void merge(Manifest other) throws ManifestException {
870         merge(other, false);
871     }
872
873     /**
874      * Merge the contents of the given manifest into this manifest
875      *
876      * @param other the Manifest to be merged with this one.
877      * @param overwriteMain whether to overwrite the main section
878      * of the current manifest
879      *
880      * @throws ManifestException if there is a problem merging the
881      * manifest according to the Manifest spec.
882      */

883     public void merge(Manifest other, boolean overwriteMain)
884          throws ManifestException {
885         if (other != null) {
886              if (overwriteMain) {
887                  mainSection = (Section) other.mainSection.clone();
888              } else {
889                  mainSection.merge(other.mainSection);
890              }
891
892              if (other.manifestVersion != null) {
893                  manifestVersion = other.manifestVersion;
894              }
895
896              Enumeration JavaDoc e = other.getSectionNames();
897              while (e.hasMoreElements()) {
898                  String JavaDoc sectionName = (String JavaDoc) e.nextElement();
899                  Section ourSection = (Section) sections.get(sectionName);
900                  Section otherSection
901                     = (Section) other.sections.get(sectionName);
902                  if (ourSection == null) {
903                      if (otherSection != null) {
904                          addConfiguredSection((Section) otherSection.clone());
905                      }
906                  } else {
907                      ourSection.merge(otherSection);
908                  }
909              }
910          }
911     }
912
913     /**
914     * Write the manifest out to a print writer.
915     *
916     * @param writer the Writer to which the manifest is written
917     *
918     * @throws IOException if the manifest cannot be written
919     */

920     public void write(PrintWriter JavaDoc writer) throws IOException JavaDoc {
921         writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
922         String JavaDoc signatureVersion
923             = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
924         if (signatureVersion != null) {
925             writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": "
926                 + signatureVersion + EOL);
927             mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
928         }
929         mainSection.write(writer);
930
931         // add it back
932
if (signatureVersion != null) {
933             try {
934                 Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
935                     signatureVersion);
936                 mainSection.addConfiguredAttribute(svAttr);
937             } catch (ManifestException e) {
938                 // shouldn't happen - ignore
939
}
940         }
941
942         Enumeration JavaDoc e = sectionIndex.elements();
943         while (e.hasMoreElements()) {
944             String JavaDoc sectionName = (String JavaDoc) e.nextElement();
945             Section section = getSection(sectionName);
946             section.write(writer);
947         }
948     }
949
950     /**
951      * Convert the manifest to its string representation
952      *
953      * @return a multiline string with the Manifest as it
954      * appears in a Manifest file.
955      */

956     public String JavaDoc toString() {
957         StringWriter JavaDoc sw = new StringWriter JavaDoc();
958         try {
959             write(new PrintWriter JavaDoc(sw));
960         } catch (IOException JavaDoc e) {
961             return null;
962         }
963         return sw.toString();
964     }
965
966     /**
967      * Get the warnings for this manifest.
968      *
969      * @return an enumeration of warning strings
970      */

971     public Enumeration JavaDoc getWarnings() {
972         Vector JavaDoc warnings = new Vector JavaDoc();
973
974         Enumeration JavaDoc warnEnum = mainSection.getWarnings();
975         while (warnEnum.hasMoreElements()) {
976             warnings.addElement(warnEnum.nextElement());
977         }
978
979         // create a vector and add in the warnings for all the sections
980
Enumeration JavaDoc e = sections.elements();
981         while (e.hasMoreElements()) {
982             Section section = (Section) e.nextElement();
983             Enumeration JavaDoc e2 = section.getWarnings();
984             while (e2.hasMoreElements()) {
985                 warnings.addElement(e2.nextElement());
986             }
987         }
988
989         return warnings.elements();
990     }
991
992     /**
993      * @see java.lang.Object#hashCode
994      * @return a hashcode based on the version, main and sections.
995      */

996     public int hashCode() {
997         int hashCode = 0;
998
999         if (manifestVersion != null) {
1000            hashCode += manifestVersion.hashCode();
1001        }
1002        hashCode += mainSection.hashCode();
1003        hashCode += sections.hashCode();
1004
1005        return hashCode;
1006    }
1007
1008    /**
1009     * @see java.lang.Object#equals
1010     * @param rhs the object to check for equality.
1011     * @return true if the version, main and sections are the same.
1012     */

1013    public boolean equals(Object JavaDoc rhs) {
1014        if (rhs == null || rhs.getClass() != getClass()) {
1015            return false;
1016        }
1017
1018        if (rhs == this) {
1019            return true;
1020        }
1021
1022        Manifest rhsManifest = (Manifest) rhs;
1023        if (manifestVersion == null) {
1024            if (rhsManifest.manifestVersion != null) {
1025                return false;
1026            }
1027        } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) {
1028            return false;
1029        }
1030
1031        if (!mainSection.equals(rhsManifest.mainSection)) {
1032            return false;
1033        }
1034
1035        return sections.equals(rhsManifest.sections);
1036    }
1037
1038    /**
1039     * Get the version of the manifest
1040     *
1041     * @return the manifest's version string
1042     */

1043    public String JavaDoc getManifestVersion() {
1044        return manifestVersion;
1045    }
1046
1047    /**
1048     * Get the main section of the manifest
1049     *
1050     * @return the main section of the manifest
1051     */

1052    public Section getMainSection() {
1053        return mainSection;
1054    }
1055
1056    /**
1057     * Get a particular section from the manifest
1058     *
1059     * @param name the name of the section desired.
1060     * @return the specified section or null if that section
1061     * does not exist in the manifest
1062     */

1063    public Section getSection(String JavaDoc name) {
1064        return (Section) sections.get(name);
1065    }
1066
1067    /**
1068     * Get the section names in this manifest.
1069     *
1070     * @return an Enumeration of section names
1071     */

1072    public Enumeration JavaDoc getSectionNames() {
1073        return sectionIndex.elements();
1074    }
1075}
1076
Popular Tags