diff options
author | green <green@138bc75d-0d04-0410-961f-82ee72b054a4> | 2000-08-19 18:19:42 +0000 |
---|---|---|
committer | green <green@138bc75d-0d04-0410-961f-82ee72b054a4> | 2000-08-19 18:19:42 +0000 |
commit | 121fbaa55ea2cfc6316c95d38eaa0db137de793d (patch) | |
tree | 2dff323eee68e61f9225ea0c6c7f15f7c6bcd624 /libjava/java/util/jar | |
parent | a8081abb437b8376c9869c342ffcd3c486098a9d (diff) | |
download | gcc-121fbaa55ea2cfc6316c95d38eaa0db137de793d.tar.gz |
Sat Aug 19 11:00:53 2000 Anthony Green <green@redhat.com>
* java/util/jar/Attributes.java, java/util/jar/JarEntry.java,
java/util/jar/JarException.java, java/util/jar/JarFile.java,
java/util/jar/JarInputStream.java,
java/util/jar/JarOutputStream.java, java/util/jar/Manifest.java,
java/util/Set.java, java/util/Map.java, java/util/Bucket.java,
java/util/AbstractSet.java, java/util/BasicMapEntry.java,
java/security/cert/CRL.java, java/security/cert/CRLException.java,
java/security/cert/Certificate.java,
java/security/cert/CertificateEncodingException.java,
java/security/cert/CertificateException.java,
java/security/cert/CertificateExpiredException.java,
java/security/cert/CertificateFactory.java,
java/security/cert/CertificateFactorySpi.java,
java/security/cert/CertificateNotYetValidException.java,
java/security/cert/CertificateParsingException.java,
java/security/cert/X509CRL.java,
java/security/cert/X509CRLEntry.java,
java/security/cert/X509Certificate.java,
java/security/cert/X509Extension.java: Imported from Classpath.
* java/util/Hashtable.java: Imported from Classpath.
* java/util/zip/ZipInputStream.java: Create stub for
createZipEntry.
* gcj/javaprims.h: Updated class list.
* Makefile.in, gcj/Makefile.in: Rebuilt.
* Makefile.am (ordinary_java_source_files): Add these new classes.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@35809 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/java/util/jar')
-rw-r--r-- | libjava/java/util/jar/Attributes.java | 586 | ||||
-rw-r--r-- | libjava/java/util/jar/JarEntry.java | 148 | ||||
-rw-r--r-- | libjava/java/util/jar/JarException.java | 65 | ||||
-rw-r--r-- | libjava/java/util/jar/JarFile.java | 309 | ||||
-rw-r--r-- | libjava/java/util/jar/JarInputStream.java | 181 | ||||
-rw-r--r-- | libjava/java/util/jar/JarOutputStream.java | 98 | ||||
-rw-r--r-- | libjava/java/util/jar/Manifest.java | 406 |
7 files changed, 1706 insertions, 87 deletions
diff --git a/libjava/java/util/jar/Attributes.java b/libjava/java/util/jar/Attributes.java new file mode 100644 index 00000000000..587f4a1d130 --- /dev/null +++ b/libjava/java/util/jar/Attributes.java @@ -0,0 +1,586 @@ +/* Attributes.java -- Represents attribute name/value pairs from a Manifest + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + +package java.util.jar; + +import java.util.Collection; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +/** + * Represents attribute name/value pairs from a Manifest as a Map. + * The names of an attribute are represented by the + * <code>Attributes.Name</code> class and should confirm to the restrictions + * described in that class. Note that the Map interface that Attributes + * implements allows you to put names and values into the attribute that don't + * follow these restriction (and are not really Atrribute.Names, but if you do + * that it might cause undefined behaviour later). + * <p> + * If you use the constants defined in the inner class Name then you can be + * sure that you always access the right attribute names. This makes + * manipulating the Attributes more or less type safe. + * <p> + * Most of the methods are wrappers to implement the Map interface. The really + * usefull and often used methods are <code>getValue(Name)</code> and + * <code>getValue(String)</code>. If you actually want to set attributes you + * may want to use the <code>putValue(String, String)</code> method + * (sorry there is no public type safe <code>putValue(Name, String)</code> + * method). + * + * @see java.util.jar.Attributes.Name + * @author Mark Wielaard (mark@klomp.org) + */ +public class Attributes implements Cloneable, Map { + + // Fields + + /** + * The map that holds all the attribute name/value pairs. In this + * implementation it is actually a Hashtable, but that can be different in + * other implementations. + */ + protected Map map; + + // Inner class + + /** + * Represents a name of a Manifest Attribute. Defines a couple of well + * know names for the general main attributes, stand alone application + * attributes, applet attributes, extension identification attributes, + * package versioning and sealing attributes, file contents attributes, + * bean objects attribute and signing attributes. See the + * <p> + * The characters of a Name must obey the following restrictions: + * <ul> + * <li> Must contain at least one character + * <li> The first character must be alphanumeric (a-z, A-Z, 0-9) + * <li> All other characters must be alphanumeric, a '-' or a '_' + * </ul> + * <p> + * When comparing Names (with <code>equals</code>) all characters are + * converted to lowercase. But you can get the original case sensitive + * string with the <code>toString()</code> method. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + public static class Name { + + // Fields + + // General Main Attributes + + /** + * General main attribute - + * the version of this Manifest file. + */ + public static final Name MANIFEST_VERSION + = new Name("Manifest-Version"); + /** + * General main attribute - + * tool and version that created this Manifest file. + */ + public static final Name CREATED_BY + = new Name("Created-By"); + /** + * General main attribute - + * the version of the jar file signature. + */ + public static final Name SIGNATURE_VERSION + = new Name("Signature-Version"); + /** + * General main attribute - + * (relative) URLs of the libraries/classpaths that the Classes in + * this jar file depend on. + */ + public static final Name CLASS_PATH + = new Name("Class-Path"); + + /** + * Stand alone application attribute - + * the entry (without the .class ending) that is the main + * class of this jar file. + */ + public static final Name MAIN_CLASS + = new Name("Main-Class"); + + /** + * Applet attribute - + * a list of extension libraries that the applet in this + * jar file depends on. + * For every named extension there should be some Attributes in the + * Manifest manifest file with the following Names: + * <ul> + * <li> <extension>-Extension-Name: + * unique name of the extension + * <li> <extension>-Specification-Version: + * minimum specification version + * <li> <extension>-Implementation-Version: + * minimum implementation version + * <li> <extension>-Implementation-Vendor-Id: + * unique id of implementation vendor + * <li> <extension>-Implementation-URL: + * where the latest version of the extension library can be found + * </ul> + */ + public static final Name EXTENSION_LIST + = new Name("Extension-List"); + + /** + * Extension identification attribute - + * the name if the extension library contained in the jar. + */ + public static final Name EXTENSION_NAME + = new Name("Extension-Name"); + /** + * Extension identification attribute - + * synonym for <code>EXTENSTION_NAME</code>. + */ + public static final Name EXTENSION_INSTALLATION + = EXTENSION_NAME; + + // Package versioning and sealing attributes + /** + * Package versioning - + * name of extension library contained in this jar. + */ + public static final Name IMPLEMENTATION_TITLE + = new Name("Implementation-Title"); + /** + * Package versioning - + * version of the extension library contained in this jar. + */ + public static final Name IMPLEMENTATION_VERSION + = new Name("Implementation-Version"); + /** + * Package versioning - + * name of extension library creator contained in this jar. + */ + public static final Name IMPLEMENTATION_VENDOR + = new Name("Implementation-Vendor"); + /** + * Package versioning - + * unique id of extension library creator. + */ + public static final Name IMPLEMENTATION_VENDOR_ID + = new Name("Implementation-Vendor-Id"); + /** + * Package versioning - + * location where this implementation can be downloaded. + */ + public static final Name IMPLEMENTATION_URL + = new Name("Implementation-URL"); + /** + * Package versioning - + * title of the specification contained in this jar. + */ + public static final Name SPECIFICATION_TITLE + = new Name("Specification-Title"); + /** + * Package versioning - + * version of the specification contained in this jar. + */ + public static final Name SPECIFICATION_VERSION + = new Name("Specification-Version"); + /** + * Package versioning - + * organisation that maintains the specification contains in this + * jar. + */ + public static final Name SPECIFICATION_VENDOR + = new Name("Specification-Vendor"); + /** + * Package sealing - + * whether (all) package(s) is(/are) sealed. Value is either "true" + * or "false". + */ + public static final Name SEALED + = new Name("Sealed"); + + /** + * File contents attribute - + * Mime type and subtype for the jar entry. + */ + public static final Name CONTENT_TYPE + = new Name("Content-Type"); + + /** + * Bean objects attribute - + * whether the entry is a Java Bean. Value is either "true" or "false". + */ + public static final Name JAVA_BEAN + = new Name("Java-Bean"); + + /** + * Signing attribute - + * application specific signing attribute. Must be understood by + * the manifest parser when present to validate the jar (entry). + */ + public static final Name MAGIC + = new Name("Magic"); + + /** The (lowercase) String representation of this Name */ + private final String name; + /** The original String given to the constructor */ + private final String origName; + + // Constructor + + /** + * Creates a new Name from the given String. + * Throws an IllegalArgumentException if the given String is empty or + * contains any illegal Name characters. + * + * @param name the name of the new Name + * @exception IllegalArgumentException if name isn't a valid String + * representation of a Name + * @exception NullPointerException if name is null + */ + public Name(String name) throws IllegalArgumentException, + NullPointerException { + // name must not be null + // this will throw a NullPointerException if it is + char chars[] = name.toCharArray(); + + // there must be at least one character + if (chars.length == 0) + throw new IllegalArgumentException( + "There must be at least one character in a name"); + + // first character must be alphanum + char c = chars[0]; + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9'))) + throw new IllegalArgumentException( + "First character must be alphanum"); + + // all other characters must be alphanums, '-' or '_' + for (int i = 1; i < chars.length; i++) { + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + (c == '-') || (c == '_'))) + throw new IllegalArgumentException( + "Characters must be alphanums, '-' or '_'"); + } + + // Still here? Then convert to lower case and be done. + // Store the original name for toString(); + this.origName = name; + this.name = name.toLowerCase(); + } + + /** + * Returns the hash code of the (lowercase) String representation of + * this Name. + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Checks if another object is equal to this Name object. + * Another object is equal to this Name object if it is an instance of + * Name and the (lowercase) string representation of the name is equal. + */ + public boolean equals(Object o) { + // Quick and dirty check + if (name == o) + return true; + + try { + // Note that the constructor already converts the strings to + // lowercase. + String otherName = ((Name)o).name; + return name.equals(otherName); + } catch (ClassCastException cce) { + return false; + } catch (NullPointerException npe) { + return false; + } + } + + /** + * Returns the string representation of this Name as given to the + * constructor (not neccesarily the lower case representation). + */ + public String toString() { + return origName; + } + } + + // Constructors + + /** + * Creates an empty Attributes map. + */ + public Attributes() { + map = new Hashtable(); + } + + /** + * Creates an empty Attributes map with the given initial size. + * @param size the initial size of the underlying map + */ + public Attributes(int size) { + map = new Hashtable(size); + } + + /** + * Creates an Attributes map with the initial values taken from another + * Attributes map. + * @param attr Attributes map to take the initial values from + */ + public Attributes(Attributes attr) { + map = new Hashtable(attr.map); + } + + // Methods + + /** + * Gets the value of an attribute name given as a String. + * + * @param name a String describing the Name to look for + * @return the value gotten from the map of null when not found + */ + public String getValue(String name) { + return (String)get(new Name(name)); + } + + /** + * Gets the value of the given attribute name. + * + * @param name the Name to look for + * @return the value gotten from the map of null when not found + */ + public String getValue(Name name) { + return (String)get(name); + } + + /** + * Stores an attribute name (represented by a String) and value in this + * Attributes map. + * When the (case insensitive string) name already exists the value is + * replaced and the old value is returned. + * + * @param name a (case insensitive) String representation of the attribite + * name to add/replace + * @param value the (new) value of the attribute name + * @returns the old value of the attribute name or null if it didn't exist + * yet + */ + public String putValue(String name, String value) + { + return putValue(new Name(name), value); + } + + /** + * Stores an attribute name (represented by a String) and value in this + * Attributes map. + * When the name already exists the value is replaced and the old value + * is returned. + * <p> + * I don't know why there is no public method with this signature. I think + * there should be one. + * + * @param name the attribite name to add/replace + * @param value the (new) value of the attribute name + * @returns the old value of the attribute name or null if it didn't exist + * yet + */ + private String putValue(Name name, String value) + { + return (String)put(name, value); + } + + // Methods from Cloneable interface + + /** + * Return a clone of this attribute map. + */ + public Object clone() { + return new Attributes(this); + } + + // Methods from Map interface + + /** + * Removes all attributes. + */ + public void clear() { + map.clear(); + } + + /** + * Checks to see if there is an attribute with the specified name. + * XXX - what if the object is a String? + * + * @param attrName the name of the attribute to check + * @return true if there is an attribute with the specified name, false + * otherwise + */ + public boolean containsKey(Object attrName) { + return map.containsKey(attrName); + } + + /** + * Checks to see if there is an attribute name with the specified value. + * + * @param attrValue the value of a attribute to check + * @return true if there is an attribute name with the specified value, + * false otherwise + */ + public boolean containsValue(Object attrValue) { + return map.containsValue(attrValue); + } + + /** + * Gives a Set of atrribute name and values pairs as MapEntries. + * @see java.util.Map.Entry + * @see java.util.Map#entrySet() + * + * @return a set of attribute name value pairs + */ + public Set entrySet() { + return map.entrySet(); + } + + /** + * Checks to see if two Attributes are equal. The supplied object must be + * a real instance of Attributes and contain the same attribute name/value + * pairs. + * + * @param o another Attribute object which should be checked for equality + * @return true if the object is an instance of Attributes and contains the + * same name/value pairs, false otherwise + */ + public boolean equals(Object o) { + // quick and dirty check + if (this == o) + return true; + + try { + return map.equals(((Attributes)o).map); + } catch (ClassCastException cce) { + return false; + } catch (NullPointerException npe) { + return false; + } + } + + /** + * Gets the value of a specified attribute name. + * XXX - what if the object is a String? + * + * @param attrName the name of the attribute we want the value of + * @return the value of the specified attribute name or null when there is + * no such attribute name + */ + public Object get(Object attrName) { + return map.get(attrName); + } + + /** + * Returns the hashcode of the attribute name/value map. + */ + public int hashCode() { + return map.hashCode(); + } + + /** + * Returns true if there are no attributes set, false otherwise. + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Gives a Set of all the values of defined attribute names. + */ + public Set keySet() { + return map.keySet(); + } + + /** + * Adds or replaces a attribute name/value pair. + * XXX - What if the name is a string? What if the name is neither a Name + * nor a String? What if the value is not a string? + * + * @param name the name of the attribute + * @param value the (new) value of the attribute + * @return the old value of the attribute or null when there was no old + * attribute with this name + */ + public Object put(Object name, Object value) { + return map.put(name, value); + } + + /** + * Adds or replaces all attribute name/value pairs from another + * Attributes object to this one. The supplied Map must be an instance of + * Attributes. + * + * @param attr the Attributes object to merge with this one + * @exception ClassCastException if the supplied map is not an instance of + * Attributes + */ + public void putAll(Map attr) { + if (!(attr instanceof Attributes)) { + throw new ClassCastException( + "Supplied Map is not an instance of Attributes"); + } + map.putAll(attr); + } + + /** + * Remove a attribute name/value pair. + * XXX - What if the name is a String? + * + * @param name the name of the attribute name/value pair to remove + * @return the old value of the attribute or null if the attribute didn't + * exist + */ + public Object remove(Object name) { + return map.remove(name); + } + + /** + * Returns the number of defined attribute name/value pairs. + */ + public int size() { + return map.size(); + } + + /** + * Returns all the values of the defined attribute name/value pairs as a + * Collection. + */ + public Collection values() { + return map.values(); + } +} diff --git a/libjava/java/util/jar/JarEntry.java b/libjava/java/util/jar/JarEntry.java index eaebd1ad7d5..e15f1a4f336 100644 --- a/libjava/java/util/jar/JarEntry.java +++ b/libjava/java/util/jar/JarEntry.java @@ -1,39 +1,139 @@ -/* Copyright (C) 1999 Free Software Foundation +/* JarEntry.java - Represents an entry in a jar file + Copyright (C) 2000 Free Software Foundation, Inc. - This file is part of libgcj. +This file is part of GNU Classpath. -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ package java.util.jar; -import java.util.zip.*; +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.zip.ZipEntry; /** - * Does not implement the security and manifest methods. + * Extension to a ZipEntry that contains manifest attributes and certificates. + * Both the Atrributes and the Certificates can be null when not set. + * Note that the <code>getCertificates()</code> method only returns a + * valid value after all of the data of the entry has been read. + * <p> + * There are no public methods to set the attributes or certificate of an + * Entru. Only JarEntries created by the classes in <code>java.util.jar</code> + * will have these properties set. * - * @author Kresten Krab Thorup <krab@gnu.org> - * @date August 10, 1999. + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) */ + +public class JarEntry extends ZipEntry { + + // (Packge local) fields + + Attributes attr; + Certificate certs[]; + + // Constructors + + /** + * Creates a new JarEntry with the specified name and no attributes or + * or certificates. Calls <code>super(name)</code> so all other (zip)entry + * fields are null or -1. + * + * @param name the name of the new jar entry + * @exception NullPointerException when the supplied name is null + * @exception IllegalArgumentException when the supplied name is longer + * than 65535 bytes + */ + public JarEntry(String name) throws NullPointerException, + IllegalArgumentException { + super(name); + attr = null; + certs = null; + } -public class JarEntry extends ZipEntry -{ - ZipEntry zip; + /** + * Creates a new JarEntry with the specified ZipEntry as template for + * all properties of the entry. Both attributes and certificates will be + * null. + * + * @param entry the ZipEntry whose fields should be copied + */ + public JarEntry(ZipEntry entry) { + super(entry); + attr = null; + certs = null; + } - public JarEntry (ZipEntry ent) - { - super (ent); - } + /** + * Creates a new JarEntry with the specified JarEntry as template for + * all properties of the entry. + * + * @param entry the jarEntry whose fields should be copied + */ + public JarEntry(JarEntry entry) { + super(entry); + try { + attr = entry.getAttributes(); + } catch(IOException _) {} + certs = entry.getCertificates(); + } - public JarEntry (JarEntry ent) - { - super (ent); - } + // Methods - public JarEntry (String name) - { - super (name); - } + /** + * Returns a copy of the Attributes set for this entry. + * When no Attributes are set in the manifest null is returned. + * + * @return a copy of the Attributes set for this entry + * @exception IOException This will never be thrown. It is here for + * binary compatibility. + */ + public Attributes getAttributes() throws IOException { + if (attr != null) { + return (Attributes) attr.clone(); + } else { + return null; + } + } + /** + * Returns a copy of the certificates set for this entry. + * When no certificates are set or when not all data of this entry has + * been read null is returned. + * <p> + * To make sure that this call returns a valid value you must read all + * data from the JarInputStream for this entry. + * When you don't need the data for an entry but want to know the + * certificates that are set for the entry then you can skip all data by + * calling <code>skip(entry.getSize())</code> on the JarInputStream for + * the entry. + * + * @return a copy of the certificates set for this entry + */ + public Certificate[] getCertificates() { + if (certs != null) { + return (Certificate []) certs.clone(); + } else { + return null; + } + } } diff --git a/libjava/java/util/jar/JarException.java b/libjava/java/util/jar/JarException.java new file mode 100644 index 00000000000..827f9e2a904 --- /dev/null +++ b/libjava/java/util/jar/JarException.java @@ -0,0 +1,65 @@ +/* Attributes.java -- exception thrown to indicate an problem with a jar file + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + +package java.util.jar; + +import java.util.zip.ZipException; + +/** + * This exception is thrown to indicate an problem with a jar file. + * It can be constructed with or without a descriptive message of the problem. + * <p> + * Note that none of the methods in the java.util.jar package actually declare + * to throw this exception, most just declare that they throw an IOException + * which is super class of JarException. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarException extends ZipException { + + // Constructors + + /** + * Create a new JarException without a descriptive error message. + */ + public JarException() { + super(); + } + + /** + * Create a new JarException with a descriptive error message indicating + * what went wrong. This message can later be retrieved by calling the + * <code>getMessage()</code> method. + * @see java.lang.Throwable@getMessage() + * + * @param message The descriptive error message + */ + public JarException(String message) { + super(message); + } +} diff --git a/libjava/java/util/jar/JarFile.java b/libjava/java/util/jar/JarFile.java index b36338c218b..3f1823a00b4 100644 --- a/libjava/java/util/jar/JarFile.java +++ b/libjava/java/util/jar/JarFile.java @@ -1,56 +1,277 @@ -/* Copyright (C) 1999 Free Software Foundation +/* JarFile.java - Representation of a jar file + Copyright (C) 2000 Free Software Foundation, Inc. - This file is part of libgcj. +This file is part of GNU Classpath. -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ package java.util.jar; -import java.util.zip.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.util.Enumeration; /** - * Does not implement any of the security. Just a place holder, so - * that I can implement URLClassLoader. + * Representation of a jar file. + * <p> + * Note that this class is not a subclass of java.io.File but a subclass of + * java.util.zip.ZipFile and you can only read JarFiles with it (although + * there are constructors that take a File object). + * <p> + * XXX - verification of Manifest signatures is not yet implemented. * - * @author Kresten Krab Thorup <krab@gnu.org> - * @date August 10, 1999. + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) */ +public class JarFile extends ZipFile { + + // Fields + + /** The name of the manifest entry: META-INF/MANIFEST.MF */ + public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + /** + * The manifest of this file, if any, otherwise null. + * Read by the constructor. + */ + private final Manifest manifest; + + /** Wether to verify the manifest and all entries */ + private boolean verify; + + // Constructors + + /** + * Creates a new JarFile, tries to read the manifest and if the manifest + * exists verifies it. + * + * @param fileName the name of the file to open + * @exception FileNotFoundException if the fileName cannot be found + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(String fileName) throws FileNotFoundException, + IOException { + this (fileName, true); + } + + /** + * Creates a new JarFile, tries to read the manifest and if the manifest + * exists and verify is true verfies it. + * + * @param fileName the name of the file to open + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @exception FileNotFoundException if the fileName cannot be found + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(String fileName, boolean verify) throws + FileNotFoundException, + IOException { + super(fileName); + manifest = readManifest(); + if (verify) + verify(); + } + + /** + * Creates a new JarFile, tries to read the manifest and if the manifest + * exists verifies it. + * + * @param file the file to open as a jar file + * @exception FileNotFoundException if the file does not exits + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(File file) throws FileNotFoundException, + IOException { + this (file, true); + } + + /** + * Creates a new JarFile, tries to read the manifest and if the manifest + * exists and verify is true verfies it. + * + * @param file the file to open to open as a jar file + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @exception FileNotFoundException if file does not exist + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(File file, boolean verify) throws FileNotFoundException, + IOException { + super(file); + manifest = readManifest(); + if (verify) + verify(); + } + + /** + * XXX - not yet implemented in java.util.zip.ZipFile + * + * @param file the file to open to open as a jar file + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @param mode XXX - see ZipFile + * @exception FileNotFoundException XXX + * @exception IOException XXX + * @exception IllegalArgumentException XXX + * + * @since 1.3 + */ + public JarFile(File file, boolean verify, int mode) throws + FileNotFoundException, + IOException, + IllegalArgumentException { + // XXX - For now don't use super(file, mode) + this(file, verify); + /* super(file, mode); + manifest = readManifest(); + if (verify) + verify(); */ + } + + // Methods + + /** + * XXX - should verify the manifest file + */ + private void verify() { + // only check if manifest is not null + if (manifest == null) { + verify = false; + return; + } + + verify = true; + // XXX - verify manifest + } + + /** + * Parses and returns the manifest if it exists, otherwise returns null. + */ + private Manifest readManifest() { + try { + ZipEntry manEntry = super.getEntry(MANIFEST_NAME); + if (manEntry != null) { + InputStream in = super.getInputStream(manEntry); + return new Manifest(in); + } else { + return null; + } + } catch (IOException ioe) { + return null; + } + } + + /** + * Returns a enumeration of all the entries in the JarFile. + * Note that also the Jar META-INF entries are returned. + * + * @exception IllegalStateException when the JarFile is already closed + */ + public Enumeration entries() throws IllegalStateException { + return new JarEnumeration(super.entries()); + } + + /** + * Wraps a given Zip Entries Enumeration. For every zip entry a + * JarEntry is created and the corresponding Attributes are looked up. + * XXX - Should also look up the certificates. + */ + private class JarEnumeration implements Enumeration { + + private final Enumeration entries; + + JarEnumeration(Enumeration e) { + entries = e; + } + + public boolean hasMoreElements() { + return entries.hasMoreElements(); + } + + public Object nextElement() { + ZipEntry zip = (ZipEntry) entries.nextElement(); + JarEntry jar = new JarEntry(zip); + if (manifest != null) { + jar.attr = manifest.getAttributes(jar.getName()); + } + // XXX jar.certs + return jar; + } + } + + /** + * XXX + * It actually returns a JarEntry not a zipEntry + * @param name XXX + */ + public ZipEntry getEntry(String name) { + ZipEntry entry = super.getEntry(name); + if (entry != null) { + JarEntry jarEntry = new JarEntry(getEntry(name)); + if (manifest != null) { + jarEntry.attr = manifest.getAttributes(name); + // XXX jarEntry.certs + } + return jarEntry; + } + return null; + } + + /** + * XXX should verify the inputstream + * @param entry XXX + * @exception ZipException XXX + * @exception IOException XXX + */ + public synchronized InputStream getInputStream(ZipEntry entry) throws + ZipException, + IOException { + return super.getInputStream(entry); // XXX verify + } + + /** + * Returns the JarEntry that belongs to the name if such an entry + * exists in the JarFile. Returns null otherwise + * Convenience method that just casts the result from <code>getEntry</code> + * to a JarEntry. + * + * @param name the jar entry name to look up + * @return the JarEntry if it exists, null otherwise + */ + public JarEntry getJarEntry(String name) { + return (JarEntry)getEntry(name); + } -public class JarFile extends ZipFile -{ - private boolean verify; - - public JarFile (String file) throws java.io.IOException - { - super (file); - } - - public JarFile (File file) throws java.io.IOException - { - super (file); - } - - public JarFile (String file, boolean verify) throws java.io.IOException - { - super (file); - this.verify = verify; - } - - public JarFile (File file, boolean verify) throws java.io.IOException - { - super (file); - this.verify = verify; - } - - public JarEntry getJarEntry (String name) - { - ZipEntry ent = getEntry(name); - if (ent == null) - return null; - else - return new JarEntry(ent); - } + /** + * Returns the manifest for this JarFile or null when the JarFile does not + * contain a manifest file. + */ + public Manifest getManifest() { + return manifest; + } } diff --git a/libjava/java/util/jar/JarInputStream.java b/libjava/java/util/jar/JarInputStream.java index 33daf61c5fb..c8f5340637b 100644 --- a/libjava/java/util/jar/JarInputStream.java +++ b/libjava/java/util/jar/JarInputStream.java @@ -1,32 +1,175 @@ -/* Copyright (C) 1999 Free Software Foundation +/* JarInputStream.java - InputStream for reading jar files + Copyright (C) 2000 Free Software Foundation, Inc. - This file is part of libgcj. +This file is part of GNU Classpath. -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ package java.util.jar; -import java.util.zip.*; +import java.io.InputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; /** - * Does not implement any of the security. Just a place holder, so - * that I can implement URLClassLoader. + * InputStream for reading jar files. + * XXX - verification of the signatures in the Manifest file is not yet + * implemented. * - * @author Kresten Krab Thorup <krab@gnu.org> - * @date August 10, 1999. + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) */ + +public class JarInputStream extends ZipInputStream { + + // Fields + + /** The manifest for this file or null when there was no manifest. */ + private Manifest manifest; + + /** The first real JarEntry for this file. Used by readManifest() to store + an entry that isn't the manifest but that should be returned by + getNextEntry next time it is called. Null when no firstEntry was read + while searching for the manifest entry, or when it has already been + returned by getNextEntry(). */ + private JarEntry firstEntry; + + // Constructors + + /** + * Creates a new JarInputStream and tries to read the manifest. + * If such a manifest is present the JarInputStream tries to verify all + * the entry signatures while reading. + * + * @param in InputStream to read the jar from + * @exception IOException when an error occurs when opening or reading + */ + public JarInputStream(InputStream in) throws IOException { + this(in, true); + } + + /** + * Creates a new JarInputStream and tries to read the manifest. + * If such a manifest is present and verify is true, the JarInputStream + * tries to verify all the entry signatures while reading. + * + * @param in InputStream to read the jar from + * @param verify wheter or not to verify the manifest entries + * @exception IOException when an error occurs when opening or reading + */ + public JarInputStream(InputStream in, boolean verify) throws IOException { + super(in); + readManifest(verify); + } -public class JarInputStream extends ZipInputStream -{ - public JarEntry getNextJarEntry () throws java.io.IOException - { - return new JarEntry (getNextEntry ()); + // Methods + + /** + * Set the manifest if found. Skips all entries that start with "META-INF/" + * + * @param verify when true (and a Manifest is found) checks the Manifest, + * when false no check is performed + * @exception IOException if an error occurs while reading + */ + private void readManifest(boolean verify) throws IOException { + firstEntry = (JarEntry) super.getNextEntry(); + while ((firstEntry != null) && + firstEntry.getName().startsWith("META-INF/")) { + if(firstEntry.getName().equals(JarFile.MANIFEST_NAME)) { + manifest = new Manifest(this); + } + firstEntry = (JarEntry) super.getNextEntry(); + } + closeEntry(); + + if (verify) { + // XXX + } + } + + /** + * Creates a JarEntry for a particular name and consults the manifest + * for the Attributes of the entry. + * Used by <code>ZipEntry.getNextEntry()</code> + * + * @param name the name of the new entry + */ + protected ZipEntry createZipEntry(String name) { + ZipEntry zipEntry = super.createZipEntry(name); + JarEntry jarEntry = new JarEntry(zipEntry); + if (manifest != null) { + jarEntry.attr = manifest.getAttributes(name); + } + return jarEntry; + } + + /** + * Returns the Manifest for the jar file or null if there was no Manifest. + */ + public Manifest getManifest() { + return manifest; } - public JarInputStream (java.io.InputStream is) - { - super(is); - } + /** + * Returns the next entry or null when there are no more entries. + * Does actually return a JarEntry, if you don't want to cast it yourself + * use <code>getNextJarEntry()</code>. Does not return any entries found + * at the beginning of the ZipFile that are special + * (those that start with "META-INF/"). + * + * @exception IOException if an IO error occurs when reading the entry + */ + public ZipEntry getNextEntry() throws IOException { + ZipEntry entry; + if (firstEntry != null) { + entry = firstEntry; + firstEntry = null; + } else { + entry = super.getNextEntry(); + } + return entry; + } + + /** + * Returns the next jar entry or null when there are no more entries. + * + * @exception IOException if an IO error occurs when reading the entry + */ + public JarEntry getNextJarEntry() throws IOException { + return (JarEntry)getNextEntry(); + } + + /** + * XXX + * + * @param buf XXX + * @param off XXX + * @param len XXX + * @return XXX + * @exception IOException XXX + */ + public int read(byte[] buf, int off, int len) throws IOException { + // XXX if (verify) {} + return super.read(buf, off, len); + } } diff --git a/libjava/java/util/jar/JarOutputStream.java b/libjava/java/util/jar/JarOutputStream.java new file mode 100644 index 00000000000..78bb35979ea --- /dev/null +++ b/libjava/java/util/jar/JarOutputStream.java @@ -0,0 +1,98 @@ +/* JarOutputStream.java - OutputStream for writing jar files + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + +package java.util.jar; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * OutputStream for writing jar files. + * A special ZipOutputStream that can take JarEntries and can have a optional + * Manifest as first entry. + * + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarOutputStream extends ZipOutputStream { + + // Constructors + + /** + * Creates a new JarOutputStream without a manifest entry. + * + * @param out the stream to create the new jar on + * @exception IOException if something unexpected happend + */ + public JarOutputStream(OutputStream out) throws IOException { + this(out, null); + } + + /** + * Creates a new JarOutputStream with a manifest entry. + * The manifest will be the first entry in the jar. + * + * @param out the stream to create the new jar on + * @param man the manifest that should be put in the jar file or null + * for no manifest entry + * @exception IOException if something unexpected happend + */ + public JarOutputStream(OutputStream out, Manifest man) throws IOException { + super(out); + if (man != null) + writeManifest(man); + } + + // Methods + + /** + * Writes the manifest to a new JarEntry in this JarOutputStream with as + * name JarFile.MANIFEST_NAME. + * + * @param manifest the non null manifest to be written + * @exception IOException if something unexpected happend + */ + private void writeManifest(Manifest manifest) throws IOException { + // Create a new Jar Entry for the Manifest + JarEntry entry = new JarEntry(JarFile.MANIFEST_NAME); + putNextEntry(entry); + manifest.write(this); + closeEntry(); + } + + /** + * Prepares the JarOutputStream for writing the next entry. + * This implementation just calls <code>super.putNextEntre()</code>. + * + * @param entry The information for the next entry + * @exception IOException when some unexpected I/O exception occured + */ + public void putNextEntry(ZipEntry entry) throws IOException { + super.putNextEntry(entry); // XXX + } +} diff --git a/libjava/java/util/jar/Manifest.java b/libjava/java/util/jar/Manifest.java new file mode 100644 index 00000000000..a4034e2c65c --- /dev/null +++ b/libjava/java/util/jar/Manifest.java @@ -0,0 +1,406 @@ +/* Attributes.java -- Reads, writes and manipulaties jar manifest files + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + +package java.util.jar; + +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.io.Reader; +import java.io.Writer; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Reads, writes and manipulaties jar manifest files. + * XXX + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ +public class Manifest implements Cloneable { + + // Fields + + /** The main attributes of the manifest (jar file). */ + private final Attributes mainAttr; + + /** A map of atrributes for all entries described in this Manifest. */ + private final Map entries; + + // Constructors + + /** + * Creates a new empty Manifest. + */ + public Manifest() { + mainAttr = new Attributes(); + entries = new Hashtable(); + } + + /** + * Creates a Manifest from the supplied input stream. + * + * @see read(Inputstream) + * @see write(OutputStream) + * + * @param InputStream the input stream to read the manifest from + * @exception IOException when an i/o exception occurs or the input stream + * does not describe a valid manifest + */ + public Manifest(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Creates a Manifest from another Manifest. + * Makes a deep copy of the main attributes, but a shallow copy of + * the other entries. This means that you can freely add, change or remove + * the main attributes or the entries of the new manifest without effecting + * the original manifest, but adding, changing or removing attributes from + * a particular entry also changes the attributes of that entry in the + * original manifest. + * + * @see clone() + * @param man the Manifest to copy from + */ + public Manifest (Manifest man) { + mainAttr = new Attributes(man.getMainAttributes()); + entries = new Hashtable(man.getEntries()); + } + + // Methods + + /** + * Gets the main attributes of this Manifest. + */ + public Attributes getMainAttributes() { + return mainAttr; + } + + /** + * Gets a map of entry Strings to Attributes for all the entries described + * in this manifest. Adding, changing or removing from this entries map + * changes the entries of this manifest. + */ + public Map getEntries() { + return entries; + } + + /** + * Returns the Attributes associated with the Entry. + * <p> + * Implemented as: + * <code>return (Attributes)getEntries().get(entryName)</code> + * + * @param entryName the name of the entry to look up + * @return the attributes associated with the entry or null when none + */ + public Attributes getAttributes(String entryName) { + return (Attributes)getEntries().get(entryName); + } + + /** + * Clears the main attributes and removes all the entries from the + * manifest. + */ + public void clear() { + mainAttr.clear(); + entries.clear(); + } + + /** + * XXX + */ + public void read(InputStream in) throws IOException { + BufferedReader br = new BufferedReader( + new InputStreamReader(in, "8859_1")); + read_main_section(getMainAttributes(), br); + read_individual_sections(getEntries(), br); + } + + // Private Static methods for reading the Manifest file from BufferedReader + + private static void read_main_section(Attributes attr, + BufferedReader br) throws + IOException { + read_version_info(attr, br); + read_attributes(attr, br); + } + + private static void read_version_info(Attributes attr, + BufferedReader br) throws + IOException { + String version_header = Attributes.Name.MANIFEST_VERSION.toString(); + try { + String value = expect_header(version_header, br); + attr.putValue(version_header, value); + } catch (IOException ioe) { + throw new JarException( + "Manifest should start with a " + version_header + + ": " + ioe.getMessage()); + } + } + + private static String expect_header(String header, BufferedReader br) + throws IOException { + + String s = br.readLine(); + if (s == null) { + throw new JarException("unexpected end of file"); + } + return expect_header(header, br, s); + } + + private static String expect_header(String header, BufferedReader br, + String s) throws IOException { + try { + String name = s.substring(0, header.length() + 1); + if (name.equalsIgnoreCase(header + ":")) { + String value_start = s.substring(header.length() + 2); + return read_header_value(value_start, br); + } + } catch (IndexOutOfBoundsException iobe) {} + // If we arrive here, something went wrong + throw new JarException("unexpected '" + s + "'"); + } + + private static String read_header_value(String s, BufferedReader br) + throws IOException { + boolean try_next = true; + while (try_next) { + // Lets see if there is something on the next line + br.mark(1); + if (br.read() == ' ') { + s += br.readLine(); + } else { + br.reset(); + try_next = false; + } + } + return s; + } + + private static void read_attributes(Attributes attr, + BufferedReader br) throws + IOException { + String s = br.readLine(); + while (s != null && (!s.equals(""))) { + read_attribute(attr, s, br); + s = br.readLine(); + } + } + + private static void read_attribute(Attributes attr, String s, + BufferedReader br) throws IOException { + try { + int colon = s.indexOf(": "); + String name = s.substring(0, colon); + String value_start = s.substring(colon+2); + String value = read_header_value(value_start, br); + attr.putValue(name, value); + } catch (IndexOutOfBoundsException iobe) { + throw new JarException( + "Manifest contains a bad header: " + s); + } + } + + private static void read_individual_sections(Map entries, + BufferedReader br) throws + IOException { + String s = br.readLine(); + while (s != null && (!s.equals(""))) { + Attributes attr = read_section_name(s, br, entries); + read_attributes(attr, br); + s = br.readLine(); + } + } + + private static Attributes read_section_name(String s, BufferedReader br, + Map entries) throws + JarException { + try { + String name = expect_header("Name", br, s); + Attributes attr = new Attributes(); + entries.put(name, attr); + return attr; + } catch(IOException ioe) { + throw new JarException + ("Section should start with a Name header: " + + ioe.getMessage()); + } + } + + /** + * XXX + */ + 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"); + } + } + + // Private Static functions for writing the Manifest file to a PrintWriter + + private static void write_main_section(Attributes attr, + PrintWriter pw) + throws JarException { + + write_version_info(attr, pw); + write_main_attributes(attr, pw); + } + + private static void write_version_info(Attributes attr, PrintWriter pw) { + // First check if there is already a version attribute set + String version = attr.getValue(Attributes.Name.MANIFEST_VERSION); + if (version == null) { + version = "1.0"; + } + write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw); + } + + private static void write_header(String name, String value, + PrintWriter pw) { + pw.print(name + ": "); + + int last = 68 - name.length(); + if (last > value.length()) { + pw.println(value); + } else { + 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; + } + } + + private static void write_main_attributes(Attributes attr, + PrintWriter pw) throws + JarException { + Iterator it = attr.entrySet().iterator(); + while(it.hasNext()) { + 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); + } + } + } + + private static void write_attribute_entry(Map.Entry entry, + PrintWriter pw) throws + JarException { + String name = entry.getKey().toString(); + String value = entry.getValue().toString(); + + if (name.equalsIgnoreCase("Name")) { + throw new JarException("Attributes cannot be called 'Name'"); + } + if (name.startsWith("From")) { + throw new JarException( + "Header cannot start with the four letters 'From'" + + name); + } + write_header(name, value, pw); + } + + private static void write_individual_sections(Map entries, + PrintWriter pw) + throws JarException { + + 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(); + } + } + + private static void write_entry_attributes(Attributes attr, + PrintWriter pw) throws + JarException { + Iterator it = attr.entrySet().iterator(); + while(it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + write_attribute_entry(entry, pw); + } + } + + /** + * Makes a deep copy of the main attributes, but a shallow copy of + * the other entries. This means that you can freely add, change or remove + * the main attributes or the entries of the new manifest without effecting + * the original manifest, but adding, changing or removing attributes from + * a particular entry also changes the attributes of that entry in the + * original manifest. Calls <CODE>new Manifest(this)</CODE>. + */ + public Object clone() { + return new Manifest(this); + } + + /** + * Checks if another object is equal to this Manifest object. + * Another Object is equal to this Manifest object if it is an instance of + * Manifest and the main attributes and the entries of the other manifest + * are equal to this one. + */ + public boolean equals(Object o) { + return (o instanceof Manifest) && + (mainAttr.equals(((Manifest)o).mainAttr)) && + (entries.equals(((Manifest)o).entries)); + } + + /** + * Calculates the hash code of the manifest. Implemented by a xor of the + * hash code of the main attributes with the hash code of the entries map. + */ + public int hashCode() { + return mainAttr.hashCode() ^ entries.hashCode(); + } + +} |