Home » openjdk-7 » java » util » jar » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package java.util.jar;
   27   
   28   import java.io.DataInputStream;
   29   import java.io.DataOutputStream;
   30   import java.io.IOException;
   31   import java.util.HashMap;
   32   import java.util.Map;
   33   import java.util.Set;
   34   import java.util.Collection;
   35   import java.util.AbstractSet;
   36   import java.util.Iterator;
   37   import sun.util.logging.PlatformLogger;
   38   import java.util.Comparator;
   39   import sun.misc.ASCIICaseInsensitiveComparator;
   40   
   41   /**
   42    * The Attributes class maps Manifest attribute names to associated string
   43    * values. Valid attribute names are case-insensitive, are restricted to
   44    * the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed 70
   45    * characters in length. Attribute values can contain any characters and
   46    * will be UTF8-encoded when written to the output stream.  See the
   47    * <a href="../../../../technotes/guides/jar/jar.html">JAR File Specification</a>
   48    * for more information about valid attribute names and values.
   49    *
   50    * @author  David Connelly
   51    * @see     Manifest
   52    * @since   1.2
   53    */
   54   public class Attributes implements Map<Object,Object>, Cloneable {
   55       /**
   56        * The attribute name-value mappings.
   57        */
   58       protected Map<Object,Object> map;
   59   
   60       /**
   61        * Constructs a new, empty Attributes object with default size.
   62        */
   63       public Attributes() {
   64           this(11);
   65       }
   66   
   67       /**
   68        * Constructs a new, empty Attributes object with the specified
   69        * initial size.
   70        *
   71        * @param size the initial number of attributes
   72        */
   73       public Attributes(int size) {
   74           map = new HashMap(size);
   75       }
   76   
   77       /**
   78        * Constructs a new Attributes object with the same attribute name-value
   79        * mappings as in the specified Attributes.
   80        *
   81        * @param attr the specified Attributes
   82        */
   83       public Attributes(Attributes attr) {
   84           map = new HashMap(attr);
   85       }
   86   
   87   
   88       /**
   89        * Returns the value of the specified attribute name, or null if the
   90        * attribute name was not found.
   91        *
   92        * @param name the attribute name
   93        * @return the value of the specified attribute name, or null if
   94        *         not found.
   95        */
   96       public Object get(Object name) {
   97           return map.get(name);
   98       }
   99   
  100       /**
  101        * Returns the value of the specified attribute name, specified as
  102        * a string, or null if the attribute was not found. The attribute
  103        * name is case-insensitive.
  104        * <p>
  105        * This method is defined as:
  106        * <pre>
  107        *      return (String)get(new Attributes.Name((String)name));
  108        * </pre>
  109        *
  110        * @param name the attribute name as a string
  111        * @return the String value of the specified attribute name, or null if
  112        *         not found.
  113        * @throws IllegalArgumentException if the attribute name is invalid
  114        */
  115       public String getValue(String name) {
  116           return (String)get(new Attributes.Name(name));
  117       }
  118   
  119       /**
  120        * Returns the value of the specified Attributes.Name, or null if the
  121        * attribute was not found.
  122        * <p>
  123        * This method is defined as:
  124        * <pre>
  125        *     return (String)get(name);
  126        * </pre>
  127        *
  128        * @param name the Attributes.Name object
  129        * @return the String value of the specified Attribute.Name, or null if
  130        *         not found.
  131        */
  132       public String getValue(Name name) {
  133           return (String)get(name);
  134       }
  135   
  136       /**
  137        * Associates the specified value with the specified attribute name
  138        * (key) in this Map. If the Map previously contained a mapping for
  139        * the attribute name, the old value is replaced.
  140        *
  141        * @param name the attribute name
  142        * @param value the attribute value
  143        * @return the previous value of the attribute, or null if none
  144        * @exception ClassCastException if the name is not a Attributes.Name
  145        *            or the value is not a String
  146        */
  147       public Object put(Object name, Object value) {
  148           return map.put((Attributes.Name)name, (String)value);
  149       }
  150   
  151       /**
  152        * Associates the specified value with the specified attribute name,
  153        * specified as a String. The attributes name is case-insensitive.
  154        * If the Map previously contained a mapping for the attribute name,
  155        * the old value is replaced.
  156        * <p>
  157        * This method is defined as:
  158        * <pre>
  159        *      return (String)put(new Attributes.Name(name), value);
  160        * </pre>
  161        *
  162        * @param name the attribute name as a string
  163        * @param value the attribute value
  164        * @return the previous value of the attribute, or null if none
  165        * @exception IllegalArgumentException if the attribute name is invalid
  166        */
  167       public String putValue(String name, String value) {
  168           return (String)put(new Name(name), value);
  169       }
  170   
  171       /**
  172        * Removes the attribute with the specified name (key) from this Map.
  173        * Returns the previous attribute value, or null if none.
  174        *
  175        * @param name attribute name
  176        * @return the previous value of the attribute, or null if none
  177        */
  178       public Object remove(Object name) {
  179           return map.remove(name);
  180       }
  181   
  182       /**
  183        * Returns true if this Map maps one or more attribute names (keys)
  184        * to the specified value.
  185        *
  186        * @param value the attribute value
  187        * @return true if this Map maps one or more attribute names to
  188        *         the specified value
  189        */
  190       public boolean containsValue(Object value) {
  191           return map.containsValue(value);
  192       }
  193   
  194       /**
  195        * Returns true if this Map contains the specified attribute name (key).
  196        *
  197        * @param name the attribute name
  198        * @return true if this Map contains the specified attribute name
  199        */
  200       public boolean containsKey(Object name) {
  201           return map.containsKey(name);
  202       }
  203   
  204       /**
  205        * Copies all of the attribute name-value mappings from the specified
  206        * Attributes to this Map. Duplicate mappings will be replaced.
  207        *
  208        * @param attr the Attributes to be stored in this map
  209        * @exception ClassCastException if attr is not an Attributes
  210        */
  211       public void putAll(Map<?,?> attr) {
  212           // ## javac bug?
  213           if (!Attributes.class.isInstance(attr))
  214               throw new ClassCastException();
  215           for (Map.Entry<?,?> me : (attr).entrySet())
  216               put(me.getKey(), me.getValue());
  217       }
  218   
  219       /**
  220        * Removes all attributes from this Map.
  221        */
  222       public void clear() {
  223           map.clear();
  224       }
  225   
  226       /**
  227        * Returns the number of attributes in this Map.
  228        */
  229       public int size() {
  230           return map.size();
  231       }
  232   
  233       /**
  234        * Returns true if this Map contains no attributes.
  235        */
  236       public boolean isEmpty() {
  237           return map.isEmpty();
  238       }
  239   
  240       /**
  241        * Returns a Set view of the attribute names (keys) contained in this Map.
  242        */
  243       public Set<Object> keySet() {
  244           return map.keySet();
  245       }
  246   
  247       /**
  248        * Returns a Collection view of the attribute values contained in this Map.
  249        */
  250       public Collection<Object> values() {
  251           return map.values();
  252       }
  253   
  254       /**
  255        * Returns a Collection view of the attribute name-value mappings
  256        * contained in this Map.
  257        */
  258       public Set<Map.Entry<Object,Object>> entrySet() {
  259           return map.entrySet();
  260       }
  261   
  262       /**
  263        * Compares the specified Attributes object with this Map for equality.
  264        * Returns true if the given object is also an instance of Attributes
  265        * and the two Attributes objects represent the same mappings.
  266        *
  267        * @param o the Object to be compared
  268        * @return true if the specified Object is equal to this Map
  269        */
  270       public boolean equals(Object o) {
  271           return map.equals(o);
  272       }
  273   
  274       /**
  275        * Returns the hash code value for this Map.
  276        */
  277       public int hashCode() {
  278           return map.hashCode();
  279       }
  280   
  281       /**
  282        * Returns a copy of the Attributes, implemented as follows:
  283        * <pre>
  284        *     public Object clone() { return new Attributes(this); }
  285        * </pre>
  286        * Since the attribute names and values are themselves immutable,
  287        * the Attributes returned can be safely modified without affecting
  288        * the original.
  289        */
  290       public Object clone() {
  291           return new Attributes(this);
  292       }
  293   
  294       /*
  295        * Writes the current attributes to the specified data output stream.
  296        * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
  297        */
  298        void write(DataOutputStream os) throws IOException {
  299           Iterator it = entrySet().iterator();
  300           while (it.hasNext()) {
  301               Map.Entry e = (Map.Entry)it.next();
  302               StringBuffer buffer = new StringBuffer(
  303                                           ((Name)e.getKey()).toString());
  304               buffer.append(": ");
  305   
  306               String value = (String)e.getValue();
  307               if (value != null) {
  308                   byte[] vb = value.getBytes("UTF8");
  309                   value = new String(vb, 0, 0, vb.length);
  310               }
  311               buffer.append(value);
  312   
  313               buffer.append("\r\n");
  314               Manifest.make72Safe(buffer);
  315               os.writeBytes(buffer.toString());
  316           }
  317           os.writeBytes("\r\n");
  318       }
  319   
  320       /*
  321        * Writes the current attributes to the specified data output stream,
  322        * make sure to write out the MANIFEST_VERSION or SIGNATURE_VERSION
  323        * attributes first.
  324        *
  325        * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
  326        */
  327       void writeMain(DataOutputStream out) throws IOException
  328       {
  329           // write out the *-Version header first, if it exists
  330           String vername = Name.MANIFEST_VERSION.toString();
  331           String version = getValue(vername);
  332           if (version == null) {
  333               vername = Name.SIGNATURE_VERSION.toString();
  334               version = getValue(vername);
  335           }
  336   
  337           if (version != null) {
  338               out.writeBytes(vername+": "+version+"\r\n");
  339           }
  340   
  341           // write out all attributes except for the version
  342           // we wrote out earlier
  343           Iterator it = entrySet().iterator();
  344           while (it.hasNext()) {
  345               Map.Entry e = (Map.Entry)it.next();
  346               String name = ((Name)e.getKey()).toString();
  347               if ((version != null) && ! (name.equalsIgnoreCase(vername))) {
  348   
  349                   StringBuffer buffer = new StringBuffer(name);
  350                   buffer.append(": ");
  351   
  352                   String value = (String)e.getValue();
  353                   if (value != null) {
  354                       byte[] vb = value.getBytes("UTF8");
  355                       value = new String(vb, 0, 0, vb.length);
  356                   }
  357                   buffer.append(value);
  358   
  359                   buffer.append("\r\n");
  360                   Manifest.make72Safe(buffer);
  361                   out.writeBytes(buffer.toString());
  362               }
  363           }
  364           out.writeBytes("\r\n");
  365       }
  366   
  367       /*
  368        * Reads attributes from the specified input stream.
  369        * XXX Need to handle UTF8 values.
  370        */
  371       void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
  372           String name = null, value = null;
  373           byte[] lastline = null;
  374   
  375           int len;
  376           while ((len = is.readLine(lbuf)) != -1) {
  377               boolean lineContinued = false;
  378               if (lbuf[--len] != '\n') {
  379                   throw new IOException("line too long");
  380               }
  381               if (len > 0 && lbuf[len-1] == '\r') {
  382                   --len;
  383               }
  384               if (len == 0) {
  385                   break;
  386               }
  387               int i = 0;
  388               if (lbuf[0] == ' ') {
  389                   // continuation of previous line
  390                   if (name == null) {
  391                       throw new IOException("misplaced continuation line");
  392                   }
  393                   lineContinued = true;
  394                   byte[] buf = new byte[lastline.length + len - 1];
  395                   System.arraycopy(lastline, 0, buf, 0, lastline.length);
  396                   System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
  397                   if (is.peek() == ' ') {
  398                       lastline = buf;
  399                       continue;
  400                   }
  401                   value = new String(buf, 0, buf.length, "UTF8");
  402                   lastline = null;
  403               } else {
  404                   while (lbuf[i++] != ':') {
  405                       if (i >= len) {
  406                           throw new IOException("invalid header field");
  407                       }
  408                   }
  409                   if (lbuf[i++] != ' ') {
  410                       throw new IOException("invalid header field");
  411                   }
  412                   name = new String(lbuf, 0, 0, i - 2);
  413                   if (is.peek() == ' ') {
  414                       lastline = new byte[len - i];
  415                       System.arraycopy(lbuf, i, lastline, 0, len - i);
  416                       continue;
  417                   }
  418                   value = new String(lbuf, i, len - i, "UTF8");
  419               }
  420               try {
  421                   if ((putValue(name, value) != null) && (!lineContinued)) {
  422                       PlatformLogger.getLogger("java.util.jar").warning(
  423                                        "Duplicate name in Manifest: " + name
  424                                        + ".\n"
  425                                        + "Ensure that the manifest does not "
  426                                        + "have duplicate entries, and\n"
  427                                        + "that blank lines separate "
  428                                        + "individual sections in both your\n"
  429                                        + "manifest and in the META-INF/MANIFEST.MF "
  430                                        + "entry in the jar file.");
  431                   }
  432               } catch (IllegalArgumentException e) {
  433                   throw new IOException("invalid header field name: " + name);
  434               }
  435           }
  436       }
  437   
  438       /**
  439        * The Attributes.Name class represents an attribute name stored in
  440        * this Map. Valid attribute names are case-insensitive, are restricted
  441        * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
  442        * 70 characters in length. Attribute values can contain any characters
  443        * and will be UTF8-encoded when written to the output stream.  See the
  444        * <a href="../../../../technotes/guides/jar/jar.html">JAR File Specification</a>
  445        * for more information about valid attribute names and values.
  446        */
  447       public static class Name {
  448           private String name;
  449           private int hashCode = -1;
  450   
  451           /**
  452            * Constructs a new attribute name using the given string name.
  453            *
  454            * @param name the attribute string name
  455            * @exception IllegalArgumentException if the attribute name was
  456            *            invalid
  457            * @exception NullPointerException if the attribute name was null
  458            */
  459           public Name(String name) {
  460               if (name == null) {
  461                   throw new NullPointerException("name");
  462               }
  463               if (!isValid(name)) {
  464                   throw new IllegalArgumentException(name);
  465               }
  466               this.name = name.intern();
  467           }
  468   
  469           private static boolean isValid(String name) {
  470               int len = name.length();
  471               if (len > 70 || len == 0) {
  472                   return false;
  473               }
  474               for (int i = 0; i < len; i++) {
  475                   if (!isValid(name.charAt(i))) {
  476                       return false;
  477                   }
  478               }
  479               return true;
  480           }
  481   
  482           private static boolean isValid(char c) {
  483               return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
  484           }
  485   
  486           private static boolean isAlpha(char c) {
  487               return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  488           }
  489   
  490           private static boolean isDigit(char c) {
  491               return c >= '0' && c <= '9';
  492           }
  493   
  494           /**
  495            * Compares this attribute name to another for equality.
  496            * @param o the object to compare
  497            * @return true if this attribute name is equal to the
  498            *         specified attribute object
  499            */
  500           public boolean equals(Object o) {
  501               if (o instanceof Name) {
  502                   Comparator c = ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER;
  503                   return c.compare(name, ((Name)o).name) == 0;
  504               } else {
  505                   return false;
  506               }
  507           }
  508   
  509           /**
  510            * Computes the hash value for this attribute name.
  511            */
  512           public int hashCode() {
  513               if (hashCode == -1) {
  514                   hashCode = ASCIICaseInsensitiveComparator.lowerCaseHashCode(name);
  515               }
  516               return hashCode;
  517           }
  518   
  519           /**
  520            * Returns the attribute name as a String.
  521            */
  522           public String toString() {
  523               return name;
  524           }
  525   
  526           /**
  527            * <code>Name</code> object for <code>Manifest-Version</code>
  528            * manifest attribute. This attribute indicates the version number
  529            * of the manifest standard to which a JAR file's manifest conforms.
  530            * @see <a href="../../../../technotes/guides/jar/jar.html#JAR Manifest">
  531            *      Manifest and Signature Specification</a>
  532            */
  533           public static final Name MANIFEST_VERSION = new Name("Manifest-Version");
  534   
  535           /**
  536            * <code>Name</code> object for <code>Signature-Version</code>
  537            * manifest attribute used when signing JAR files.
  538            * @see <a href="../../../../technotes/guides/jar/jar.html#JAR Manifest">
  539            *      Manifest and Signature Specification</a>
  540            */
  541           public static final Name SIGNATURE_VERSION = new Name("Signature-Version");
  542   
  543           /**
  544            * <code>Name</code> object for <code>Content-Type</code>
  545            * manifest attribute.
  546            */
  547           public static final Name CONTENT_TYPE = new Name("Content-Type");
  548   
  549           /**
  550            * <code>Name</code> object for <code>Class-Path</code>
  551            * manifest attribute. Bundled extensions can use this attribute
  552            * to find other JAR files containing needed classes.
  553            * @see <a href="../../../../technotes/guides/extensions/spec.html#bundled">
  554            *      Extensions Specification</a>
  555            */
  556           public static final Name CLASS_PATH = new Name("Class-Path");
  557   
  558           /**
  559            * <code>Name</code> object for <code>Main-Class</code> manifest
  560            * attribute used for launching applications packaged in JAR files.
  561            * The <code>Main-Class</code> attribute is used in conjunction
  562            * with the <code>-jar</code> command-line option of the
  563            * <tt>java</tt> application launcher.
  564            */
  565           public static final Name MAIN_CLASS = new Name("Main-Class");
  566   
  567           /**
  568            * <code>Name</code> object for <code>Sealed</code> manifest attribute
  569            * used for sealing.
  570            * @see <a href="../../../../technotes/guides/extensions/spec.html#sealing">
  571            *      Extension Sealing</a>
  572            */
  573           public static final Name SEALED = new Name("Sealed");
  574   
  575          /**
  576            * <code>Name</code> object for <code>Extension-List</code> manifest attribute
  577            * used for declaring dependencies on installed extensions.
  578            * @see <a href="../../../../technotes/guides/extensions/spec.html#dependency">
  579            *      Installed extension dependency</a>
  580            */
  581           public static final Name EXTENSION_LIST = new Name("Extension-List");
  582   
  583           /**
  584            * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
  585            * used for declaring dependencies on installed extensions.
  586            * @see <a href="../../../../technotes/guides/extensions/spec.html#dependency">
  587            *      Installed extension dependency</a>
  588            */
  589           public static final Name EXTENSION_NAME = new Name("Extension-Name");
  590   
  591           /**
  592            * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
  593            * used for declaring dependencies on installed extensions.
  594            * @see <a href="../../../../technotes/guides/extensions/spec.html#dependency">
  595            *      Installed extension dependency</a>
  596            */
  597           public static final Name EXTENSION_INSTALLATION = new Name("Extension-Installation");
  598   
  599           /**
  600            * <code>Name</code> object for <code>Implementation-Title</code>
  601            * manifest attribute used for package versioning.
  602            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  603            *      Java Product Versioning Specification</a>
  604            */
  605           public static final Name IMPLEMENTATION_TITLE = new Name("Implementation-Title");
  606   
  607           /**
  608            * <code>Name</code> object for <code>Implementation-Version</code>
  609            * manifest attribute used for package versioning.
  610            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  611            *      Java Product Versioning Specification</a>
  612            */
  613           public static final Name IMPLEMENTATION_VERSION = new Name("Implementation-Version");
  614   
  615           /**
  616            * <code>Name</code> object for <code>Implementation-Vendor</code>
  617            * manifest attribute used for package versioning.
  618            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  619            *      Java Product Versioning Specification</a>
  620            */
  621           public static final Name IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor");
  622   
  623           /**
  624            * <code>Name</code> object for <code>Implementation-Vendor-Id</code>
  625            * manifest attribute used for package versioning.
  626            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  627            *      Java Product Versioning Specification</a>
  628            */
  629           public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
  630   
  631          /**
  632            * <code>Name</code> object for <code>Implementation-Vendor-URL</code>
  633            * manifest attribute used for package versioning.
  634            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  635            *      Java Product Versioning Specification</a>
  636            */
  637           public static final Name IMPLEMENTATION_URL = new Name("Implementation-URL");
  638   
  639           /**
  640            * <code>Name</code> object for <code>Specification-Title</code>
  641            * manifest attribute used for package versioning.
  642            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  643            *      Java Product Versioning Specification</a>
  644            */
  645           public static final Name SPECIFICATION_TITLE = new Name("Specification-Title");
  646   
  647           /**
  648            * <code>Name</code> object for <code>Specification-Version</code>
  649            * manifest attribute used for package versioning.
  650            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  651            *      Java Product Versioning Specification</a>
  652            */
  653           public static final Name SPECIFICATION_VERSION = new Name("Specification-Version");
  654   
  655           /**
  656            * <code>Name</code> object for <code>Specification-Vendor</code>
  657            * manifest attribute used for package versioning.
  658            * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
  659            *      Java Product Versioning Specification</a>
  660            */
  661           public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor");
  662       }
  663   }

Home » openjdk-7 » java » util » jar » [javadoc | source]