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