0N/A/*
2362N/A * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.util.jar;
0N/A
0N/Aimport java.io.DataInputStream;
0N/Aimport java.io.DataOutputStream;
0N/Aimport java.io.IOException;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.Map;
0N/Aimport java.util.Set;
0N/Aimport java.util.Collection;
0N/Aimport java.util.AbstractSet;
0N/Aimport java.util.Iterator;
1672N/Aimport sun.util.logging.PlatformLogger;
0N/Aimport java.util.Comparator;
0N/Aimport sun.misc.ASCIICaseInsensitiveComparator;
0N/A
0N/A/**
0N/A * The Attributes class maps Manifest attribute names to associated string
0N/A * values. Valid attribute names are case-insensitive, are restricted to
0N/A * the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed 70
0N/A * characters in length. Attribute values can contain any characters and
0N/A * will be UTF8-encoded when written to the output stream. See the
0N/A * <a href="../../../../technotes/guides/jar/jar.html">JAR File Specification</a>
0N/A * for more information about valid attribute names and values.
0N/A *
0N/A * @author David Connelly
0N/A * @see Manifest
0N/A * @since 1.2
0N/A */
0N/Apublic class Attributes implements Map<Object,Object>, Cloneable {
0N/A /**
0N/A * The attribute name-value mappings.
0N/A */
0N/A protected Map<Object,Object> map;
0N/A
0N/A /**
0N/A * Constructs a new, empty Attributes object with default size.
0N/A */
0N/A public Attributes() {
0N/A this(11);
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new, empty Attributes object with the specified
0N/A * initial size.
0N/A *
0N/A * @param size the initial number of attributes
0N/A */
0N/A public Attributes(int size) {
0N/A map = new HashMap(size);
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new Attributes object with the same attribute name-value
0N/A * mappings as in the specified Attributes.
0N/A *
0N/A * @param attr the specified Attributes
0N/A */
0N/A public Attributes(Attributes attr) {
0N/A map = new HashMap(attr);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the value of the specified attribute name, or null if the
0N/A * attribute name was not found.
0N/A *
0N/A * @param name the attribute name
0N/A * @return the value of the specified attribute name, or null if
0N/A * not found.
0N/A */
0N/A public Object get(Object name) {
0N/A return map.get(name);
0N/A }
0N/A
0N/A /**
0N/A * Returns the value of the specified attribute name, specified as
0N/A * a string, or null if the attribute was not found. The attribute
0N/A * name is case-insensitive.
0N/A * <p>
0N/A * This method is defined as:
0N/A * <pre>
0N/A * return (String)get(new Attributes.Name((String)name));
0N/A * </pre>
0N/A *
0N/A * @param name the attribute name as a string
0N/A * @return the String value of the specified attribute name, or null if
0N/A * not found.
0N/A * @throws IllegalArgumentException if the attribute name is invalid
0N/A */
0N/A public String getValue(String name) {
0N/A return (String)get(new Attributes.Name(name));
0N/A }
0N/A
0N/A /**
0N/A * Returns the value of the specified Attributes.Name, or null if the
0N/A * attribute was not found.
0N/A * <p>
0N/A * This method is defined as:
0N/A * <pre>
0N/A * return (String)get(name);
0N/A * </pre>
0N/A *
0N/A * @param name the Attributes.Name object
0N/A * @return the String value of the specified Attribute.Name, or null if
0N/A * not found.
0N/A */
0N/A public String getValue(Name name) {
0N/A return (String)get(name);
0N/A }
0N/A
0N/A /**
0N/A * Associates the specified value with the specified attribute name
0N/A * (key) in this Map. If the Map previously contained a mapping for
0N/A * the attribute name, the old value is replaced.
0N/A *
0N/A * @param name the attribute name
0N/A * @param value the attribute value
0N/A * @return the previous value of the attribute, or null if none
0N/A * @exception ClassCastException if the name is not a Attributes.Name
0N/A * or the value is not a String
0N/A */
0N/A public Object put(Object name, Object value) {
0N/A return map.put((Attributes.Name)name, (String)value);
0N/A }
0N/A
0N/A /**
0N/A * Associates the specified value with the specified attribute name,
0N/A * specified as a String. The attributes name is case-insensitive.
0N/A * If the Map previously contained a mapping for the attribute name,
0N/A * the old value is replaced.
0N/A * <p>
0N/A * This method is defined as:
0N/A * <pre>
0N/A * return (String)put(new Attributes.Name(name), value);
0N/A * </pre>
0N/A *
0N/A * @param name the attribute name as a string
0N/A * @param value the attribute value
0N/A * @return the previous value of the attribute, or null if none
0N/A * @exception IllegalArgumentException if the attribute name is invalid
0N/A */
0N/A public String putValue(String name, String value) {
0N/A return (String)put(new Name(name), value);
0N/A }
0N/A
0N/A /**
0N/A * Removes the attribute with the specified name (key) from this Map.
0N/A * Returns the previous attribute value, or null if none.
0N/A *
0N/A * @param name attribute name
0N/A * @return the previous value of the attribute, or null if none
0N/A */
0N/A public Object remove(Object name) {
0N/A return map.remove(name);
0N/A }
0N/A
0N/A /**
0N/A * Returns true if this Map maps one or more attribute names (keys)
0N/A * to the specified value.
0N/A *
0N/A * @param value the attribute value
0N/A * @return true if this Map maps one or more attribute names to
0N/A * the specified value
0N/A */
0N/A public boolean containsValue(Object value) {
0N/A return map.containsValue(value);
0N/A }
0N/A
0N/A /**
0N/A * Returns true if this Map contains the specified attribute name (key).
0N/A *
0N/A * @param name the attribute name
0N/A * @return true if this Map contains the specified attribute name
0N/A */
0N/A public boolean containsKey(Object name) {
0N/A return map.containsKey(name);
0N/A }
0N/A
0N/A /**
0N/A * Copies all of the attribute name-value mappings from the specified
0N/A * Attributes to this Map. Duplicate mappings will be replaced.
0N/A *
0N/A * @param attr the Attributes to be stored in this map
0N/A * @exception ClassCastException if attr is not an Attributes
0N/A */
0N/A public void putAll(Map<?,?> attr) {
0N/A // ## javac bug?
0N/A if (!Attributes.class.isInstance(attr))
0N/A throw new ClassCastException();
0N/A for (Map.Entry<?,?> me : (attr).entrySet())
0N/A put(me.getKey(), me.getValue());
0N/A }
0N/A
0N/A /**
0N/A * Removes all attributes from this Map.
0N/A */
0N/A public void clear() {
0N/A map.clear();
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of attributes in this Map.
0N/A */
0N/A public int size() {
0N/A return map.size();
0N/A }
0N/A
0N/A /**
0N/A * Returns true if this Map contains no attributes.
0N/A */
0N/A public boolean isEmpty() {
0N/A return map.isEmpty();
0N/A }
0N/A
0N/A /**
0N/A * Returns a Set view of the attribute names (keys) contained in this Map.
0N/A */
0N/A public Set<Object> keySet() {
0N/A return map.keySet();
0N/A }
0N/A
0N/A /**
0N/A * Returns a Collection view of the attribute values contained in this Map.
0N/A */
0N/A public Collection<Object> values() {
0N/A return map.values();
0N/A }
0N/A
0N/A /**
0N/A * Returns a Collection view of the attribute name-value mappings
0N/A * contained in this Map.
0N/A */
0N/A public Set<Map.Entry<Object,Object>> entrySet() {
0N/A return map.entrySet();
0N/A }
0N/A
0N/A /**
0N/A * Compares the specified Attributes object with this Map for equality.
0N/A * Returns true if the given object is also an instance of Attributes
0N/A * and the two Attributes objects represent the same mappings.
0N/A *
0N/A * @param o the Object to be compared
0N/A * @return true if the specified Object is equal to this Map
0N/A */
0N/A public boolean equals(Object o) {
0N/A return map.equals(o);
0N/A }
0N/A
0N/A /**
0N/A * Returns the hash code value for this Map.
0N/A */
0N/A public int hashCode() {
0N/A return map.hashCode();
0N/A }
0N/A
0N/A /**
0N/A * Returns a copy of the Attributes, implemented as follows:
0N/A * <pre>
0N/A * public Object clone() { return new Attributes(this); }
0N/A * </pre>
0N/A * Since the attribute names and values are themselves immutable,
0N/A * the Attributes returned can be safely modified without affecting
0N/A * the original.
0N/A */
0N/A public Object clone() {
0N/A return new Attributes(this);
0N/A }
0N/A
0N/A /*
0N/A * Writes the current attributes to the specified data output stream.
0N/A * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
0N/A */
0N/A void write(DataOutputStream os) throws IOException {
0N/A Iterator it = entrySet().iterator();
0N/A while (it.hasNext()) {
0N/A Map.Entry e = (Map.Entry)it.next();
0N/A StringBuffer buffer = new StringBuffer(
0N/A ((Name)e.getKey()).toString());
0N/A buffer.append(": ");
0N/A
0N/A String value = (String)e.getValue();
0N/A if (value != null) {
0N/A byte[] vb = value.getBytes("UTF8");
0N/A value = new String(vb, 0, 0, vb.length);
0N/A }
0N/A buffer.append(value);
0N/A
0N/A buffer.append("\r\n");
0N/A Manifest.make72Safe(buffer);
0N/A os.writeBytes(buffer.toString());
0N/A }
0N/A os.writeBytes("\r\n");
0N/A }
0N/A
0N/A /*
0N/A * Writes the current attributes to the specified data output stream,
0N/A * make sure to write out the MANIFEST_VERSION or SIGNATURE_VERSION
0N/A * attributes first.
0N/A *
0N/A * XXX Need to handle UTF8 values and break up lines longer than 72 bytes
0N/A */
0N/A void writeMain(DataOutputStream out) throws IOException
0N/A {
0N/A // write out the *-Version header first, if it exists
0N/A String vername = Name.MANIFEST_VERSION.toString();
0N/A String version = getValue(vername);
0N/A if (version == null) {
0N/A vername = Name.SIGNATURE_VERSION.toString();
0N/A version = getValue(vername);
0N/A }
0N/A
0N/A if (version != null) {
0N/A out.writeBytes(vername+": "+version+"\r\n");
0N/A }
0N/A
0N/A // write out all attributes except for the version
0N/A // we wrote out earlier
0N/A Iterator it = entrySet().iterator();
0N/A while (it.hasNext()) {
0N/A Map.Entry e = (Map.Entry)it.next();
0N/A String name = ((Name)e.getKey()).toString();
0N/A if ((version != null) && ! (name.equalsIgnoreCase(vername))) {
0N/A
0N/A StringBuffer buffer = new StringBuffer(name);
0N/A buffer.append(": ");
0N/A
0N/A String value = (String)e.getValue();
0N/A if (value != null) {
0N/A byte[] vb = value.getBytes("UTF8");
0N/A value = new String(vb, 0, 0, vb.length);
0N/A }
0N/A buffer.append(value);
0N/A
0N/A buffer.append("\r\n");
0N/A Manifest.make72Safe(buffer);
0N/A out.writeBytes(buffer.toString());
0N/A }
0N/A }
0N/A out.writeBytes("\r\n");
0N/A }
0N/A
0N/A /*
0N/A * Reads attributes from the specified input stream.
0N/A * XXX Need to handle UTF8 values.
0N/A */
0N/A void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
0N/A String name = null, value = null;
0N/A byte[] lastline = null;
0N/A
0N/A int len;
0N/A while ((len = is.readLine(lbuf)) != -1) {
0N/A boolean lineContinued = false;
0N/A if (lbuf[--len] != '\n') {
0N/A throw new IOException("line too long");
0N/A }
0N/A if (len > 0 && lbuf[len-1] == '\r') {
0N/A --len;
0N/A }
0N/A if (len == 0) {
0N/A break;
0N/A }
0N/A int i = 0;
0N/A if (lbuf[0] == ' ') {
0N/A // continuation of previous line
0N/A if (name == null) {
0N/A throw new IOException("misplaced continuation line");
0N/A }
0N/A lineContinued = true;
0N/A byte[] buf = new byte[lastline.length + len - 1];
0N/A System.arraycopy(lastline, 0, buf, 0, lastline.length);
0N/A System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
0N/A if (is.peek() == ' ') {
0N/A lastline = buf;
0N/A continue;
0N/A }
0N/A value = new String(buf, 0, buf.length, "UTF8");
0N/A lastline = null;
0N/A } else {
0N/A while (lbuf[i++] != ':') {
0N/A if (i >= len) {
0N/A throw new IOException("invalid header field");
0N/A }
0N/A }
0N/A if (lbuf[i++] != ' ') {
0N/A throw new IOException("invalid header field");
0N/A }
0N/A name = new String(lbuf, 0, 0, i - 2);
0N/A if (is.peek() == ' ') {
0N/A lastline = new byte[len - i];
0N/A System.arraycopy(lbuf, i, lastline, 0, len - i);
0N/A continue;
0N/A }
0N/A value = new String(lbuf, i, len - i, "UTF8");
0N/A }
0N/A try {
0N/A if ((putValue(name, value) != null) && (!lineContinued)) {
1672N/A PlatformLogger.getLogger("java.util.jar").warning(
0N/A "Duplicate name in Manifest: " + name
0N/A + ".\n"
0N/A + "Ensure that the manifest does not "
0N/A + "have duplicate entries, and\n"
0N/A + "that blank lines separate "
0N/A + "individual sections in both your\n"
0N/A + "manifest and in the META-INF/MANIFEST.MF "
0N/A + "entry in the jar file.");
0N/A }
0N/A } catch (IllegalArgumentException e) {
0N/A throw new IOException("invalid header field name: " + name);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * The Attributes.Name class represents an attribute name stored in
0N/A * this Map. Valid attribute names are case-insensitive, are restricted
0N/A * to the ASCII characters in the set [0-9a-zA-Z_-], and cannot exceed
0N/A * 70 characters in length. Attribute values can contain any characters
0N/A * and will be UTF8-encoded when written to the output stream. See the
0N/A * <a href="../../../../technotes/guides/jar/jar.html">JAR File Specification</a>
0N/A * for more information about valid attribute names and values.
0N/A */
0N/A public static class Name {
0N/A private String name;
0N/A private int hashCode = -1;
0N/A
0N/A /**
0N/A * Constructs a new attribute name using the given string name.
0N/A *
0N/A * @param name the attribute string name
0N/A * @exception IllegalArgumentException if the attribute name was
0N/A * invalid
0N/A * @exception NullPointerException if the attribute name was null
0N/A */
0N/A public Name(String name) {
0N/A if (name == null) {
0N/A throw new NullPointerException("name");
0N/A }
0N/A if (!isValid(name)) {
0N/A throw new IllegalArgumentException(name);
0N/A }
0N/A this.name = name.intern();
0N/A }
0N/A
0N/A private static boolean isValid(String name) {
0N/A int len = name.length();
0N/A if (len > 70 || len == 0) {
0N/A return false;
0N/A }
0N/A for (int i = 0; i < len; i++) {
0N/A if (!isValid(name.charAt(i))) {
0N/A return false;
0N/A }
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A private static boolean isValid(char c) {
0N/A return isAlpha(c) || isDigit(c) || c == '_' || c == '-';
0N/A }
0N/A
0N/A private static boolean isAlpha(char c) {
0N/A return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
0N/A }
0N/A
0N/A private static boolean isDigit(char c) {
0N/A return c >= '0' && c <= '9';
0N/A }
0N/A
0N/A /**
0N/A * Compares this attribute name to another for equality.
0N/A * @param o the object to compare
0N/A * @return true if this attribute name is equal to the
0N/A * specified attribute object
0N/A */
0N/A public boolean equals(Object o) {
0N/A if (o instanceof Name) {
0N/A Comparator c = ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER;
0N/A return c.compare(name, ((Name)o).name) == 0;
0N/A } else {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Computes the hash value for this attribute name.
0N/A */
0N/A public int hashCode() {
0N/A if (hashCode == -1) {
0N/A hashCode = ASCIICaseInsensitiveComparator.lowerCaseHashCode(name);
0N/A }
0N/A return hashCode;
0N/A }
0N/A
0N/A /**
0N/A * Returns the attribute name as a String.
0N/A */
0N/A public String toString() {
0N/A return name;
0N/A }
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Manifest-Version</code>
0N/A * manifest attribute. This attribute indicates the version number
0N/A * of the manifest standard to which a JAR file's manifest conforms.
0N/A * @see <a href="../../../../technotes/guides/jar/jar.html#JAR Manifest">
0N/A * Manifest and Signature Specification</a>
0N/A */
0N/A public static final Name MANIFEST_VERSION = new Name("Manifest-Version");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Signature-Version</code>
0N/A * manifest attribute used when signing JAR files.
0N/A * @see <a href="../../../../technotes/guides/jar/jar.html#JAR Manifest">
0N/A * Manifest and Signature Specification</a>
0N/A */
0N/A public static final Name SIGNATURE_VERSION = new Name("Signature-Version");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Content-Type</code>
0N/A * manifest attribute.
0N/A */
0N/A public static final Name CONTENT_TYPE = new Name("Content-Type");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Class-Path</code>
0N/A * manifest attribute. Bundled extensions can use this attribute
0N/A * to find other JAR files containing needed classes.
0N/A * @see <a href="../../../../technotes/guides/extensions/spec.html#bundled">
0N/A * Extensions Specification</a>
0N/A */
0N/A public static final Name CLASS_PATH = new Name("Class-Path");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Main-Class</code> manifest
0N/A * attribute used for launching applications packaged in JAR files.
0N/A * The <code>Main-Class</code> attribute is used in conjunction
0N/A * with the <code>-jar</code> command-line option of the
0N/A * <tt>java</tt> application launcher.
0N/A */
0N/A public static final Name MAIN_CLASS = new Name("Main-Class");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Sealed</code> manifest attribute
0N/A * used for sealing.
0N/A * @see <a href="../../../../technotes/guides/extensions/spec.html#sealing">
0N/A * Extension Sealing</a>
0N/A */
0N/A public static final Name SEALED = new Name("Sealed");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Extension-List</code> manifest attribute
0N/A * used for declaring dependencies on installed extensions.
0N/A * @see <a href="../../../../technotes/guides/extensions/spec.html#dependency">
0N/A * Installed extension dependency</a>
0N/A */
0N/A public static final Name EXTENSION_LIST = new Name("Extension-List");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
0N/A * used for declaring dependencies on installed extensions.
0N/A * @see <a href="../../../../technotes/guides/extensions/spec.html#dependency">
0N/A * Installed extension dependency</a>
0N/A */
0N/A public static final Name EXTENSION_NAME = new Name("Extension-Name");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Extension-Name</code> manifest attribute
0N/A * used for declaring dependencies on installed extensions.
0N/A * @see <a href="../../../../technotes/guides/extensions/spec.html#dependency">
0N/A * Installed extension dependency</a>
0N/A */
0N/A public static final Name EXTENSION_INSTALLATION = new Name("Extension-Installation");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Implementation-Title</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name IMPLEMENTATION_TITLE = new Name("Implementation-Title");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Implementation-Version</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name IMPLEMENTATION_VERSION = new Name("Implementation-Version");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Implementation-Vendor</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name IMPLEMENTATION_VENDOR = new Name("Implementation-Vendor");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Implementation-Vendor-Id</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name IMPLEMENTATION_VENDOR_ID = new Name("Implementation-Vendor-Id");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Implementation-Vendor-URL</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name IMPLEMENTATION_URL = new Name("Implementation-URL");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Specification-Title</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name SPECIFICATION_TITLE = new Name("Specification-Title");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Specification-Version</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name SPECIFICATION_VERSION = new Name("Specification-Version");
0N/A
0N/A /**
0N/A * <code>Name</code> object for <code>Specification-Vendor</code>
0N/A * manifest attribute used for package versioning.
0N/A * @see <a href="../../../../technotes/guides/versioning/spec/versioning2.html#wp90779">
0N/A * Java Product Versioning Specification</a>
0N/A */
0N/A public static final Name SPECIFICATION_VENDOR = new Name("Specification-Vendor");
0N/A }
0N/A}