home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 12.4 KB | 450 lines |
- /*
- * @(#)Attributes.java 1.18 98/03/18
- *
- * Copyright 1997, 1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.util.jar;
-
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Set;
- import java.util.Collection;
- import java.util.AbstractSet;
- import java.util.Iterator;
-
- /**
- * The Attributes class maps Manifest attribute names to associated string
- * values. Attribute names are case-insensitive and restricted to the ASCII
- * characters in the set [0-9a-zA-Z_-]. Attribute values can contain any
- * characters and will be UTF8-encoded when written to the output stream.
- *
- * @author David Connelly
- * @version 1.18, 03/18/98
- * @see Manifest
- * @since JDK1.2
- */
- public
- class Attributes implements Map, Cloneable {
- /**
- * The attribute name-value mappings.
- */
- protected Map amap;
-
- /**
- * Constructs a new, empty Attributes object.
- */
- public Attributes() {
- amap = new HashMap(11);
- }
-
- /**
- * Constructs a new Attributes object with the same attribute name-value
- * mappings as in the specified Attributes.
- */
- public Attributes(Attributes attr) {
- amap = new HashMap(attr);
- }
-
- /**
- * The Attributes.Name class represents an attribute name stored in
- * this Map. Attribute names are case-insensitive and restricted to
- * the ASCII characters in the set [0-9a-zA-Z_-].
- */
- public static class Name {
- private String name;
- private int hashcode = -1;
-
- /**
- * Constructs a new attribute name using the given string name.
- * @param name the attribute string name
- * @exception IllegalArgumentException if the attribute name was
- * invalid
- * @exception NullPointerException if the attribute name was null
- */
- public Name(String name) {
- if (name == null) {
- throw new NullPointerException("name");
- }
- if (!isValid(name)) {
- throw new IllegalArgumentException("name");
- }
- this.name = name;
- }
-
- private boolean isValid(String name) {
- int len = name.length();
- if (len > 70) {
- return false;
- }
- for (int i = 0; i < len; i++) {
- char c = name.charAt(i);
- if (!isAlpha(c) && !isDigit(c) && c != '_' && c != '-') {
- return false;
- }
- }
- return true;
- }
-
- private boolean isAlpha(char c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
- }
-
- private boolean isDigit(char c) {
- return c >= '0' && c <= '9';
- }
-
- /**
- * Compares this attribute name to another for equality.
- * @param o the object to compare
- */
- public boolean equals(Object o) {
- if (o instanceof Name) {
- return name.equalsIgnoreCase(((Name)o).name);
- } else {
- return false;
- }
- }
-
- public int hashCode() {
- if (hashcode == -1) {
- hashcode = name.toLowerCase().hashCode();
- }
- return hashcode;
- }
-
- /**
- * Returns the attribute name as a String.
- */
- public String toString() {
- return name;
- }
-
- /**
- * Some predefined attribute names.
- */
- public static final Name
- MANIFEST_VERSION = new Name("Manifest-Version"),
- SIGNATURE_VERSION = new Name("Signature-Version"),
- ARCHIVE_SEALED = new Name("Archive-Sealed"),
- PACKAGE_SEALED = new Name("Package-Sealed"),
- CONTENT_TYPE = new Name("Content-Type"),
- CLASS_PATH = new Name("Class-Path"),
- MAIN_CLASS = new Name("Main-Class"),
- IMPLEMENTATION_TITLE = new Name("Package-Title"),
- IMPLEMENTATION_VERSION = new Name("Package-Version"),
- IMPLEMENTATION_VENDOR = new Name("Package-Vendor"),
- SPECIFICATION_TITLE = new Name("Specification-Title"),
- SPECIFICATION_VERSION = new Name("Specification-Version"),
- SPECIFICATION_VENDOR = new Name("Specification-Vendor");
- }
-
- /**
- * Returns the value of the specified attribute name, or null if the
- * attribute name was not found.
- *
- * @param name the attribute name
- * @exception ClassCastException if name is not a Attributes.Name
- * @exception NullPointerException if name is null
- */
- public Object get(Object name) {
- return (String)amap.get((Name)name);
- }
-
- /**
- * Returns the value of the specified attribute name, specified as
- * a string, or null if the attribute was not found. The attribute
- * name is case-insensitive.
- * <p>
- * This method is merely shorthand for the expression:
- * <pre>
- * (String)get(new Attributes.Name((String)name));
- * </pre>
- *
- * @param name the attribute name as a string
- * @exception NullPointerException if the name is null
- */
- public String getValue(String name) {
- return (String)get(new Name(name));
- }
-
- /**
- * Returns the value of the specified Attributes.Name, or null if the
- * attribute was not found.
- * <p>
- * This method is merely shorthand for the expression:
- * <pre>
- * (String)get(name)
- * </pre>
- *
- * @param name the Attributes.Name object
- * @exception NullPointerException if name is null
- */
- public String getValue(Name name) {
- return (String)get(name);
- }
-
- /**
- * Associates the specified value with the specified attribute name
- * (key) in this Map. If the Map previously contained a mapping for
- * the attribute name, the old value is replaced.
- *
- * @param name the attribute name
- * @param value the attribute value
- * @return the previous value of the attribute, or null if none
- * @exception ClassCastException if the name is not a Attributes.Name
- * or the value is not a String
- * @ecception NullPointerException if name or value is null
- */
- public Object put(Object name, Object value) {
- return (String)amap.put((Name)name, (String)value);
- }
-
- /**
- * Associates the specified value with the specified attribute name,
- * specified as a String. The attributes name is case-insensitive.
- * If the Map previously contained a mapping for the attribute name,
- * the old value is replaced.
- * <p>
- * This method is merely shorthand for the expression:
- * <pre>
- * (String)put(new Attributes.Name(name), value);
- * </pre>
- *
- * @param the attribute name as a string
- * @param value the attribute value
- * @exception IllegalArgumentException if the attribute name is invalid
- * @exception NullPointerException if name or value is null
- */
- public String putValue(String name, String value) {
- return (String)put(new Name(name), value);
- }
-
- /**
- * Removes the attribute with the specified name (key) from this Map.
- * Returns the previous attribute value, or null if none.
- *
- * @param name attribute name
- * @exception ClassCastException if the name is not a Attributes.Name
- * @exception NullPointerException if the name is null
- */
- public Object remove(Object name) {
- return (String)amap.remove((Name)name);
- }
-
- /**
- * Returns true if this Map maps one or more attribute names (keys)
- * to the specified value.
- *
- * @param value the attribute value
- */
- public boolean containsValue(Object value) {
- return amap.containsValue(value);
- }
-
- /**
- * Returns true if this Map contains the specified attribute name (key).
- *
- * @param name the attribute name
- * @exception ClassCastException if the name is not a Attributes.Name
- * @exception NullPointerException if the name is null
- */
- public boolean containsKey(Object name) {
- return amap.containsKey((Name)name);
- }
-
- /**
- * Copies all of the attribute name-value mappings from the specified
- * Attributes to this Map. Duplicate mappings will be replaced.
- *
- * @param attr the Attributes to be stored in this map
- * @exception ClassCastException if attr is not an instance of Attributes
- * @exception NullPointerException if attr is null
- */
- public void putAll(Map attr) {
- amap.putAll((Attributes)attr);
- }
-
- /**
- * Removes all attributes from this Map.
- */
- public void clear() {
- amap.clear();
- }
-
- /**
- * Returns the number of attributes in this Map.
- */
- public int size() {
- return amap.size();
- }
-
- /**
- * Returns true if this Map contains no attributes.
- */
- public boolean isEmpty() {
- return amap.isEmpty();
- }
-
- /**
- * Returns a Set view of the attribute names (keys) contained in this Map.
- */
- public Set keySet() {
- return amap.keySet();
- }
-
- /**
- * Returns a Collection view of the attribute values contained in this Map.
- */
- public Collection values() {
- return amap.values();
- }
-
- /**
- * Returns a Collection view of the attribute name-value mappings
- * contained in this Map.
- */
- public Set entries() {
- return new AbstractSet() {
- private Set c = amap.entries();
- public int size() {
- return c.size();
- }
- public Iterator iterator() {
- return new Iterator() {
- private Iterator it = c.iterator();
- public boolean hasNext() {
- return it.hasNext();
- }
- public Object next() {
- return new Entry((Map.Entry)it.next());
- }
- public void remove() {
- it.remove();
- }
- };
- }
- };
- }
-
- // XXX Workaround for inner classes bug
- static class Entry implements Map.Entry {
- Map.Entry e;
- Entry(Map.Entry e) {
- this.e = e;
- }
- public Object getKey() {
- return (Name)e.getKey();
- }
- public Object getValue() {
- return (String)e.getValue();
- }
- public Object setValue(Object o) {
- return (String)e.setValue((String)o);
- }
- public boolean equals(Object o) {
- if (o instanceof Entry) {
- return e.equals(((Entry)o).e);
- } else {
- return false;
- }
- }
- public int hashCode() {
- return e.hashCode();
- }
- }
-
- /**
- * Compares the specified Attributes object with this Map for equality.
- * Returns true if the given object is also an instance of Attributes
- * and the two Attributes objects represent the same mappings.
- *
- * @param o the Object to be compared
- * @return true if the specified Object is equal to this Map
- */
- public boolean equals(Object o) {
- if (o instanceof Attributes) {
- return amap.equals(((Attributes)o).amap);
- } else {
- return false;
- }
- }
-
- /**
- * Returns the hash code value for this Map.
- */
- public int hashCode() {
- return amap.hashCode();
- }
-
- /**
- * Returns a copy of the Attributes, implemented as follows:
- * <pre>
- * public Object clone() { return new Attributes(this); }
- * </pre>
- * Since the attribute names and values are themselves immutable,
- * the Attributes returned can be safely modified without affecting
- * the original.
- */
- public Object clone() {
- return new Attributes(this);
- }
-
- /*
- * Writes the current attributes to the specified data output stream.
- * XXX Need to handle UTF8 values and break up lines longer than
- * 72 bytes (why do we still need to do this?)
- */
- void write(DataOutputStream os) throws IOException {
- Iterator it = entries().iterator();
- while (it.hasNext()) {
- Entry e = (Entry)it.next();
- os.writeBytes(e.getKey().toString());
- os.writeBytes(": ");
- os.writeBytes(e.getValue().toString());
- os.writeBytes("\r\n");
- }
- os.writeBytes("\r\n");
- }
-
- /*
- * Reads attributes from the specified input stream.
- * XXX Need to handle UTF8 values.
- */
- void read(DataInputStream is) throws IOException {
- String s, name = null, value = null;
- while ((s = is.readLine()) != null && s.length() > 0) {
- if (s.charAt(0) == ' ') {
- // Continuation of previous line
- if (name == null) {
- throw new IOException("invalid header field format");
- }
- value = value + s.substring(1);
- } else {
- int i = s.indexOf(':');
- if (i < 1 || i > s.length() - 2 || s.charAt(i + 1) != ' ') {
- throw new IOException("invalid header field format");
- }
- name = s.substring(0, i);
- value = s.substring(i + 2);
- }
- try {
- putValue(name, value);
- } catch (IllegalArgumentException e) {
- throw new IOException("invalid header field name: " + name);
- }
- }
- }
- }
-