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 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 public static Specification[] getSpecifications( final Manifest manifest ) 133 throws ParseException 134 { 135 if( null == manifest ) 136 { 137 return new Specification[ 0 ]; 138 } 139 final ArrayList results = new ArrayList(); 140 final Map entries = manifest.getEntries(); 141 final Iterator keys = entries.keySet().iterator(); 142 while( keys.hasNext() ) 143 { 144 final String key = (String)keys.next(); 145 final Attributes attributes = (Attributes)entries.get( key ); 146 final Specification specification = getSpecification( key, attributes ); 147 if( null != specification ) 148 { 149 results.add( specification ); 150 } 151 } 152 final ArrayList trimmedResults = removeDuplicates( results ); 153 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 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 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 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 if( null == specificationTitle ) 200 { 201 throw new NullPointerException( "specificationTitle" ); 202 } 203 m_specificationTitle = specificationTitle; 204 m_specificationVendor = specificationVendor; 205 if( null != specificationVersion ) 206 { 207 try 208 { 209 m_specificationVersion = new DeweyDecimal( specificationVersion ); 210 } 211 catch( final NumberFormatException nfe ) 212 { 213 final String error = "Bad specification version format '" + specificationVersion + 214 "' in '" + specificationTitle + "'. (Reason: " + nfe + ")"; 215 throw new IllegalArgumentException( error ); 216 } 217 } 218 m_implementationTitle = implementationTitle; 219 m_implementationVendor = implementationVendor; 220 m_implementationVersion = implementationVersion; 221 String[] copy = null; 222 if( null != sections ) 223 { 224 copy = new String[ sections.length ]; 225 System.arraycopy( sections, 0, copy, 0, sections.length ); 226 } 227 m_sections = copy; 228 } 229 /*** 230 * Get the title of the specification. 231 * 232 * @return the title of speciication 233 */ 234 public String getSpecificationTitle() 235 { 236 return m_specificationTitle; 237 } 238 /*** 239 * Get the vendor of the specification. 240 * 241 * @return the vendor of the specification. 242 */ 243 public String getSpecificationVendor() 244 { 245 return m_specificationVendor; 246 } 247 /*** 248 * Get the title of the specification. 249 * 250 * @return the title of the specification. 251 */ 252 public String getImplementationTitle() 253 { 254 return m_implementationTitle; 255 } 256 /*** 257 * Get the version of the specification. 258 * 259 * @return the version of the specification. 260 */ 261 public DeweyDecimal getSpecificationVersion() 262 { 263 return m_specificationVersion; 264 } 265 /*** 266 * Get the vendor of the extensions implementation. 267 * 268 * @return the vendor of the extensions implementation. 269 */ 270 public String getImplementationVendor() 271 { 272 return m_implementationVendor; 273 } 274 /*** 275 * Get the version of the implementation. 276 * 277 * @return the version of the implementation. 278 */ 279 public String getImplementationVersion() 280 { 281 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 public String[] getSections() 291 { 292 if( null == m_sections ) 293 { 294 return null; 295 } 296 else 297 { 298 final String[] sections = new String[ m_sections.length ]; 299 System.arraycopy( m_sections, 0, sections, 0, m_sections.length ); 300 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 public Compatability getCompatibilityWith( final Specification other ) 312 { 313 // Specification Name must match 314 if( !m_specificationTitle.equals( other.getSpecificationTitle() ) ) 315 { 316 return INCOMPATIBLE; 317 } 318 // Available specification version must be >= required 319 final DeweyDecimal specificationVersion = other.getSpecificationVersion(); 320 if( null != specificationVersion ) 321 { 322 if( null == m_specificationVersion || 323 !isCompatible( m_specificationVersion, specificationVersion ) ) 324 { 325 return REQUIRE_SPECIFICATION_UPGRADE; 326 } 327 } 328 // Implementation Vendor ID must match 329 final String implementationVendor = other.getImplementationVendor(); 330 if( null != implementationVendor ) 331 { 332 if( null == m_implementationVendor || 333 !m_implementationVendor.equals( implementationVendor ) ) 334 { 335 return REQUIRE_VENDOR_SWITCH; 336 } 337 } 338 // Implementation version must be >= required 339 final String implementationVersion = other.getImplementationVersion(); 340 if( null != implementationVersion ) 341 { 342 if( null == m_implementationVersion || 343 !m_implementationVersion.equals( implementationVersion ) ) 344 { 345 return REQUIRE_IMPLEMENTATION_CHANGE; 346 } 347 } 348 // This available optional package satisfies the requirements 349 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 public boolean isCompatibleWith( final Specification other ) 360 { 361 return ( COMPATIBLE == getCompatibilityWith( other ) ); 362 } 363 /*** 364 * Return a String representation of this object. 365 * 366 * @return string representation of object. 367 */ 368 public String toString() 369 { 370 final String lineSeparator = System.getProperty( "line.separator" ); 371 final String brace = ": "; 372 final StringBuffer sb = new StringBuffer( SPECIFICATION_TITLE.toString() ); 373 sb.append( brace ); 374 sb.append( m_specificationTitle ); 375 sb.append( lineSeparator ); 376 if( null != m_specificationVersion ) 377 { 378 sb.append( SPECIFICATION_VERSION ); 379 sb.append( brace ); 380 sb.append( m_specificationVersion ); 381 sb.append( lineSeparator ); 382 } 383 if( null != m_specificationVendor ) 384 { 385 sb.append( SPECIFICATION_VENDOR ); 386 sb.append( brace ); 387 sb.append( m_specificationVendor ); 388 sb.append( lineSeparator ); 389 } 390 if( null != m_implementationTitle ) 391 { 392 sb.append( IMPLEMENTATION_TITLE ); 393 sb.append( brace ); 394 sb.append( m_implementationTitle ); 395 sb.append( lineSeparator ); 396 } 397 if( null != m_implementationVersion ) 398 { 399 sb.append( IMPLEMENTATION_VERSION ); 400 sb.append( brace ); 401 sb.append( m_implementationVersion ); 402 sb.append( lineSeparator ); 403 } 404 if( null != m_implementationVendor ) 405 { 406 sb.append( IMPLEMENTATION_VENDOR ); 407 sb.append( brace ); 408 sb.append( m_implementationVendor ); 409 sb.append( lineSeparator ); 410 } 411 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 private boolean isCompatible( final DeweyDecimal first, final DeweyDecimal second ) 421 { 422 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 private static ArrayList removeDuplicates( final ArrayList list ) 435 { 436 final ArrayList results = new ArrayList(); 437 final ArrayList sections = new ArrayList(); 438 while( list.size() > 0 ) 439 { 440 final Specification specification = (Specification)list.remove( 0 ); 441 final Iterator iterator = list.iterator(); 442 while( iterator.hasNext() ) 443 { 444 final Specification other = (Specification)iterator.next(); 445 if( isEqual( specification, other ) ) 446 { 447 final String[] otherSections = other.getSections(); 448 if( null != sections ) 449 { 450 sections.addAll( Arrays.asList( otherSections ) ); 451 } 452 iterator.remove(); 453 } 454 } 455 final Specification merged = 456 mergeInSections( specification, sections ); 457 results.add( merged ); 458 //Reset list of sections 459 sections.clear(); 460 } 461 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 private static boolean isEqual( final Specification specification, 472 final Specification other ) 473 { 474 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 private static Specification mergeInSections( final Specification specification, 491 final ArrayList sectionsToAdd ) 492 { 493 if( 0 == sectionsToAdd.size() ) 494 { 495 return specification; 496 } 497 else 498 { 499 sectionsToAdd.addAll( Arrays.asList( specification.getSections() ) ); 500 final String[] sections = 501 (String[])sectionsToAdd.toArray( new String[ sectionsToAdd.size() ] ); 502 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 private static String getTrimmedString( final String value ) 518 { 519 if( null == value ) 520 { 521 return null; 522 } 523 else 524 { 525 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 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 final String name = getTrimmedString( attributes.getValue( SPECIFICATION_TITLE ) ); 542 if( null == name ) 543 { 544 return null; 545 } 546 final String specVendor = getTrimmedString( attributes.getValue( SPECIFICATION_VENDOR ) ); 547 if( null == specVendor ) 548 { 549 throw new ParseException( "Missing " + SPECIFICATION_VENDOR, 0 ); 550 } 551 final String specVersion = getTrimmedString( attributes.getValue( SPECIFICATION_VERSION ) ); 552 if( null == specVersion ) 553 { 554 throw new ParseException( "Missing " + SPECIFICATION_VERSION, 0 ); 555 } 556 final String impTitle = getTrimmedString( attributes.getValue( IMPLEMENTATION_TITLE ) ); 557 if( null == impTitle ) 558 { 559 throw new ParseException( "Missing " + IMPLEMENTATION_TITLE, 0 ); 560 } 561 final String impVersion = getTrimmedString( attributes.getValue( IMPLEMENTATION_VERSION ) ); 562 if( null == impVersion ) 563 { 564 throw new ParseException( "Missing " + IMPLEMENTATION_VERSION, 0 ); 565 } 566 final String impVendor = getTrimmedString( attributes.getValue( IMPLEMENTATION_VENDOR ) ); 567 if( null == impVendor ) 568 { 569 throw new ParseException( "Missing " + IMPLEMENTATION_VENDOR, 0 ); 570 } 571 return new Specification( name, specVersion, specVendor, 572 impTitle, impVersion, impVendor, 573 new String[]{section} ); 574 } 575 }

This page was automatically generated by Maven