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