Merge from the pain train
[official-gcc.git] / libjava / java / util / jar / Manifest.java
blobddc201b45b5cebbfdb74d5448c23a6db6de773c6
1 /* Manifest.java -- Reads, writes and manipulaties jar manifest files
2 Copyright (C) 2000, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package java.util.jar;
40 import java.io.BufferedReader;
41 import java.io.BufferedWriter;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.InputStreamReader;
45 import java.io.OutputStream;
46 import java.io.OutputStreamWriter;
47 import java.io.PrintWriter;
48 import java.util.Hashtable;
49 import java.util.Iterator;
50 import java.util.Map;
52 /**
53 * Reads, writes and manipulaties jar manifest files.
54 * XXX
56 * @since 1.2
57 * @author Mark Wielaard (mark@klomp.org)
59 public class Manifest implements Cloneable
61 // Fields
63 /** The main attributes of the manifest (jar file). */
64 private final Attributes mainAttr;
66 /** A map of atrributes for all entries described in this Manifest. */
67 private final Map entries;
69 // Constructors
71 /**
72 * Creates a new empty Manifest.
74 public Manifest()
76 mainAttr = new Attributes();
77 entries = new Hashtable();
80 /**
81 * Creates a Manifest from the supplied input stream.
83 * @see read(Inputstream)
84 * @see write(OutputStream)
86 * @param InputStream the input stream to read the manifest from
87 * @exception IOException when an i/o exception occurs or the input stream
88 * does not describe a valid manifest
90 public Manifest(InputStream in) throws IOException
92 this();
93 read(in);
96 /**
97 * Creates a Manifest from another Manifest.
98 * Makes a deep copy of the main attributes, but a shallow copy of
99 * the other entries. This means that you can freely add, change or remove
100 * the main attributes or the entries of the new manifest without effecting
101 * the original manifest, but adding, changing or removing attributes from
102 * a particular entry also changes the attributes of that entry in the
103 * original manifest.
105 * @see clone()
106 * @param man the Manifest to copy from
108 public Manifest(Manifest man)
110 mainAttr = new Attributes(man.getMainAttributes());
111 entries = new Hashtable(man.getEntries());
114 // Methods
117 * Gets the main attributes of this Manifest.
119 public Attributes getMainAttributes()
121 return mainAttr;
125 * Gets a map of entry Strings to Attributes for all the entries described
126 * in this manifest. Adding, changing or removing from this entries map
127 * changes the entries of this manifest.
129 public Map getEntries()
131 return entries;
135 * Returns the Attributes associated with the Entry.
136 * <p>
137 * Implemented as:
138 * <code>return (Attributes)getEntries().get(entryName)</code>
140 * @param entryName the name of the entry to look up
141 * @return the attributes associated with the entry or null when none
143 public Attributes getAttributes(String entryName)
145 return (Attributes) getEntries().get(entryName);
149 * Clears the main attributes and removes all the entries from the
150 * manifest.
152 public void clear()
154 mainAttr.clear();
155 entries.clear();
159 * XXX
161 public void read(InputStream in) throws IOException
163 BufferedReader br =
164 new BufferedReader(new InputStreamReader(in, "8859_1"));
165 read_main_section(getMainAttributes(), br);
166 read_individual_sections(getEntries(), br);
169 // Private Static methods for reading the Manifest file from BufferedReader
171 private static void read_main_section(Attributes attr,
172 BufferedReader br) throws IOException
174 // According to the spec we should actually call read_version_info() here.
175 read_attributes(attr, br);
176 // Explicitly set Manifest-Version attribute if not set in Main
177 // attributes of Manifest.
178 if (attr.getValue(Attributes.Name.MANIFEST_VERSION) == null)
179 attr.putValue(Attributes.Name.MANIFEST_VERSION, "0.0");
183 * Pedantic method that requires the next attribute in the Manifest to be
184 * the "Manifest-Version". This follows the Manifest spec closely but
185 * reject some jar Manifest files out in the wild.
187 private static void read_version_info(Attributes attr,
188 BufferedReader br) throws IOException
190 String version_header = Attributes.Name.MANIFEST_VERSION.toString();
193 String value = expect_header(version_header, br);
194 attr.putValue(Attributes.Name.MANIFEST_VERSION, value);
196 catch (IOException ioe)
198 throw new JarException("Manifest should start with a " +
199 version_header + ": " + ioe.getMessage());
203 private static String expect_header(String header, BufferedReader br)
204 throws IOException
206 String s = br.readLine();
207 if (s == null)
209 throw new JarException("unexpected end of file");
211 return expect_header(header, br, s);
214 private static String expect_header(String header, BufferedReader br,
215 String s) throws IOException
219 String name = s.substring(0, header.length() + 1);
220 if (name.equalsIgnoreCase(header + ":"))
222 String value_start = s.substring(header.length() + 2);
223 return read_header_value(value_start, br);
226 catch (IndexOutOfBoundsException iobe)
229 // If we arrive here, something went wrong
230 throw new JarException("unexpected '" + s + "'");
233 private static String read_header_value(String s, BufferedReader br)
234 throws IOException
236 boolean try_next = true;
237 while (try_next)
239 // Lets see if there is something on the next line
240 br.mark(1);
241 if (br.read() == ' ')
243 s += br.readLine();
245 else
247 br.reset();
248 try_next = false;
251 return s;
254 private static void read_attributes(Attributes attr,
255 BufferedReader br) throws IOException
257 String s = br.readLine();
258 while (s != null && (!s.equals("")))
260 read_attribute(attr, s, br);
261 s = br.readLine();
265 private static void read_attribute(Attributes attr, String s,
266 BufferedReader br) throws IOException
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);
276 catch (IndexOutOfBoundsException iobe)
278 throw new JarException("Manifest contains a bad header: " + s);
282 private static void read_individual_sections(Map entries,
283 BufferedReader br) throws
284 IOException
286 String s = br.readLine();
287 while (s != null && (!s.equals("")))
289 Attributes attr = read_section_name(s, br, entries);
290 read_attributes(attr, br);
291 s = br.readLine();
295 private static Attributes read_section_name(String s, BufferedReader br,
296 Map entries) throws JarException
300 String name = expect_header("Name", br, s);
301 Attributes attr = new Attributes();
302 entries.put(name, attr);
303 return attr;
305 catch (IOException ioe)
307 throw new JarException
308 ("Section should start with a Name header: " + ioe.getMessage());
313 * XXX
315 public void write(OutputStream out) throws IOException
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())
325 throw new JarException("Error while writing manifest");
329 // Private Static functions for writing the Manifest file to a PrintWriter
331 private static void write_main_section(Attributes attr,
332 PrintWriter pw) throws JarException
334 write_version_info(attr, pw);
335 write_main_attributes(attr, pw);
338 private static void write_version_info(Attributes attr, PrintWriter pw)
340 // First check if there is already a version attribute set
341 String version = attr.getValue(Attributes.Name.MANIFEST_VERSION);
342 if (version == null)
344 version = "1.0";
346 write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw);
349 private static void write_header(String name, String value, PrintWriter pw)
351 pw.print(name + ": ");
353 int last = 68 - name.length();
354 if (last > value.length())
356 pw.println(value);
358 else
360 pw.println(value.substring(0, last));
362 while (last < value.length())
364 pw.print(" ");
365 int end = (last + 69);
366 if (end > value.length())
368 pw.println(value.substring(last));
370 else
372 pw.println(value.substring(last, end));
374 last = end;
378 private static void write_main_attributes(Attributes attr, PrintWriter pw)
379 throws JarException
381 Iterator it = attr.entrySet().iterator();
382 while (it.hasNext())
384 Map.Entry entry = (Map.Entry) it.next();
385 // Don't print the manifest version again
386 if (!Attributes.Name.MANIFEST_VERSION.equals(entry.getKey()))
388 write_attribute_entry(entry, pw);
393 private static void write_attribute_entry(Map.Entry entry, PrintWriter pw)
394 throws JarException
396 String name = entry.getKey().toString();
397 String value = entry.getValue().toString();
399 if (name.equalsIgnoreCase("Name"))
401 throw new JarException("Attributes cannot be called 'Name'");
403 if (name.startsWith("From"))
405 throw new
406 JarException("Header cannot start with the four letters 'From'" +
407 name);
409 write_header(name, value, pw);
412 private static void write_individual_sections(Map entries, PrintWriter pw)
413 throws JarException
416 Iterator it = entries.entrySet().iterator();
417 while (it.hasNext())
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();
426 private static void write_entry_attributes(Attributes attr, PrintWriter pw)
427 throws JarException
429 Iterator it = attr.entrySet().iterator();
430 while (it.hasNext())
432 Map.Entry entry = (Map.Entry) it.next();
433 write_attribute_entry(entry, pw);
438 * Makes a deep copy of the main attributes, but a shallow copy of
439 * the other entries. This means that you can freely add, change or remove
440 * the main attributes or the entries of the new manifest without effecting
441 * the original manifest, but adding, changing or removing attributes from
442 * a particular entry also changes the attributes of that entry in the
443 * original manifest. Calls <CODE>new Manifest(this)</CODE>.
445 public Object clone()
447 return new Manifest(this);
451 * Checks if another object is equal to this Manifest object.
452 * Another Object is equal to this Manifest object if it is an instance of
453 * Manifest and the main attributes and the entries of the other manifest
454 * are equal to this one.
456 public boolean equals(Object o)
458 return (o instanceof Manifest) &&
459 (mainAttr.equals(((Manifest) o).mainAttr)) &&
460 (entries.equals(((Manifest) o).entries));
464 * Calculates the hash code of the manifest. Implemented by a xor of the
465 * hash code of the main attributes with the hash code of the entries map.
467 public int hashCode()
469 return mainAttr.hashCode() ^ entries.hashCode();