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