View Javadoc
1 /* 2 * Copyright (C) The Spice Group. All rights reserved. 3 * 4 * This software is published under the terms of the Spice 5 * Software License version 1.1, a copy of which has been included 6 * with this distribution in the LICENSE.txt file. 7 * 8 * This product includes software developed by the 9  * Apache Software Foundation (http://www.apache.org/). 10 */ 11 package org.codehaus.spice.extension; 12 import java.util.ArrayList; 13 import java.util.Iterator; 14 import java.util.Map; 15 import java.util.StringTokenizer; 16 import java.util.jar.Attributes; 17 import java.util.jar.Manifest; 18 /*** 19 * <p>Utility class that represents either an available "Optional Package" 20 * (formerly known as "Standard Extension") as described in the manifest 21 * of a JAR file, or the requirement for such an optional package.</p> 22 * 23 * <p>For more information about optional packages, see the document 24 * <em>Optional Package Versioning</em> in the documentation bundle for your 25 * Java2 Standard Edition package, in file 26 * <code>guide/extensions/versioning.html</code>.</p> 27 * 28 * @author <a href="mailto:craigmcc at apache.org">Craig R. McClanahan</a> 29 * @author <a href="mailto:peter at realityforge.org">Peter Donald</a> 30 * @version $Revision: 1.1 $ $Date: 2003/12/02 07:56:59 $ 31 */ 32 public final class Extension 33 { 34 /*** 35 * Manifest Attribute Name object for EXTENSION_LIST. 36 * @see Attributes.Name#EXTENSION_LIST 37 */ 38 public static final Attributes.Name EXTENSION_LIST = Attributes.Name.EXTENSION_LIST; 39 /*** 40 * <code>Name</code> object for <code>Optional-Extension-List</code> 41 * manifest attribute used for declaring optional dependencies on 42 * installed extensions. Note that the dependencies declared by this method 43 * are not required for the library to operate but if present will be used. 44 * It is NOT part of the official "Optional Package" specification. 45 * 46 * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/spec.html#dependnecy"> 47 * Installed extension dependency</a> 48 */ 49 public static final Attributes.Name OPTIONAL_EXTENSION_LIST = 50 new Attributes.Name( "Optional-Extension-List" ); 51 /*** 52 * Manifest Attribute Name object for EXTENSION_NAME. 53 * @see Attributes.Name#EXTENSION_NAME 54 */ 55 public static final Attributes.Name EXTENSION_NAME = 56 Attributes.Name.EXTENSION_NAME; 57 /*** 58 * Manifest Attribute Name object for SPECIFICATION_VERSION. 59 * @see Attributes.Name#SPECIFICATION_VERSION 60 */ 61 public static final Attributes.Name SPECIFICATION_VERSION = 62 Attributes.Name.SPECIFICATION_VERSION; 63 /*** 64 * Manifest Attribute Name object for SPECIFICATION_VENDOR. 65 * @see Attributes.Name#SPECIFICATION_VENDOR 66 */ 67 public static final Attributes.Name SPECIFICATION_VENDOR = 68 Attributes.Name.SPECIFICATION_VENDOR; 69 /*** 70 * Manifest Attribute Name object for IMPLEMENTATION_VERSION. 71 * @see Attributes.Name#IMPLEMENTATION_VERSION 72 */ 73 public static final Attributes.Name IMPLEMENTATION_VERSION = 74 Attributes.Name.IMPLEMENTATION_VERSION; 75 /*** 76 * Manifest Attribute Name object for IMPLEMENTATION_VENDOR. 77 * @see Attributes.Name#IMPLEMENTATION_VENDOR 78 */ 79 public static final Attributes.Name IMPLEMENTATION_VENDOR = 80 Attributes.Name.IMPLEMENTATION_VENDOR; 81 /*** 82 * Manifest Attribute Name object for IMPLEMENTATION_URL. 83 * @see Attributes.Name#IMPLEMENTATION_URL 84 */ 85 public static final Attributes.Name IMPLEMENTATION_URL = 86 Attributes.Name.IMPLEMENTATION_URL; 87 /*** 88 * Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID. 89 * @see Attributes.Name#IMPLEMENTATION_VENDOR_ID 90 */ 91 public static final Attributes.Name IMPLEMENTATION_VENDOR_ID = 92 Attributes.Name.IMPLEMENTATION_VENDOR_ID; 93 /*** 94 * Enum indicating that extension is compatible with other extension. 95 */ 96 public static final Compatability COMPATIBLE = 97 new Compatability( "COMPATIBLE" ); 98 /*** 99 * Enum indicating that extension requires an upgrade 100 * of specification to be compatible with other extension. 101 */ 102 public static final Compatability REQUIRE_SPECIFICATION_UPGRADE = 103 new Compatability( "REQUIRE_SPECIFICATION_UPGRADE" ); 104 /*** 105 * Enum indicating that extension requires a vendor 106 * switch to be compatible with other extension. 107 */ 108 public static final Compatability REQUIRE_VENDOR_SWITCH = 109 new Compatability( "REQUIRE_VENDOR_SWITCH" ); 110 /*** 111 * Enum indicating that extension requires an upgrade 112 * of implementation to be compatible with other extension. 113 */ 114 public static final Compatability REQUIRE_IMPLEMENTATION_UPGRADE = 115 new Compatability( "REQUIRE_IMPLEMENTATION_UPGRADE" ); 116 /*** 117 * Enum indicating that extension is incompatible with 118 * other extension in ways other than other enums 119 * indicate). ie For example the other extension may have 120 * a different ID. 121 */ 122 public static final Compatability INCOMPATIBLE = 123 new Compatability( "INCOMPATIBLE" ); 124 /*** 125 * The name of the optional package being made available, or required. 126 */ 127 private String m_extensionName; 128 /*** 129 * The version number (dotted decimal notation) of the specification 130 * to which this optional package conforms. 131 */ 132 private DeweyDecimal m_specificationVersion; 133 /*** 134 * The name of the company or organization that originated the 135 * specification to which this optional package conforms. 136 */ 137 private String m_specificationVendor; 138 /*** 139 * The unique identifier of the company that produced the optional 140 * package contained in this JAR file. 141 */ 142 private String m_implementationVendorID; 143 /*** 144 * The name of the company or organization that produced this 145 * implementation of this optional package. 146 */ 147 private String m_implementationVendor; 148 /*** 149 * The version number (dotted decimal notation) for this implementation 150 * of the optional package. 151 */ 152 private String m_implementationVersion; 153 /*** 154 * The URL from which the most recent version of this optional package 155 * can be obtained if it is not already installed. 156 */ 157 private String m_implementationURL; 158 /*** 159 * Return an array of <code>Extension</code> objects representing optional 160 * packages that are available in the JAR file associated with the 161 * specified <code>Manifest</code>. If there are no such optional 162 * packages, a zero-length array is returned. 163 * 164 * @param manifest Manifest to be parsed 165 * @return the "available" extensions in specified manifest 166 */ 167 public static Extension[] getAvailable( final Manifest manifest ) 168 { 169 if( null == manifest ) 170 { 171 return new Extension[ 0 ]; 172 } 173 final ArrayList results = new ArrayList(); 174 final Attributes mainAttributes = manifest.getMainAttributes(); 175 if( null != mainAttributes ) 176 { 177 final Extension extension = getExtension( "", mainAttributes ); 178 if( null != extension ) 179 { 180 results.add( extension ); 181 } 182 } 183 final Map entries = manifest.getEntries(); 184 final Iterator keys = entries.keySet().iterator(); 185 while( keys.hasNext() ) 186 { 187 final String key = (String)keys.next(); 188 final Attributes attributes = (Attributes)entries.get( key ); 189 final Extension extension = getExtension( "", attributes ); 190 if( null != extension ) 191 { 192 results.add( extension ); 193 } 194 } 195 return (Extension[])results.toArray( new Extension[ results.size() ] ); 196 } 197 /*** 198 * Retrieve the set of <code>Extension</code> objects that are available 199 * by the specified Manifest objects. If there are no such optional 200 * packages, a zero-length list is returned. 201 * 202 * @param manifests the manifests to scan 203 * @return the extensions 204 */ 205 public static Extension[] getAvailable( final Manifest[] manifests ) 206 { 207 final ArrayList set = new ArrayList(); 208 for( int i = 0; i < manifests.length; i++ ) 209 { 210 final Extension[] extensions = getAvailable( manifests[ i ] ); 211 for( int j = 0; j < extensions.length; j++ ) 212 { 213 set.add( extensions[ j ] ); 214 } 215 } 216 return (Extension[])set.toArray( new Extension[ set.size() ] ); 217 } 218 /*** 219 * Return the set of <code>Extension</code> objects representing optional 220 * packages that are required by the application contained in the JAR 221 * file associated with the specified <code>Manifest</code>. If there 222 * are no such optional packages, a zero-length list is returned. 223 * 224 * @param manifest Manifest to be parsed 225 * @return the dependencies that are specified in manifes 226 */ 227 public static Extension[] getRequired( final Manifest manifest ) 228 { 229 return getListed( manifest, EXTENSION_LIST ); 230 } 231 /*** 232 * Retrieve the set of <code>Extension</code> objects that are required 233 * by the specified Manifest objects. If there are no such optional 234 * packages, a zero-length list is returned. 235 * 236 * @param manifests the manifests to scan 237 * @return the extensions 238 */ 239 public static Extension[] getRequired( final Manifest[] manifests ) 240 { 241 final ArrayList set = new ArrayList(); 242 for( int i = 0; i < manifests.length; i++ ) 243 { 244 final Extension[] extensions = getRequired( manifests[ i ] ); 245 for( int j = 0; j < extensions.length; j++ ) 246 { 247 set.add( extensions[ j ] ); 248 } 249 } 250 return (Extension[])set.toArray( new Extension[ set.size() ] ); 251 } 252 /*** 253 * Return the set of <code>Extension</code> objects representing "Optional 254 * Packages" that the application declares they will use if present. If 255 * there are no such optional packages, a zero-length list is returned. 256 * 257 * @param manifest Manifest to be parsed 258 * @return the optional dependencies that are specified in manifest 259 */ 260 public static Extension[] getOptions( final Manifest manifest ) 261 { 262 return getListed( manifest, OPTIONAL_EXTENSION_LIST ); 263 } 264 /*** 265 * Add Extension to the specified manifest Attributes. 266 * 267 * @param attributes the attributes of manifest to add to 268 * @param extension the extension 269 */ 270 public static void addExtension( final Extension extension, 271 final Attributes attributes ) 272 { 273 addExtension( extension, "", attributes ); 274 } 275 /*** 276 * Add Extension to the specified manifest Attributes. 277 * Use the specified prefix so that dependencies can added 278 * with a prefix such as "java3d-" etc. 279 * 280 * @param attributes the attributes of manifest to add to 281 * @param extension the extension 282 * @param prefix the name to prefix to extension 283 */ 284 public static void addExtension( final Extension extension, 285 final String prefix, 286 final Attributes attributes ) 287 { 288 attributes.putValue( prefix + EXTENSION_NAME, 289 extension.getExtensionName() ); 290 final String specificationVendor = extension.getSpecificationVendor(); 291 if( null != specificationVendor ) 292 { 293 attributes.putValue( prefix + SPECIFICATION_VENDOR, 294 specificationVendor ); 295 } 296 final DeweyDecimal specificationVersion = extension.getSpecificationVersion(); 297 if( null != specificationVersion ) 298 { 299 attributes.putValue( prefix + SPECIFICATION_VERSION, 300 specificationVersion.toString() ); 301 } 302 final String implementationVendorID = extension.getImplementationVendorID(); 303 if( null != implementationVendorID ) 304 { 305 attributes.putValue( prefix + IMPLEMENTATION_VENDOR_ID, 306 implementationVendorID ); 307 } 308 final String implementationVendor = extension.getImplementationVendor(); 309 if( null != implementationVendor ) 310 { 311 attributes.putValue( prefix + IMPLEMENTATION_VENDOR, 312 implementationVendor ); 313 } 314 final String implementationVersion = extension.getImplementationVersion(); 315 if( null != implementationVersion ) 316 { 317 attributes.putValue( prefix + IMPLEMENTATION_VERSION, 318 implementationVersion.toString() ); 319 } 320 final String implementationURL = extension.getImplementationURL(); 321 if( null != implementationURL ) 322 { 323 attributes.putValue( prefix + IMPLEMENTATION_URL, 324 implementationURL ); 325 } 326 } 327 /*** 328 * The constructor to create Extension object. 329 * Note that every component is allowed to be specified 330 * but only the extensionName is mandatory. 331 * 332 * @param extensionName the name of extension. 333 * @param specificationVersion the specification Version of extension. 334 * @param specificationVendor the specification Vendor of extension. 335 * @param implementationVersion the implementation Version of extension. 336 * @param implementationVendor the implementation Vendor of extension. 337 * @param implementationVendorId the implementation VendorId of extension. 338 * @param implementationURL the implementation URL of extension. 339 */ 340 public Extension( final String extensionName, 341 final String specificationVersion, 342 final String specificationVendor, 343 final String implementationVersion, 344 final String implementationVendor, 345 final String implementationVendorId, 346 final String implementationURL ) 347 { 348 if( null == extensionName ) 349 { 350 throw new NullPointerException( "extensionName" ); 351 } 352 m_extensionName = extensionName; 353 m_specificationVendor = specificationVendor; 354 if( null != specificationVersion ) 355 { 356 try 357 { 358 m_specificationVersion = new DeweyDecimal( specificationVersion ); 359 } 360 catch( final NumberFormatException nfe ) 361 { 362 final String error = "Bad specification version format '" + specificationVersion + 363 "' in '" + extensionName + "'. (Reason: " + nfe + ")"; 364 throw new IllegalArgumentException( error ); 365 } 366 } 367 m_implementationURL = implementationURL; 368 m_implementationVendor = implementationVendor; 369 m_implementationVendorID = implementationVendorId; 370 m_implementationVersion = implementationVersion; 371 } 372 /*** 373 * Get the name of the extension. 374 * 375 * @return the name of the extension 376 */ 377 public String getExtensionName() 378 { 379 return m_extensionName; 380 } 381 /*** 382 * Get the vendor of the extensions specification. 383 * 384 * @return the vendor of the extensions specification. 385 */ 386 public String getSpecificationVendor() 387 { 388 return m_specificationVendor; 389 } 390 /*** 391 * Get the version of the extensions specification. 392 * 393 * @return the version of the extensions specification. 394 */ 395 public DeweyDecimal getSpecificationVersion() 396 { 397 return m_specificationVersion; 398 } 399 /*** 400 * Get the url of the extensions implementation. 401 * 402 * @return the url of the extensions implementation. 403 */ 404 public String getImplementationURL() 405 { 406 return m_implementationURL; 407 } 408 /*** 409 * Get the vendor of the extensions implementation. 410 * 411 * @return the vendor of the extensions implementation. 412 */ 413 public String getImplementationVendor() 414 { 415 return m_implementationVendor; 416 } 417 /*** 418 * Get the vendorID of the extensions implementation. 419 * 420 * @return the vendorID of the extensions implementation. 421 */ 422 public String getImplementationVendorID() 423 { 424 return m_implementationVendorID; 425 } 426 /*** 427 * Get the version of the extensions implementation. 428 * 429 * @return the version of the extensions implementation. 430 */ 431 public String getImplementationVersion() 432 { 433 return m_implementationVersion; 434 } 435 /*** 436 * Return a Compatibility enum indicating the relationship of this 437 * <code>Extension</code> with the specified <code>Extension</code>. 438 * 439 * @param required Description of the required optional package 440 * @return the enum indicating the compatability (or lack thereof) 441 * of specifed extension 442 */ 443 public Compatability getCompatibilityWith( final Extension required ) 444 { 445 // Extension Name must match 446 if( !m_extensionName.equals( required.getExtensionName() ) ) 447 { 448 return INCOMPATIBLE; 449 } 450 // Available specification version must be >= required 451 final DeweyDecimal specificationVersion = required.getSpecificationVersion(); 452 if( null != specificationVersion ) 453 { 454 if( null == m_specificationVersion || 455 !isCompatible( m_specificationVersion, specificationVersion ) ) 456 { 457 return REQUIRE_SPECIFICATION_UPGRADE; 458 } 459 } 460 // Implementation Vendor ID must match 461 final String implementationVendorId = required.getImplementationVendorID(); 462 if( null != implementationVendorId ) 463 { 464 if( null == m_implementationVendorID || 465 !m_implementationVendorID.equals( implementationVendorId ) ) 466 { 467 return REQUIRE_VENDOR_SWITCH; 468 } 469 } 470 471 // This available optional package satisfies the requirements 472 return COMPATIBLE; 473 } 474 /*** 475 * Return <code>true</code> if the specified <code>Extension</code> 476 * (which represents an optional package required by an application) 477 * is satisfied by this <code>Extension</code> (which represents an 478 * optional package that is already installed. Otherwise, return 479 * <code>false</code>. 480 * 481 * @param required Description of the required optional package 482 * @return true if the specified extension is compatible with this extension 483 */ 484 public boolean isCompatibleWith( final Extension required ) 485 { 486 return ( COMPATIBLE == getCompatibilityWith( required ) ); 487 } 488 /*** 489 * Return a String representation of this object. 490 * 491 * @return string representation of object. 492 */ 493 public String toString() 494 { 495 final String lineSeparator = System.getProperty( "line.separator" ); 496 final String brace = ": "; 497 final StringBuffer sb = new StringBuffer( EXTENSION_NAME.toString() ); 498 sb.append( brace ); 499 sb.append( m_extensionName ); 500 sb.append( lineSeparator ); 501 if( null != m_specificationVersion ) 502 { 503 sb.append( SPECIFICATION_VERSION ); 504 sb.append( brace ); 505 sb.append( m_specificationVersion ); 506 sb.append( lineSeparator ); 507 } 508 if( null != m_specificationVendor ) 509 { 510 sb.append( SPECIFICATION_VENDOR ); 511 sb.append( brace ); 512 sb.append( m_specificationVendor ); 513 sb.append( lineSeparator ); 514 } 515 if( null != m_implementationVersion ) 516 { 517 sb.append( IMPLEMENTATION_VERSION ); 518 sb.append( brace ); 519 sb.append( m_implementationVersion ); 520 sb.append( lineSeparator ); 521 } 522 if( null != m_implementationVendorID ) 523 { 524 sb.append( IMPLEMENTATION_VENDOR_ID ); 525 sb.append( brace ); 526 sb.append( m_implementationVendorID ); 527 sb.append( lineSeparator ); 528 } 529 if( null != m_implementationVendor ) 530 { 531 sb.append( IMPLEMENTATION_VENDOR ); 532 sb.append( brace ); 533 sb.append( m_implementationVendor ); 534 sb.append( lineSeparator ); 535 } 536 if( null != m_implementationURL ) 537 { 538 sb.append( IMPLEMENTATION_URL ); 539 sb.append( brace ); 540 sb.append( m_implementationURL ); 541 sb.append( lineSeparator ); 542 } 543 return sb.toString(); 544 } 545 /*** 546 * Return <code>true</code> if the first version number is greater than 547 * or equal to the second; otherwise return <code>false</code>. 548 * 549 * @param first First version number (dotted decimal) 550 * @param second Second version number (dotted decimal) 551 */ 552 private boolean isCompatible( final DeweyDecimal first, final DeweyDecimal second ) 553 { 554 return first.isGreaterThanOrEqual( second ); 555 } 556 /*** 557 * Retrieve all the extensions listed under a particular key 558 * (Usually EXTENSION_LIST or OPTIONAL_EXTENSION_LIST). 559 * 560 * @param manifest the manifest to extract extensions from 561 * @param listKey the key used to get list (Usually 562 * EXTENSION_LIST or OPTIONAL_EXTENSION_LIST) 563 * @return the list of listed extensions 564 */ 565 private static Extension[] getListed( final Manifest manifest, 566 final Attributes.Name listKey ) 567 { 568 final ArrayList results = new ArrayList(); 569 final Attributes mainAttributes = manifest.getMainAttributes(); 570 if( null != mainAttributes ) 571 { 572 getExtension( mainAttributes, results, listKey ); 573 } 574 final Map entries = manifest.getEntries(); 575 final Iterator keys = entries.keySet().iterator(); 576 while( keys.hasNext() ) 577 { 578 final String key = (String)keys.next(); 579 final Attributes attributes = (Attributes)entries.get( key ); 580 getExtension( attributes, results, listKey ); 581 } 582 return (Extension[])results.toArray( new Extension[ 0 ] ); 583 } 584 /*** 585 * Add required optional packages defined in the specified attributes entry, if any. 586 * 587 * @param attributes Attributes to be parsed 588 * @param required list to add required optional packages to 589 * @param listKey the key to use to lookup list, usually EXTENSION_LIST 590 * or OPTIONAL_EXTENSION_LIST 591 */ 592 private static void getExtension( final Attributes attributes, 593 final ArrayList required, 594 final Attributes.Name listKey ) 595 { 596 final String names = attributes.getValue( listKey ); 597 if( null == names ) 598 { 599 return; 600 } 601 final String[] extentions = split( names, " " ); 602 for( int i = 0; i < extentions.length; i++ ) 603 { 604 final String prefix = extentions[ i ] + "-"; 605 final Extension extension = getExtension( prefix, attributes ); 606 if( null != extension ) 607 { 608 required.add( extension ); 609 } 610 } 611 } 612 /*** 613 * Splits the string on every token into an array of strings. 614 * 615 * @param string the string 616 * @param onToken the token 617 * @return the resultant array 618 */ 619 private static final String[] split( final String string, final String onToken ) 620 { 621 final StringTokenizer tokenizer = new StringTokenizer( string, onToken ); 622 final String[] result = new String[ tokenizer.countTokens() ]; 623 for( int i = 0; i < result.length; i++ ) 624 { 625 result[ i ] = tokenizer.nextToken(); 626 } 627 return result; 628 } 629 /*** 630 * Extract an Extension from Attributes. 631 * Prefix indicates the prefix checked for each string. 632 * Usually the prefix is <em>"<extension>-"</em> if looking for a 633 * <b>Required</b> extension. If you are looking for an <b>Available</b> extension 634 * then the prefix is <em>""</em>. 635 * 636 * @param prefix the prefix for each attribute name 637 * @param attributes Attributes to searched 638 * @return the new Extension object, or null 639 */ 640 private static Extension getExtension( final String prefix, final Attributes attributes ) 641 { 642 //WARNING: We trim the values of all the attributes because 643 //Some extension declarations are badly defined (ie have spaces 644 //after version or vendorID) 645 final String nameKey = prefix + EXTENSION_NAME; 646 final String name = getTrimmedString( attributes.getValue( nameKey ) ); 647 if( null == name ) 648 { 649 return null; 650 } 651 final String specVendorKey = prefix + SPECIFICATION_VENDOR; 652 final String specVendor = getTrimmedString( attributes.getValue( specVendorKey ) ); 653 final String specVersionKey = prefix + SPECIFICATION_VERSION; 654 final String specVersion = getTrimmedString( attributes.getValue( specVersionKey ) ); 655 final String impVersionKey = prefix + IMPLEMENTATION_VERSION; 656 final String impVersion = getTrimmedString( attributes.getValue( impVersionKey ) ); 657 final String impVendorKey = prefix + IMPLEMENTATION_VENDOR; 658 final String impVendor = getTrimmedString( attributes.getValue( impVendorKey ) ); 659 final String impVendorIDKey = prefix + IMPLEMENTATION_VENDOR_ID; 660 final String impVendorId = getTrimmedString( attributes.getValue( impVendorIDKey ) ); 661 final String impURLKey = prefix + IMPLEMENTATION_URL; 662 final String impURL = getTrimmedString( attributes.getValue( impURLKey ) ); 663 return new Extension( name, specVersion, specVendor, impVersion, 664 impVendor, impVendorId, impURL ); 665 } 666 /*** 667 * Trim the supplied string if the string is non-null 668 * 669 * @param value the string to trim or null 670 * @return the trimmed string or null 671 */ 672 private static String getTrimmedString( final String value ) 673 { 674 if( null == value ) 675 { 676 return null; 677 } 678 else 679 { 680 return value.trim(); 681 } 682 } 683 }

This page was automatically generated by Maven