You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Manifest.java 35 kB

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