home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / jar / Attributes.java next >
Encoding:
Java Source  |  1998-03-20  |  12.4 KB  |  450 lines

  1. /*
  2.  * @(#)Attributes.java    1.18 98/03/18
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util.jar;
  16.  
  17. import java.io.DataInputStream;
  18. import java.io.DataOutputStream;
  19. import java.io.IOException;
  20. import java.util.HashMap;
  21. import java.util.Map;
  22. import java.util.Set;
  23. import java.util.Collection;
  24. import java.util.AbstractSet;
  25. import java.util.Iterator;
  26.  
  27. /**
  28.  * The Attributes class maps Manifest attribute names to associated string
  29.  * values. Attribute names are case-insensitive and restricted to the ASCII
  30.  * characters in the set [0-9a-zA-Z_-]. Attribute values can contain any
  31.  * characters and will be UTF8-encoded when written to the output stream.
  32.  *
  33.  * @author  David Connelly
  34.  * @version 1.18, 03/18/98
  35.  * @see        Manifest
  36.  * @since   JDK1.2
  37.  */
  38. public
  39. class Attributes implements Map, Cloneable {
  40.     /**
  41.      * The attribute name-value mappings.
  42.      */
  43.     protected Map amap;
  44.  
  45.     /**
  46.      * Constructs a new, empty Attributes object.
  47.      */
  48.     public Attributes() {
  49.     amap = new HashMap(11);
  50.     }
  51.  
  52.     /**
  53.      * Constructs a new Attributes object with the same attribute name-value
  54.      * mappings as in the specified Attributes.
  55.      */
  56.     public Attributes(Attributes attr) {
  57.     amap = new HashMap(attr);
  58.     }
  59.  
  60.     /**
  61.      * The Attributes.Name class represents an attribute name stored in
  62.      * this Map. Attribute names are case-insensitive and restricted to
  63.      * the ASCII characters in the set [0-9a-zA-Z_-].
  64.      */
  65.     public static class Name {
  66.     private String name;
  67.     private int hashcode = -1;
  68.  
  69.     /**
  70.      * Constructs a new attribute name using the given string name.
  71.      * @param name the attribute string name
  72.      * @exception IllegalArgumentException if the attribute name was
  73.      *            invalid
  74.      * @exception NullPointerException if the attribute name was null
  75.      */
  76.     public Name(String name) {
  77.         if (name == null) {
  78.         throw new NullPointerException("name");
  79.         }
  80.         if (!isValid(name)) {
  81.         throw new IllegalArgumentException("name");
  82.         }
  83.         this.name = name;
  84.     }
  85.  
  86.     private boolean isValid(String name) {
  87.         int len = name.length();
  88.         if (len > 70) {
  89.         return false;
  90.         }
  91.         for (int i = 0; i < len; i++) {
  92.         char c = name.charAt(i);
  93.         if (!isAlpha(c) && !isDigit(c) && c != '_' && c != '-') {
  94.             return false;
  95.         }
  96.         }
  97.         return true;
  98.     }
  99.  
  100.     private boolean isAlpha(char c) {
  101.         return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  102.     }
  103.  
  104.     private boolean isDigit(char c) {
  105.         return c >= '0' && c <= '9';
  106.     }
  107.  
  108.     /**
  109.      * Compares this attribute name to another for equality.
  110.      * @param o the object to compare
  111.      */
  112.     public boolean equals(Object o) {
  113.         if (o instanceof Name) {
  114.         return name.equalsIgnoreCase(((Name)o).name);
  115.         } else {
  116.         return false;
  117.         }
  118.     }
  119.       
  120.         public int hashCode() {
  121.         if (hashcode == -1) {
  122.         hashcode = name.toLowerCase().hashCode();
  123.         }
  124.         return hashcode;
  125.     }
  126.  
  127.     /**
  128.      * Returns the attribute name as a String.
  129.      */
  130.     public String toString() {
  131.         return name;
  132.     }
  133.  
  134.     /**
  135.      * Some predefined attribute names.
  136.      */
  137.         public static final Name
  138.         MANIFEST_VERSION       = new Name("Manifest-Version"),
  139.         SIGNATURE_VERSION      = new Name("Signature-Version"),
  140.         ARCHIVE_SEALED         = new Name("Archive-Sealed"),
  141.         PACKAGE_SEALED         = new Name("Package-Sealed"),
  142.         CONTENT_TYPE           = new Name("Content-Type"),
  143.         CLASS_PATH             = new Name("Class-Path"),
  144.         MAIN_CLASS           = new Name("Main-Class"),
  145.         IMPLEMENTATION_TITLE   = new Name("Package-Title"),
  146.         IMPLEMENTATION_VERSION = new Name("Package-Version"),
  147.         IMPLEMENTATION_VENDOR  = new Name("Package-Vendor"),
  148.         SPECIFICATION_TITLE    = new Name("Specification-Title"),
  149.         SPECIFICATION_VERSION  = new Name("Specification-Version"),
  150.         SPECIFICATION_VENDOR   = new Name("Specification-Vendor");
  151.     }
  152.  
  153.     /**
  154.      * Returns the value of the specified attribute name, or null if the
  155.      * attribute name was not found.
  156.      *
  157.      * @param name the attribute name
  158.      * @exception ClassCastException if name is not a Attributes.Name
  159.      * @exception NullPointerException if name is null
  160.      */
  161.     public Object get(Object name) {
  162.     return (String)amap.get((Name)name);
  163.     }
  164.  
  165.     /**
  166.      * Returns the value of the specified attribute name, specified as
  167.      * a string, or null if the attribute was not found. The attribute
  168.      * name is case-insensitive.
  169.      * <p>
  170.      * This method is merely shorthand for the expression:
  171.      * <pre>
  172.      *        (String)get(new Attributes.Name((String)name));
  173.      * </pre>
  174.      *
  175.      * @param name the attribute name as a string
  176.      * @exception NullPointerException if the name is null
  177.      */
  178.     public String getValue(String name) {
  179.     return (String)get(new Name(name));
  180.     }
  181.  
  182.     /**
  183.      * Returns the value of the specified Attributes.Name, or null if the
  184.      * attribute was not found.
  185.      * <p>
  186.      * This method is merely shorthand for the expression:
  187.      * <pre>
  188.      *     (String)get(name)
  189.      * </pre>
  190.      *
  191.      * @param name the Attributes.Name object
  192.      * @exception NullPointerException if name is null
  193.      */
  194.     public String getValue(Name name) {
  195.     return (String)get(name);
  196.     }
  197.  
  198.     /**
  199.      * Associates the specified value with the specified attribute name
  200.      * (key) in this Map. If the Map previously contained a mapping for
  201.      * the attribute name, the old value is replaced.
  202.      *
  203.      * @param name the attribute name
  204.      * @param value the attribute value
  205.      * @return the previous value of the attribute, or null if none
  206.      * @exception ClassCastException if the name is not a Attributes.Name
  207.      *            or the value is not a String
  208.      * @ecception NullPointerException if name or value is null 
  209.      */
  210.     public Object put(Object name, Object value) {
  211.     return (String)amap.put((Name)name, (String)value);
  212.     }
  213.  
  214.     /**
  215.      * Associates the specified value with the specified attribute name,
  216.      * specified as a String. The attributes name is case-insensitive.
  217.      * If the Map previously contained a mapping for the attribute name,
  218.      * the old value is replaced.
  219.      * <p>
  220.      * This method is merely shorthand for the expression:
  221.      * <pre>
  222.      *        (String)put(new Attributes.Name(name), value);
  223.      * </pre>
  224.      *
  225.      * @param the attribute name as a string
  226.      * @param value the attribute value
  227.      * @exception IllegalArgumentException if the attribute name is invalid
  228.      * @exception NullPointerException if name or value is null
  229.      */
  230.     public String putValue(String name, String value) {
  231.     return (String)put(new Name(name), value);
  232.     }
  233.  
  234.     /**
  235.      * Removes the attribute with the specified name (key) from this Map.
  236.      * Returns the previous attribute value, or null if none.
  237.      *
  238.      * @param name attribute name
  239.      * @exception ClassCastException if the name is not a Attributes.Name
  240.      * @exception NullPointerException if the name is null
  241.      */
  242.     public Object remove(Object name) {
  243.     return (String)amap.remove((Name)name);
  244.     }
  245.  
  246.     /**
  247.      * Returns true if this Map maps one or more attribute names (keys)
  248.      * to the specified value.
  249.      *
  250.      * @param value the attribute value
  251.      */
  252.     public boolean containsValue(Object value) {
  253.     return amap.containsValue(value);
  254.     }
  255.  
  256.     /**
  257.      * Returns true if this Map contains the specified attribute name (key).
  258.      *
  259.      * @param name the attribute name
  260.      * @exception ClassCastException if the name is not a Attributes.Name
  261.      * @exception NullPointerException if the name is null
  262.      */
  263.     public boolean containsKey(Object name) {
  264.     return amap.containsKey((Name)name);
  265.     }
  266.  
  267.     /**
  268.      * Copies all of the attribute name-value mappings from the specified
  269.      * Attributes to this Map. Duplicate mappings will be replaced.
  270.      *
  271.      * @param attr the Attributes to be stored in this map
  272.      * @exception ClassCastException if attr is not an instance of Attributes
  273.      * @exception NullPointerException if attr is null
  274.      */
  275.     public void putAll(Map attr) {
  276.     amap.putAll((Attributes)attr);
  277.     }
  278.  
  279.     /**
  280.      * Removes all attributes from this Map.
  281.      */
  282.     public void clear() {
  283.     amap.clear();
  284.     }
  285.  
  286.     /**
  287.      * Returns the number of attributes in this Map.
  288.      */
  289.     public int size() {
  290.     return amap.size();
  291.     }
  292.  
  293.     /**
  294.      * Returns true if this Map contains no attributes.
  295.      */
  296.     public boolean isEmpty() {
  297.     return amap.isEmpty();
  298.     }
  299.  
  300.     /**
  301.      * Returns a Set view of the attribute names (keys) contained in this Map.
  302.      */
  303.     public Set keySet() {
  304.     return amap.keySet();
  305.     }
  306.  
  307.     /**
  308.      * Returns a Collection view of the attribute values contained in this Map.
  309.      */
  310.     public Collection values() {
  311.     return amap.values();
  312.     }
  313.  
  314.     /**
  315.      * Returns a Collection view of the attribute name-value mappings
  316.      * contained in this Map.
  317.      */
  318.     public Set entries() {
  319.     return new AbstractSet() {
  320.         private Set c = amap.entries();
  321.         public int size() {
  322.         return c.size();
  323.         }
  324.         public Iterator iterator() {
  325.         return new Iterator() {
  326.             private Iterator it = c.iterator();
  327.             public boolean hasNext() {
  328.             return it.hasNext();
  329.             }
  330.             public Object next() {
  331.             return new Entry((Map.Entry)it.next());
  332.             }
  333.             public void remove() {
  334.             it.remove();
  335.             }
  336.         };
  337.         }
  338.     };
  339.     }
  340.  
  341.     // XXX Workaround for inner classes bug
  342.     static class Entry implements Map.Entry {
  343.     Map.Entry e;
  344.     Entry(Map.Entry e) {
  345.         this.e = e;
  346.     }
  347.     public Object getKey() {
  348.         return (Name)e.getKey();
  349.     }
  350.     public Object getValue() {
  351.         return (String)e.getValue();
  352.     }
  353.     public Object setValue(Object o) {
  354.         return (String)e.setValue((String)o);
  355.     }
  356.     public boolean equals(Object o) {
  357.         if (o instanceof Entry) {
  358.         return e.equals(((Entry)o).e);
  359.         } else {
  360.         return false;
  361.         }
  362.     }
  363.     public int hashCode() {
  364.         return e.hashCode();
  365.     }
  366.     }
  367.  
  368.     /**
  369.      * Compares the specified Attributes object with this Map for equality.
  370.      * Returns true if the given object is also an instance of Attributes
  371.      * and the two Attributes objects represent the same mappings.
  372.      *
  373.      * @param o the Object to be compared
  374.      * @return true if the specified Object is equal to this Map
  375.      */
  376.     public boolean equals(Object o) {
  377.     if (o instanceof Attributes) {
  378.         return amap.equals(((Attributes)o).amap);
  379.     } else {
  380.         return false;
  381.     }
  382.     }
  383.  
  384.     /**
  385.      * Returns the hash code value for this Map.
  386.      */
  387.     public int hashCode() {
  388.     return amap.hashCode();
  389.     }
  390.  
  391.     /**
  392.      * Returns a copy of the Attributes, implemented as follows:
  393.      * <pre>
  394.      *     public Object clone() { return new Attributes(this); }
  395.      * </pre>
  396.      * Since the attribute names and values are themselves immutable,
  397.      * the Attributes returned can be safely modified without affecting
  398.      * the original.
  399.      */
  400.     public Object clone() {
  401.     return new Attributes(this);
  402.     }
  403.  
  404.     /*
  405.      * Writes the current attributes to the specified data output stream.
  406.      * XXX Need to handle UTF8 values and break up lines longer than
  407.      *     72 bytes (why do we still need to do this?)
  408.      */
  409.      void write(DataOutputStream os) throws IOException {
  410.     Iterator it = entries().iterator();
  411.     while (it.hasNext()) {
  412.         Entry e = (Entry)it.next();
  413.         os.writeBytes(e.getKey().toString());
  414.         os.writeBytes(": ");
  415.         os.writeBytes(e.getValue().toString());
  416.         os.writeBytes("\r\n");
  417.     }
  418.     os.writeBytes("\r\n");
  419.     }
  420.  
  421.     /*
  422.      * Reads attributes from the specified input stream.
  423.      * XXX Need to handle UTF8 values.
  424.      */
  425.     void read(DataInputStream is) throws IOException {
  426.     String s, name = null, value = null;
  427.     while ((s = is.readLine()) != null && s.length() > 0) {
  428.         if (s.charAt(0) == ' ') {
  429.         // Continuation of previous line
  430.         if (name == null) {
  431.             throw new IOException("invalid header field format");
  432.         }
  433.         value = value + s.substring(1);
  434.         } else {
  435.         int i = s.indexOf(':');
  436.         if (i < 1 || i > s.length() - 2 || s.charAt(i + 1) != ' ') {
  437.             throw new IOException("invalid header field format");
  438.         }
  439.         name = s.substring(0, i);
  440.         value = s.substring(i + 2);
  441.         }
  442.         try {
  443.         putValue(name, value);
  444.         } catch (IllegalArgumentException e) {
  445.         throw new IOException("invalid header field name: " + name);
  446.         }
  447.     }
  448.     }
  449. }
  450.