KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > jar > Attributes


1 /*
2  * @(#)Attributes.java 1.50 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util.jar;
9
10 import java.io.DataInputStream JavaDoc;
11 import java.io.DataOutputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.Set JavaDoc;
16 import java.util.Collection JavaDoc;
17 import java.util.AbstractSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.logging.Logger JavaDoc;
20 import java.util.Comparator JavaDoc;
21 import sun.misc.ASCIICaseInsensitiveComparator;
22
23 /**
24  * The Attributes class maps Manifest attribute names to associated string
25  * values. Valid attribute names are case-insensitive, are restricted to
26  * the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed 70
27  * characters in length. Attribute values can contain any characters and
28  * will be UTF8-encoded when written to the output stream. See the
29  * <a HREF="../../../../guide/jar/jar.html">JAR File Specification</a>
30  * for more information about valid attribute names and values.
31  *
32  * @author David Connelly
33  * @version 1.50, 05/05/04
34  * @see Manifest
35  * @since 1.2
36  */

37 public class Attributes implements Map JavaDoc<Object JavaDoc,Object JavaDoc>, Cloneable JavaDoc {
38     /**
39      * The attribute name-value mappings.
40      */

41     protected Map JavaDoc<Object JavaDoc,Object JavaDoc> map;
42
43     /**
44      * Constructs a new, empty Attributes object with default size.
45      */

46     public Attributes() {
47     this(11);
48     }
49
50     /**
51      * Constructs a new, empty Attributes object with the specified
52      * initial size.
53      *
54      * @param size the initial number of attributes
55      */

56     public Attributes(int size) {
57     map = new HashMap JavaDoc(size);
58     }
59
60     /**
61      * Constructs a new Attributes object with the same attribute name-value
62      * mappings as in the specified Attributes.
63      *
64      * @param attr the specified Attributes
65      */

66     public Attributes(Attributes JavaDoc attr) {
67     map = new HashMap JavaDoc(attr);
68     }
69
70
71     /**
72      * Returns the value of the specified attribute name, or null if the
73      * attribute name was not found.
74      *
75      * @param name the attribute name
76      * @return the value of the specified attribute name, or null if
77      * not found.
78      */

79     public Object JavaDoc get(Object JavaDoc name) {
80     return map.get(name);
81     }
82
83     /**
84      * Returns the value of the specified attribute name, specified as
85      * a string, or null if the attribute was not found. The attribute
86      * name is case-insensitive.
87      * <p>
88      * This method is defined as:
89      * <pre>
90      * return (String)get(new Attributes.Name((String)name));
91      * </pre>
92      *
93      * @param name the attribute name as a string
94      * @return the String value of the specified attribute name, or null if
95      * not found.
96      * @throws IllegalArgumentException if the attribute name is invalid
97      */

98     public String JavaDoc getValue(String JavaDoc name) {
99         return (String JavaDoc)get(new Attributes.Name JavaDoc((String JavaDoc)name));
100     }
101
102     /**
103      * Returns the value of the specified Attributes.Name, or null if the
104      * attribute was not found.
105      * <p>
106      * This method is defined as:
107      * <pre>
108      * return (String)get(name);
109      * </pre>
110      *
111      * @param name the Attributes.Name object
112      * @return the String value of the specified Attribute.Name, or null if
113      * not found.
114      */

115     public String JavaDoc getValue(Name name) {
116     return (String JavaDoc)get(name);
117     }
118
119     /**
120      * Associates the specified value with the specified attribute name
121      * (key) in this Map. If the Map previously contained a mapping for
122      * the attribute name, the old value is replaced.
123      *
124      * @param name the attribute name
125      * @param value the attribute value
126      * @return the previous value of the attribute, or null if none
127      * @exception ClassCastException if the name is not a Attributes.Name
128      * or the value is not a String
129      */

130     public Object JavaDoc put(Object JavaDoc name, Object JavaDoc value) {
131         return map.put((Attributes.Name JavaDoc)name, (String JavaDoc)value);
132     }
133
134     /**
135      * Associates the specified value with the specified attribute name,
136      * specified as a String. The attributes name is case-insensitive.
137      * If the Map previously contained a mapping for the attribute name,
138      * the old value is replaced.
139      * <p>
140      * This method is defined as:
141      * <pre>
142      * return (String)put(new Attributes.Name(name), value);
143      * </pre>
144      *
145      * @param name the attribute name as a string
146      * @param value the attribute value
147      * @return the previous value of the attribute, or null if none
148      * @exception IllegalArgumentException if the attribute name is invalid
149      */

150     public String JavaDoc putValue(String JavaDoc name, String JavaDoc value) {
151     return (String JavaDoc)put(new Name(name), value);
152     }
153
154     /**
155      * Removes the attribute with the specified name (key) from this Map.
156      * Returns the previous attribute value, or null if none.
157      *
158      * @param name attribute name
159      * @return the previous value of the attribute, or null if none
160      */

161     public Object JavaDoc remove(Object JavaDoc name) {
162     return map.remove(name);
163     }
164
165     /**
166      * Returns true if this Map maps one or more attribute names (keys)
167      * to the specified value.
168      *
169      * @param value the attribute value
170      * @return true if this Map maps one or more attribute names to
171      * the specified value
172      */

173     public boolean containsValue(Object JavaDoc value) {
174     return map.containsValue(value);
175     }
176
177     /**
178      * Returns true if this Map contains the specified attribute name (key).
179      *
180      * @param name the attribute name
181      * @return true if this Map contains the specified attribute name
182      */

183     public boolean containsKey(Object JavaDoc name) {
184     return map.containsKey(name);
185     }
186
187     /**
188      * Copies all of the attribute name-value mappings from the specified
189      * Attributes to this Map. Duplicate mappings will be replaced.
190      *
191      * @param attr the Attributes to be stored in this map
192      * @exception ClassCastException if attr is not an Attributes
193      */

194     public void putAll(Map JavaDoc<?,?> attr) {
195     // ## javac bug?
196
if (!Attributes JavaDoc.class.isInstance(attr))
197         throw new ClassCastException JavaDoc();
198     for (Map.Entry JavaDoc<?,?> me : (attr).entrySet())
199         put(me.getKey(), me.getValue());
200     }
201
202     /**
203      * Removes all attributes from this Map.
204      */

205     public void clear() {
206     map.clear();
207     }
208
209     /**
210      * Returns the number of attributes in this Map.
211      */

212     public int size() {
213     return map.size();
214     }
215
216     /**
217      * Returns true if this Map contains no attributes.
218      */

219     public boolean isEmpty() {
220     return map.isEmpty();
221     }
222
223     /**
224      * Returns a Set view of the attribute names (keys) contained in this Map.
225      */

226     public Set JavaDoc<Object JavaDoc> keySet() {
227     return map.keySet();
228     }
229
230     /**
231      * Returns a Collection view of the attribute values contained in this Map.
232      */

233     public Collection JavaDoc<Object JavaDoc> values() {
234     return map.values();
235     }
236
237     /**
238      * Returns a Collection view of the attribute name-value mappings
239      * contained in this Map.
240      */

241     public Set JavaDoc<Map.Entry JavaDoc<Object JavaDoc,Object JavaDoc>> entrySet() {
242     return map.entrySet();
243     }
244
245     /**
246      * Compares the specified Attributes object with this Map for equality.
247      * Returns true if the given object is also an instance of Attributes
248      * and the two Attributes objects represent the same mappings.
249      *
250      * @param o the Object to be compared
251      * @return true if the specified Object is equal to this Map
252      */

253     public boolean equals(Object JavaDoc o) {
254     return map.equals(o);
255     }
256
257     /**
258      * Returns the hash code value for this Map.
259      */

260     public int hashCode() {
261     return map.hashCode();
262     }
263
264     /**
265      * Returns a copy of the Attributes, implemented as follows:
266      * <pre>
267      * public Object clone() { return new Attributes(this); }
268      * </pre>
269      * Since the attribute names and values are themselves immutable,
270      * the Attributes returned can be safely modified without affecting
271      * the original.
272      */

273     public Object JavaDoc clone() {
274     return new Attributes JavaDoc(this);
275     }
276
277     /*
278      * Writes the current attributes to the specified data output stream.
279      * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
280      */

281      void write(DataOutputStream JavaDoc os) throws IOException JavaDoc {
282     Iterator JavaDoc it = entrySet().iterator();
283     while (it.hasNext()) {
284         Map.Entry JavaDoc e = (Map.Entry JavaDoc)it.next();
285             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(
286                                         ((Name)e.getKey()).toString());
287         buffer.append(": ");
288
289             String JavaDoc value = (String JavaDoc)e.getValue();
290             if (value != null) {
291                 byte[] vb = value.getBytes("UTF8");
292                 value = new String JavaDoc(vb, 0, 0, vb.length);
293             }
294             buffer.append(value);
295
296         buffer.append("\r\n");
297             Manifest.make72Safe(buffer);
298             os.writeBytes(buffer.toString());
299     }
300     os.writeBytes("\r\n");
301     }
302
303     /*
304      * Writes the current attributes to the specified data output stream,
305      * make sure to write out the MANIFEST_VERSION or SIGNATURE_VERSION
306      * attributes first.
307      *
308      * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
309      */

310     void writeMain(DataOutputStream JavaDoc out) throws IOException JavaDoc
311     {
312     // write out the *-Version header first, if it exists
313
String JavaDoc vername = Name.MANIFEST_VERSION.toString();
314     String JavaDoc version = getValue(vername);
315     if (version == null) {
316         vername = Name.SIGNATURE_VERSION.toString();
317         version = getValue(vername);
318     }
319
320     if (version != null) {
321         out.writeBytes(vername+": "+version+"\r\n");
322     }
323
324     // write out all attributes except for the version
325
// we wrote out earlier
326
Iterator JavaDoc it = entrySet().iterator();
327     while (it.hasNext()) {
328         Map.Entry JavaDoc e = (Map.Entry JavaDoc)it.next();
329         String JavaDoc name = ((Name)e.getKey()).toString();
330         if ((version != null) && ! (name.equalsIgnoreCase(vername))) {
331
332                 StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(name);
333         buffer.append(": ");
334
335                 String JavaDoc value = (String JavaDoc)e.getValue();
336                 if (value != null) {
337                     byte[] vb = value.getBytes("UTF8");
338                     value = new String JavaDoc(vb, 0, 0, vb.length);
339                 }
340                 buffer.append(value);
341
342         buffer.append("\r\n");
343                 Manifest.make72Safe(buffer);
344                 out.writeBytes(buffer.toString());
345         }
346     }
347     out.writeBytes("\r\n");
348     }
349
350     /*
351      * Reads attributes from the specified input stream.
352      * XXX Need to handle UTF8 values.
353      */

354     void read(Manifest.FastInputStream JavaDoc is, byte[] lbuf) throws IOException JavaDoc {
355     String JavaDoc name = null, value = null;
356         byte[] lastline = null;
357
358     int len;
359     while ((len = is.readLine(lbuf)) != -1) {
360             boolean lineContinued = false;
361         if (lbuf[--len] != '\n') {
362         throw new IOException JavaDoc("line too long");
363         }
364         if (len > 0 && lbuf[len-1] == '\r') {
365         --len;
366         }
367         if (len == 0) {
368         break;
369         }
370         int i = 0;
371         if (lbuf[0] == ' ') {
372         // continuation of previous line
373
if (name == null) {
374             throw new IOException JavaDoc("misplaced continuation line");
375         }
376                 lineContinued = true;
377                 byte[] buf = new byte[lastline.length + len - 1];
378                 System.arraycopy(lastline, 0, buf, 0, lastline.length);
379                 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
380                 if (is.peek() == ' ') {
381                     lastline = buf;
382                     continue;
383                 }
384         value = new String JavaDoc(buf, 0, buf.length, "UTF8");
385                 lastline = null;
386         } else {
387                 while (lbuf[i++] != ':') {
388                     if (i >= len) {
389             throw new IOException JavaDoc("invalid header field");
390                     }
391                 }
392                 if (lbuf[i++] != ' ') {
393             throw new IOException JavaDoc("invalid header field");
394                 }
395                 name = new String JavaDoc(lbuf, 0, 0, i - 2);
396                 if (is.peek() == ' ') {
397                     lastline = new byte[len - i];
398                     System.arraycopy(lbuf, i, lastline, 0, len - i);
399                     continue;
400                 }
401                 value = new String JavaDoc(lbuf, i, len - i, "UTF8");
402             }
403         try {
404         if ((putValue(name, value) != null) && (!lineContinued)) {
405                     Logger.getLogger("java.util.jar").warning(
406                                      "Duplicate name in Manifest: " + name);
407                 }
408         } catch (IllegalArgumentException JavaDoc e) {
409         throw new IOException JavaDoc("invalid header field name: " + name);
410         }
411     }
412     }
413
414     /**
415      * The Attributes.Name class represents an attribute name stored in
416      * this Map. Valid attribute names are case-insensitive, are restricted
417      * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
418      * 70 characters in length. Attribute values can contain any characters
419      * and will be UTF8-encoded when written to the output stream. See the
420      * <a HREF="../../../../guide/jar/jar.html">JAR File Specification</a>
421      * for more information about valid attribute names and values.
422      */

423     public static class Name {
424     private String JavaDoc name;
425     private int hashCode = -1;
426
427     /**
428      * Constructs a new attribute name using the given string name.
429      *
430      * @param name the attribute string name
431      * @exception IllegalArgumentException if the attribute name was
432      * invalid
433      * @exception NullPointerException if the attribute name was null
434      */

435     public Name(String JavaDoc name) {
436         if (name == null) {
437         throw new NullPointerException JavaDoc("name");
438         }
439         if (!isValid(name)) {
440         throw new IllegalArgumentException JavaDoc(name);
441         }
442         this.name = name.intern();
443     }
444
445     private static boolean isValid(String JavaDoc name) {
446         int len = name.length();
447         if (len > 70 || len == 0) {
448         return false;
449         }
450         for (int i = 0; i < len; i++) {
451         if (!isValid(name.charAt(i))) {
452             return false;
453         }
454         }
455         return true;
456     }
457
458     private static boolean isValid(char c) {
459         return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
460     }
461
462     private static boolean isAlpha(char c) {
463         return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
464     }
465
466     private static boolean isDigit(char c) {
467         return c >= '0' && c <= '9';
468     }
469
470     /**
471      * Compares this attribute name to another for equality.
472      * @param o the object to compare
473          * @return true if this attribute name is equal to the
474          * specified attribute object
475      */

476     public boolean equals(Object JavaDoc o) {
477         if (o instanceof Name) {
478         Comparator JavaDoc c = ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER;
479         return c.compare(name, ((Name)o).name) == 0;
480         } else {
481         return false;
482         }
483     }
484       
485     /**
486      * Computes the hash value for this attribute name.
487      */

488         public int hashCode() {
489         if (hashCode == -1) {
490         hashCode = ASCIICaseInsensitiveComparator.lowerCaseHashCode(name);
491         }
492         return hashCode;
493     }
494
495     /**
496      * Returns the attribute name as a String.
497      */

498     public String JavaDoc toString() {
499         return name;
500     }
501
502         /**
503          * <code>Name</code> object for <code>Manifest-Version</code>
504          * manifest attribute. This attribute indicates the version number
505          * of the manifest standard to which a JAR file's manifest conforms.
506          * @see <a HREF="../../../../guide/jar/jar.html#JAR Manifest">
507          * Manifest and Signature Specification</a>
508          */

509         public static final Name MANIFEST_VERSION = new Name("Manifest-Version");
510
511         /**
512          * <code>Name</code> object for <code>Signature-Version</code>
513          * manifest attribute used when signing JAR files.
514          * @see <a HREF="../../../../guide/jar/jar.html#JAR Manifest">
515          * Manifest and Signature Specification</a>
516          */

517         public static final Name SIGNATURE_VERSION = new Name("Signature-Version");
518
519         /**
520          * <code>Name</code> object for <code>Content-Type</code>
521          * manifest attribute.
522          */

523         public static final Name CONTENT_TYPE = new Name("Content-Type");
524
525         /**
526          * <code>Name</code> object for <code>Class-Path</code>
527          * manifest attribute. Bundled extensions can use this attribute
528          * to find other JAR files containing needed classes.
529          * @see <a HREF="../../../../guide/extensions/spec.html#bundled">
530          * Extensions Specification</a>
531          */

532         public static final Name CLASS_PATH = new Name("Class-Path");
533
534         /**
535          * <code>Name</code> object for <code>Main-Class</code> manifest
536          * attribute used for launching applications packaged in JAR files.
537          * The <code>Main-Class</code> attribute is used in conjunction
538          * with the <code>-jar</code> command-line option of the
539          * <tt>java</tt> application launcher.
540          */

541         public static final Name MAIN_CLASS = new Name("Main-Class");
542
543         /**
544          * <code>Name</code> object for <code>Sealed</code> manifest attribute
545          * used for sealing.
546          * @see <a HREF="../../../../guide/extensions/spec.html#sealing">
547          * Extension Sealing</a>
548          */

549         public static final Name SEALED = new Name("Sealed");
550
551        /**
552          * <code>Name</code> object for <code>Extension-List</code> manifest attribute
553          * used for declaring dependencies on installed extensions.
554          * @see <a HREF="../../../../guide/extensions/spec.html#dependency">
555          * Installed extension dependency</a>
556          */

557         public static final Name EXTENSION_LIST = new Name("Extension-List");
558
559         /**
560          * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
561          * used for declaring dependencies on installed extensions.
562          * @see <a HREF="../../../../guide/extensions/spec.html#dependency">
563          * Installed extension dependency</a>
564          */

565         public static final Name EXTENSION_NAME = new Name("Extension-Name");
566
567         /**
568          * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
569          * used for declaring dependencies on installed extensions.
570          * @see <a HREF="../../../../guide/extensions/spec.html#dependency">
571          * Installed extension dependency</a>
572          */

573         public static final Name EXTENSION_INSTALLATION = new Name("Extension-Installation");
574
575         /**
576          * <code>Name</code> object for <code>Implementation-Title</code>
577          * manifest attribute used for package versioning.
578          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
579          * Java Product Versioning Specification</a>
580          */

581         public static final Name IMPLEMENTATION_TITLE = new Name("Implementation-Title");
582
583         /**
584          * <code>Name</code> object for <code>Implementation-Version</code>
585          * manifest attribute used for package versioning.
586          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
587          * Java Product Versioning Specification</a>
588          */

589         public static final Name IMPLEMENTATION_VERSION = new Name("Implementation-Version");
590
591         /**
592          * <code>Name</code> object for <code>Implementation-Vendor</code>
593          * manifest attribute used for package versioning.
594          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
595          * Java Product Versioning Specification</a>
596          */

597         public static final Name IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor");
598
599     /**
600          * <code>Name</code> object for <code>Implementation-Vendor-Id</code>
601          * manifest attribute used for package versioning.
602          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
603          * Java Product Versioning Specification</a>
604          */

605         public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
606
607        /**
608          * <code>Name</code> object for <code>Implementation-Vendor-URL</code>
609          * manifest attribute used for package versioning.
610          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
611          * Java Product Versioning Specification</a>
612          */

613         public static final Name IMPLEMENTATION_URL = new Name("Implementation-URL");
614
615         /**
616          * <code>Name</code> object for <code>Specification-Title</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 SPECIFICATION_TITLE = new Name("Specification-Title");
622
623         /**
624          * <code>Name</code> object for <code>Specification-Version</code>
625          * manifest attribute used for package versioning.
626          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
627          * Java Product Versioning Specification</a>
628          */

629         public static final Name SPECIFICATION_VERSION = new Name("Specification-Version");
630
631         /**
632          * <code>Name</code> object for <code>Specification-Vendor</code>
633          * manifest attribute used for package versioning.
634          * @see <a HREF="../../../../guide/versioning/spec/VersioningSpecification.html#PackageVersioning">
635          * Java Product Versioning Specification</a>
636          */

637         public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor");
638     }
639 }
640
Popular Tags