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