source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

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