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