1 /*
   2  * Copyright (c) 1997, 2018, 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.DataOutputStream;
  29 import java.io.IOException;
  30 import java.util.Collection;
  31 import java.util.HashMap;
  32 import java.util.LinkedHashMap;
  33 import java.util.Map;
  34 import java.util.Objects;
  35 import java.util.Set;
  36 
  37 import jdk.internal.misc.VM;
  38 import jdk.internal.vm.annotation.Stable;
  39 import sun.util.logging.PlatformLogger;
  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. There must be a colon and a SPACE after the name;
  46  * the combined length will not exceed 72 characters.
  47  * Attribute values can contain any characters and
  48  * will be UTF8-encoded when written to the output stream.  See the
  49  * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
  50  * for more information about valid attribute names and values.
  51  *
  52  * <p>This map and its views have a predictable iteration order, namely the
  53  * order that keys were inserted into the map, as with {@link LinkedHashMap}.
  54  *
  55  * @author  David Connelly
  56  * @see     Manifest
  57  * @since   1.2
  58  */
  59 public class Attributes implements Map<Object,Object>, Cloneable {
  60     /**
  61      * The attribute name-value mappings.
  62      */
  63     protected Map<Object,Object> map;
  64 
  65     /**
  66      * Constructs a new, empty Attributes object with default size.
  67      */
  68     public Attributes() {
  69         this(11);
  70     }
  71 
  72     /**
  73      * Constructs a new, empty Attributes object with the specified
  74      * initial size.
  75      *
  76      * @param size the initial number of attributes
  77      */
  78     public Attributes(int size) {
  79         map = new LinkedHashMap<>(size);
  80     }
  81 
  82     /**
  83      * Constructs a new Attributes object with the same attribute name-value
  84      * mappings as in the specified Attributes.
  85      *
  86      * @param attr the specified Attributes
  87      */
  88     public Attributes(Attributes attr) {
  89         map = new LinkedHashMap<>(attr);
  90     }
  91 
  92 
  93     /**
  94      * Returns the value of the specified attribute name, or null if the
  95      * attribute name was not found.
  96      *
  97      * @param name the attribute name
  98      * @return the value of the specified attribute name, or null if
  99      *         not found.
 100      */
 101     public Object get(Object name) {
 102         return map.get(name);
 103     }
 104 
 105     /**
 106      * Returns the value of the specified attribute name, specified as
 107      * a string, or null if the attribute was not found. The attribute
 108      * name is case-insensitive.
 109      * <p>
 110      * This method is defined as:
 111      * <pre>
 112      *      return (String)get(new Attributes.Name((String)name));
 113      * </pre>
 114      *
 115      * @param name the attribute name as a string
 116      * @return the String value of the specified attribute name, or null if
 117      *         not found.
 118      * @throws IllegalArgumentException if the attribute name is invalid
 119      */
 120     public String getValue(String name) {
 121         return (String)get(Name.of(name));
 122     }
 123 
 124     /**
 125      * Returns the value of the specified Attributes.Name, or null if the
 126      * attribute was not found.
 127      * <p>
 128      * This method is defined as:
 129      * <pre>
 130      *     return (String)get(name);
 131      * </pre>
 132      *
 133      * @param name the Attributes.Name object
 134      * @return the String value of the specified Attribute.Name, or null if
 135      *         not found.
 136      */
 137     public String getValue(Name name) {
 138         return (String)get(name);
 139     }
 140 
 141     /**
 142      * Associates the specified value with the specified attribute name
 143      * (key) in this Map. If the Map previously contained a mapping for
 144      * the attribute name, the old value is replaced.
 145      *
 146      * @param name the attribute name
 147      * @param value the attribute value
 148      * @return the previous value of the attribute, or null if none
 149      * @exception ClassCastException if the name is not a Attributes.Name
 150      *            or the value is not a String
 151      */
 152     public Object put(Object name, Object value) {
 153         return map.put((Attributes.Name)name, (String)value);
 154     }
 155 
 156     /**
 157      * Associates the specified value with the specified attribute name,
 158      * specified as a String. The attributes name is case-insensitive.
 159      * If the Map previously contained a mapping for the attribute name,
 160      * the old value is replaced.
 161      * <p>
 162      * This method is defined as:
 163      * <pre>
 164      *      return (String)put(new Attributes.Name(name), value);
 165      * </pre>
 166      *
 167      * @param name the attribute name as a string
 168      * @param value the attribute value
 169      * @return the previous value of the attribute, or null if none
 170      * @exception IllegalArgumentException if the attribute name is invalid
 171      */
 172     public String putValue(String name, String value) {
 173         return (String)put(Name.of(name), value);
 174     }
 175 
 176     /**
 177      * Removes the attribute with the specified name (key) from this Map.
 178      * Returns the previous attribute value, or null if none.
 179      *
 180      * @param name attribute name
 181      * @return the previous value of the attribute, or null if none
 182      */
 183     public Object remove(Object name) {
 184         return map.remove(name);
 185     }
 186 
 187     /**
 188      * Returns true if this Map maps one or more attribute names (keys)
 189      * to the specified value.
 190      *
 191      * @param value the attribute value
 192      * @return true if this Map maps one or more attribute names to
 193      *         the specified value
 194      */
 195     public boolean containsValue(Object value) {
 196         return map.containsValue(value);
 197     }
 198 
 199     /**
 200      * Returns true if this Map contains the specified attribute name (key).
 201      *
 202      * @param name the attribute name
 203      * @return true if this Map contains the specified attribute name
 204      */
 205     public boolean containsKey(Object name) {
 206         return map.containsKey(name);
 207     }
 208 
 209     /**
 210      * Copies all of the attribute name-value mappings from the specified
 211      * Attributes to this Map. Duplicate mappings will be replaced.
 212      *
 213      * @param attr the Attributes to be stored in this map
 214      * @exception ClassCastException if attr is not an Attributes
 215      */
 216     public void putAll(Map<?,?> attr) {
 217         // ## javac bug?
 218         if (!Attributes.class.isInstance(attr))
 219             throw new ClassCastException();
 220         for (Map.Entry<?,?> me : (attr).entrySet())
 221             put(me.getKey(), me.getValue());
 222     }
 223 
 224     /**
 225      * Removes all attributes from this Map.
 226      */
 227     public void clear() {
 228         map.clear();
 229     }
 230 
 231     /**
 232      * Returns the number of attributes in this Map.
 233      */
 234     public int size() {
 235         return map.size();
 236     }
 237 
 238     /**
 239      * Returns true if this Map contains no attributes.
 240      */
 241     public boolean isEmpty() {
 242         return map.isEmpty();
 243     }
 244 
 245     /**
 246      * Returns a Set view of the attribute names (keys) contained in this Map.
 247      */
 248     public Set<Object> keySet() {
 249         return map.keySet();
 250     }
 251 
 252     /**
 253      * Returns a Collection view of the attribute values contained in this Map.
 254      */
 255     public Collection<Object> values() {
 256         return map.values();
 257     }
 258 
 259     /**
 260      * Returns a Collection view of the attribute name-value mappings
 261      * contained in this Map.
 262      */
 263     public Set<Map.Entry<Object,Object>> entrySet() {
 264         return map.entrySet();
 265     }
 266 
 267     /**
 268      * Compares the specified Attributes object with this Map for equality.
 269      * Returns true if the given object is also an instance of Attributes
 270      * and the two Attributes objects represent the same mappings.
 271      *
 272      * @param o the Object to be compared
 273      * @return true if the specified Object is equal to this Map
 274      */
 275     public boolean equals(Object o) {
 276         return map.equals(o);
 277     }
 278 
 279     /**
 280      * Returns the hash code value for this Map.
 281      */
 282     public int hashCode() {
 283         return map.hashCode();
 284     }
 285 
 286     /**
 287      * Returns a copy of the Attributes, implemented as follows:
 288      * <pre>
 289      *     public Object clone() { return new Attributes(this); }
 290      * </pre>
 291      * Since the attribute names and values are themselves immutable,
 292      * the Attributes returned can be safely modified without affecting
 293      * the original.
 294      */
 295     public Object clone() {
 296         return new Attributes(this);
 297     }
 298 
 299     /*
 300      * Writes the current attributes to the specified data output stream.
 301      * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
 302      */
 303      @SuppressWarnings("deprecation")
 304      void write(DataOutputStream os) throws IOException {
 305          for (Entry<Object, Object> e : entrySet()) {
 306              StringBuffer buffer = new StringBuffer(
 307                                          ((Name) e.getKey()).toString());
 308              buffer.append(": ");
 309 
 310              String value = (String) e.getValue();
 311              if (value != null) {
 312                  byte[] vb = value.getBytes("UTF8");
 313                  value = new String(vb, 0, 0, vb.length);
 314              }
 315              buffer.append(value);
 316 
 317              Manifest.make72Safe(buffer);
 318              buffer.append("\r\n");
 319              os.writeBytes(buffer.toString());
 320          }
 321         os.writeBytes("\r\n");
 322     }
 323 
 324     /*
 325      * Writes the current attributes to the specified data output stream,
 326      * make sure to write out the MANIFEST_VERSION or SIGNATURE_VERSION
 327      * attributes first.
 328      *
 329      * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
 330      */
 331     @SuppressWarnings("deprecation")
 332     void writeMain(DataOutputStream out) throws IOException
 333     {
 334         // write out the *-Version header first, if it exists
 335         String vername = Name.MANIFEST_VERSION.toString();
 336         String version = getValue(vername);
 337         if (version == null) {
 338             vername = Name.SIGNATURE_VERSION.toString();
 339             version = getValue(vername);
 340         }
 341 
 342         if (version != null) {
 343             out.writeBytes(vername+": "+version+"\r\n");
 344         }
 345 
 346         // write out all attributes except for the version
 347         // we wrote out earlier
 348         for (Entry<Object, Object> e : entrySet()) {
 349             String name = ((Name) e.getKey()).toString();
 350             if ((version != null) && !(name.equalsIgnoreCase(vername))) {
 351 
 352                 StringBuffer buffer = new StringBuffer(name);
 353                 buffer.append(": ");
 354 
 355                 String value = (String) e.getValue();
 356                 if (value != null) {
 357                     byte[] vb = value.getBytes("UTF8");
 358                     value = new String(vb, 0, 0, vb.length);
 359                 }
 360                 buffer.append(value);
 361 
 362                 Manifest.make72Safe(buffer);
 363                 buffer.append("\r\n");
 364                 out.writeBytes(buffer.toString());
 365             }
 366         }
 367         out.writeBytes("\r\n");
 368     }
 369 
 370     /*
 371      * Reads attributes from the specified input stream.
 372      * XXX Need to handle UTF8 values.
 373      */
 374     void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
 375         read(is, lbuf, null, 0);
 376     }
 377 
 378     @SuppressWarnings("deprecation")
 379     int read(Manifest.FastInputStream is, byte[] lbuf, String filename, int lineNumber) throws IOException {
 380         String name = null, value;
 381         byte[] lastline = null;
 382 
 383         int len;
 384         while ((len = is.readLine(lbuf)) != -1) {
 385             boolean lineContinued = false;
 386             byte c = lbuf[--len];
 387             lineNumber++;
 388 
 389             if (c != '\n' && c != '\r') {
 390                 throw new IOException("line too long ("
 391                             + Manifest.getErrorPosition(filename, lineNumber) + ")");
 392             }
 393             if (len > 0 && lbuf[len-1] == '\r') {
 394                 --len;
 395             }
 396             if (len == 0) {
 397                 break;
 398             }
 399             int i = 0;
 400             if (lbuf[0] == ' ') {
 401                 // continuation of previous line
 402                 if (name == null) {
 403                     throw new IOException("misplaced continuation line ("
 404                                 + Manifest.getErrorPosition(filename, lineNumber) + ")");
 405                 }
 406                 lineContinued = true;
 407                 byte[] buf = new byte[lastline.length + len - 1];
 408                 System.arraycopy(lastline, 0, buf, 0, lastline.length);
 409                 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
 410                 if (is.peek() == ' ') {
 411                     lastline = buf;
 412                     continue;
 413                 }
 414                 value = new String(buf, 0, buf.length, "UTF8");
 415                 lastline = null;
 416             } else {
 417                 while (lbuf[i++] != ':') {
 418                     if (i >= len) {
 419                         throw new IOException("invalid header field ("
 420                                     + Manifest.getErrorPosition(filename, lineNumber) + ")");
 421                     }
 422                 }
 423                 if (lbuf[i++] != ' ') {
 424                     throw new IOException("invalid header field ("
 425                                 + Manifest.getErrorPosition(filename, lineNumber) + ")");
 426                 }
 427                 name = new String(lbuf, 0, 0, i - 2);
 428                 if (is.peek() == ' ') {
 429                     lastline = new byte[len - i];
 430                     System.arraycopy(lbuf, i, lastline, 0, len - i);
 431                     continue;
 432                 }
 433                 value = new String(lbuf, i, len - i, "UTF8");
 434             }
 435             try {
 436                 if ((putValue(name, value) != null) && (!lineContinued)) {
 437                     PlatformLogger.getLogger("java.util.jar").warning(
 438                                      "Duplicate name in Manifest: " + name
 439                                      + ".\n"
 440                                      + "Ensure that the manifest does not "
 441                                      + "have duplicate entries, and\n"
 442                                      + "that blank lines separate "
 443                                      + "individual sections in both your\n"
 444                                      + "manifest and in the META-INF/MANIFEST.MF "
 445                                      + "entry in the jar file.");
 446                 }
 447             } catch (IllegalArgumentException e) {
 448                 throw new IOException("invalid header field name: " + name
 449                             + " (" + Manifest.getErrorPosition(filename, lineNumber) + ")");
 450             }
 451         }
 452         return lineNumber;
 453     }
 454 
 455     /**
 456      * The Attributes.Name class represents an attribute name stored in
 457      * this Map. Valid attribute names are case-insensitive, are restricted
 458      * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
 459      * 70 characters in length. Attribute values can contain any characters
 460      * and will be UTF8-encoded when written to the output stream.  See the
 461      * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>
 462      * for more information about valid attribute names and values.
 463      */
 464     public static class Name {
 465         private final String name;
 466         private final int hashCode;
 467 
 468         /**
 469          * Avoid allocation for common Names
 470          */
 471         private static @Stable Map<String, Name> KNOWN_NAMES;
 472 
 473         static final Name of(String name) {
 474             Name n = KNOWN_NAMES.get(name);
 475             if (n != null) {
 476                 return n;
 477             }
 478             return new Name(name);
 479         }
 480 
 481         /**
 482          * Constructs a new attribute name using the given string name.
 483          *
 484          * @param name the attribute string name
 485          * @exception IllegalArgumentException if the attribute name was
 486          *            invalid
 487          * @exception NullPointerException if the attribute name was null
 488          */
 489         public Name(String name) {
 490             this.hashCode = hash(name);
 491             this.name = name.intern();
 492         }
 493 
 494         // Checks the string is valid
 495         private final int hash(String name) {
 496             Objects.requireNonNull(name, "name");
 497             int len = name.length();
 498             if (len > 70 || len == 0) {
 499                 throw new IllegalArgumentException(name);
 500             }
 501             // Calculate hash code case insensitively
 502             int h = 0;
 503             for (int i = 0; i < len; i++) {
 504                 char c = name.charAt(i);
 505                 if (c >= 'a' && c <= 'z') {
 506                     // hashcode must be identical for upper and lower case
 507                     h = h * 31 + (c - 0x20);
 508                 } else if ((c >= 'A' && c <= 'Z' ||
 509                         c >= '0' && c <= '9' ||
 510                         c == '_' || c == '-')) {
 511                     h = h * 31 + c;
 512                 } else {
 513                     throw new IllegalArgumentException(name);
 514                 }
 515             }
 516             return h;
 517         }
 518 
 519         /**
 520          * Compares this attribute name to another for equality.
 521          * @param o the object to compare
 522          * @return true if this attribute name is equal to the
 523          *         specified attribute object
 524          */
 525         public boolean equals(Object o) {
 526             if (this == o) {
 527                 return true;
 528             }
 529             if (o instanceof Name) {
 530                 Name other = (Name)o;
 531                 return other.name.equalsIgnoreCase(name);
 532             } else {
 533                 return false;
 534             }
 535         }
 536 
 537         /**
 538          * Computes the hash value for this attribute name.
 539          */
 540         public int hashCode() {
 541             return hashCode;
 542         }
 543 
 544         /**
 545          * Returns the attribute name as a String.
 546          */
 547         public String toString() {
 548             return name;
 549         }
 550 
 551         /**
 552          * {@code Name} object for {@code Manifest-Version}
 553          * manifest attribute. This attribute indicates the version number
 554          * of the manifest standard to which a JAR file's manifest conforms.
 555          * @see <a href="{@docRoot}/../specs/jar/jar.html#jar-manifest">
 556          *      Manifest and Signature Specification</a>
 557          */
 558         public static final Name MANIFEST_VERSION;
 559 
 560         /**
 561          * {@code Name} object for {@code Signature-Version}
 562          * manifest attribute used when signing JAR files.
 563          * @see <a href="{@docRoot}/../specs/jar/jar.html#jar-manifest">
 564          *      Manifest and Signature Specification</a>
 565          */
 566         public static final Name SIGNATURE_VERSION;
 567 
 568         /**
 569          * {@code Name} object for {@code Content-Type}
 570          * manifest attribute.
 571          */
 572         public static final Name CONTENT_TYPE;
 573 
 574         /**
 575          * {@code Name} object for {@code Class-Path}
 576          * manifest attribute.
 577          * @see <a href="{@docRoot}/../specs/jar/jar.html#class-path-attribute">
 578          *      JAR file specification</a>
 579          */
 580         public static final Name CLASS_PATH;
 581 
 582         /**
 583          * {@code Name} object for {@code Main-Class} manifest
 584          * attribute used for launching applications packaged in JAR files.
 585          * The {@code Main-Class} attribute is used in conjunction
 586          * with the {@code -jar} command-line option of the
 587          * {@code java} application launcher.
 588          */
 589         public static final Name MAIN_CLASS;
 590 
 591         /**
 592          * {@code Name} object for {@code Sealed} manifest attribute
 593          * used for sealing.
 594          * @see <a href="{@docRoot}/../specs/jar/jar.html#package-sealing">
 595          *      Package Sealing</a>
 596          */
 597         public static final Name SEALED;
 598 
 599         /**
 600          * {@code Name} object for {@code Extension-List} manifest attribute
 601          * used for the extension mechanism that is no longer supported.
 602          */
 603         public static final Name EXTENSION_LIST;
 604 
 605         /**
 606          * {@code Name} object for {@code Extension-Name} manifest attribute.
 607          * used for the extension mechanism that is no longer supported.
 608          */
 609         public static final Name EXTENSION_NAME;
 610 
 611         /**
 612          * {@code Name} object for {@code Extension-Installation} manifest attribute.
 613          *
 614          * @deprecated Extension mechanism is no longer supported.
 615          */
 616         @Deprecated
 617         public static final Name EXTENSION_INSTALLATION;
 618 
 619         /**
 620          * {@code Name} object for {@code Implementation-Title}
 621          * manifest attribute used for package versioning.
 622          */
 623         public static final Name IMPLEMENTATION_TITLE;
 624 
 625         /**
 626          * {@code Name} object for {@code Implementation-Version}
 627          * manifest attribute used for package versioning.
 628          */
 629         public static final Name IMPLEMENTATION_VERSION;
 630 
 631         /**
 632          * {@code Name} object for {@code Implementation-Vendor}
 633          * manifest attribute used for package versioning.
 634          */
 635         public static final Name IMPLEMENTATION_VENDOR;
 636 
 637         /**
 638          * {@code Name} object for {@code Implementation-Vendor-Id}
 639          * manifest attribute.
 640          *
 641          * @deprecated Extension mechanism is no longer supported.
 642          */
 643         @Deprecated
 644         public static final Name IMPLEMENTATION_VENDOR_ID;
 645 
 646         /**
 647          * {@code Name} object for {@code Implementation-URL}
 648          * manifest attribute.
 649          *
 650          * @deprecated Extension mechanism is no longer supported.
 651          */
 652         @Deprecated
 653         public static final Name IMPLEMENTATION_URL;
 654 
 655         /**
 656          * {@code Name} object for {@code Specification-Title}
 657          * manifest attribute used for package versioning.
 658          */
 659         public static final Name SPECIFICATION_TITLE;
 660 
 661         /**
 662          * {@code Name} object for {@code Specification-Version}
 663          * manifest attribute used for package versioning.
 664          */
 665         public static final Name SPECIFICATION_VERSION;
 666 
 667         /**
 668          * {@code Name} object for {@code Specification-Vendor}
 669          * manifest attribute used for package versioning.
 670          */
 671         public static final Name SPECIFICATION_VENDOR;
 672 
 673         /**
 674          * {@code Name} object for {@code Multi-Release}
 675          * manifest attribute that indicates this is a multi-release JAR file.
 676          *
 677          * @since   9
 678          */
 679         public static final Name MULTI_RELEASE;
 680 
 681         private static void addName(Map<String, Name> names, Name name) {
 682             names.put(name.name, name);
 683         }
 684 
 685         static {
 686 
 687             VM.initializeFromArchive(Attributes.Name.class);
 688 
 689             if (KNOWN_NAMES == null) {
 690                 MANIFEST_VERSION = new Name("Manifest-Version");
 691                 SIGNATURE_VERSION = new Name("Signature-Version");
 692                 CONTENT_TYPE = new Name("Content-Type");
 693                 CLASS_PATH = new Name("Class-Path");
 694                 MAIN_CLASS = new Name("Main-Class");
 695                 SEALED = new Name("Sealed");
 696                 EXTENSION_LIST = new Name("Extension-List");
 697                 EXTENSION_NAME = new Name("Extension-Name");
 698                 EXTENSION_INSTALLATION = new Name("Extension-Installation");
 699                 IMPLEMENTATION_TITLE = new Name("Implementation-Title");
 700                 IMPLEMENTATION_VERSION = new Name("Implementation-Version");
 701                 IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor");
 702                 IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
 703                 IMPLEMENTATION_URL = new Name("Implementation-URL");
 704                 SPECIFICATION_TITLE = new Name("Specification-Title");
 705                 SPECIFICATION_VERSION = new Name("Specification-Version");
 706                 SPECIFICATION_VENDOR = new Name("Specification-Vendor");
 707                 MULTI_RELEASE = new Name("Multi-Release");
 708 
 709                 var names = new HashMap<String, Name>(64);
 710                 addName(names, MANIFEST_VERSION);
 711                 addName(names, SIGNATURE_VERSION);
 712                 addName(names, CONTENT_TYPE);
 713                 addName(names, CLASS_PATH);
 714                 addName(names, MAIN_CLASS);
 715                 addName(names, SEALED);
 716                 addName(names, EXTENSION_LIST);
 717                 addName(names, EXTENSION_NAME);
 718                 addName(names, IMPLEMENTATION_TITLE);
 719                 addName(names, IMPLEMENTATION_VERSION);
 720                 addName(names, IMPLEMENTATION_VENDOR);
 721                 addName(names, SPECIFICATION_TITLE);
 722                 addName(names, SPECIFICATION_VERSION);
 723                 addName(names, SPECIFICATION_VENDOR);
 724                 addName(names, MULTI_RELEASE);
 725 
 726                 // Common attributes used in MANIFEST.MF et.al; adding these has a
 727                 // small footprint cost, but is likely to be quickly paid for by
 728                 // reducing allocation when reading and parsing typical manifests
 729                 addName(names, new Name("Add-Exports"));
 730                 addName(names, new Name("Add-Opens"));
 731                 addName(names, new Name("Ant-Version"));
 732                 addName(names, new Name("Archiver-Version"));
 733                 addName(names, new Name("Build-Jdk"));
 734                 addName(names, new Name("Built-By"));
 735                 addName(names, new Name("Bnd-LastModified"));
 736                 addName(names, new Name("Bundle-Description"));
 737                 addName(names, new Name("Bundle-DocURL"));
 738                 addName(names, new Name("Bundle-License"));
 739                 addName(names, new Name("Bundle-ManifestVersion"));
 740                 addName(names, new Name("Bundle-Name"));
 741                 addName(names, new Name("Bundle-Vendor"));
 742                 addName(names, new Name("Bundle-Version"));
 743                 addName(names, new Name("Bundle-SymbolicName"));
 744                 addName(names, new Name("Created-By"));
 745                 addName(names, new Name("Export-Package"));
 746                 addName(names, new Name("Import-Package"));
 747                 addName(names, new Name("Name"));
 748                 addName(names, new Name("SHA1-Digest"));
 749                 addName(names, new Name("X-Compile-Source-JDK"));
 750                 addName(names, new Name("X-Compile-Target-JDK"));
 751                 KNOWN_NAMES = Map.copyOf(names);
 752             } else {
 753                 // Even if KNOWN_NAMES was read from archive, we still need
 754                 // to initialize the public constants
 755                 MANIFEST_VERSION = KNOWN_NAMES.get("Manifest-Version");
 756                 SIGNATURE_VERSION = KNOWN_NAMES.get("Signature-Version");
 757                 CONTENT_TYPE = KNOWN_NAMES.get("Content-Type");
 758                 CLASS_PATH = KNOWN_NAMES.get("Class-Path");
 759                 MAIN_CLASS = KNOWN_NAMES.get("Main-Class");
 760                 SEALED = KNOWN_NAMES.get("Sealed");
 761                 EXTENSION_LIST = KNOWN_NAMES.get("Extension-List");
 762                 EXTENSION_NAME = KNOWN_NAMES.get("Extension-Name");
 763                 EXTENSION_INSTALLATION = KNOWN_NAMES.get("Extension-Installation");
 764                 IMPLEMENTATION_TITLE = KNOWN_NAMES.get("Implementation-Title");
 765                 IMPLEMENTATION_VERSION = KNOWN_NAMES.get("Implementation-Version");
 766                 IMPLEMENTATION_VENDOR = KNOWN_NAMES.get("Implementation-Vendor");
 767                 IMPLEMENTATION_VENDOR_ID = KNOWN_NAMES.get("Implementation-Vendor-Id");
 768                 IMPLEMENTATION_URL = KNOWN_NAMES.get("Implementation-URL");
 769                 SPECIFICATION_TITLE = KNOWN_NAMES.get("Specification-Title");
 770                 SPECIFICATION_VERSION = KNOWN_NAMES.get("Specification-Version");
 771                 SPECIFICATION_VENDOR = KNOWN_NAMES.get("Specification-Vendor");
 772                 MULTI_RELEASE = KNOWN_NAMES.get("Multi-Release");
 773             }
 774         }
 775     }
 776 }