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 39 kB

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