KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * @(#)Manifest.java 1.45 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.FilterInputStream JavaDoc;
11 import java.io.DataOutputStream JavaDoc;
12 import java.io.InputStream JavaDoc;
13 import java.io.OutputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.util.Map JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.Iterator JavaDoc;
18
19 /**
20  * The Manifest class is used to maintain Manifest entry names and their
21  * associated Attributes. There are main Manifest Attributes as well as
22  * per-entry Attributes. For information on the Manifest format, please
23  * see the
24  * <a HREF="../../../../guide/jar/jar.html">
25  * Manifest format specification</a>.
26  *
27  * @author David Connelly
28  * @version 1.45, 05/05/04
29  * @see Attributes
30  * @since 1.2
31  */

32 public class Manifest implements Cloneable JavaDoc {
33     // manifest main attributes
34
private Attributes JavaDoc attr = new Attributes JavaDoc();
35
36     // manifest entries
37
private Map JavaDoc entries = new HashMap JavaDoc();
38
39     /**
40      * Constructs a new, empty Manifest.
41      */

42     public Manifest() {
43     }
44
45     /**
46      * Constructs a new Manifest from the specified input stream.
47      *
48      * @param is the input stream containing manifest data
49      * @throws IOException if an I/O error has occured
50      */

51     public Manifest(InputStream JavaDoc is) throws IOException JavaDoc {
52     read(is);
53     }
54
55     /**
56      * Constructs a new Manifest that is a copy of the specified Manifest.
57      *
58      * @param man the Manifest to copy
59      */

60     public Manifest(Manifest JavaDoc man) {
61     attr.putAll(man.getMainAttributes());
62     entries.putAll(man.getEntries());
63     }
64
65     /**
66      * Returns the main Attributes for the Manifest.
67      * @return the main Attributes for the Manifest
68      */

69     public Attributes JavaDoc getMainAttributes() {
70     return attr;
71     }
72
73     /**
74      * Returns a Map of the entries contained in this Manifest. Each entry
75      * is represented by a String name (key) and associated Attributes (value).
76      *
77      * @return a Map of the entries contained in this Manifest
78      */

79     public Map JavaDoc<String JavaDoc,Attributes JavaDoc> getEntries() {
80     return entries;
81     }
82
83     /**
84      * Returns the Attributes for the specified entry name.
85      * This method is defined as:
86      * <pre>
87      * return (Attributes)getEntries().get(name)
88      * </pre>
89      *
90      * @param name entry name
91      * @return the Attributes for the specified entry name
92      */

93     public Attributes JavaDoc getAttributes(String JavaDoc name) {
94     return (Attributes JavaDoc)getEntries().get(name);
95     }
96
97     /**
98      * Clears the main Attributes as well as the entries in this Manifest.
99      */

100     public void clear() {
101     attr.clear();
102     entries.clear();
103     }
104
105     /**
106      * Writes the Manifest to the specified OutputStream.
107      * Attributes.Name.MANIFEST_VERSION must be set in
108      * MainAttributes prior to invoking this method.
109      *
110      * @param out the output stream
111      * @exception IOException if an I/O error has occurred
112      * @see #getMainAttributes
113      */

114     public void write(OutputStream JavaDoc out) throws IOException JavaDoc {
115     DataOutputStream JavaDoc dos = new DataOutputStream JavaDoc(out);
116     // Write out the main attributes for the manifest
117
attr.writeMain(dos);
118     // Now write out the pre-entry attributes
119
Iterator JavaDoc it = entries.entrySet().iterator();
120     while (it.hasNext()) {
121         Map.Entry JavaDoc e = (Map.Entry JavaDoc)it.next();
122             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("Name: ");
123             String JavaDoc value = (String JavaDoc)e.getKey();
124             if (value != null) {
125                 byte[] vb = value.getBytes("UTF8");
126                 value = new String JavaDoc(vb, 0, 0, vb.length);
127             }
128         buffer.append(value);
129         buffer.append("\r\n");
130             make72Safe(buffer);
131             dos.writeBytes(buffer.toString());
132         ((Attributes JavaDoc)e.getValue()).write(dos);
133     }
134     dos.flush();
135     }
136
137     /**
138      * Adds line breaks to enforce a maximum 72 bytes per line.
139      */

140     static void make72Safe(StringBuffer JavaDoc line) {
141         int length = line.length();
142         if (length > 72) {
143             int index = 70;
144             while (index < length - 2) {
145                 line.insert(index, "\r\n ");
146                 index += 72;
147                 length += 3;
148             }
149         }
150         return;
151     }
152
153     /**
154      * Reads the Manifest from the specified InputStream. The entry
155      * names and attributes read will be merged in with the current
156      * manifest entries.
157      *
158      * @param is the input stream
159      * @exception IOException if an I/O error has occurred
160      */

161     public void read(InputStream JavaDoc is) throws IOException JavaDoc {
162     // Buffered input stream for reading manifest data
163
FastInputStream fis = new FastInputStream(is);
164     // Line buffer
165
byte[] lbuf = new byte[512];
166     // Read the main attributes for the manifest
167
attr.read(fis, lbuf);
168     // Total number of entries, attributes read
169
int ecount = 0, acount = 0;
170     // Average size of entry attributes
171
int asize = 2;
172     // Now parse the manifest entries
173
int len;
174     String JavaDoc name = null;
175         boolean skipEmptyLines = true;
176         byte[] lastline = null;
177
178     while ((len = fis.readLine(lbuf)) != -1) {
179         if (lbuf[--len] != '\n') {
180         throw new IOException JavaDoc("manifest line too long");
181         }
182         if (len > 0 && lbuf[len-1] == '\r') {
183         --len;
184         }
185             if (len == 0 && skipEmptyLines) {
186                 continue;
187             }
188             skipEmptyLines = false;
189
190         if (name == null) {
191         name = parseName(lbuf, len);
192         if (name == null) {
193             throw new IOException JavaDoc("invalid manifest format");
194         }
195                 if (fis.peek() == ' ') {
196             // name is wrapped
197
lastline = new byte[len - 6];
198                     System.arraycopy(lbuf, 6, lastline, 0, len - 6);
199                     continue;
200                 }
201         } else {
202         // continuation line
203
byte[] buf = new byte[lastline.length + len - 1];
204                 System.arraycopy(lastline, 0, buf, 0, lastline.length);
205                 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
206                 if (fis.peek() == ' ') {
207             // name is wrapped
208
lastline = buf;
209             continue;
210             }
211         name = new String JavaDoc(buf, 0, buf.length, "UTF8");
212                 lastline = null;
213         }
214         Attributes JavaDoc attr = getAttributes(name);
215         if (attr == null) {
216         attr = new Attributes JavaDoc(asize);
217         entries.put(name, attr);
218         }
219         attr.read(fis, lbuf);
220         ecount++;
221         acount += attr.size();
222         //XXX: Fix for when the average is 0. When it is 0,
223
// you get an Attributes object with an initial
224
// capacity of 0, which tickles a bug in HashMap.
225
asize = Math.max(2, acount / ecount);
226
227         name = null;
228             skipEmptyLines = true;
229     }
230     }
231
232     private String JavaDoc parseName(byte[] lbuf, int len) {
233     if (toLower(lbuf[0]) == 'n' && toLower(lbuf[1]) == 'a' &&
234         toLower(lbuf[2]) == 'm' && toLower(lbuf[3]) == 'e' &&
235         lbuf[4] == ':' && lbuf[5] == ' ') {
236             try {
237             return new String JavaDoc(lbuf, 6, len - 6, "UTF8");
238             }
239             catch (Exception JavaDoc e) {
240             }
241     }
242     return null;
243     }
244
245     private int toLower(int c) {
246     return (c >= 'A' && c <= 'Z') ? 'a' + (c - 'A') : c;
247     }
248
249     /**
250      * Returns true if the specified Object is also a Manifest and has
251      * the same main Attributes and entries.
252      *
253      * @param o the object to be compared
254      * @return true if the specified Object is also a Manifest and has
255      * the same main Attributes and entries
256      */

257     public boolean equals(Object JavaDoc o) {
258     if (o instanceof Manifest JavaDoc) {
259         Manifest JavaDoc m = (Manifest JavaDoc)o;
260         return attr.equals(m.getMainAttributes()) &&
261            entries.equals(m.getEntries());
262     } else {
263         return false;
264     }
265     }
266
267     /**
268      * Returns the hash code for this Manifest.
269      */

270     public int hashCode() {
271     return attr.hashCode() + entries.hashCode();
272     }
273
274     /**
275      * Returns a shallow copy of this Manifest. The shallow copy is
276      * implemented as follows:
277      * <pre>
278      * public Object clone() { return new Manifest(this); }
279      * </pre>
280      * @return a shallow copy of this Manifest
281      */

282     public Object JavaDoc clone() {
283     return new Manifest JavaDoc(this);
284     }
285
286     /*
287      * A fast buffered input stream for parsing manifest files.
288      */

289     static class FastInputStream extends FilterInputStream JavaDoc {
290     private byte buf[];
291     private int count = 0;
292     private int pos = 0;
293
294     FastInputStream(InputStream JavaDoc in) {
295         this(in, 8192);
296     }
297
298     FastInputStream(InputStream JavaDoc in, int size) {
299         super(in);
300         buf = new byte[size];
301     }
302
303     public int read() throws IOException JavaDoc {
304         if (pos >= count) {
305         fill();
306         if (pos >= count) {
307             return -1;
308         }
309         }
310         return buf[pos++] & 0xff;
311     }
312
313     public int read(byte[] b, int off, int len) throws IOException JavaDoc {
314         int avail = count - pos;
315         if (avail <= 0) {
316         if (len >= buf.length) {
317             return in.read(b, off, len);
318         }
319         fill();
320         avail = count - pos;
321         if (avail <= 0) {
322             return -1;
323         }
324         }
325         if (len > avail) {
326         len = avail;
327         }
328         System.arraycopy(buf, pos, b, off, len);
329         pos += len;
330         return len;
331     }
332
333     /*
334      * Reads 'len' bytes from the input stream, or until an end-of-line
335      * is reached. Returns the number of bytes read.
336      */

337     public int readLine(byte[] b, int off, int len) throws IOException JavaDoc {
338         byte[] tbuf = this.buf;
339         int total = 0;
340         while (total < len) {
341         int avail = count - pos;
342         if (avail <= 0) {
343             fill();
344             avail = count - pos;
345             if (avail <= 0) {
346             return -1;
347             }
348         }
349         int n = len - total;
350         if (n > avail) {
351             n = avail;
352         }
353         int tpos = pos;
354         int maxpos = tpos + n;
355         while (tpos < maxpos && tbuf[tpos++] != '\n') ;
356         n = tpos - pos;
357         System.arraycopy(tbuf, pos, b, off, n);
358         off += n;
359         total += n;
360         pos = tpos;
361         if (tbuf[tpos-1] == '\n') {
362             break;
363         }
364         }
365         return total;
366     }
367
368     public byte peek() throws IOException JavaDoc {
369         if (pos == count)
370         fill();
371         return buf[pos];
372     }
373
374     public int readLine(byte[] b) throws IOException JavaDoc {
375         return readLine(b, 0, b.length);
376     }
377
378     public long skip(long n) throws IOException JavaDoc {
379         if (n <= 0) {
380         return 0;
381         }
382         long avail = count - pos;
383         if (avail <= 0) {
384         return in.skip(n);
385         }
386         if (n > avail) {
387         n = avail;
388         }
389         pos += n;
390         return n;
391     }
392
393     public int available() throws IOException JavaDoc {
394         return (count - pos) + in.available();
395     }
396
397     public void close() throws IOException JavaDoc {
398         if (in != null) {
399         in.close();
400         in = null;
401         buf = null;
402         }
403     }
404
405     private void fill() throws IOException JavaDoc {
406         count = pos = 0;
407         int n = in.read(buf, 0, buf.length);
408         if (n > 0) {
409         count = n;
410         }
411     }
412     }
413 }
414
Popular Tags