source: trunk/gcc/libjava/java/util/jar/Manifest.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 12.8 KB
Line 
1/* Attributes.java -- Reads, writes and manipulaties jar manifest files
2 Copyright (C) 2000 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38package java.util.jar;
39
40import java.io.BufferedReader;
41import java.io.BufferedWriter;
42import java.io.IOException;
43import java.io.InputStream;
44import java.io.InputStreamReader;
45import java.io.OutputStream;
46import java.io.OutputStreamWriter;
47import java.io.PrintWriter;
48import java.io.Reader;
49import java.io.Writer;
50import java.util.Hashtable;
51import java.util.Iterator;
52import java.util.Map;
53import java.util.Set;
54
55/**
56 * Reads, writes and manipulaties jar manifest files.
57 * XXX
58 *
59 * @since 1.2
60 * @author Mark Wielaard (mark@klomp.org)
61 */
62public class Manifest implements Cloneable
63{
64 // Fields
65
66 /** The main attributes of the manifest (jar file). */
67 private final Attributes mainAttr;
68
69 /** A map of atrributes for all entries described in this Manifest. */
70 private final Map entries;
71
72 // Constructors
73
74 /**
75 * Creates a new empty Manifest.
76 */
77 public Manifest()
78 {
79 mainAttr = new Attributes();
80 entries = new Hashtable();
81 }
82
83 /**
84 * Creates a Manifest from the supplied input stream.
85 *
86 * @see read(Inputstream)
87 * @see write(OutputStream)
88 *
89 * @param InputStream the input stream to read the manifest from
90 * @exception IOException when an i/o exception occurs or the input stream
91 * does not describe a valid manifest
92 */
93 public Manifest(InputStream in) throws IOException
94 {
95 this();
96 read(in);
97 }
98
99 /**
100 * Creates a Manifest from another Manifest.
101 * Makes a deep copy of the main attributes, but a shallow copy of
102 * the other entries. This means that you can freely add, change or remove
103 * the main attributes or the entries of the new manifest without effecting
104 * the original manifest, but adding, changing or removing attributes from
105 * a particular entry also changes the attributes of that entry in the
106 * original manifest.
107 *
108 * @see clone()
109 * @param man the Manifest to copy from
110 */
111 public Manifest(Manifest man)
112 {
113 mainAttr = new Attributes(man.getMainAttributes());
114 entries = new Hashtable(man.getEntries());
115 }
116
117 // Methods
118
119 /**
120 * Gets the main attributes of this Manifest.
121 */
122 public Attributes getMainAttributes()
123 {
124 return mainAttr;
125 }
126
127 /**
128 * Gets a map of entry Strings to Attributes for all the entries described
129 * in this manifest. Adding, changing or removing from this entries map
130 * changes the entries of this manifest.
131 */
132 public Map getEntries()
133 {
134 return entries;
135 }
136
137 /**
138 * Returns the Attributes associated with the Entry.
139 * <p>
140 * Implemented as:
141 * <code>return (Attributes)getEntries().get(entryName)</code>
142 *
143 * @param entryName the name of the entry to look up
144 * @return the attributes associated with the entry or null when none
145 */
146 public Attributes getAttributes(String entryName)
147 {
148 return (Attributes) getEntries().get(entryName);
149 }
150
151 /**
152 * Clears the main attributes and removes all the entries from the
153 * manifest.
154 */
155 public void clear()
156 {
157 mainAttr.clear();
158 entries.clear();
159 }
160
161 /**
162 * XXX
163 */
164 public void read(InputStream in) throws IOException
165 {
166 BufferedReader br =
167 new BufferedReader(new InputStreamReader(in, "8859_1"));
168 read_main_section(getMainAttributes(), br);
169 read_individual_sections(getEntries(), br);
170 }
171
172 // Private Static methods for reading the Manifest file from BufferedReader
173
174 private static void read_main_section(Attributes attr,
175 BufferedReader br) throws IOException
176 {
177 // According to the spec we should actually call read_version_info() here.
178 read_attributes(attr, br);
179 // Explicitly set Manifest-Version attribute if not set in Main
180 // attributes of Manifest.
181 if (attr.getValue(Attributes.Name.MANIFEST_VERSION) == null)
182 attr.putValue(Attributes.Name.MANIFEST_VERSION, "0.0");
183 }
184
185 /**
186 * Pedantic method that requires the next attribute in the Manifest to be
187 * the "Manifest-Version". This follows the Manifest spec closely but
188 * reject some jar Manifest files out in the wild.
189 */
190 private static void read_version_info(Attributes attr,
191 BufferedReader br) throws IOException
192 {
193 String version_header = Attributes.Name.MANIFEST_VERSION.toString();
194 try
195 {
196 String value = expect_header(version_header, br);
197 attr.putValue(Attributes.Name.MANIFEST_VERSION, value);
198 }
199 catch (IOException ioe)
200 {
201 throw new JarException("Manifest should start with a " +
202 version_header + ": " + ioe.getMessage());
203 }
204 }
205
206 private static String expect_header(String header, BufferedReader br)
207 throws IOException
208 {
209 String s = br.readLine();
210 if (s == null)
211 {
212 throw new JarException("unexpected end of file");
213 }
214 return expect_header(header, br, s);
215 }
216
217 private static String expect_header(String header, BufferedReader br,
218 String s) throws IOException
219 {
220 try
221 {
222 String name = s.substring(0, header.length() + 1);
223 if (name.equalsIgnoreCase(header + ":"))
224 {
225 String value_start = s.substring(header.length() + 2);
226 return read_header_value(value_start, br);
227 }
228 }
229 catch (IndexOutOfBoundsException iobe)
230 {
231 }
232 // If we arrive here, something went wrong
233 throw new JarException("unexpected '" + s + "'");
234 }
235
236 private static String read_header_value(String s, BufferedReader br)
237 throws IOException
238 {
239 boolean try_next = true;
240 while (try_next)
241 {
242 // Lets see if there is something on the next line
243 br.mark(1);
244 if (br.read() == ' ')
245 {
246 s += br.readLine();
247 }
248 else
249 {
250 br.reset();
251 try_next = false;
252 }
253 }
254 return s;
255 }
256
257 private static void read_attributes(Attributes attr,
258 BufferedReader br) throws IOException
259 {
260 String s = br.readLine();
261 while (s != null && (!s.equals("")))
262 {
263 read_attribute(attr, s, br);
264 s = br.readLine();
265 }
266 }
267
268 private static void read_attribute(Attributes attr, String s,
269 BufferedReader br) throws IOException
270 {
271 try
272 {
273 int colon = s.indexOf(": ");
274 String name = s.substring(0, colon);
275 String value_start = s.substring(colon + 2);
276 String value = read_header_value(value_start, br);
277 attr.putValue(name, value);
278 }
279 catch (IndexOutOfBoundsException iobe)
280 {
281 throw new JarException("Manifest contains a bad header: " + s);
282 }
283 }
284
285 private static void read_individual_sections(Map entries,
286 BufferedReader br) throws
287 IOException
288 {
289 String s = br.readLine();
290 while (s != null && (!s.equals("")))
291 {
292 Attributes attr = read_section_name(s, br, entries);
293 read_attributes(attr, br);
294 s = br.readLine();
295 }
296 }
297
298 private static Attributes read_section_name(String s, BufferedReader br,
299 Map entries) throws JarException
300 {
301 try
302 {
303 String name = expect_header("Name", br, s);
304 Attributes attr = new Attributes();
305 entries.put(name, attr);
306 return attr;
307 }
308 catch (IOException ioe)
309 {
310 throw new JarException
311 ("Section should start with a Name header: " + ioe.getMessage());
312 }
313 }
314
315 /**
316 * XXX
317 */
318 public void write(OutputStream out) throws IOException
319 {
320 PrintWriter pw =
321 new PrintWriter(new
322 BufferedWriter(new OutputStreamWriter(out, "8859_1")));
323 write_main_section(getMainAttributes(), pw);
324 pw.println();
325 write_individual_sections(getEntries(), pw);
326 if (pw.checkError())
327 {
328 throw new JarException("Error while writing manifest");
329 }
330 }
331
332 // Private Static functions for writing the Manifest file to a PrintWriter
333
334 private static void write_main_section(Attributes attr,
335 PrintWriter pw) throws JarException
336 {
337 write_version_info(attr, pw);
338 write_main_attributes(attr, pw);
339 }
340
341 private static void write_version_info(Attributes attr, PrintWriter pw)
342 {
343 // First check if there is already a version attribute set
344 String version = attr.getValue(Attributes.Name.MANIFEST_VERSION);
345 if (version == null)
346 {
347 version = "1.0";
348 }
349 write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw);
350 }
351
352 private static void write_header(String name, String value, PrintWriter pw)
353 {
354 pw.print(name + ": ");
355
356 int last = 68 - name.length();
357 if (last > value.length())
358 {
359 pw.println(value);
360 }
361 else
362 {
363 pw.println(value.substring(0, last));
364 }
365 while (last < value.length())
366 {
367 pw.print(" ");
368 int end = (last + 69);
369 if (end > value.length())
370 {
371 pw.println(value.substring(last));
372 }
373 else
374 {
375 pw.println(value.substring(last, end));
376 }
377 last = end;
378 }
379 }
380
381 private static void write_main_attributes(Attributes attr, PrintWriter pw)
382 throws JarException
383 {
384 Iterator it = attr.entrySet().iterator();
385 while (it.hasNext())
386 {
387 Map.Entry entry = (Map.Entry) it.next();
388 // Don't print the manifest version again
389 if (!Attributes.Name.MANIFEST_VERSION.equals(entry.getKey()))
390 {
391 write_attribute_entry(entry, pw);
392 }
393 }
394 }
395
396 private static void write_attribute_entry(Map.Entry entry, PrintWriter pw)
397 throws JarException
398 {
399 String name = entry.getKey().toString();
400 String value = entry.getValue().toString();
401
402 if (name.equalsIgnoreCase("Name"))
403 {
404 throw new JarException("Attributes cannot be called 'Name'");
405 }
406 if (name.startsWith("From"))
407 {
408 throw new
409 JarException("Header cannot start with the four letters 'From'" +
410 name);
411 }
412 write_header(name, value, pw);
413 }
414
415 private static void write_individual_sections(Map entries, PrintWriter pw)
416 throws JarException
417 {
418
419 Iterator it = entries.entrySet().iterator();
420 while (it.hasNext())
421 {
422 Map.Entry entry = (Map.Entry) it.next();
423 write_header("Name", entry.getKey().toString(), pw);
424 write_entry_attributes((Attributes) entry.getValue(), pw);
425 pw.println();
426 }
427 }
428
429 private static void write_entry_attributes(Attributes attr, PrintWriter pw)
430 throws JarException
431 {
432 Iterator it = attr.entrySet().iterator();
433 while (it.hasNext())
434 {
435 Map.Entry entry = (Map.Entry) it.next();
436 write_attribute_entry(entry, pw);
437 }
438 }
439
440 /**
441 * Makes a deep copy of the main attributes, but a shallow copy of
442 * the other entries. This means that you can freely add, change or remove
443 * the main attributes or the entries of the new manifest without effecting
444 * the original manifest, but adding, changing or removing attributes from
445 * a particular entry also changes the attributes of that entry in the
446 * original manifest. Calls <CODE>new Manifest(this)</CODE>.
447 */
448 public Object clone()
449 {
450 return new Manifest(this);
451 }
452
453 /**
454 * Checks if another object is equal to this Manifest object.
455 * Another Object is equal to this Manifest object if it is an instance of
456 * Manifest and the main attributes and the entries of the other manifest
457 * are equal to this one.
458 */
459 public boolean equals(Object o)
460 {
461 return (o instanceof Manifest) &&
462 (mainAttr.equals(((Manifest) o).mainAttr)) &&
463 (entries.equals(((Manifest) o).entries));
464 }
465
466 /**
467 * Calculates the hash code of the manifest. Implemented by a xor of the
468 * hash code of the main attributes with the hash code of the entries map.
469 */
470 public int hashCode()
471 {
472 return mainAttr.hashCode() ^ entries.hashCode();
473 }
474
475}
Note: See TracBrowser for help on using the repository browser.