hello all, this patch --not committed-- allows a JAR's Manifest file to be generated with platform-independent line-endings. since there are no Mauve tests --the only test i used was the jarsigner tool i'm developing-- i would appreciate it if somebody can go over the patch and check if it's correct.
2006-03-06 Raif S. Naffah <[EMAIL PROTECTED]>
* java/util/jar/Manifest.java: Removed unused imports.
(CRLF): New constant.
(read): Added method documentation.
Use UTF-8 encoding instead of 8859_1.
(write): Added method documentation.
Use BufferedOutputStream (w/ 4K buffer) instead of PrintWriter.
(write_main_section): Replace PrintWriter arg with OutputStream.
Replace JarException with IOException.
(write_version_info): Likewise.
(write_main_attributes): Likewise.
(write_attribute_entry): Likewise.
(write_individual_sections): Likewise.
(write_entry_attributes): Likewise.
(write_header): Replace PrintWriter arg with OutputStream.
Re-implemented.
TIA + cheers;
rsn
Index: Manifest.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/util/jar/Manifest.java,v
retrieving revision 1.10
diff -u -r1.10 Manifest.java
--- Manifest.java 13 Sep 2005 22:19:15 -0000 1.10
+++ Manifest.java 6 Mar 2006 09:27:36 -0000
@@ -37,14 +37,12 @@
package java.util.jar;
+import java.io.BufferedOutputStream;
import java.io.BufferedReader;
-import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
@@ -60,6 +58,9 @@
{
// Fields
+ /** Platform-independent line-ending. */
+ private static final byte[] CRLF = new byte[] { 0x0D, 0x0A };
+
/** The main attributes of the manifest (jar file). */
private final Attributes mainAttr;
@@ -156,12 +157,14 @@
}
/**
- * XXX
+ * Read and merge a <code>Mainfest</code> from the designated input stream.
+ *
+ * @param in the input stream to read from.
+ * @throws IOException if an I/O related exception occurs during the process.
*/
public void read(InputStream in) throws IOException
{
- BufferedReader br =
- new BufferedReader(new InputStreamReader(in, "8859_1"));
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
read_main_section(getMainAttributes(), br);
read_individual_sections(getEntries(), br);
}
@@ -310,32 +313,35 @@
}
/**
- * XXX
+ * Writes the contents of this <code>Manifest</code> to the designated
+ * output stream. Line-endings are platform-independent and consist of the
+ * 2-codepoint sequence <code>0x0D</code> and <code>0x0A</code>.
+ *
+ * @param out the output stream to write this <code>Manifest</code> to.
+ * @throws IOException if an I/O related exception occurs during the process.
*/
public void write(OutputStream out) throws IOException
{
- PrintWriter pw =
- new PrintWriter(new
- BufferedWriter(new OutputStreamWriter(out, "8859_1")));
- write_main_section(getMainAttributes(), pw);
- pw.println();
- write_individual_sections(getEntries(), pw);
- if (pw.checkError())
- {
- throw new JarException("Error while writing manifest");
- }
+ BufferedOutputStream bos = out instanceof BufferedOutputStream
+ ? (BufferedOutputStream) out
+ : new BufferedOutputStream(out, 4096);
+ write_main_section(getMainAttributes(), bos);
+ bos.write(CRLF);
+ write_individual_sections(getEntries(), bos);
+ bos.flush();
}
// Private Static functions for writing the Manifest file to a PrintWriter
- private static void write_main_section(Attributes attr,
- PrintWriter pw) throws JarException
+ private static void write_main_section(Attributes attr, OutputStream out)
+ throws IOException
{
- write_version_info(attr, pw);
- write_main_attributes(attr, pw);
+ write_version_info(attr, out);
+ write_main_attributes(attr, out);
}
- private static void write_version_info(Attributes attr, PrintWriter pw)
+ private static void write_version_info(Attributes attr, OutputStream out)
+ throws IOException
{
// First check if there is already a version attribute set
String version = attr.getValue(Attributes.Name.MANIFEST_VERSION);
@@ -343,40 +349,75 @@
{
version = "1.0";
}
- write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw);
+ write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, out);
}
- private static void write_header(String name, String value, PrintWriter pw)
+ /**
+ * The basic method for writing <code>Mainfest</code> attributes. This
+ * implementation respects the rule stated in the Jar Specification concerning
+ * the maximum allowed line length; i.e.
+ *
+ * <pre>
+ * No line may be longer than 72 bytes (not characters), in its UTF8-encoded
+ * form. If a value would make the initial line longer than this, it should
+ * be continued on extra lines (each starting with a single SPACE).
+ * </pre>
+ *
+ * @param name the name of the attribute.
+ * @param value the value of the attribute.
+ * @param out the output stream to write the attribute's name/value pair to.
+ * @throws IOException if an I/O related exception occurs during the process.
+ */
+ private static void write_header(String name, String value, OutputStream out)
+ throws IOException
{
- pw.print(name + ": ");
-
- int last = 68 - name.length();
- if (last > value.length())
- {
- pw.println(value);
+ String target = name + ":";
+ byte[] b = target.getBytes("UTF-8");
+ if (b.length > 72)
+ throw new IOException("Attribute's name already longer than 72 bytes");
+
+ if (b.length == 72)
+ {
+ out.write(b);
+ out.write(CRLF);
+ target = " " + value;
}
else
+ target = target + " " + value;
+
+ int n;
+ while (true)
{
- pw.println(value.substring(0, last));
- }
- while (last < value.length())
- {
- pw.print(" ");
- int end = (last + 69);
- if (end > value.length())
- {
- pw.println(value.substring(last));
- }
- else
- {
- pw.println(value.substring(last, end));
- }
- last = end;
+ b = target.getBytes("UTF-8");
+ if (b.length < 73)
+ {
+ out.write(b);
+ break;
+ }
+
+ // find an appropriate character position to break on
+ n = 72;
+ while (true)
+ {
+ b = target.substring(0, n).getBytes("UTF-8");
+ if (b.length < 73)
+ break;
+
+ n--;
+ if (n < 1)
+ throw new IOException("Header is unbreakable and longer than 72 bytes");
+ }
+
+ out.write(b);
+ out.write(CRLF);
+ target = " " + target.substring(n);
}
+
+ out.write(CRLF);
}
- private static void write_main_attributes(Attributes attr, PrintWriter pw)
- throws JarException
+ private static void write_main_attributes(Attributes attr, OutputStream out)
+ throws IOException
{
Iterator it = attr.entrySet().iterator();
while (it.hasNext())
@@ -384,14 +425,12 @@
Map.Entry entry = (Map.Entry) it.next();
// Don't print the manifest version again
if (!Attributes.Name.MANIFEST_VERSION.equals(entry.getKey()))
- {
- write_attribute_entry(entry, pw);
- }
+ write_attribute_entry(entry, out);
}
}
- private static void write_attribute_entry(Map.Entry entry, PrintWriter pw)
- throws JarException
+ private static void write_attribute_entry(Map.Entry entry, OutputStream out)
+ throws IOException
{
String name = entry.getKey().toString();
String value = entry.getValue().toString();
@@ -406,31 +445,31 @@
JarException("Header cannot start with the four letters 'From'" +
name);
}
- write_header(name, value, pw);
+ write_header(name, value, out);
}
- private static void write_individual_sections(Map entries, PrintWriter pw)
- throws JarException
+ private static void write_individual_sections(Map entries, OutputStream out)
+ throws IOException
{
Iterator it = entries.entrySet().iterator();
while (it.hasNext())
{
- Map.Entry entry = (Map.Entry) it.next();
- write_header("Name", entry.getKey().toString(), pw);
- write_entry_attributes((Attributes) entry.getValue(), pw);
- pw.println();
+ Map.Entry entry = (Map.Entry) it.next();
+ write_header("Name", entry.getKey().toString(), out);
+ write_entry_attributes((Attributes) entry.getValue(), out);
+ out.write(CRLF);
}
}
- private static void write_entry_attributes(Attributes attr, PrintWriter pw)
- throws JarException
+ private static void write_entry_attributes(Attributes attr, OutputStream out)
+ throws IOException
{
Iterator it = attr.entrySet().iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry) it.next();
- write_attribute_entry(entry, pw);
+ write_attribute_entry(entry, out);
}
}
pgpR9AyZ3CJL1.pgp
Description: PGP signature
