KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > loom > extension > Specification


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.loom.extension;
9
10 import java.text.ParseException JavaDoc;
11 import java.util.ArrayList JavaDoc;
12 import java.util.Arrays JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.jar.Attributes JavaDoc;
16 import java.util.jar.Manifest JavaDoc;
17
18 /**
19  * <p>Utility class that represents either an available "Optional Package"
20  * (formerly known as "Standard Extension") as described in the manifest of a
21  * 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 <code>guide/extensions/versioning.html</code>.</p>
26  *
27  * @author Peter Donald
28  * @version $Revision: 1.1 $ $Date: 2004/04/19 21:40:38 $
29  */

30 public final class Specification
31 {
32     /**
33      * Manifest Attribute Name object for SPECIFICATION_TITLE.
34      *
35      * @see Attributes.Name#SPECIFICATION_TITLE
36      */

37     public static final Attributes.Name JavaDoc SPECIFICATION_TITLE = Attributes.Name.SPECIFICATION_TITLE;
38     /**
39      * Manifest Attribute Name object for SPECIFICATION_VERSION.
40      *
41      * @see Attributes.Name#SPECIFICATION_VERSION
42      */

43     public static final Attributes.Name JavaDoc SPECIFICATION_VERSION = Attributes.Name.SPECIFICATION_VERSION;
44     /**
45      * Manifest Attribute Name object for SPECIFICATION_VENDOR.
46      *
47      * @see Attributes.Name#SPECIFICATION_VENDOR
48      */

49     public static final Attributes.Name JavaDoc SPECIFICATION_VENDOR = Attributes.Name.SPECIFICATION_VENDOR;
50     /**
51      * Manifest Attribute Name object for IMPLEMENTATION_TITLE.
52      *
53      * @see Attributes.Name#IMPLEMENTATION_TITLE
54      */

55     public static final Attributes.Name JavaDoc IMPLEMENTATION_TITLE = Attributes.Name.IMPLEMENTATION_TITLE;
56     /**
57      * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
58      *
59      * @see Attributes.Name#IMPLEMENTATION_VERSION
60      */

61     public static final Attributes.Name JavaDoc IMPLEMENTATION_VERSION = Attributes.Name.IMPLEMENTATION_VERSION;
62     /**
63      * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
64      *
65      * @see Attributes.Name#IMPLEMENTATION_VENDOR
66      */

67     public static final Attributes.Name JavaDoc IMPLEMENTATION_VENDOR = Attributes.Name.IMPLEMENTATION_VENDOR;
68     /**
69      * Enum indicating that extension is compatible with other Package
70      * Specification.
71      */

72     public static final Compatability COMPATIBLE =
73         new Compatability( "COMPATIBLE" );
74     /**
75      * Enum indicating that extension requires an upgrade of specification to be
76      * compatible with other Package Specification.
77      */

78     public static final Compatability REQUIRE_SPECIFICATION_UPGRADE =
79         new Compatability( "REQUIRE_SPECIFICATION_UPGRADE" );
80     /**
81      * Enum indicating that extension requires a vendor switch to be compatible
82      * with other Package Specification.
83      */

84     public static final Compatability REQUIRE_VENDOR_SWITCH =
85         new Compatability( "REQUIRE_VENDOR_SWITCH" );
86     /**
87      * Enum indicating that extension requires an upgrade of implementation to
88      * be compatible with other Package Specification.
89      */

90     public static final Compatability REQUIRE_IMPLEMENTATION_CHANGE =
91         new Compatability( "REQUIRE_IMPLEMENTATION_CHANGE" );
92     /**
93      * Enum indicating that extension is incompatible with other Package
94      * Specification in ways other than other enums indicate). ie For example
95      * the other Package Specification may have a different ID.
96      */

97     public static final Compatability INCOMPATIBLE =
98         new Compatability( "INCOMPATIBLE" );
99     /** The name of the Package Specification. */
100     private String JavaDoc m_specificationTitle;
101     /**
102      * The version number (dotted decimal notation) of the specification to
103      * which this optional package conforms.
104      */

105     private DeweyDecimal m_specificationVersion;
106     /**
107      * The name of the company or organization that originated the specification
108      * to which this specification conforms.
109      */

110     private String JavaDoc m_specificationVendor;
111     /** The title of implementation. */
112     private String JavaDoc m_implementationTitle;
113     /**
114      * The name of the company or organization that produced this implementation
115      * of this specification.
116      */

117     private String JavaDoc m_implementationVendor;
118     /**
119      * The version string for implementation. The version string is opaque.
120      */

121     private String JavaDoc m_implementationVersion;
122     /** The sections of jar that the specification applies to. */
123     private String JavaDoc[] m_sections;
124
125     /**
126      * Return an array of <code>Package Specification</code> objects. If there
127      * 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 JavaDoc manifest )
133         throws ParseException JavaDoc
134     {
135         if( null == manifest )
136         {
137             return new Specification[ 0 ];
138         }
139         final ArrayList JavaDoc results = new ArrayList JavaDoc();
140         final Map JavaDoc entries = manifest.getEntries();
141         final Iterator JavaDoc keys = entries.keySet().iterator();
142         while( keys.hasNext() )
143         {
144             final String JavaDoc key = (String JavaDoc)keys.next();
145             final Attributes JavaDoc attributes = (Attributes JavaDoc)entries.get( key );
146             final Specification specification = getSpecification( key,
147                                                                   attributes );
148             if( null != specification )
149             {
150                 results.add( specification );
151             }
152         }
153         final ArrayList JavaDoc trimmedResults = removeDuplicates( results );
154         return (Specification[])trimmedResults.toArray( new Specification[ 0 ] );
155     }
156
157     /**
158      * The constructor to create Package Specification object. Note that every
159      * component is allowed to be specified but only the specificationTitle is
160      * mandatory.
161      *
162      * @param specificationTitle the name of specification.
163      * @param specificationVersion the specification Version.
164      * @param specificationVendor the specification Vendor.
165      * @param implementationTitle the title of implementation.
166      * @param implementationVersion the implementation Version.
167      * @param implementationVendor the implementation Vendor.
168      */

169     public Specification( final String JavaDoc specificationTitle,
170                           final String JavaDoc specificationVersion,
171                           final String JavaDoc specificationVendor,
172                           final String JavaDoc implementationTitle,
173                           final String JavaDoc implementationVersion,
174                           final String JavaDoc implementationVendor )
175     {
176         this( specificationTitle, specificationVersion, specificationVendor,
177               implementationTitle, implementationVersion, implementationVendor,
178               null );
179     }
180
181     /**
182      * The constructor to create Package Specification object. Note that every
183      * component is allowed to be specified but only the specificationTitle is
184      * mandatory.
185      *
186      * @param specificationTitle the name of specification.
187      * @param specificationVersion the specification Version.
188      * @param specificationVendor the specification Vendor.
189      * @param implementationTitle the title of implementation.
190      * @param implementationVersion the implementation Version.
191      * @param implementationVendor the implementation Vendor.
192      * @param sections the sections/packages that Specification applies to.
193      */

194     public Specification( final String JavaDoc specificationTitle,
195                           final String JavaDoc specificationVersion,
196                           final String JavaDoc specificationVendor,
197                           final String JavaDoc implementationTitle,
198                           final String JavaDoc implementationVersion,
199                           final String JavaDoc implementationVendor,
200                           final String JavaDoc[] sections )
201     {
202         if( null == specificationTitle )
203         {
204             throw new NullPointerException JavaDoc( "specificationTitle" );
205         }
206         m_specificationTitle = specificationTitle;
207         m_specificationVendor = specificationVendor;
208         if( null != specificationVersion )
209         {
210             try
211             {
212                 m_specificationVersion =
213                 new DeweyDecimal( specificationVersion );
214             }
215             catch( final NumberFormatException JavaDoc nfe )
216             {
217                 final String JavaDoc error = "Bad specification version format '" +
218                     specificationVersion +
219                     "' in '" + specificationTitle + "'. (Reason: " + nfe + ")";
220                 throw new IllegalArgumentException JavaDoc( error );
221             }
222         }
223         m_implementationTitle = implementationTitle;
224         m_implementationVendor = implementationVendor;
225         m_implementationVersion = implementationVersion;
226         String JavaDoc[] copy = null;
227         if( null != sections )
228         {
229             copy = new String JavaDoc[ sections.length ];
230             System.arraycopy( sections, 0, copy, 0, sections.length );
231         }
232         m_sections = copy;
233     }
234
235     /**
236      * Get the title of the specification.
237      *
238      * @return the title of speciication
239      */

240     public String JavaDoc getSpecificationTitle()
241     {
242         return m_specificationTitle;
243     }
244
245     /**
246      * Get the vendor of the specification.
247      *
248      * @return the vendor of the specification.
249      */

250     public String JavaDoc getSpecificationVendor()
251     {
252         return m_specificationVendor;
253     }
254
255     /**
256      * Get the title of the specification.
257      *
258      * @return the title of the specification.
259      */

260     public String JavaDoc getImplementationTitle()
261     {
262         return m_implementationTitle;
263     }
264
265     /**
266      * Get the version of the specification.
267      *
268      * @return the version of the specification.
269      */

270     public DeweyDecimal getSpecificationVersion()
271     {
272         return m_specificationVersion;
273     }
274
275     /**
276      * Get the vendor of the extensions implementation.
277      *
278      * @return the vendor of the extensions implementation.
279      */

280     public String JavaDoc getImplementationVendor()
281     {
282         return m_implementationVendor;
283     }
284
285     /**
286      * Get the version of the implementation.
287      *
288      * @return the version of the implementation.
289      */

290     public String JavaDoc getImplementationVersion()
291     {
292         return m_implementationVersion;
293     }
294
295     /**
296      * Return an array containing sections to which specification applies or
297      * null if relevant to no sections.
298      *
299      * @return an array containing sections to which specification applies or
300      * null if relevant to no sections.
301      */

302     public String JavaDoc[] getSections()
303     {
304         if( null == m_sections )
305         {
306             return null;
307         }
308         else
309         {
310             final String JavaDoc[] sections = new String JavaDoc[ m_sections.length ];
311             System.arraycopy( m_sections, 0, sections, 0, m_sections.length );
312             return sections;
313         }
314     }
315
316     /**
317      * Return a Compatibility enum indicating the relationship of this
318      * <code>Package Specification</code> with the specified
319      * <code>Extension</code>.
320      *
321      * @param other the other specification
322      * @return the enum indicating the compatability (or lack thereof) of
323      * specifed Package Specification
324      */

325     public Compatability getCompatibilityWith( final Specification other )
326     {
327         // Specification Name must match
328
if( !m_specificationTitle.equals( other.getSpecificationTitle() ) )
329         {
330             return INCOMPATIBLE;
331         }
332         // Available specification version must be >= required
333
final DeweyDecimal specificationVersion = other.getSpecificationVersion();
334         if( null != specificationVersion )
335         {
336             if( null == m_specificationVersion ||
337                 !isCompatible( m_specificationVersion, specificationVersion ) )
338             {
339                 return REQUIRE_SPECIFICATION_UPGRADE;
340             }
341         }
342         // Implementation Vendor ID must match
343
final String JavaDoc implementationVendor = other.getImplementationVendor();
344         if( null != implementationVendor )
345         {
346             if( null == m_implementationVendor ||
347                 !m_implementationVendor.equals( implementationVendor ) )
348             {
349                 return REQUIRE_VENDOR_SWITCH;
350             }
351         }
352         // Implementation version must be >= required
353
final String JavaDoc implementationVersion = other.getImplementationVersion();
354         if( null != implementationVersion )
355         {
356             if( null == m_implementationVersion ||
357                 !m_implementationVersion.equals( implementationVersion ) )
358             {
359                 return REQUIRE_IMPLEMENTATION_CHANGE;
360             }
361         }
362         // This available optional package satisfies the requirements
363
return COMPATIBLE;
364     }
365
366     /**
367      * Return <code>true</code> if the specified <code>package</code> is
368      * satisfied by this <code>Specification</code>. Otherwise, return
369      * <code>false</code>.
370      *
371      * @param other the specification
372      * @return true if the specification is compatible with this specification
373      */

374     public boolean isCompatibleWith( final Specification other )
375     {
376         return ( COMPATIBLE == getCompatibilityWith( other ) );
377     }
378
379     /**
380      * Return a String representation of this object.
381      *
382      * @return string representation of object.
383      */

384     public String JavaDoc toString()
385     {
386         final String JavaDoc lineSeparator = System.getProperty( "line.separator" );
387         final String JavaDoc brace = ": ";
388         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc(
389             SPECIFICATION_TITLE.toString() );
390         sb.append( brace );
391         sb.append( m_specificationTitle );
392         sb.append( lineSeparator );
393         if( null != m_specificationVersion )
394         {
395             sb.append( SPECIFICATION_VERSION );
396             sb.append( brace );
397             sb.append( m_specificationVersion );
398             sb.append( lineSeparator );
399         }
400         if( null != m_specificationVendor )
401         {
402             sb.append( SPECIFICATION_VENDOR );
403             sb.append( brace );
404             sb.append( m_specificationVendor );
405             sb.append( lineSeparator );
406         }
407         if( null != m_implementationTitle )
408         {
409             sb.append( IMPLEMENTATION_TITLE );
410             sb.append( brace );
411             sb.append( m_implementationTitle );
412             sb.append( lineSeparator );
413         }
414         if( null != m_implementationVersion )
415         {
416             sb.append( IMPLEMENTATION_VERSION );
417             sb.append( brace );
418             sb.append( m_implementationVersion );
419             sb.append( lineSeparator );
420         }
421         if( null != m_implementationVendor )
422         {
423             sb.append( IMPLEMENTATION_VENDOR );
424             sb.append( brace );
425             sb.append( m_implementationVendor );
426             sb.append( lineSeparator );
427         }
428         return sb.toString();
429     }
430
431     /**
432      * Return <code>true</code> if the first version number is greater than or
433      * equal to the second; otherwise return <code>false</code>.
434      *
435      * @param first First version number (dotted decimal)
436      * @param second Second version number (dotted decimal)
437      */

438     private boolean isCompatible( final DeweyDecimal first,
439                                   final DeweyDecimal second )
440     {
441         return first.isGreaterThanOrEqual( second );
442     }
443
444     /**
445      * Combine all specifications objects that are identical except for the
446      * sections.
447      *
448      * <p>Note this is very inefficent and should probably be fixed in the
449      * future.</p>
450      *
451      * @param list the array of results to trim
452      * @return an array list with all duplicates removed
453      */

454     private static ArrayList JavaDoc removeDuplicates( final ArrayList JavaDoc list )
455     {
456         final ArrayList JavaDoc results = new ArrayList JavaDoc();
457         final ArrayList JavaDoc sections = new ArrayList JavaDoc();
458         while( list.size() > 0 )
459         {
460             final Specification specification = (Specification)list.remove( 0 );
461             final Iterator JavaDoc iterator = list.iterator();
462             while( iterator.hasNext() )
463             {
464                 final Specification other = (Specification)iterator.next();
465                 if( isEqual( specification, other ) )
466                 {
467                     final String JavaDoc[] otherSections = other.getSections();
468                     if( null != sections )
469                     {
470                         sections.addAll( Arrays.asList( otherSections ) );
471                     }
472                     iterator.remove();
473                 }
474             }
475             final Specification merged =
476                 mergeInSections( specification, sections );
477             results.add( merged );
478             //Reset list of sections
479
sections.clear();
480         }
481         return results;
482     }
483
484     /**
485      * Test if two specifications are equal except for their sections.
486      *
487      * @param specification one specificaiton
488      * @param other the ohter specification
489      * @return true if two specifications are equal except for their sections,
490      * else false
491      */

492     private static boolean isEqual( final Specification specification,
493                                     final Specification other )
494     {
495         return
496             specification.getSpecificationTitle().equals(
497                 other.getSpecificationTitle() ) &&
498             specification.getSpecificationVersion().isEqual(
499                 other.getSpecificationVersion() ) &&
500             specification.getSpecificationVendor().equals(
501                 other.getSpecificationVendor() ) &&
502             specification.getImplementationTitle().equals(
503                 other.getImplementationTitle() ) &&
504             specification.getImplementationVersion().equals(
505                 other.getImplementationVersion() ) &&
506             specification.getImplementationVendor().equals(
507                 other.getImplementationVendor() );
508     }
509
510     /**
511      * Merge the specified sections into specified section and return result. If
512      * no sections to be added then just return original specification.
513      *
514      * @param specification the specification
515      * @param sectionsToAdd the list of sections to merge
516      * @return the merged specification
517      */

518     private static Specification mergeInSections(
519         final Specification specification,
520         final ArrayList JavaDoc sectionsToAdd )
521     {
522         if( 0 == sectionsToAdd.size() )
523         {
524             return specification;
525         }
526         else
527         {
528             sectionsToAdd.addAll( Arrays.asList( specification.getSections() ) );
529             final String JavaDoc[] sections =
530                 (String JavaDoc[])sectionsToAdd.toArray(
531                     new String JavaDoc[ sectionsToAdd.size() ] );
532             return new Specification( specification.getSpecificationTitle(),
533                                       specification.getSpecificationVersion()
534                                       .toString(),
535                                       specification.getSpecificationVendor(),
536                                       specification.getImplementationTitle(),
537                                       specification.getImplementationVersion(),
538                                       specification.getImplementationVendor(),
539                                       sections );
540         }
541     }
542
543     /**
544      * Trim the supplied string if the string is non-null
545      *
546      * @param value the string to trim or null
547      * @return the trimmed string or null
548      */

549     private static String JavaDoc getTrimmedString( final String JavaDoc value )
550     {
551         if( null == value )
552         {
553             return null;
554         }
555         else
556         {
557             return value.trim();
558         }
559     }
560
561     /**
562      * Extract an Package Specification from Attributes.
563      *
564      * @param attributes Attributes to searched
565      * @return the new Specification object, or null
566      */

567     private static Specification getSpecification( final String JavaDoc section,
568                                                    final Attributes JavaDoc attributes )
569         throws ParseException JavaDoc
570     {
571         //WARNING: We trim the values of all the attributes because
572
//Some extension declarations are badly defined (ie have spaces
573
//after version or vendor)
574
final String JavaDoc name = getTrimmedString(
575             attributes.getValue( SPECIFICATION_TITLE ) );
576         if( null == name )
577         {
578             return null;
579         }
580         final String JavaDoc specVendor = getTrimmedString(
581             attributes.getValue( SPECIFICATION_VENDOR ) );
582         if( null == specVendor )
583         {
584             throw new ParseException JavaDoc( "Missing " + SPECIFICATION_VENDOR, 0 );
585         }
586         final String JavaDoc specVersion = getTrimmedString(
587             attributes.getValue( SPECIFICATION_VERSION ) );
588         if( null == specVersion )
589         {
590             throw new ParseException JavaDoc( "Missing " + SPECIFICATION_VERSION, 0 );
591         }
592         final String JavaDoc impTitle = getTrimmedString(
593             attributes.getValue( IMPLEMENTATION_TITLE ) );
594         if( null == impTitle )
595         {
596             throw new ParseException JavaDoc( "Missing " + IMPLEMENTATION_TITLE, 0 );
597         }
598         final String JavaDoc impVersion = getTrimmedString(
599             attributes.getValue( IMPLEMENTATION_VERSION ) );
600         if( null == impVersion )
601         {
602             throw new ParseException JavaDoc( "Missing " + IMPLEMENTATION_VERSION, 0 );
603         }
604         final String JavaDoc impVendor = getTrimmedString(
605             attributes.getValue( IMPLEMENTATION_VENDOR ) );
606         if( null == impVendor )
607         {
608             throw new ParseException JavaDoc( "Missing " + IMPLEMENTATION_VENDOR, 0 );
609         }
610         return new Specification( name, specVersion, specVendor,
611                                   impTitle, impVersion, impVendor,
612                                   new String JavaDoc[]{section} );
613     }
614 }
615
Popular Tags