1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51:
52:
59: public class Manifest implements Cloneable
60: {
61:
62:
63:
64: private final Attributes mainAttr;
65:
66:
67: private final Map entries;
68:
69:
70:
71:
74: public Manifest()
75: {
76: mainAttr = new Attributes();
77: entries = new Hashtable();
78: }
79:
80:
90: public Manifest(InputStream in) throws IOException
91: {
92: this();
93: read(in);
94: }
95:
96:
108: public Manifest(Manifest man)
109: {
110: mainAttr = new Attributes(man.getMainAttributes());
111: entries = new Hashtable(man.getEntries());
112: }
113:
114:
115:
116:
119: public Attributes getMainAttributes()
120: {
121: return mainAttr;
122: }
123:
124:
129: public Map getEntries()
130: {
131: return entries;
132: }
133:
134:
143: public Attributes getAttributes(String entryName)
144: {
145: return (Attributes) getEntries().get(entryName);
146: }
147:
148:
152: public void clear()
153: {
154: mainAttr.clear();
155: entries.clear();
156: }
157:
158:
161: public void read(InputStream in) throws IOException
162: {
163: BufferedReader br =
164: new BufferedReader(new InputStreamReader(in, "8859_1"));
165: read_main_section(getMainAttributes(), br);
166: read_individual_sections(getEntries(), br);
167: }
168:
169:
170:
171: private static void read_main_section(Attributes attr,
172: BufferedReader br) throws IOException
173: {
174:
175: read_attributes(attr, br);
176:
177:
178: if (attr.getValue(Attributes.Name.MANIFEST_VERSION) == null)
179: attr.putValue(Attributes.Name.MANIFEST_VERSION, "0.0");
180: }
181:
182:
187: private static void read_version_info(Attributes attr,
188: BufferedReader br) throws IOException
189: {
190: String version_header = Attributes.Name.MANIFEST_VERSION.toString();
191: try
192: {
193: String value = expect_header(version_header, br);
194: attr.putValue(Attributes.Name.MANIFEST_VERSION, value);
195: }
196: catch (IOException ioe)
197: {
198: throw new JarException("Manifest should start with a " +
199: version_header + ": " + ioe.getMessage());
200: }
201: }
202:
203: private static String expect_header(String header, BufferedReader br)
204: throws IOException
205: {
206: String s = br.readLine();
207: if (s == null)
208: {
209: throw new JarException("unexpected end of file");
210: }
211: return expect_header(header, br, s);
212: }
213:
214: private static String expect_header(String header, BufferedReader br,
215: String s) throws IOException
216: {
217: try
218: {
219: String name = s.substring(0, header.length() + 1);
220: if (name.equalsIgnoreCase(header + ":"))
221: {
222: String value_start = s.substring(header.length() + 2);
223: return read_header_value(value_start, br);
224: }
225: }
226: catch (IndexOutOfBoundsException iobe)
227: {
228: }
229:
230: throw new JarException("unexpected '" + s + "'");
231: }
232:
233: private static String read_header_value(String s, BufferedReader br)
234: throws IOException
235: {
236: boolean try_next = true;
237: while (try_next)
238: {
239:
240: br.mark(1);
241: if (br.read() == ' ')
242: {
243: s += br.readLine();
244: }
245: else
246: {
247: br.reset();
248: try_next = false;
249: }
250: }
251: return s;
252: }
253:
254: private static void read_attributes(Attributes attr,
255: BufferedReader br) throws IOException
256: {
257: String s = br.readLine();
258: while (s != null && (!s.equals("")))
259: {
260: read_attribute(attr, s, br);
261: s = br.readLine();
262: }
263: }
264:
265: private static void read_attribute(Attributes attr, String s,
266: BufferedReader br) throws IOException
267: {
268: try
269: {
270: int colon = s.indexOf(": ");
271: String name = s.substring(0, colon);
272: String value_start = s.substring(colon + 2);
273: String value = read_header_value(value_start, br);
274: attr.putValue(name, value);
275: }
276: catch (IndexOutOfBoundsException iobe)
277: {
278: throw new JarException("Manifest contains a bad header: " + s);
279: }
280: }
281:
282: private static void read_individual_sections(Map entries,
283: BufferedReader br) throws
284: IOException
285: {
286: String s = br.readLine();
287: while (s != null && (!s.equals("")))
288: {
289: Attributes attr = read_section_name(s, br, entries);
290: read_attributes(attr, br);
291: s = br.readLine();
292: }
293: }
294:
295: private static Attributes read_section_name(String s, BufferedReader br,
296: Map entries) throws JarException
297: {
298: try
299: {
300: String name = expect_header("Name", br, s);
301: Attributes attr = new Attributes();
302: entries.put(name, attr);
303: return attr;
304: }
305: catch (IOException ioe)
306: {
307: throw new JarException
308: ("Section should start with a Name header: " + ioe.getMessage());
309: }
310: }
311:
312:
315: public void write(OutputStream out) throws IOException
316: {
317: PrintWriter pw =
318: new PrintWriter(new
319: BufferedWriter(new OutputStreamWriter(out, "8859_1")));
320: write_main_section(getMainAttributes(), pw);
321: pw.println();
322: write_individual_sections(getEntries(), pw);
323: if (pw.checkError())
324: {
325: throw new JarException("Error while writing manifest");
326: }
327: }
328:
329:
330:
331: private static void write_main_section(Attributes attr,
332: PrintWriter pw) throws JarException
333: {
334: write_version_info(attr, pw);
335: write_main_attributes(attr, pw);
336: }
337:
338: private static void write_version_info(Attributes attr, PrintWriter pw)
339: {
340:
341: String version = attr.getValue(Attributes.Name.MANIFEST_VERSION);
342: if (version == null)
343: {
344: version = "1.0";
345: }
346: write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw);
347: }
348:
349: private static void write_header(String name, String value, PrintWriter pw)
350: {
351: pw.print(name + ": ");
352:
353: int last = 68 - name.length();
354: if (last > value.length())
355: {
356: pw.println(value);
357: }
358: else
359: {
360: pw.println(value.substring(0, last));
361: }
362: while (last < value.length())
363: {
364: pw.print(" ");
365: int end = (last + 69);
366: if (end > value.length())
367: {
368: pw.println(value.substring(last));
369: }
370: else
371: {
372: pw.println(value.substring(last, end));
373: }
374: last = end;
375: }
376: }
377:
378: private static void write_main_attributes(Attributes attr, PrintWriter pw)
379: throws JarException
380: {
381: Iterator it = attr.entrySet().iterator();
382: while (it.hasNext())
383: {
384: Map.Entry entry = (Map.Entry) it.next();
385:
386: if (!Attributes.Name.MANIFEST_VERSION.equals(entry.getKey()))
387: {
388: write_attribute_entry(entry, pw);
389: }
390: }
391: }
392:
393: private static void write_attribute_entry(Map.Entry entry, PrintWriter pw)
394: throws JarException
395: {
396: String name = entry.getKey().toString();
397: String value = entry.getValue().toString();
398:
399: if (name.equalsIgnoreCase("Name"))
400: {
401: throw new JarException("Attributes cannot be called 'Name'");
402: }
403: if (name.startsWith("From"))
404: {
405: throw new
406: JarException("Header cannot start with the four letters 'From'" +
407: name);
408: }
409: write_header(name, value, pw);
410: }
411:
412: private static void write_individual_sections(Map entries, PrintWriter pw)
413: throws JarException
414: {
415:
416: Iterator it = entries.entrySet().iterator();
417: while (it.hasNext())
418: {
419: Map.Entry entry = (Map.Entry) it.next();
420: write_header("Name", entry.getKey().toString(), pw);
421: write_entry_attributes((Attributes) entry.getValue(), pw);
422: pw.println();
423: }
424: }
425:
426: private static void write_entry_attributes(Attributes attr, PrintWriter pw)
427: throws JarException
428: {
429: Iterator it = attr.entrySet().iterator();
430: while (it.hasNext())
431: {
432: Map.Entry entry = (Map.Entry) it.next();
433: write_attribute_entry(entry, pw);
434: }
435: }
436:
437:
445: public Object clone()
446: {
447: return new Manifest(this);
448: }
449:
450:
456: public boolean equals(Object o)
457: {
458: return (o instanceof Manifest) &&
459: (mainAttr.equals(((Manifest) o).mainAttr)) &&
460: (entries.equals(((Manifest) o).entries));
461: }
462:
463:
467: public int hashCode()
468: {
469: return mainAttr.hashCode() ^ entries.hashCode();
470: }
471:
472: }