Clover coverage report - Extension - 1.1
Coverage timestamp: Tue Dec 2 2003 18:57:12 EST
file stats: LOC: 576   Methods: 19
NCLOC: 340   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
Specification.java 0% 0% 0% 0%
coverage
 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   
 package org.codehaus.spice.extension;
 9   
 import java.text.ParseException;
 10   
 import java.util.ArrayList;
 11   
 import java.util.Arrays;
 12   
 import java.util.Iterator;
 13   
 import java.util.Map;
 14   
 import java.util.jar.Attributes;
 15   
 import java.util.jar.Manifest;
 16   
 /**
 17   
  * <p>Utility class that represents either an available "Optional Package"
 18   
  * (formerly known as "Standard Extension") as described in the manifest
 19   
  * of a JAR file, or the requirement for such an optional package.</p>
 20   
  *
 21   
  * <p>For more information about optional packages, see the document
 22   
  * <em>Optional Package Versioning</em> in the documentation bundle for your
 23   
  * Java2 Standard Edition package, in file
 24   
  * <code>guide/extensions/versioning.html</code>.</p>
 25   
  *
 26   
  * @author <a href="mailto:peter at realityforge.org">Peter Donald</a>
 27   
  * @version $Revision: 1.1 $ $Date: 2003/12/02 07:56:59 $
 28   
  */
 29   
 public final class Specification
 30   
 {
 31   
     /**
 32   
      * Manifest Attribute Name object for SPECIFICATION_TITLE.
 33   
      * @see Attributes.Name#SPECIFICATION_TITLE
 34   
      */
 35   
     public static final Attributes.Name SPECIFICATION_TITLE = Attributes.Name.SPECIFICATION_TITLE;
 36   
     /**
 37   
      * Manifest Attribute Name object for SPECIFICATION_VERSION.
 38   
      * @see Attributes.Name#SPECIFICATION_VERSION
 39   
      */
 40   
     public static final Attributes.Name SPECIFICATION_VERSION = Attributes.Name.SPECIFICATION_VERSION;
 41   
     /**
 42   
      * Manifest Attribute Name object for SPECIFICATION_VENDOR.
 43   
      * @see Attributes.Name#SPECIFICATION_VENDOR
 44   
      */
 45   
     public static final Attributes.Name SPECIFICATION_VENDOR = Attributes.Name.SPECIFICATION_VENDOR;
 46   
     /**
 47   
      * Manifest Attribute Name object for IMPLEMENTATION_TITLE.
 48   
      * @see Attributes.Name#IMPLEMENTATION_TITLE
 49   
      */
 50   
     public static final Attributes.Name IMPLEMENTATION_TITLE = Attributes.Name.IMPLEMENTATION_TITLE;
 51   
     /**
 52   
      * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
 53   
      * @see Attributes.Name#IMPLEMENTATION_VERSION
 54   
      */
 55   
     public static final Attributes.Name IMPLEMENTATION_VERSION = Attributes.Name.IMPLEMENTATION_VERSION;
 56   
     /**
 57   
      * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
 58   
      * @see Attributes.Name#IMPLEMENTATION_VENDOR
 59   
      */
 60   
     public static final Attributes.Name IMPLEMENTATION_VENDOR = Attributes.Name.IMPLEMENTATION_VENDOR;
 61   
     /**
 62   
      * Enum indicating that extension is compatible with other Package
 63   
      * Specification.
 64   
      */
 65   
     public static final Compatability COMPATIBLE =
 66   
         new Compatability( "COMPATIBLE" );
 67   
     /**
 68   
      * Enum indicating that extension requires an upgrade
 69   
      * of specification to be compatible with other Package Specification.
 70   
      */
 71   
     public static final Compatability REQUIRE_SPECIFICATION_UPGRADE =
 72   
         new Compatability( "REQUIRE_SPECIFICATION_UPGRADE" );
 73   
     /**
 74   
      * Enum indicating that extension requires a vendor
 75   
      * switch to be compatible with other Package Specification.
 76   
      */
 77   
     public static final Compatability REQUIRE_VENDOR_SWITCH =
 78   
         new Compatability( "REQUIRE_VENDOR_SWITCH" );
 79   
     /**
 80   
      * Enum indicating that extension requires an upgrade
 81   
      * of implementation to be compatible with other Package Specification.
 82   
      */
 83   
     public static final Compatability REQUIRE_IMPLEMENTATION_CHANGE =
 84   
         new Compatability( "REQUIRE_IMPLEMENTATION_CHANGE" );
 85   
     /**
 86   
      * Enum indicating that extension is incompatible with
 87   
      * other Package Specification in ways other than other enums
 88   
      * indicate). ie For example the other Package Specification
 89   
      * may have a different ID.
 90   
      */
 91   
     public static final Compatability INCOMPATIBLE =
 92   
         new Compatability( "INCOMPATIBLE" );
 93   
     /**
 94   
      * The name of the Package Specification.
 95   
      */
 96   
     private String m_specificationTitle;
 97   
     /**
 98   
      * The version number (dotted decimal notation) of the specification
 99   
      * to which this optional package conforms.
 100   
      */
 101   
     private DeweyDecimal m_specificationVersion;
 102   
     /**
 103   
      * The name of the company or organization that originated the
 104   
      * specification to which this specification conforms.
 105   
      */
 106   
     private String m_specificationVendor;
 107   
     /**
 108   
      * The title of implementation.
 109   
      */
 110   
     private String m_implementationTitle;
 111   
     /**
 112   
      * The name of the company or organization that produced this
 113   
      * implementation of this specification.
 114   
      */
 115   
     private String m_implementationVendor;
 116   
     /**
 117   
      * The version string for implementation. The version string is
 118   
      * opaque.
 119   
      */
 120   
     private String m_implementationVersion;
 121   
     /**
 122   
      * The sections of jar that the specification applies to.
 123   
      */
 124   
     private String[] m_sections;
 125   
     /**
 126   
      * Return an array of <code>Package Specification</code> objects.
 127   
      * If there are no such optional packages, a zero-length array is returned.
 128   
      *
 129   
      * @param manifest Manifest to be parsed
 130   
      * @return the Package Specifications extensions in specified manifest
 131   
      */
 132  0
     public static Specification[] getSpecifications( final Manifest manifest )
 133   
         throws ParseException
 134   
     {
 135  0
         if( null == manifest )
 136   
         {
 137  0
             return new Specification[ 0 ];
 138   
         }
 139  0
         final ArrayList results = new ArrayList();
 140  0
         final Map entries = manifest.getEntries();
 141  0
         final Iterator keys = entries.keySet().iterator();
 142  0
         while( keys.hasNext() )
 143   
         {
 144  0
             final String key = (String)keys.next();
 145  0
             final Attributes attributes = (Attributes)entries.get( key );
 146  0
             final Specification specification = getSpecification( key, attributes );
 147  0
             if( null != specification )
 148   
             {
 149  0
                 results.add( specification );
 150   
             }
 151   
         }
 152  0
         final ArrayList trimmedResults = removeDuplicates( results );
 153  0
         return (Specification[])trimmedResults.toArray( new Specification[ 0 ] );
 154   
     }
 155   
     /**
 156   
      * The constructor to create Package Specification object.
 157   
      * Note that every component is allowed to be specified
 158   
      * but only the specificationTitle is mandatory.
 159   
      *
 160   
      * @param specificationTitle the name of specification.
 161   
      * @param specificationVersion the specification Version.
 162   
      * @param specificationVendor the specification Vendor.
 163   
      * @param implementationTitle the title of implementation.
 164   
      * @param implementationVersion the implementation Version.
 165   
      * @param implementationVendor the implementation Vendor.
 166   
      */
 167  0
     public Specification( final String specificationTitle,
 168   
                           final String specificationVersion,
 169   
                           final String specificationVendor,
 170   
                           final String implementationTitle,
 171   
                           final String implementationVersion,
 172   
                           final String implementationVendor )
 173   
     {
 174  0
         this( specificationTitle, specificationVersion, specificationVendor,
 175   
               implementationTitle, implementationVersion, implementationVendor,
 176   
               null );
 177   
     }
 178   
     /**
 179   
      * The constructor to create Package Specification object.
 180   
      * Note that every component is allowed to be specified
 181   
      * but only the specificationTitle is mandatory.
 182   
      *
 183   
      * @param specificationTitle the name of specification.
 184   
      * @param specificationVersion the specification Version.
 185   
      * @param specificationVendor the specification Vendor.
 186   
      * @param implementationTitle the title of implementation.
 187   
      * @param implementationVersion the implementation Version.
 188   
      * @param implementationVendor the implementation Vendor.
 189   
      * @param sections the sections/packages that Specification applies to.
 190   
      */
 191  0
     public Specification( final String specificationTitle,
 192   
                           final String specificationVersion,
 193   
                           final String specificationVendor,
 194   
                           final String implementationTitle,
 195   
                           final String implementationVersion,
 196   
                           final String implementationVendor,
 197   
                           final String[] sections )
 198   
     {
 199  0
         if( null == specificationTitle )
 200   
         {
 201  0
             throw new NullPointerException( "specificationTitle" );
 202   
         }
 203  0
         m_specificationTitle = specificationTitle;
 204  0
         m_specificationVendor = specificationVendor;
 205  0
         if( null != specificationVersion )
 206   
         {
 207  0
             try
 208   
             {
 209  0
                 m_specificationVersion = new DeweyDecimal( specificationVersion );
 210   
             }
 211   
             catch( final NumberFormatException nfe )
 212   
             {
 213  0
                 final String error = "Bad specification version format '" + specificationVersion +
 214   
                     "' in '" + specificationTitle + "'. (Reason: " + nfe + ")";
 215  0
                 throw new IllegalArgumentException( error );
 216   
             }
 217   
         }
 218  0
         m_implementationTitle = implementationTitle;
 219  0
         m_implementationVendor = implementationVendor;
 220  0
         m_implementationVersion = implementationVersion;
 221  0
         String[] copy = null;
 222  0
         if( null != sections )
 223   
         {
 224  0
             copy = new String[ sections.length ];
 225  0
             System.arraycopy( sections, 0, copy, 0, sections.length );
 226   
         }
 227  0
         m_sections = copy;
 228   
     }
 229   
     /**
 230   
      * Get the title of the specification.
 231   
      *
 232   
      * @return the title of speciication
 233   
      */
 234  0
     public String getSpecificationTitle()
 235   
     {
 236  0
         return m_specificationTitle;
 237   
     }
 238   
     /**
 239   
      * Get the vendor of the specification.
 240   
      *
 241   
      * @return the vendor of the specification.
 242   
      */
 243  0
     public String getSpecificationVendor()
 244   
     {
 245  0
         return m_specificationVendor;
 246   
     }
 247   
     /**
 248   
      * Get the title of the specification.
 249   
      *
 250   
      * @return the title of the specification.
 251   
      */
 252  0
     public String getImplementationTitle()
 253   
     {
 254  0
         return m_implementationTitle;
 255   
     }
 256   
     /**
 257   
      * Get the version of the specification.
 258   
      *
 259   
      * @return the version of the specification.
 260   
      */
 261  0
     public DeweyDecimal getSpecificationVersion()
 262   
     {
 263  0
         return m_specificationVersion;
 264   
     }
 265   
     /**
 266   
      * Get the vendor of the extensions implementation.
 267   
      *
 268   
      * @return the vendor of the extensions implementation.
 269   
      */
 270  0
     public String getImplementationVendor()
 271   
     {
 272  0
         return m_implementationVendor;
 273   
     }
 274   
     /**
 275   
      * Get the version of the implementation.
 276   
      *
 277   
      * @return the version of the implementation.
 278   
      */
 279  0
     public String getImplementationVersion()
 280   
     {
 281  0
         return m_implementationVersion;
 282   
     }
 283   
     /**
 284   
      * Return an array containing sections to which specification applies
 285   
      * or null if relevant to no sections.
 286   
      *
 287   
      * @return an array containing sections to which specification applies
 288   
      *         or null if relevant to no sections.
 289   
      */
 290  0
     public String[] getSections()
 291   
     {
 292  0
         if( null == m_sections )
 293   
         {
 294  0
             return null;
 295   
         }
 296   
         else
 297   
         {
 298  0
             final String[] sections = new String[ m_sections.length ];
 299  0
             System.arraycopy( m_sections, 0, sections, 0, m_sections.length );
 300  0
             return sections;
 301   
         }
 302   
     }
 303   
     /**
 304   
      * Return a Compatibility enum indicating the relationship of this
 305   
      * <code>Package Specification</code> with the specified <code>Extension</code>.
 306   
      *
 307   
      * @param other the other specification
 308   
      * @return the enum indicating the compatability (or lack thereof)
 309   
      *         of specifed Package Specification
 310   
      */
 311  0
     public Compatability getCompatibilityWith( final Specification other )
 312   
     {
 313   
         // Specification Name must match
 314  0
         if( !m_specificationTitle.equals( other.getSpecificationTitle() ) )
 315   
         {
 316  0
             return INCOMPATIBLE;
 317   
         }
 318   
         // Available specification version must be >= required
 319  0
         final DeweyDecimal specificationVersion = other.getSpecificationVersion();
 320  0
         if( null != specificationVersion )
 321   
         {
 322  0
             if( null == m_specificationVersion ||
 323   
                 !isCompatible( m_specificationVersion, specificationVersion ) )
 324   
             {
 325  0
                 return REQUIRE_SPECIFICATION_UPGRADE;
 326   
             }
 327   
         }
 328   
         // Implementation Vendor ID must match
 329  0
         final String implementationVendor = other.getImplementationVendor();
 330  0
         if( null != implementationVendor )
 331   
         {
 332  0
             if( null == m_implementationVendor ||
 333   
                 !m_implementationVendor.equals( implementationVendor ) )
 334   
             {
 335  0
                 return REQUIRE_VENDOR_SWITCH;
 336   
             }
 337   
         }
 338   
         // Implementation version must be >= required
 339  0
         final String implementationVersion = other.getImplementationVersion();
 340  0
         if( null != implementationVersion )
 341   
         {
 342  0
             if( null == m_implementationVersion ||
 343   
                 !m_implementationVersion.equals( implementationVersion ) )
 344   
             {
 345  0
                 return REQUIRE_IMPLEMENTATION_CHANGE;
 346   
             }
 347   
         }
 348   
         // This available optional package satisfies the requirements
 349  0
         return COMPATIBLE;
 350   
     }
 351   
     /**
 352   
      * Return <code>true</code> if the specified <code>package</code>
 353   
      * is satisfied by this <code>Specification</code>. Otherwise, return
 354   
      * <code>false</code>.
 355   
      *
 356   
      * @param other the specification
 357   
      * @return true if the specification is compatible with this specification
 358   
      */
 359  0
     public boolean isCompatibleWith( final Specification other )
 360   
     {
 361  0
         return ( COMPATIBLE == getCompatibilityWith( other ) );
 362   
     }
 363   
     /**
 364   
      * Return a String representation of this object.
 365   
      *
 366   
      * @return string representation of object.
 367   
      */
 368  0
     public String toString()
 369   
     {
 370  0
         final String lineSeparator = System.getProperty( "line.separator" );
 371  0
         final String brace = ": ";
 372  0
         final StringBuffer sb = new StringBuffer( SPECIFICATION_TITLE.toString() );
 373  0
         sb.append( brace );
 374  0
         sb.append( m_specificationTitle );
 375  0
         sb.append( lineSeparator );
 376  0
         if( null != m_specificationVersion )
 377   
         {
 378  0
             sb.append( SPECIFICATION_VERSION );
 379  0
             sb.append( brace );
 380  0
             sb.append( m_specificationVersion );
 381  0
             sb.append( lineSeparator );
 382   
         }
 383  0
         if( null != m_specificationVendor )
 384   
         {
 385  0
             sb.append( SPECIFICATION_VENDOR );
 386  0
             sb.append( brace );
 387  0
             sb.append( m_specificationVendor );
 388  0
             sb.append( lineSeparator );
 389   
         }
 390  0
         if( null != m_implementationTitle )
 391   
         {
 392  0
             sb.append( IMPLEMENTATION_TITLE );
 393  0
             sb.append( brace );
 394  0
             sb.append( m_implementationTitle );
 395  0
             sb.append( lineSeparator );
 396   
         }
 397  0
         if( null != m_implementationVersion )
 398   
         {
 399  0
             sb.append( IMPLEMENTATION_VERSION );
 400  0
             sb.append( brace );
 401  0
             sb.append( m_implementationVersion );
 402  0
             sb.append( lineSeparator );
 403   
         }
 404  0
         if( null != m_implementationVendor )
 405   
         {
 406  0
             sb.append( IMPLEMENTATION_VENDOR );
 407  0
             sb.append( brace );
 408  0
             sb.append( m_implementationVendor );
 409  0
             sb.append( lineSeparator );
 410   
         }
 411  0
         return sb.toString();
 412   
     }
 413   
     /**
 414   
      * Return <code>true</code> if the first version number is greater than
 415   
      * or equal to the second; otherwise return <code>false</code>.
 416   
      *
 417   
      * @param first First version number (dotted decimal)
 418   
      * @param second Second version number (dotted decimal)
 419   
      */
 420  0
     private boolean isCompatible( final DeweyDecimal first, final DeweyDecimal second )
 421   
     {
 422  0
         return first.isGreaterThanOrEqual( second );
 423   
     }
 424   
     /**
 425   
      * Combine all specifications objects that are identical except
 426   
      * for the sections.
 427   
      *
 428   
      * <p>Note this is very inefficent and should probably be fixed
 429   
      * in the future.</p>
 430   
      *
 431   
      * @param list the array of results to trim
 432   
      * @return an array list with all duplicates removed
 433   
      */
 434  0
     private static ArrayList removeDuplicates( final ArrayList list )
 435   
     {
 436  0
         final ArrayList results = new ArrayList();
 437  0
         final ArrayList sections = new ArrayList();
 438  0
         while( list.size() > 0 )
 439   
         {
 440  0
             final Specification specification = (Specification)list.remove( 0 );
 441  0
             final Iterator iterator = list.iterator();
 442  0
             while( iterator.hasNext() )
 443   
             {
 444  0
                 final Specification other = (Specification)iterator.next();
 445  0
                 if( isEqual( specification, other ) )
 446   
                 {
 447  0
                     final String[] otherSections = other.getSections();
 448  0
                     if( null != sections )
 449   
                     {
 450  0
                         sections.addAll( Arrays.asList( otherSections ) );
 451   
                     }
 452  0
                     iterator.remove();
 453   
                 }
 454   
             }
 455  0
             final Specification merged =
 456   
                 mergeInSections( specification, sections );
 457  0
             results.add( merged );
 458   
             //Reset list of sections
 459  0
             sections.clear();
 460   
         }
 461  0
         return results;
 462   
     }
 463   
     /**
 464   
      * Test if two specifications are equal except for their sections.
 465   
      *
 466   
      * @param specification one specificaiton
 467   
      * @param other the ohter specification
 468   
      * @return true if two specifications are equal except for their
 469   
      *         sections, else false
 470   
      */
 471  0
     private static boolean isEqual( final Specification specification,
 472   
                                     final Specification other )
 473   
     {
 474  0
         return
 475   
             specification.getSpecificationTitle().equals( other.getSpecificationTitle() ) &&
 476   
             specification.getSpecificationVersion().isEqual( other.getSpecificationVersion() ) &&
 477   
             specification.getSpecificationVendor().equals( other.getSpecificationVendor() ) &&
 478   
             specification.getImplementationTitle().equals( other.getImplementationTitle() ) &&
 479   
             specification.getImplementationVersion().equals( other.getImplementationVersion() ) &&
 480   
             specification.getImplementationVendor().equals( other.getImplementationVendor() );
 481   
     }
 482   
     /**
 483   
      * Merge the specified sections into specified section and return result.
 484   
      * If no sections to be added then just return original specification.
 485   
      *
 486   
      * @param specification the specification
 487   
      * @param sectionsToAdd the list of sections to merge
 488   
      * @return the merged specification
 489   
      */
 490  0
     private static Specification mergeInSections( final Specification specification,
 491   
                                                   final ArrayList sectionsToAdd )
 492   
     {
 493  0
         if( 0 == sectionsToAdd.size() )
 494   
         {
 495  0
             return specification;
 496   
         }
 497   
         else
 498   
         {
 499  0
             sectionsToAdd.addAll( Arrays.asList( specification.getSections() ) );
 500  0
             final String[] sections =
 501   
                 (String[])sectionsToAdd.toArray( new String[ sectionsToAdd.size() ] );
 502  0
             return new Specification( specification.getSpecificationTitle(),
 503   
                                       specification.getSpecificationVersion().toString(),
 504   
                                       specification.getSpecificationVendor(),
 505   
                                       specification.getImplementationTitle(),
 506   
                                       specification.getImplementationVersion(),
 507   
                                       specification.getImplementationVendor(),
 508   
                                       sections );
 509   
         }
 510   
     }
 511   
     /**
 512   
      * Trim the supplied string if the string is non-null
 513   
      *
 514   
      * @param value the string to trim or null
 515   
      * @return the trimmed string or null
 516   
      */
 517  0
     private static String getTrimmedString( final String value )
 518   
     {
 519  0
         if( null == value )
 520   
         {
 521  0
             return null;
 522   
         }
 523   
         else
 524   
         {
 525  0
             return value.trim();
 526   
         }
 527   
     }
 528   
     /**
 529   
      * Extract an Package Specification from Attributes.
 530   
      *
 531   
      * @param attributes Attributes to searched
 532   
      * @return the new Specification object, or null
 533   
      */
 534  0
     private static Specification getSpecification( final String section,
 535   
                                                    final Attributes attributes )
 536   
         throws ParseException
 537   
     {
 538   
         //WARNING: We trim the values of all the attributes because
 539   
         //Some extension declarations are badly defined (ie have spaces
 540   
         //after version or vendor)
 541  0
         final String name = getTrimmedString( attributes.getValue( SPECIFICATION_TITLE ) );
 542  0
         if( null == name )
 543   
         {
 544  0
             return null;
 545   
         }
 546  0
         final String specVendor = getTrimmedString( attributes.getValue( SPECIFICATION_VENDOR ) );
 547  0
         if( null == specVendor )
 548   
         {
 549  0
             throw new ParseException( "Missing " + SPECIFICATION_VENDOR, 0 );
 550   
         }
 551  0
         final String specVersion = getTrimmedString( attributes.getValue( SPECIFICATION_VERSION ) );
 552  0
         if( null == specVersion )
 553   
         {
 554  0
             throw new ParseException( "Missing " + SPECIFICATION_VERSION, 0 );
 555   
         }
 556  0
         final String impTitle = getTrimmedString( attributes.getValue( IMPLEMENTATION_TITLE ) );
 557  0
         if( null == impTitle )
 558   
         {
 559  0
             throw new ParseException( "Missing " + IMPLEMENTATION_TITLE, 0 );
 560   
         }
 561  0
         final String impVersion = getTrimmedString( attributes.getValue( IMPLEMENTATION_VERSION ) );
 562  0
         if( null == impVersion )
 563   
         {
 564  0
             throw new ParseException( "Missing " + IMPLEMENTATION_VERSION, 0 );
 565   
         }
 566  0
         final String impVendor = getTrimmedString( attributes.getValue( IMPLEMENTATION_VENDOR ) );
 567  0
         if( null == impVendor )
 568   
         {
 569  0
             throw new ParseException( "Missing " + IMPLEMENTATION_VENDOR, 0 );
 570   
         }
 571  0
         return new Specification( name, specVersion, specVendor,
 572   
                                   impTitle, impVersion, impVendor,
 573   
                                   new String[]{section} );
 574   
     }
 575   
 }
 576