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