001: /*
002: * @(#)Attributes.java 1.47 03/06/24
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util.jar;
029:
030: import java.io.DataInputStream;
031: import java.io.DataOutputStream;
032: import java.io.IOException;
033: import java.util.HashMap;
034: import java.util.Map;
035: import java.util.Set;
036: import java.util.Collection;
037: import java.util.AbstractSet;
038: import java.util.Iterator;
039:
040: /**
041: * The Attributes class maps Manifest attribute names to associated string
042: * values. Valid attribute names are case-insensitive, are restricted to
043: * the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed 70
044: * characters in length. Attribute values can contain any characters and
045: * will be UTF8-encoded when written to the output stream. See the
046: * <a href="../../../../guide/jar/jar.html">JAR File Specification</a>
047: * for more information about valid attribute names and values.
048: *
049: * @author David Connelly
050: * @version 1.47, 06/24/03
051: * @see Manifest
052: * @since 1.2
053: */
054: public class Attributes implements Map, Cloneable {
055: /**
056: * The attribute name-value mappings.
057: */
058: protected Map map;
059:
060: /**
061: * Constructs a new, empty Attributes object with default size.
062: */
063: public Attributes() {
064: this (11);
065: }
066:
067: /**
068: * Constructs a new, empty Attributes object with the specified
069: * initial size.
070: *
071: * @param size the initial number of attributes
072: */
073: public Attributes(int size) {
074: map = new HashMap(size);
075: }
076:
077: /**
078: * Constructs a new Attributes object with the same attribute name-value
079: * mappings as in the specified Attributes.
080: *
081: * @param attr the specified Attributes
082: */
083: public Attributes(Attributes attr) {
084: map = new HashMap(attr);
085: }
086:
087: /**
088: * Returns the value of the specified attribute name, or null if the
089: * attribute name was not found.
090: *
091: * @param name the attribute name
092: * @return the value of the specified attribute name, or null if
093: * not found.
094: */
095: public Object get(Object name) {
096: return map.get(name);
097: }
098:
099: /**
100: * Returns the value of the specified attribute name, specified as
101: * a string, or null if the attribute was not found. The attribute
102: * name is case-insensitive.
103: * <p>
104: * This method is defined as:
105: * <pre>
106: * return (String)get(new Attributes.Name((String)name));
107: * </pre>
108: *
109: * @param name the attribute name as a string
110: * @return the String value of the specified attribute name, or null if
111: * not found.
112: * @throws IllegalArgumentException if the attribute name is invalid
113: */
114: public String getValue(String name) {
115: return (String) get(new Attributes.Name((String) name));
116: }
117:
118: /**
119: * Returns the value of the specified Attributes.Name, or null if the
120: * attribute was not found.
121: * <p>
122: * This method is defined as:
123: * <pre>
124: * return (String)get(name);
125: * </pre>
126: *
127: * @param name the Attributes.Name object
128: * @return the String value of the specified Attribute.Name, or null if
129: * not found.
130: */
131: public String getValue(Name name) {
132: return (String) get(name);
133: }
134:
135: /**
136: * Associates the specified value with the specified attribute name
137: * (key) in this Map. If the Map previously contained a mapping for
138: * the attribute name, the old value is replaced.
139: *
140: * @param name the attribute name
141: * @param value the attribute value
142: * @return the previous value of the attribute, or null if none
143: * @exception ClassCastException if the name is not a Attributes.Name
144: * or the value is not a String
145: */
146: public Object put(Object name, Object value) {
147: return map.put((Attributes.Name) name, (String) value);
148: }
149:
150: /**
151: * Associates the specified value with the specified attribute name,
152: * specified as a String. The attributes name is case-insensitive.
153: * If the Map previously contained a mapping for the attribute name,
154: * the old value is replaced.
155: * <p>
156: * This method is defined as:
157: * <pre>
158: * return (String)put(new Attributes.Name(name), value);
159: * </pre>
160: *
161: * @param name the attribute name as a string
162: * @param value the attribute value
163: * @return the previous value of the attribute, or null if none
164: * @exception IllegalArgumentException if the attribute name is invalid
165: */
166: public String putValue(String name, String value) {
167: return (String) put(new Name(name), value);
168: }
169:
170: /**
171: * Removes the attribute with the specified name (key) from this Map.
172: * Returns the previous attribute value, or null if none.
173: *
174: * @param name attribute name
175: * @return the previous value of the attribute, or null if none
176: */
177: public Object remove(Object name) {
178: return map.remove(name);
179: }
180:
181: /**
182: * Returns true if this Map maps one or more attribute names (keys)
183: * to the specified value.
184: *
185: * @param value the attribute value
186: * @return true if this Map maps one or more attribute names to
187: * the specified value
188: */
189: public boolean containsValue(Object value) {
190: return map.containsValue(value);
191: }
192:
193: /**
194: * Returns true if this Map contains the specified attribute name (key).
195: *
196: * @param name the attribute name
197: * @return true if this Map contains the specified attribute name
198: */
199: public boolean containsKey(Object name) {
200: return map.containsKey(name);
201: }
202:
203: /**
204: * Copies all of the attribute name-value mappings from the specified
205: * Attributes to this Map. Duplicate mappings will be replaced.
206: *
207: * @param attr the Attributes to be stored in this map
208: * @exception ClassCastException if attr is not an Attributes
209: */
210: public void putAll(Map attr) {
211: map.putAll((Attributes) attr);
212: }
213:
214: /**
215: * Removes all attributes from this Map.
216: */
217: public void clear() {
218: map.clear();
219: }
220:
221: /**
222: * Returns the number of attributes in this Map.
223: */
224: public int size() {
225: return map.size();
226: }
227:
228: /**
229: * Returns true if this Map contains no attributes.
230: */
231: public boolean isEmpty() {
232: return map.isEmpty();
233: }
234:
235: /**
236: * Returns a Set view of the attribute names (keys) contained in this Map.
237: */
238: public Set keySet() {
239: return map.keySet();
240: }
241:
242: /**
243: * Returns a Collection view of the attribute values contained in this Map.
244: */
245: public Collection values() {
246: return map.values();
247: }
248:
249: /**
250: * Returns a Collection view of the attribute name-value mappings
251: * contained in this Map.
252: */
253: public Set entrySet() {
254: return map.entrySet();
255: }
256:
257: /**
258: * Compares the specified Attributes object with this Map for equality.
259: * Returns true if the given object is also an instance of Attributes
260: * and the two Attributes objects represent the same mappings.
261: *
262: * @param o the Object to be compared
263: * @return true if the specified Object is equal to this Map
264: */
265: public boolean equals(Object o) {
266: return map.equals(o);
267: }
268:
269: /**
270: * Returns the hash code value for this Map.
271: */
272: public int hashCode() {
273: return map.hashCode();
274: }
275:
276: /**
277: * Returns a copy of the Attributes, implemented as follows:
278: * <pre>
279: * public Object clone() { return new Attributes(this); }
280: * </pre>
281: * Since the attribute names and values are themselves immutable,
282: * the Attributes returned can be safely modified without affecting
283: * the original.
284: */
285: public Object clone() {
286: return new Attributes(this );
287: }
288:
289: /*
290: * Writes the current attributes to the specified data output stream.
291: * TODO: Need to handle UTF8 values and break up lines longer than 72 bytes
292: */
293: void write(DataOutputStream os) throws IOException {
294: Iterator it = entrySet().iterator();
295: while (it.hasNext()) {
296: Map.Entry e = (Map.Entry) it.next();
297: StringBuffer buffer = new StringBuffer(((Name) e.getKey())
298: .toString());
299: buffer.append(": ");
300:
301: String value = (String) e.getValue();
302: if (value != null) {
303: byte[] vb = value.getBytes("UTF8");
304: value = new String(vb, 0, vb.length);
305: }
306: buffer.append(value);
307:
308: buffer.append("\r\n");
309: Manifest.make72Safe(buffer);
310: os.writeBytes(buffer.toString());
311: }
312: os.writeBytes("\r\n");
313: }
314:
315: /*
316: * Writes the current attributes to the specified data output stream,
317: * make sure to write out the MANIFEST_VERSION or SIGNATURE_VERSION
318: * attributes first.
319: *
320: * TODO: Need to handle UTF8 values and break up lines longer than 72 bytes
321: */
322: void writeMain(DataOutputStream out) throws IOException {
323: // write out the *-Version header first, if it exists
324: String vername = Name.MANIFEST_VERSION.toString();
325: String version = getValue(vername);
326: if (version == null) {
327: vername = Name.SIGNATURE_VERSION.toString();
328: version = getValue(vername);
329: }
330:
331: if (version != null) {
332: out.writeBytes(vername + ": " + version + "\r\n");
333: }
334:
335: // write out all attributes except for the version
336: // we wrote out earlier
337: Iterator it = entrySet().iterator();
338: while (it.hasNext()) {
339: Map.Entry e = (Map.Entry) it.next();
340: String name = ((Name) e.getKey()).toString();
341: if ((version != null) && !(name.equalsIgnoreCase(vername))) {
342:
343: StringBuffer buffer = new StringBuffer(name);
344: buffer.append(": ");
345:
346: String value = (String) e.getValue();
347: if (value != null) {
348: byte[] vb = value.getBytes("UTF8");
349: value = new String(vb, 0, vb.length);
350: }
351: buffer.append(value);
352:
353: buffer.append("\r\n");
354: Manifest.make72Safe(buffer);
355: out.writeBytes(buffer.toString());
356: }
357: }
358: out.writeBytes("\r\n");
359: }
360:
361: /*
362: * Reads attributes from the specified input stream.
363: * TODO: Need to handle UTF8 values.
364: */
365: void read(Manifest.FastInputStream is, byte[] lbuf)
366: throws IOException {
367: String name = null, value = null;
368: byte[] lastline = null;
369:
370: int len;
371: while ((len = is.readLine(lbuf)) != -1) {
372: boolean lineContinued = false;
373: if (lbuf[--len] != '\n') {
374: throw new IOException("line too long");
375: }
376: if (len > 0 && lbuf[len - 1] == '\r') {
377: --len;
378: }
379: if (len == 0) {
380: break;
381: }
382: int i = 0;
383: if (lbuf[0] == ' ') {
384: // continuation of previous line
385: if (name == null) {
386: throw new IOException("misplaced continuation line");
387: }
388: lineContinued = true;
389: byte[] buf = new byte[lastline.length + len - 1];
390: System.arraycopy(lastline, 0, buf, 0, lastline.length);
391: System
392: .arraycopy(lbuf, 1, buf, lastline.length,
393: len - 1);
394: if (is.peek() == ' ') {
395: lastline = buf;
396: continue;
397: }
398: value = new String(buf, 0, buf.length, "UTF8");
399: lastline = null;
400: } else {
401: while (lbuf[i++] != ':') {
402: if (i >= len) {
403: throw new IOException("invalid header field");
404: }
405: }
406: if (lbuf[i++] != ' ') {
407: throw new IOException("invalid header field");
408: }
409: name = new String(lbuf, 0, i - 2);
410: if (is.peek() == ' ') {
411: lastline = new byte[len - i];
412: System.arraycopy(lbuf, i, lastline, 0, len - i);
413: continue;
414: }
415: value = new String(lbuf, i, len - i, "UTF8");
416: }
417: try {
418: if ((putValue(name, value) != null) && (!lineContinued)) {
419: /* If Logging available
420: Logger.getLogger("java.util.jar").warning(
421: "Duplicate name in Manifest: " +
422: name);
423: */
424: }
425: } catch (IllegalArgumentException e) {
426: throw new IOException("invalid header field name: "
427: + name);
428: }
429: }
430: }
431:
432: /**
433: * The Attributes.Name class represents an attribute name stored in
434: * this Map. Valid attribute names are case-insensitive, are restricted
435: * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
436: * 70 characters in length. Attribute values can contain any characters
437: * and will be UTF8-encoded when written to the output stream. See the
438: * <a href="../../../../guide/jar/jar.html">JAR File Specification</a>
439: * for more information about valid attribute names and values.
440: */
441: public static class Name {
442: private String name;
443: private int hashCode = -1;
444:
445: /**
446: * Constructs a new attribute name using the given string name.
447: *
448: * @param name the attribute string name
449: * @exception IllegalArgumentException if the attribute name was
450: * invalid
451: * @exception NullPointerException if the attribute name was null
452: */
453: public Name(String name) {
454: if (name == null) {
455: throw new NullPointerException("name");
456: }
457: if (!isValid(name)) {
458: throw new IllegalArgumentException(name);
459: }
460: this .name = name.intern();
461: }
462:
463: private static boolean isValid(String name) {
464: int len = name.length();
465: if (len > 70 || len == 0) {
466: return false;
467: }
468: for (int i = 0; i < len; i++) {
469: if (!isValid(name.charAt(i))) {
470: return false;
471: }
472: }
473: return true;
474: }
475:
476: private static boolean isValid(char c) {
477: return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
478: }
479:
480: private static boolean isAlpha(char c) {
481: return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
482: }
483:
484: private static boolean isDigit(char c) {
485: return c >= '0' && c <= '9';
486: }
487:
488: /**
489: * Compares this attribute name to another for equality.
490: * @param o the object to compare
491: * @return true if this attribute name is equal to the
492: * specified attribute object
493: */
494: public boolean equals(Object o) {
495: if (o instanceof Name) {
496: return name.equalsIgnoreCase(((Name) o).name);
497: } else {
498: return false;
499: }
500: }
501:
502: /**
503: * Computes the hash value for this attribute name.
504: */
505: public int hashCode() {
506: if (hashCode == -1) {
507: hashCode = name.toLowerCase().hashCode();
508: }
509: return hashCode;
510: }
511:
512: /**
513: * Returns the attribute name as a String.
514: */
515: public String toString() {
516: return name;
517: }
518:
519: /**
520: * <code>Name</code> object for <code>Manifest-Version</code>
521: * manifest attribute. This attribute indicates the version number
522: * of the manifest standard to which a JAR file's manifest conforms.
523: * @see <a href="../../../../guide/jar/jar.html#JAR Manifest">
524: * Manifest and Signature Specification</a>
525: */
526: public static final Name MANIFEST_VERSION = new Name(
527: "Manifest-Version");
528:
529: /**
530: * <code>Name</code> object for <code>Signature-Version</code>
531: * manifest attribute used when signing JAR files.
532: * @see <a href="../../../../guide/jar/jar.html#JAR Manifest">
533: * Manifest and Signature Specification</a>
534: */
535: public static final Name SIGNATURE_VERSION = new Name(
536: "Signature-Version");
537:
538: /**
539: * <code>Name</code> object for <code>Content-Type</code>
540: * manifest attribute.
541: */
542: public static final Name CONTENT_TYPE = new Name("Content-Type");
543:
544: /**
545: * <code>Name</code> object for <code>Class-Path</code>
546: * manifest attribute. Bundled extensions can use this attribute
547: * to find other JAR files containing needed classes.
548: * @see <a href="../../../../guide/extensions/spec.html#bundled">
549: * Extensions Specification</a>
550: */
551: public static final Name CLASS_PATH = new Name("Class-Path");
552:
553: /**
554: * <code>Name</code> object for <code>Main-Class</code> manifest
555: * attribute used for launching applications packaged in JAR files.
556: * The <code>Main-Class</code> attribute is used in conjunction
557: * with the <code>-jar</code> command-line option of the
558: * <tt>java</tt> application launcher.
559: */
560: public static final Name MAIN_CLASS = new Name("Main-Class");
561:
562: /**
563: * <code>Name</code> object for <code>Sealed</code> manifest attribute
564: * used for sealing.
565: * @see <a href="../../../../guide/extensions/spec.html#sealing">
566: * Extension Sealing</a>
567: */
568: public static final Name SEALED = new Name("Sealed");
569:
570: /**
571: * <code>Name</code> object for <code>Extension-List</code> manifest attribute
572: * used for declaring dependencies on installed extensions.
573: * @see <a href="../../../../guide/extensions/spec.html#dependnecy">
574: * Installed extension dependency</a>
575: */
576: public static final Name EXTENSION_LIST = new Name(
577: "Extension-List");
578:
579: /**
580: * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
581: * used for declaring dependencies on installed extensions.
582: * @see <a href="../../../../guide/extensions/spec.html#dependency">
583: * Installed extension dependency</a>
584: */
585: public static final Name EXTENSION_NAME = new Name(
586: "Extension-Name");
587:
588: /**
589: * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
590: * used for declaring dependencies on installed extensions.
591: * @see <a href="../../../../guide/extensions/spec.html#dependency">
592: * Installed extension dependency</a>
593: */
594: public static final Name EXTENSION_INSTALLATION = new Name(
595: "Extension-Installation");
596:
597: /**
598: * <code>Name</code> object for <code>Implementation-Title</code>
599: * manifest attribute used for package versioning.
600: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
601: * Java Product Versioning Specification</a>
602: */
603: public static final Name IMPLEMENTATION_TITLE = new Name(
604: "Implementation-Title");
605:
606: /**
607: * <code>Name</code> object for <code>Implementation-Version</code>
608: * manifest attribute used for package versioning.
609: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
610: * Java Product Versioning Specification</a>
611: */
612: public static final Name IMPLEMENTATION_VERSION = new Name(
613: "Implementation-Version");
614:
615: /**
616: * <code>Name</code> object for <code>Implementation-Vendor</code>
617: * manifest attribute used for package versioning.
618: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
619: * Java Product Versioning Specification</a>
620: */
621: public static final Name IMPLEMENTATION_VENDOR = new Name(
622: "Implementation-Vendor");
623:
624: /**
625: * <code>Name</code> object for <code>Implementation-Vendor-Id</code>
626: * manifest attribute used for package versioning.
627: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
628: * Java Product Versioning Specification</a>
629: */
630: public static final Name IMPLEMENTATION_VENDOR_ID = new Name(
631: "Implementation-Vendor-Id");
632:
633: /**
634: * <code>Name</code> object for <code>Implementation-Vendor-URL</code>
635: * manifest attribute used for package versioning.
636: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
637: * Java Product Versioning Specification</a>
638: */
639: public static final Name IMPLEMENTATION_URL = new Name(
640: "Implementation-URL");
641:
642: /**
643: * <code>Name</code> object for <code>Specification-Title</code>
644: * manifest attribute used for package versioning.
645: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
646: * Java Product Versioning Specification</a>
647: */
648: public static final Name SPECIFICATION_TITLE = new Name(
649: "Specification-Title");
650:
651: /**
652: * <code>Name</code> object for <code>Specification-Version</code>
653: * manifest attribute used for package versioning.
654: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
655: * Java Product Versioning Specification</a>
656: */
657: public static final Name SPECIFICATION_VERSION = new Name(
658: "Specification-Version");
659:
660: /**
661: * <code>Name</code> object for <code>Specification-Vendor</code>
662: * manifest attribute used for package versioning.
663: * @see <a href="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
664: * Java Product Versioning Specification</a>
665: */
666: public static final Name SPECIFICATION_VENDOR = new Name(
667: "Specification-Vendor");
668: }
669: }
|