| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.tools.ant.taskdefs.optional.extension; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| import java.util.jar.Attributes; |
| import java.util.jar.Manifest; |
| |
| import org.apache.tools.ant.util.StringUtils; |
| |
| /** |
| * <p>Utility class that represents either an available "Optional Package" |
| * (formerly known as "Standard Extension") as described in the manifest |
| * of a JAR file, or the requirement for such an optional package.</p> |
| * |
| * <p>For more information about optional packages, see the document |
| * <em>Optional Package Versioning</em> in the documentation bundle for your |
| * Java2 Standard Edition package, in file |
| * <code>guide/extensions/versioning.html</code>.</p> |
| * |
| */ |
| public final class Extension { |
| /** |
| * Manifest Attribute Name object for EXTENSION_LIST. |
| */ |
| public static final Attributes.Name EXTENSION_LIST |
| = new Attributes.Name("Extension-List"); |
| |
| /** |
| * <code>Name</code> object for <code>Optional-Extension-List</code> |
| * manifest attribute used for declaring optional dependencies on |
| * installed extensions. Note that the dependencies declared by this method |
| * are not required for the library to operate but if present will be used. |
| * It is NOT part of the official "Optional Package" specification. |
| * |
| * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/spec.html#dependnecy"> |
| * Installed extension dependency</a> |
| */ |
| public static final Attributes.Name OPTIONAL_EXTENSION_LIST |
| = new Attributes.Name("Optional-Extension-List"); |
| |
| /** |
| * Manifest Attribute Name object for EXTENSION_NAME. |
| */ |
| public static final Attributes.Name EXTENSION_NAME = |
| new Attributes.Name("Extension-Name"); |
| /** |
| * Manifest Attribute Name object for SPECIFICATION_VERSION. |
| */ |
| public static final Attributes.Name SPECIFICATION_VERSION |
| = Attributes.Name.SPECIFICATION_VERSION; |
| |
| /** |
| * Manifest Attribute Name object for SPECIFICATION_VENDOR. |
| */ |
| public static final Attributes.Name SPECIFICATION_VENDOR |
| = Attributes.Name.SPECIFICATION_VENDOR; |
| |
| /** |
| * Manifest Attribute Name object for IMPLEMENTATION_VERSION. |
| */ |
| public static final Attributes.Name IMPLEMENTATION_VERSION |
| = Attributes.Name.IMPLEMENTATION_VERSION; |
| |
| /** |
| * Manifest Attribute Name object for IMPLEMENTATION_VENDOR. |
| */ |
| public static final Attributes.Name IMPLEMENTATION_VENDOR |
| = Attributes.Name.IMPLEMENTATION_VENDOR; |
| |
| /** |
| * Manifest Attribute Name object for IMPLEMENTATION_URL. |
| */ |
| public static final Attributes.Name IMPLEMENTATION_URL |
| = new Attributes.Name("Implementation-URL"); |
| |
| /** |
| * Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID. |
| */ |
| public static final Attributes.Name IMPLEMENTATION_VENDOR_ID |
| = new Attributes.Name("Implementation-Vendor-Id"); |
| |
| /** |
| * Enum indicating that extension is compatible with other extension. |
| */ |
| public static final Compatibility COMPATIBLE |
| = new Compatibility("COMPATIBLE"); |
| |
| /** |
| * Enum indicating that extension requires an upgrade |
| * of specification to be compatible with other extension. |
| */ |
| public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE |
| = new Compatibility("REQUIRE_SPECIFICATION_UPGRADE"); |
| |
| /** |
| * Enum indicating that extension requires a vendor |
| * switch to be compatible with other extension. |
| */ |
| public static final Compatibility REQUIRE_VENDOR_SWITCH |
| = new Compatibility("REQUIRE_VENDOR_SWITCH"); |
| |
| /** |
| * Enum indicating that extension requires an upgrade |
| * of implementation to be compatible with other extension. |
| */ |
| public static final Compatibility REQUIRE_IMPLEMENTATION_UPGRADE |
| = new Compatibility("REQUIRE_IMPLEMENTATION_UPGRADE"); |
| |
| /** |
| * Enum indicating that extension is incompatible with |
| * other extension in ways other than other enums |
| * indicate). For example the other extension may have |
| * a different ID. |
| */ |
| public static final Compatibility INCOMPATIBLE |
| = new Compatibility("INCOMPATIBLE"); |
| |
| /** |
| * The name of the optional package being made available, or required. |
| */ |
| private String extensionName; |
| |
| /** |
| * The version number (dotted decimal notation) of the specification |
| * to which this optional package conforms. |
| */ |
| private DeweyDecimal specificationVersion; |
| |
| /** |
| * The name of the company or organization that originated the |
| * specification to which this optional package conforms. |
| */ |
| private String specificationVendor; |
| |
| /** |
| * The unique identifier of the company that produced the optional |
| * package contained in this JAR file. |
| */ |
| private String implementationVendorID; |
| |
| /** |
| * The name of the company or organization that produced this |
| * implementation of this optional package. |
| */ |
| private String implementationVendor; |
| |
| /** |
| * The version number (dotted decimal notation) for this implementation |
| * of the optional package. |
| */ |
| private DeweyDecimal implementationVersion; |
| |
| /** |
| * The URL from which the most recent version of this optional package |
| * can be obtained if it is not already installed. |
| */ |
| private String implementationURL; |
| |
| /** |
| * Return an array of <code>Extension</code> objects representing optional |
| * packages that are available in the JAR file associated with the |
| * specified <code>Manifest</code>. If there are no such optional |
| * packages, a zero-length array is returned. |
| * |
| * @param manifest Manifest to be parsed |
| * @return the "available" extensions in specified manifest |
| */ |
| public static Extension[] getAvailable(final Manifest manifest) { |
| if (null == manifest) { |
| return new Extension[ 0 ]; |
| } |
| |
| final ArrayList results = new ArrayList(); |
| |
| final Attributes mainAttributes = manifest.getMainAttributes(); |
| if (null != mainAttributes) { |
| final Extension extension = getExtension("", mainAttributes); |
| if (null != extension) { |
| results.add(extension); |
| } |
| } |
| |
| final Map entries = manifest.getEntries(); |
| final Iterator keys = entries.keySet().iterator(); |
| while (keys.hasNext()) { |
| final String key = (String) keys.next(); |
| final Attributes attributes = (Attributes) entries.get(key); |
| final Extension extension = getExtension("", attributes); |
| if (null != extension) { |
| results.add(extension); |
| } |
| } |
| |
| return (Extension[]) results.toArray(new Extension[results.size()]); |
| } |
| |
| /** |
| * Return the set of <code>Extension</code> objects representing optional |
| * packages that are required by the application contained in the JAR |
| * file associated with the specified <code>Manifest</code>. If there |
| * are no such optional packages, a zero-length list is returned. |
| * |
| * @param manifest Manifest to be parsed |
| * @return the dependencies that are specified in manifes |
| */ |
| public static Extension[] getRequired(final Manifest manifest) { |
| return getListed(manifest, Attributes.Name.EXTENSION_LIST); |
| } |
| |
| /** |
| * Return the set of <code>Extension</code> objects representing "Optional |
| * Packages" that the application declares they will use if present. If |
| * there are no such optional packages, a zero-length list is returned. |
| * |
| * @param manifest Manifest to be parsed |
| * @return the optional dependencies that are specified in manifest |
| */ |
| public static Extension[] getOptions(final Manifest manifest) { |
| return getListed(manifest, OPTIONAL_EXTENSION_LIST); |
| } |
| |
| /** |
| * Add Extension to the specified manifest Attributes. |
| * |
| * @param attributes the attributes of manifest to add to |
| * @param extension the extension |
| */ |
| public static void addExtension(final Extension extension, |
| final Attributes attributes) { |
| addExtension(extension, "", attributes); |
| } |
| |
| /** |
| * Add Extension to the specified manifest Attributes. |
| * Use the specified prefix so that dependencies can added |
| * with a prefix such as "java3d-" etc. |
| * |
| * @param attributes the attributes of manifest to add to |
| * @param extension the extension |
| * @param prefix the name to prefix to extension |
| */ |
| public static void addExtension(final Extension extension, |
| final String prefix, |
| final Attributes attributes) { |
| attributes.putValue(prefix + EXTENSION_NAME, |
| extension.getExtensionName()); |
| |
| final String specificationVendor = extension.getSpecificationVendor(); |
| if (null != specificationVendor) { |
| attributes.putValue(prefix + SPECIFICATION_VENDOR, |
| specificationVendor); |
| } |
| |
| final DeweyDecimal specificationVersion |
| = extension.getSpecificationVersion(); |
| if (null != specificationVersion) { |
| attributes.putValue(prefix + SPECIFICATION_VERSION, |
| specificationVersion.toString()); |
| } |
| |
| final String implementationVendorID |
| = extension.getImplementationVendorID(); |
| if (null != implementationVendorID) { |
| attributes.putValue(prefix + IMPLEMENTATION_VENDOR_ID, |
| implementationVendorID); |
| } |
| |
| final String implementationVendor = extension.getImplementationVendor(); |
| if (null != implementationVendor) { |
| attributes.putValue(prefix + IMPLEMENTATION_VENDOR, |
| implementationVendor); |
| } |
| |
| final DeweyDecimal implementationVersion |
| = extension.getImplementationVersion(); |
| if (null != implementationVersion) { |
| attributes.putValue(prefix + IMPLEMENTATION_VERSION, |
| implementationVersion.toString()); |
| } |
| |
| final String implementationURL = extension.getImplementationURL(); |
| if (null != implementationURL) { |
| attributes.putValue(prefix + IMPLEMENTATION_URL, |
| implementationURL); |
| } |
| } |
| |
| /** |
| * The constructor to create Extension object. |
| * Note that every component is allowed to be specified |
| * but only the extensionName is mandatory. |
| * |
| * @param extensionName the name of extension. |
| * @param specificationVersion the specification Version of extension. |
| * @param specificationVendor the specification Vendor of extension. |
| * @param implementationVersion the implementation Version of extension. |
| * @param implementationVendor the implementation Vendor of extension. |
| * @param implementationVendorId the implementation VendorId of extension. |
| * @param implementationURL the implementation URL of extension. |
| */ |
| public Extension(final String extensionName, |
| final String specificationVersion, |
| final String specificationVendor, |
| final String implementationVersion, |
| final String implementationVendor, |
| final String implementationVendorId, |
| final String implementationURL) { |
| this.extensionName = extensionName; |
| this.specificationVendor = specificationVendor; |
| |
| if (null != specificationVersion) { |
| try { |
| this.specificationVersion |
| = new DeweyDecimal(specificationVersion); |
| } catch (final NumberFormatException nfe) { |
| final String error = "Bad specification version format '" |
| + specificationVersion + "' in '" + extensionName |
| + "'. (Reason: " + nfe + ")"; |
| throw new IllegalArgumentException(error); |
| } |
| } |
| |
| this.implementationURL = implementationURL; |
| this.implementationVendor = implementationVendor; |
| this.implementationVendorID = implementationVendorId; |
| |
| if (null != implementationVersion) { |
| try { |
| this.implementationVersion |
| = new DeweyDecimal(implementationVersion); |
| } catch (final NumberFormatException nfe) { |
| final String error = "Bad implementation version format '" |
| + implementationVersion + "' in '" + extensionName |
| + "'. (Reason: " + nfe + ")"; |
| throw new IllegalArgumentException(error); |
| } |
| } |
| |
| if (null == this.extensionName) { |
| throw new NullPointerException("extensionName property is null"); |
| } |
| } |
| |
| /** |
| * Get the name of the extension. |
| * |
| * @return the name of the extension |
| */ |
| public String getExtensionName() { |
| return extensionName; |
| } |
| |
| /** |
| * Get the vendor of the extensions specification. |
| * |
| * @return the vendor of the extensions specification. |
| */ |
| public String getSpecificationVendor() { |
| return specificationVendor; |
| } |
| |
| /** |
| * Get the version of the extensions specification. |
| * |
| * @return the version of the extensions specification. |
| */ |
| public DeweyDecimal getSpecificationVersion() { |
| return specificationVersion; |
| } |
| |
| /** |
| * Get the url of the extensions implementation. |
| * |
| * @return the url of the extensions implementation. |
| */ |
| public String getImplementationURL() { |
| return implementationURL; |
| } |
| |
| /** |
| * Get the vendor of the extensions implementation. |
| * |
| * @return the vendor of the extensions implementation. |
| */ |
| public String getImplementationVendor() { |
| return implementationVendor; |
| } |
| |
| /** |
| * Get the vendorID of the extensions implementation. |
| * |
| * @return the vendorID of the extensions implementation. |
| */ |
| public String getImplementationVendorID() { |
| return implementationVendorID; |
| } |
| |
| /** |
| * Get the version of the extensions implementation. |
| * |
| * @return the version of the extensions implementation. |
| */ |
| public DeweyDecimal getImplementationVersion() { |
| return implementationVersion; |
| } |
| |
| /** |
| * Return a Compatibility enum indicating the relationship of this |
| * <code>Extension</code> with the specified <code>Extension</code>. |
| * |
| * @param required Description of the required optional package |
| * @return the enum indicating the compatibility (or lack thereof) |
| * of specifed extension |
| */ |
| public Compatibility getCompatibilityWith(final Extension required) { |
| // Extension Name must match |
| if (!extensionName.equals(required.getExtensionName())) { |
| return INCOMPATIBLE; |
| } |
| |
| // Available specification version must be >= required |
| final DeweyDecimal requiredSpecificationVersion |
| = required.getSpecificationVersion(); |
| if (null != requiredSpecificationVersion) { |
| if (null == specificationVersion |
| || !isCompatible(specificationVersion, requiredSpecificationVersion)) { |
| return REQUIRE_SPECIFICATION_UPGRADE; |
| } |
| } |
| |
| // Implementation Vendor ID must match |
| final String requiredImplementationVendorID |
| = required.getImplementationVendorID(); |
| if (null != requiredImplementationVendorID) { |
| if (null == implementationVendorID |
| || !implementationVendorID.equals(requiredImplementationVendorID)) { |
| return REQUIRE_VENDOR_SWITCH; |
| } |
| } |
| |
| // Implementation version must be >= required |
| final DeweyDecimal requiredImplementationVersion |
| = required.getImplementationVersion(); |
| if (null != requiredImplementationVersion) { |
| if (null == implementationVersion |
| || !isCompatible(implementationVersion, requiredImplementationVersion)) { |
| return REQUIRE_IMPLEMENTATION_UPGRADE; |
| } |
| } |
| |
| // This available optional package satisfies the requirements |
| return COMPATIBLE; |
| } |
| |
| /** |
| * Return <code>true</code> if the specified <code>Extension</code> |
| * (which represents an optional package required by an application) |
| * is satisfied by this <code>Extension</code> (which represents an |
| * optional package that is already installed. Otherwise, return |
| * <code>false</code>. |
| * |
| * @param required Description of the required optional package |
| * @return true if the specified extension is compatible with this extension |
| */ |
| public boolean isCompatibleWith(final Extension required) { |
| return (COMPATIBLE == getCompatibilityWith(required)); |
| } |
| |
| /** |
| * Return a String representation of this object. |
| * |
| * @return string representation of object. |
| */ |
| public String toString() { |
| final String brace = ": "; |
| |
| final StringBuffer sb = new StringBuffer(EXTENSION_NAME.toString()); |
| sb.append(brace); |
| sb.append(extensionName); |
| sb.append(StringUtils.LINE_SEP); |
| |
| if (null != specificationVersion) { |
| sb.append(SPECIFICATION_VERSION); |
| sb.append(brace); |
| sb.append(specificationVersion); |
| sb.append(StringUtils.LINE_SEP); |
| } |
| |
| if (null != specificationVendor) { |
| sb.append(SPECIFICATION_VENDOR); |
| sb.append(brace); |
| sb.append(specificationVendor); |
| sb.append(StringUtils.LINE_SEP); |
| } |
| |
| if (null != implementationVersion) { |
| sb.append(IMPLEMENTATION_VERSION); |
| sb.append(brace); |
| sb.append(implementationVersion); |
| sb.append(StringUtils.LINE_SEP); |
| } |
| |
| if (null != implementationVendorID) { |
| sb.append(IMPLEMENTATION_VENDOR_ID); |
| sb.append(brace); |
| sb.append(implementationVendorID); |
| sb.append(StringUtils.LINE_SEP); |
| } |
| |
| if (null != implementationVendor) { |
| sb.append(IMPLEMENTATION_VENDOR); |
| sb.append(brace); |
| sb.append(implementationVendor); |
| sb.append(StringUtils.LINE_SEP); |
| } |
| |
| if (null != implementationURL) { |
| sb.append(IMPLEMENTATION_URL); |
| sb.append(brace); |
| sb.append(implementationURL); |
| sb.append(StringUtils.LINE_SEP); |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Return <code>true</code> if the first version number is greater than |
| * or equal to the second; otherwise return <code>false</code>. |
| * |
| * @param first First version number (dotted decimal) |
| * @param second Second version number (dotted decimal) |
| */ |
| private boolean isCompatible(final DeweyDecimal first, |
| final DeweyDecimal second) { |
| return first.isGreaterThanOrEqual(second); |
| } |
| |
| /** |
| * Retrieve all the extensions listed under a particular key |
| * (Usually EXTENSION_LIST or OPTIONAL_EXTENSION_LIST). |
| * |
| * @param manifest the manifest to extract extensions from |
| * @param listKey the key used to get list (Usually |
| * EXTENSION_LIST or OPTIONAL_EXTENSION_LIST) |
| * @return the list of listed extensions |
| */ |
| private static Extension[] getListed(final Manifest manifest, |
| final Attributes.Name listKey) { |
| final ArrayList results = new ArrayList(); |
| final Attributes mainAttributes = manifest.getMainAttributes(); |
| |
| if (null != mainAttributes) { |
| getExtension(mainAttributes, results, listKey); |
| } |
| |
| final Map entries = manifest.getEntries(); |
| final Iterator keys = entries.keySet().iterator(); |
| while (keys.hasNext()) { |
| final String key = (String) keys.next(); |
| final Attributes attributes = (Attributes) entries.get(key); |
| getExtension(attributes, results, listKey); |
| } |
| |
| return (Extension[]) results.toArray(new Extension[results.size()]); |
| } |
| |
| /** |
| * Add required optional packages defined in the specified |
| * attributes entry, if any. |
| * |
| * @param attributes Attributes to be parsed |
| * @param required list to add required optional packages to |
| * @param listKey the key to use to lookup list, usually EXTENSION_LIST |
| * or OPTIONAL_EXTENSION_LIST |
| */ |
| private static void getExtension(final Attributes attributes, |
| final ArrayList required, |
| final Attributes.Name listKey) { |
| final String names = attributes.getValue(listKey); |
| if (null == names) { |
| return; |
| } |
| |
| final String[] extentions = split(names, " "); |
| for (int i = 0; i < extentions.length; i++) { |
| final String prefix = extentions[ i ] + "-"; |
| final Extension extension = getExtension(prefix, attributes); |
| |
| if (null != extension) { |
| required.add(extension); |
| } |
| } |
| } |
| |
| /** |
| * Splits the string on every token into an array of strings. |
| * |
| * @param string the string |
| * @param onToken the token |
| * @return the resultant array |
| */ |
| private static String[] split(final String string, |
| final String onToken) { |
| final StringTokenizer tokenizer = new StringTokenizer(string, onToken); |
| final String[] result = new String[ tokenizer.countTokens() ]; |
| |
| for (int i = 0; i < result.length; i++) { |
| result[ i ] = tokenizer.nextToken(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Extract an Extension from Attributes. |
| * Prefix indicates the prefix checked for each string. |
| * Usually the prefix is <em>"<extension>-"</em> if looking for a |
| * <b>Required</b> extension. If you are looking for an |
| * <b>Available</b> extension |
| * then the prefix is <em>""</em>. |
| * |
| * @param prefix the prefix for each attribute name |
| * @param attributes Attributes to searched |
| * @return the new Extension object, or null |
| */ |
| private static Extension getExtension(final String prefix, |
| final Attributes attributes) { |
| //WARNING: We trim the values of all the attributes because |
| //Some extension declarations are badly defined (ie have spaces |
| //after version or vendorID) |
| final String nameKey = prefix + EXTENSION_NAME; |
| final String name = getTrimmedString(attributes.getValue(nameKey)); |
| if (null == name) { |
| return null; |
| } |
| |
| final String specVendorKey = prefix + SPECIFICATION_VENDOR; |
| final String specVendor |
| = getTrimmedString(attributes.getValue(specVendorKey)); |
| final String specVersionKey = prefix + SPECIFICATION_VERSION; |
| final String specVersion |
| = getTrimmedString(attributes.getValue(specVersionKey)); |
| |
| final String impVersionKey = prefix + IMPLEMENTATION_VERSION; |
| final String impVersion |
| = getTrimmedString(attributes.getValue(impVersionKey)); |
| final String impVendorKey = prefix + IMPLEMENTATION_VENDOR; |
| final String impVendor |
| = getTrimmedString(attributes.getValue(impVendorKey)); |
| final String impVendorIDKey = prefix + IMPLEMENTATION_VENDOR_ID; |
| final String impVendorId |
| = getTrimmedString(attributes.getValue(impVendorIDKey)); |
| final String impURLKey = prefix + IMPLEMENTATION_URL; |
| final String impURL = getTrimmedString(attributes.getValue(impURLKey)); |
| |
| return new Extension(name, specVersion, specVendor, impVersion, |
| impVendor, impVendorId, impURL); |
| } |
| |
| /** |
| * Trim the supplied string if the string is non-null |
| * |
| * @param value the string to trim or null |
| * @return the trimmed string or null |
| */ |
| private static String getTrimmedString(final String value) { |
| return null == value ? null : value.trim(); |
| } |
| } |