blob: 7f0490c45bf70e8915aca59cf5e6cb0f072f7545 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
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;
/**
* <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>
*
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
* This file is from excalibur.extension package. Dont edit this file
* directly as there is no unit tests to make sure it is operational
* in ant. Edit file in excalibur and run tests there before changing
* ants file.
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
*
* @author <a href="mailto:craigmcc@apache.org">Craig R. McClanahan</a>
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
* @version $Revision$ $Date$
*/
public final class Extension
{
/**
* Manifest Attribute Name object for EXTENSION_LIST.
* @see Attributes.Name#EXTENSION_LIST
*/
public static final Attributes.Name EXTENSION_LIST =
new Attributes.Name( "Extension-List" );//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.
* @see Attributes.Name#EXTENSION_NAME
*/
public static final Attributes.Name EXTENSION_NAME =
new Attributes.Name( "Extension-Name" );//Attributes.Name.EXTENSION_NAME;
/**
* Manifest Attribute Name object for SPECIFICATION_VERSION.
* @see Attributes.Name#SPECIFICATION_VERSION
*/
public static final Attributes.Name SPECIFICATION_VERSION = Attributes.Name.SPECIFICATION_VERSION;
/**
* Manifest Attribute Name object for SPECIFICATION_VENDOR.
* @see Attributes.Name#SPECIFICATION_VENDOR
*/
public static final Attributes.Name SPECIFICATION_VENDOR = Attributes.Name.SPECIFICATION_VENDOR;
/**
* Manifest Attribute Name object for IMPLEMENTATION_VERSION.
* @see Attributes.Name#IMPLEMENTATION_VERSION
*/
public static final Attributes.Name IMPLEMENTATION_VERSION = Attributes.Name.IMPLEMENTATION_VERSION;
/**
* Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
* @see Attributes.Name#IMPLEMENTATION_VENDOR
*/
public static final Attributes.Name IMPLEMENTATION_VENDOR = Attributes.Name.IMPLEMENTATION_VENDOR;
/**
* Manifest Attribute Name object for IMPLEMENTATION_URL.
* @see Attributes.Name#IMPLEMENTATION_URL
*/
public static final Attributes.Name IMPLEMENTATION_URL =
new Attributes.Name( "Implementation-URL" );//Attributes.Name.IMPLEMENTATION_URL;
/**
* Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID.
* @see Attributes.Name#IMPLEMENTATION_VENDOR_ID
*/
public static final Attributes.Name IMPLEMENTATION_VENDOR_ID =
new Attributes.Name( "Implementation-Vendor-Id" );//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). ie 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 m_extensionName;
/**
* The version number (dotted decimal notation) of the specification
* to which this optional package conforms.
*/
private DeweyDecimal m_specificationVersion;
/**
* The name of the company or organization that originated the
* specification to which this optional package conforms.
*/
private String m_specificationVendor;
/**
* The unique identifier of the company that produced the optional
* package contained in this JAR file.
*/
private String m_implementationVendorID;
/**
* The name of the company or organization that produced this
* implementation of this optional package.
*/
private String m_implementationVendor;
/**
* The version number (dotted decimal notation) for this implementation
* of the optional package.
*/
private DeweyDecimal m_implementationVersion;
/**
* The URL from which the most recent version of this optional package
* can be obtained if it is not already installed.
*/
private String m_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[ 0 ] );
}
/**
* 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, 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 )
{
m_extensionName = extensionName;
m_specificationVendor = specificationVendor;
if( null != specificationVersion )
{
try
{
m_specificationVersion = new DeweyDecimal( specificationVersion );
}
catch( final NumberFormatException nfe )
{
final String error = "Bad specification version format '" + specificationVersion +
"' in '" + extensionName + "'. (Reason: " + nfe + ")";
throw new IllegalArgumentException( error );
}
}
m_implementationURL = implementationURL;
m_implementationVendor = implementationVendor;
m_implementationVendorID = implementationVendorId;
if( null != implementationVersion )
{
try
{
m_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 == m_extensionName )
{
throw new NullPointerException( "extensionName property is null" );
}
}
/**
* Get the name of the extension.
*
* @return the name of the extension
*/
public String getExtensionName()
{
return m_extensionName;
}
/**
* Get the vendor of the extensions specification.
*
* @return the vendor of the extensions specification.
*/
public String getSpecificationVendor()
{
return m_specificationVendor;
}
/**
* Get the version of the extensions specification.
*
* @return the version of the extensions specification.
*/
public DeweyDecimal getSpecificationVersion()
{
return m_specificationVersion;
}
/**
* Get the url of the extensions implementation.
*
* @return the url of the extensions implementation.
*/
public String getImplementationURL()
{
return m_implementationURL;
}
/**
* Get the vendor of the extensions implementation.
*
* @return the vendor of the extensions implementation.
*/
public String getImplementationVendor()
{
return m_implementationVendor;
}
/**
* Get the vendorID of the extensions implementation.
*
* @return the vendorID of the extensions implementation.
*/
public String getImplementationVendorID()
{
return m_implementationVendorID;
}
/**
* Get the version of the extensions implementation.
*
* @return the version of the extensions implementation.
*/
public DeweyDecimal getImplementationVersion()
{
return m_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( !m_extensionName.equals( required.getExtensionName() ) )
{
return INCOMPATIBLE;
}
// Available specification version must be >= required
final DeweyDecimal specificationVersion = required.getSpecificationVersion();
if( null != specificationVersion )
{
if( null == m_specificationVersion ||
!isCompatible( m_specificationVersion, specificationVersion ) )
{
return REQUIRE_SPECIFICATION_UPGRADE;
}
}
// Implementation Vendor ID must match
final String implementationVendorId = required.getImplementationVendorID();
if( null != implementationVendorId )
{
if( null == m_implementationVendorID ||
!m_implementationVendorID.equals( implementationVendorId ) )
{
return REQUIRE_VENDOR_SWITCH;
}
}
// Implementation version must be >= required
final DeweyDecimal implementationVersion = required.getImplementationVersion();
if( null != implementationVersion )
{
if( null == m_implementationVersion ||
!isCompatible( m_implementationVersion, implementationVersion ) )
{
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 lineSeparator = System.getProperty( "line.separator" );
final String brace = ": ";
final StringBuffer sb = new StringBuffer( EXTENSION_NAME.toString() );
sb.append( brace );
sb.append( m_extensionName );
sb.append( lineSeparator );
if( null != m_specificationVersion )
{
sb.append( SPECIFICATION_VERSION );
sb.append( brace );
sb.append( m_specificationVersion );
sb.append( lineSeparator );
}
if( null != m_specificationVendor )
{
sb.append( SPECIFICATION_VENDOR );
sb.append( brace );
sb.append( m_specificationVendor );
sb.append( lineSeparator );
}
if( null != m_implementationVersion )
{
sb.append( IMPLEMENTATION_VERSION );
sb.append( brace );
sb.append( m_implementationVersion );
sb.append( lineSeparator );
}
if( null != m_implementationVendorID )
{
sb.append( IMPLEMENTATION_VENDOR_ID );
sb.append( brace );
sb.append( m_implementationVendorID );
sb.append( lineSeparator );
}
if( null != m_implementationVendor )
{
sb.append( IMPLEMENTATION_VENDOR );
sb.append( brace );
sb.append( m_implementationVendor );
sb.append( lineSeparator );
}
if( null != m_implementationURL )
{
sb.append( IMPLEMENTATION_URL );
sb.append( brace );
sb.append( m_implementationURL );
sb.append( lineSeparator );
}
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[ 0 ] );
}
/**
* 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 final 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>"&lt;extension&gt;-"</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 )
{
if( null == value )
{
return null;
}
else
{
return value.trim();
}
}
}