source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Manifest.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

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