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