src/java.base/share/classes/java/util/jar/Attributes.java
changeset 49850 e286fa159ff1
parent 49111 1b33025ae610
child 50413 1234ff7199c7
equal deleted inserted replaced
49849:2aa32bb6f3dc 49850:e286fa159ff1
    26 package java.util.jar;
    26 package java.util.jar;
    27 
    27 
    28 import java.io.DataOutputStream;
    28 import java.io.DataOutputStream;
    29 import java.io.IOException;
    29 import java.io.IOException;
    30 import java.util.Collection;
    30 import java.util.Collection;
    31 import java.util.Comparator;
    31 import java.util.HashMap;
    32 import java.util.LinkedHashMap;
    32 import java.util.LinkedHashMap;
    33 import java.util.Locale;
       
    34 import java.util.Map;
    33 import java.util.Map;
       
    34 import java.util.Objects;
    35 import java.util.Set;
    35 import java.util.Set;
    36 
    36 
    37 import sun.util.logging.PlatformLogger;
    37 import sun.util.logging.PlatformLogger;
    38 
    38 
    39 /**
    39 /**
   114      * @return the String value of the specified attribute name, or null if
   114      * @return the String value of the specified attribute name, or null if
   115      *         not found.
   115      *         not found.
   116      * @throws IllegalArgumentException if the attribute name is invalid
   116      * @throws IllegalArgumentException if the attribute name is invalid
   117      */
   117      */
   118     public String getValue(String name) {
   118     public String getValue(String name) {
   119         return (String)get(new Attributes.Name(name));
   119         return (String)get(Name.of(name));
   120     }
   120     }
   121 
   121 
   122     /**
   122     /**
   123      * Returns the value of the specified Attributes.Name, or null if the
   123      * Returns the value of the specified Attributes.Name, or null if the
   124      * attribute was not found.
   124      * attribute was not found.
   166      * @param value the attribute value
   166      * @param value the attribute value
   167      * @return the previous value of the attribute, or null if none
   167      * @return the previous value of the attribute, or null if none
   168      * @exception IllegalArgumentException if the attribute name is invalid
   168      * @exception IllegalArgumentException if the attribute name is invalid
   169      */
   169      */
   170     public String putValue(String name, String value) {
   170     public String putValue(String name, String value) {
   171         return (String)put(new Name(name), value);
   171         return (String)put(Name.of(name), value);
   172     }
   172     }
   173 
   173 
   174     /**
   174     /**
   175      * Removes the attribute with the specified name (key) from this Map.
   175      * Removes the attribute with the specified name (key) from this Map.
   176      * Returns the previous attribute value, or null if none.
   176      * Returns the previous attribute value, or null if none.
   369      * Reads attributes from the specified input stream.
   369      * Reads attributes from the specified input stream.
   370      * XXX Need to handle UTF8 values.
   370      * XXX Need to handle UTF8 values.
   371      */
   371      */
   372     @SuppressWarnings("deprecation")
   372     @SuppressWarnings("deprecation")
   373     void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
   373     void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
   374         String name = null, value = null;
   374         String name = null, value;
   375         byte[] lastline = null;
   375         byte[] lastline = null;
   376 
   376 
   377         int len;
   377         int len;
   378         while ((len = is.readLine(lbuf)) != -1) {
   378         while ((len = is.readLine(lbuf)) != -1) {
   379             boolean lineContinued = false;
   379             boolean lineContinued = false;
   445      * and will be UTF8-encoded when written to the output stream.  See the
   445      * and will be UTF8-encoded when written to the output stream.  See the
   446      * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
   446      * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
   447      * for more information about valid attribute names and values.
   447      * for more information about valid attribute names and values.
   448      */
   448      */
   449     public static class Name {
   449     public static class Name {
   450         private String name;
   450         private final String name;
   451         private int hashCode = -1;
   451         private final int hashCode;
       
   452 
       
   453         /**
       
   454          * Avoid allocation for common Names
       
   455          */
       
   456         private static final Map<String, Name> KNOWN_NAMES;
       
   457 
       
   458         static final Name of(String name) {
       
   459             Name n = KNOWN_NAMES.get(name);
       
   460             if (n != null) {
       
   461                 return n;
       
   462             }
       
   463             return new Name(name);
       
   464         }
   452 
   465 
   453         /**
   466         /**
   454          * Constructs a new attribute name using the given string name.
   467          * Constructs a new attribute name using the given string name.
   455          *
   468          *
   456          * @param name the attribute string name
   469          * @param name the attribute string name
   457          * @exception IllegalArgumentException if the attribute name was
   470          * @exception IllegalArgumentException if the attribute name was
   458          *            invalid
   471          *            invalid
   459          * @exception NullPointerException if the attribute name was null
   472          * @exception NullPointerException if the attribute name was null
   460          */
   473          */
   461         public Name(String name) {
   474         public Name(String name) {
   462             if (name == null) {
   475             this.hashCode = hash(name);
   463                 throw new NullPointerException("name");
       
   464             }
       
   465             if (!isValid(name)) {
       
   466                 throw new IllegalArgumentException(name);
       
   467             }
       
   468             this.name = name.intern();
   476             this.name = name.intern();
   469         }
   477         }
   470 
   478 
   471         private static boolean isValid(String name) {
   479         // Checks the string is valid
       
   480         private final int hash(String name) {
       
   481             Objects.requireNonNull(name, "name");
   472             int len = name.length();
   482             int len = name.length();
   473             if (len > 70 || len == 0) {
   483             if (len > 70 || len == 0) {
   474                 return false;
   484                 throw new IllegalArgumentException(name);
   475             }
   485             }
       
   486             // Calculate hash code case insensitively
       
   487             int h = 0;
   476             for (int i = 0; i < len; i++) {
   488             for (int i = 0; i < len; i++) {
   477                 if (!isValid(name.charAt(i))) {
   489                 char c = name.charAt(i);
   478                     return false;
   490                 if (c >= 'a' && c <= 'z') {
       
   491                     // hashcode must be identical for upper and lower case
       
   492                     h = h * 31 + (c - 0x20);
       
   493                 } else if ((c >= 'A' && c <= 'Z' ||
       
   494                         c >= '0' && c <= '9' ||
       
   495                         c == '_' || c == '-')) {
       
   496                     h = h * 31 + c;
       
   497                 } else {
       
   498                     throw new IllegalArgumentException(name);
   479                 }
   499                 }
   480             }
   500             }
   481             return true;
   501             return h;
   482         }
       
   483 
       
   484         private static boolean isValid(char c) {
       
   485             return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
       
   486         }
       
   487 
       
   488         private static boolean isAlpha(char c) {
       
   489             return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
       
   490         }
       
   491 
       
   492         private static boolean isDigit(char c) {
       
   493             return c >= '0' && c <= '9';
       
   494         }
   502         }
   495 
   503 
   496         /**
   504         /**
   497          * Compares this attribute name to another for equality.
   505          * Compares this attribute name to another for equality.
   498          * @param o the object to compare
   506          * @param o the object to compare
   499          * @return true if this attribute name is equal to the
   507          * @return true if this attribute name is equal to the
   500          *         specified attribute object
   508          *         specified attribute object
   501          */
   509          */
   502         public boolean equals(Object o) {
   510         public boolean equals(Object o) {
       
   511             if (this == o) {
       
   512                 return true;
       
   513             }
   503             if (o instanceof Name) {
   514             if (o instanceof Name) {
   504                 Comparator<String> c = String.CASE_INSENSITIVE_ORDER;
   515                 Name other = (Name)o;
   505                 return c.compare(name, ((Name)o).name) == 0;
   516                 return other.name.equalsIgnoreCase(name);
   506             } else {
   517             } else {
   507                 return false;
   518                 return false;
   508             }
   519             }
   509         }
   520         }
   510 
   521 
   511         /**
   522         /**
   512          * Computes the hash value for this attribute name.
   523          * Computes the hash value for this attribute name.
   513          */
   524          */
   514         public int hashCode() {
   525         public int hashCode() {
   515             if (hashCode == -1) {
       
   516                 hashCode = name.toLowerCase(Locale.ROOT).hashCode();
       
   517             }
       
   518             return hashCode;
   526             return hashCode;
   519         }
   527         }
   520 
   528 
   521         /**
   529         /**
   522          * Returns the attribute name as a String.
   530          * Returns the attribute name as a String.
   571          * @see <a href="{@docRoot}/../specs/jar/jar.html#package-sealing">
   579          * @see <a href="{@docRoot}/../specs/jar/jar.html#package-sealing">
   572          *      Package Sealing</a>
   580          *      Package Sealing</a>
   573          */
   581          */
   574         public static final Name SEALED = new Name("Sealed");
   582         public static final Name SEALED = new Name("Sealed");
   575 
   583 
   576        /**
   584         /**
   577          * {@code Name} object for {@code Extension-List} manifest attribute
   585          * {@code Name} object for {@code Extension-List} manifest attribute
   578          * used for the extension mechanism that is no longer supported.
   586          * used for the extension mechanism that is no longer supported.
   579          */
   587          */
   580         public static final Name EXTENSION_LIST = new Name("Extension-List");
   588         public static final Name EXTENSION_LIST = new Name("Extension-List");
   581 
   589 
   618          * @deprecated Extension mechanism is no longer supported.
   626          * @deprecated Extension mechanism is no longer supported.
   619          */
   627          */
   620         @Deprecated
   628         @Deprecated
   621         public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
   629         public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
   622 
   630 
   623        /**
   631         /**
   624          * {@code Name} object for {@code Implementation-URL}
   632          * {@code Name} object for {@code Implementation-URL}
   625          * manifest attribute.
   633          * manifest attribute.
   626          *
   634          *
   627          * @deprecated Extension mechanism is no longer supported.
   635          * @deprecated Extension mechanism is no longer supported.
   628          */
   636          */
   652          * manifest attribute that indicates this is a multi-release JAR file.
   660          * manifest attribute that indicates this is a multi-release JAR file.
   653          *
   661          *
   654          * @since   9
   662          * @since   9
   655          */
   663          */
   656         public static final Name MULTI_RELEASE = new Name("Multi-Release");
   664         public static final Name MULTI_RELEASE = new Name("Multi-Release");
       
   665 
       
   666         private static void addName(Map<String, Name> names, Name name) {
       
   667             names.put(name.name, name);
       
   668         }
       
   669 
       
   670         static {
       
   671             var names = new HashMap<String, Name>(64);
       
   672             addName(names, MANIFEST_VERSION);
       
   673             addName(names, SIGNATURE_VERSION);
       
   674             addName(names, CONTENT_TYPE);
       
   675             addName(names, CLASS_PATH);
       
   676             addName(names, MAIN_CLASS);
       
   677             addName(names, SEALED);
       
   678             addName(names, EXTENSION_LIST);
       
   679             addName(names, EXTENSION_NAME);
       
   680             addName(names, IMPLEMENTATION_TITLE);
       
   681             addName(names, IMPLEMENTATION_VERSION);
       
   682             addName(names, IMPLEMENTATION_VENDOR);
       
   683             addName(names, SPECIFICATION_TITLE);
       
   684             addName(names, SPECIFICATION_VERSION);
       
   685             addName(names, SPECIFICATION_VENDOR);
       
   686             addName(names, MULTI_RELEASE);
       
   687 
       
   688             // Common attributes used in MANIFEST.MF et.al; adding these has a
       
   689             // small footprint cost, but is likely to be quickly paid for by
       
   690             // reducing allocation when reading and parsing typical manifests
       
   691             addName(names, new Name("Add-Exports"));
       
   692             addName(names, new Name("Add-Opens"));
       
   693             addName(names, new Name("Ant-Version"));
       
   694             addName(names, new Name("Archiver-Version"));
       
   695             addName(names, new Name("Build-Jdk"));
       
   696             addName(names, new Name("Built-By"));
       
   697             addName(names, new Name("Bnd-LastModified"));
       
   698             addName(names, new Name("Bundle-Description"));
       
   699             addName(names, new Name("Bundle-DocURL"));
       
   700             addName(names, new Name("Bundle-License"));
       
   701             addName(names, new Name("Bundle-ManifestVersion"));
       
   702             addName(names, new Name("Bundle-Name"));
       
   703             addName(names, new Name("Bundle-Vendor"));
       
   704             addName(names, new Name("Bundle-Version"));
       
   705             addName(names, new Name("Bundle-SymbolicName"));
       
   706             addName(names, new Name("Created-By"));
       
   707             addName(names, new Name("Export-Package"));
       
   708             addName(names, new Name("Import-Package"));
       
   709             addName(names, new Name("Name"));
       
   710             addName(names, new Name("SHA1-Digest"));
       
   711             addName(names, new Name("X-Compile-Source-JDK"));
       
   712             addName(names, new Name("X-Compile-Target-JDK"));
       
   713             KNOWN_NAMES = names;
       
   714         }
   657     }
   715     }
   658 }
   716 }