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