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