1. /*
  2. * @(#)Attributes.java 1.37 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.util.jar;
  11. import java.io.DataInputStream;
  12. import java.io.DataOutputStream;
  13. import java.io.IOException;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. import java.util.Set;
  17. import java.util.Collection;
  18. import java.util.AbstractSet;
  19. import java.util.Iterator;
  20. /**
  21. * The Attributes class maps Manifest attribute names to associated string
  22. * values. Attribute names are case-insensitive and restricted to the ASCII
  23. * characters in the set [0-9a-zA-Z_-]. Attribute values can contain any
  24. * characters and will be UTF8-encoded when written to the output stream.
  25. *
  26. * @author David Connelly
  27. * @version 1.37, 02/02/00
  28. * @see Manifest
  29. * @since 1.2
  30. */
  31. public class Attributes implements Map, Cloneable {
  32. /**
  33. * The attribute name-value mappings.
  34. */
  35. protected Map map;
  36. /**
  37. * Constructs a new, empty Attributes object with default size.
  38. */
  39. public Attributes() {
  40. this(11);
  41. }
  42. /**
  43. * Constructs a new, empty Attributes object with the specified
  44. * initial size.
  45. *
  46. * @param size the initial number of attributes
  47. */
  48. public Attributes(int size) {
  49. map = new HashMap(size);
  50. }
  51. /**
  52. * Constructs a new Attributes object with the same attribute name-value
  53. * mappings as in the specified Attributes.
  54. *
  55. * @param attr the specified Attributes
  56. */
  57. public Attributes(Attributes attr) {
  58. map = new HashMap(attr);
  59. }
  60. /**
  61. * Returns the value of the specified attribute name, or null if the
  62. * attribute name was not found.
  63. *
  64. * @param name the attribute name
  65. * @return the value of the specified attribute name, or null if
  66. * not found.
  67. */
  68. public Object get(Object name) {
  69. return map.get(name);
  70. }
  71. /**
  72. * Returns the value of the specified attribute name, specified as
  73. * a string, or null if the attribute was not found. The attribute
  74. * name is case-insensitive.
  75. * <p>
  76. * This method is defined as:
  77. * <pre>
  78. * return (String)get(new Attributes.Name((String)name));
  79. * </pre>
  80. *
  81. * @param name the attribute name as a string
  82. * @return the String value of the specified attribute name, or null if
  83. * not found.
  84. */
  85. public String getValue(String name) {
  86. return (String)get(new Attributes.Name((String)name));
  87. }
  88. /**
  89. * Returns the value of the specified Attributes.Name, or null if the
  90. * attribute was not found.
  91. * <p>
  92. * This method is defined as:
  93. * <pre>
  94. * return (String)get(name);
  95. * </pre>
  96. *
  97. * @param name the Attributes.Name object
  98. * @return the String value of the specified Attribute.Name, or null if
  99. * not found.
  100. */
  101. public String getValue(Name name) {
  102. return (String)get(name);
  103. }
  104. /**
  105. * Associates the specified value with the specified attribute name
  106. * (key) in this Map. If the Map previously contained a mapping for
  107. * the attribute name, the old value is replaced.
  108. *
  109. * @param name the attribute name
  110. * @param value the attribute value
  111. * @return the previous value of the attribute, or null if none
  112. * @exception ClassCastException if the name is not a Attributes.Name
  113. * or the value is not a String
  114. */
  115. public Object put(Object name, Object value) {
  116. return map.put((Attributes.Name)name, (String)value);
  117. }
  118. /**
  119. * Associates the specified value with the specified attribute name,
  120. * specified as a String. The attributes name is case-insensitive.
  121. * If the Map previously contained a mapping for the attribute name,
  122. * the old value is replaced.
  123. * <p>
  124. * This method is defined as:
  125. * <pre>
  126. * return (String)put(new Attributes.Name(name), value);
  127. * </pre>
  128. *
  129. * @param name the attribute name as a string
  130. * @param value the attribute value
  131. * @return the previous value of the attribute, or null if none
  132. * @exception IllegalArgumentException if the attribute name is invalid
  133. */
  134. public String putValue(String name, String value) {
  135. return (String)put(new Name(name), value);
  136. }
  137. /**
  138. * Removes the attribute with the specified name (key) from this Map.
  139. * Returns the previous attribute value, or null if none.
  140. *
  141. * @param name attribute name
  142. * @return the previous value of the attribute, or null if none
  143. */
  144. public Object remove(Object name) {
  145. return map.remove(name);
  146. }
  147. /**
  148. * Returns true if this Map maps one or more attribute names (keys)
  149. * to the specified value.
  150. *
  151. * @param value the attribute value
  152. * @return true if this Map maps one or more attribute names to
  153. * the specified value
  154. */
  155. public boolean containsValue(Object value) {
  156. return map.containsValue(value);
  157. }
  158. /**
  159. * Returns true if this Map contains the specified attribute name (key).
  160. *
  161. * @param name the attribute name
  162. * @return true if this Map contains the specified attribute name
  163. */
  164. public boolean containsKey(Object name) {
  165. return map.containsKey(name);
  166. }
  167. /**
  168. * Copies all of the attribute name-value mappings from the specified
  169. * Attributes to this Map. Duplicate mappings will be replaced.
  170. *
  171. * @param attr the Attributes to be stored in this map
  172. * @exception ClassCastException if attr is not an Attributes
  173. */
  174. public void putAll(Map attr) {
  175. map.putAll((Attributes)attr);
  176. }
  177. /**
  178. * Removes all attributes from this Map.
  179. */
  180. public void clear() {
  181. map.clear();
  182. }
  183. /**
  184. * Returns the number of attributes in this Map.
  185. */
  186. public int size() {
  187. return map.size();
  188. }
  189. /**
  190. * Returns true if this Map contains no attributes.
  191. */
  192. public boolean isEmpty() {
  193. return map.isEmpty();
  194. }
  195. /**
  196. * Returns a Set view of the attribute names (keys) contained in this Map.
  197. */
  198. public Set keySet() {
  199. return map.keySet();
  200. }
  201. /**
  202. * Returns a Collection view of the attribute values contained in this Map.
  203. */
  204. public Collection values() {
  205. return map.values();
  206. }
  207. /**
  208. * Returns a Collection view of the attribute name-value mappings
  209. * contained in this Map.
  210. */
  211. public Set entrySet() {
  212. return map.entrySet();
  213. }
  214. /**
  215. * Compares the specified Attributes object with this Map for equality.
  216. * Returns true if the given object is also an instance of Attributes
  217. * and the two Attributes objects represent the same mappings.
  218. *
  219. * @param o the Object to be compared
  220. * @return true if the specified Object is equal to this Map
  221. */
  222. public boolean equals(Object o) {
  223. return map.equals(o);
  224. }
  225. /**
  226. * Returns the hash code value for this Map.
  227. */
  228. public int hashCode() {
  229. return map.hashCode();
  230. }
  231. /**
  232. * Returns a copy of the Attributes, implemented as follows:
  233. * <pre>
  234. * public Object clone() { return new Attributes(this); }
  235. * </pre>
  236. * Since the attribute names and values are themselves immutable,
  237. * the Attributes returned can be safely modified without affecting
  238. * the original.
  239. */
  240. public Object clone() {
  241. return new Attributes(this);
  242. }
  243. /*
  244. * Writes the current attributes to the specified data output stream.
  245. * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
  246. */
  247. void write(DataOutputStream os) throws IOException {
  248. Iterator it = entrySet().iterator();
  249. while (it.hasNext()) {
  250. Map.Entry e = (Map.Entry)it.next();
  251. StringBuffer buffer = new StringBuffer(
  252. ((Name)e.getKey()).toString());
  253. buffer.append(": ");
  254. buffer.append((String)e.getValue());
  255. buffer.append("\r\n");
  256. Manifest.make72Safe(buffer);
  257. os.writeBytes(buffer.toString());
  258. }
  259. os.writeBytes("\r\n");
  260. }
  261. /*
  262. * Writes the current attributes to the specified data output stream,
  263. * make sure to write out the MANIFEST_VERSION or SIGNATURE_VERSION
  264. * attributes first.
  265. *
  266. * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
  267. */
  268. void writeMain(DataOutputStream out) throws IOException
  269. {
  270. // write out the *-Version header first, if it exists
  271. String vername = Name.MANIFEST_VERSION.toString();
  272. String version = getValue(vername);
  273. if (version == null) {
  274. vername = Name.SIGNATURE_VERSION.toString();
  275. version = getValue(vername);
  276. }
  277. if (version != null) {
  278. out.writeBytes(vername+": "+version+"\r\n");
  279. }
  280. // write out all attributes except for the version
  281. // we wrote out earlier
  282. Iterator it = entrySet().iterator();
  283. while (it.hasNext()) {
  284. Map.Entry e = (Map.Entry)it.next();
  285. String name = ((Name)e.getKey()).toString();
  286. if ((version != null) && ! (name.equalsIgnoreCase(vername))) {
  287. StringBuffer buffer = new StringBuffer(name);
  288. buffer.append(": ");
  289. buffer.append((String)e.getValue());
  290. buffer.append("\r\n");
  291. Manifest.make72Safe(buffer);
  292. out.writeBytes(buffer.toString());
  293. }
  294. }
  295. out.writeBytes("\r\n");
  296. }
  297. /*
  298. * Reads attributes from the specified input stream.
  299. * XXX Need to handle UTF8 values.
  300. */
  301. void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
  302. String name = null, value = null;
  303. int len;
  304. while ((len = is.readLine(lbuf)) != -1) {
  305. if (lbuf[--len] != '\n') {
  306. throw new IOException("line too long");
  307. }
  308. if (len > 0 && lbuf[len-1] == '\r') {
  309. --len;
  310. }
  311. if (len == 0) {
  312. break;
  313. }
  314. int i = 0;
  315. if (lbuf[0] == ' ') {
  316. // continuation of previous line
  317. if (name == null) {
  318. throw new IOException("misplaced continuation line");
  319. }
  320. value = value + new String(lbuf, 0, 1, len-1);
  321. } else {
  322. while (lbuf[i++] != ':') {
  323. if (i >= len) {
  324. throw new IOException("invalid header field");
  325. }
  326. }
  327. if (lbuf[i++] != ' ') {
  328. throw new IOException("invalid header field");
  329. }
  330. name = new String(lbuf, 0, 0, i - 2);
  331. value = new String(lbuf, 0, i, len - i);
  332. }
  333. try {
  334. putValue(name, value);
  335. } catch (IllegalArgumentException e) {
  336. throw new IOException("invalid header field name: " + name);
  337. }
  338. }
  339. }
  340. /**
  341. * The Attributes.Name class represents an attribute name stored in
  342. * this Map. Attribute names are case-insensitive and restricted to
  343. * the ASCII characters in the set [0-9a-zA-Z_-].
  344. */
  345. public static class Name {
  346. private String name;
  347. private int hashCode = -1;
  348. /**
  349. * Constructs a new attribute name using the given string name.
  350. *
  351. * @param name the attribute string name
  352. * @exception IllegalArgumentException if the attribute name was
  353. * invalid
  354. * @exception NullPointerException if the attribute name was null
  355. */
  356. public Name(String name) {
  357. if (name == null) {
  358. throw new NullPointerException("name");
  359. }
  360. if (!isValid(name)) {
  361. throw new IllegalArgumentException(name);
  362. }
  363. this.name = name.intern();
  364. }
  365. private static boolean isValid(String name) {
  366. int len = name.length();
  367. if (len > 70 || len == 0) {
  368. return false;
  369. }
  370. for (int i = 0; i < len; i++) {
  371. if (!isValid(name.charAt(i))) {
  372. return false;
  373. }
  374. }
  375. return true;
  376. }
  377. private static boolean isValid(char c) {
  378. return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
  379. }
  380. private static boolean isAlpha(char c) {
  381. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  382. }
  383. private static boolean isDigit(char c) {
  384. return c >= '0' && c <= '9';
  385. }
  386. /**
  387. * Compares this attribute name to another for equality.
  388. * @param o the object to compare
  389. * @return true if this attribute name is equal to the
  390. * specified attribute object
  391. */
  392. public boolean equals(Object o) {
  393. if (o instanceof Name) {
  394. return name.equalsIgnoreCase(((Name)o).name);
  395. } else {
  396. return false;
  397. }
  398. }
  399. /**
  400. * Computes the hash value for this attribute name.
  401. */
  402. public int hashCode() {
  403. if (hashCode == -1) {
  404. hashCode = name.toLowerCase().hashCode();
  405. }
  406. return hashCode;
  407. }
  408. /**
  409. * Returns the attribute name as a String.
  410. */
  411. public String toString() {
  412. return name;
  413. }
  414. /**
  415. * <code>Name</code> object for <code>Manifest-Version</code>
  416. * manifest attribute. This attribute indicates the version number
  417. * of the manifest standard to which a JAR file's manifest conforms.
  418. * @see <a href="../../../../guide/jar/manifest.html">
  419. * Manifest and Signature Specification</a>
  420. */
  421. public static final Name MANIFEST_VERSION = new Name("Manifest-Version");
  422. /**
  423. * <code>Name</code> object for <code>Signature-Version</code>
  424. * manifest attribute used when signing JAR files.
  425. * @see <a href="../../../../guide/jar/manifest.html">
  426. * Manifest and Signature Specification</a>
  427. */
  428. public static final Name SIGNATURE_VERSION = new Name("Signature-Version");
  429. /**
  430. * <code>Name</code> object for <code>Content-Type</code>
  431. * manifest attribute.
  432. */
  433. public static final Name CONTENT_TYPE = new Name("Content-Type");
  434. /**
  435. * <code>Name</code> object for <code>Class-Path</code>
  436. * manifest attribute. Bundled extensions can use this attribute
  437. * to find other JAR files containing needed classes.
  438. * @see <a href="../../../../guide/extensions/spec.html#bundled">
  439. * Extensions Specification</a>
  440. */
  441. public static final Name CLASS_PATH = new Name("Class-Path");
  442. /**
  443. * <code>Name</code> object for <code>Main-Class</code> manifest
  444. * attribute used for launching applications packaged in JAR files.
  445. * The <code>Main-Class</code> attribute is used in conjunction
  446. * with the <code>-jar</code> command-line option of the
  447. * <tt>java</tt> application launcher.
  448. */
  449. public static final Name MAIN_CLASS = new Name("Main-Class");
  450. /**
  451. * <code>Name</code> object for <code>Sealed</code> manifest attribute
  452. * used for sealing.
  453. * @see <a href="../../../../guide/extensions/spec.html#sealing">
  454. * Extension Sealing</a>
  455. */
  456. public static final Name SEALED = new Name("Sealed");
  457. /**
  458. * <code>Name</code> object for <code>Extension-List</code> manifest attribute
  459. * used for declaring dependencies on installed extensions.
  460. * @see <a href="../../../../guide/extensions/spec.html#dependnecy">
  461. * Installed extension dependency</a>
  462. */
  463. public static final Name EXTENSION_LIST = new Name("Extension-List");
  464. /**
  465. * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
  466. * used for declaring dependencies on installed extensions.
  467. * @see <a href="../../../../guide/extensions/spec.html#dependency">
  468. * Installed extension dependency</a>
  469. */
  470. public static final Name EXTENSION_NAME = new Name("Extension-Name");
  471. /**
  472. * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
  473. * used for declaring dependencies on installed extensions.
  474. * @see <a href="../../../../guide/extensions/spec.html#dependency">
  475. * Installed extension dependency</a>
  476. */
  477. public static final Name EXTENSION_INSTALLATION = new Name("Extension-Installation");
  478. /**
  479. * <code>Name</code> object for <code>Implementation-Title</code>
  480. * manifest attribute used for package versioning.
  481. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  482. * Java Product Versioning Specification</a>
  483. */
  484. public static final Name IMPLEMENTATION_TITLE = new Name("Implementation-Title");
  485. /**
  486. * <code>Name</code> object for <code>Implementation-Version</code>
  487. * manifest attribute used for package versioning.
  488. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  489. * Java Product Versioning Specification</a>
  490. */
  491. public static final Name IMPLEMENTATION_VERSION = new Name("Implementation-Version");
  492. /**
  493. * <code>Name</code> object for <code>Implementation-Vendor</code>
  494. * manifest attribute used for package versioning.
  495. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  496. * Java Product Versioning Specification</a>
  497. */
  498. public static final Name IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor");
  499. /**
  500. * <code>Name</code> object for <code>Implementation-Vendor-Id</code>
  501. * manifest attribute used for package versioning.
  502. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  503. * Java Product Versioning Specification</a>
  504. */
  505. public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
  506. /**
  507. * <code>Name</code> object for <code>Implementation-Vendor-URL</code>
  508. * manifest attribute used for package versioning.
  509. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  510. * Java Product Versioning Specification</a>
  511. */
  512. public static final Name IMPLEMENTATION_URL = new Name("Implementation-URL");
  513. /**
  514. * <code>Name</code> object for <code>Specification-Title</code>
  515. * manifest attribute used for package versioning.
  516. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  517. * Java Product Versioning Specification</a>
  518. */
  519. public static final Name SPECIFICATION_TITLE = new Name("Specification-Title");
  520. /**
  521. * <code>Name</code> object for <code>Specification-Version</code>
  522. * manifest attribute used for package versioning.
  523. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  524. * Java Product Versioning Specification</a>
  525. */
  526. public static final Name SPECIFICATION_VERSION = new Name("Specification-Version");
  527. /**
  528. * <code>Name</code> object for <code>Specification-Vendor</code>
  529. * manifest attribute used for package versioning.
  530. * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
  531. * Java Product Versioning Specification</a>
  532. */
  533. public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor");
  534. }
  535. }