1. /*
  2. * Copyright 2002-2004 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. */
  17. package org.apache.tools.ant.taskdefs.optional.extension;
  18. import java.util.ArrayList;
  19. import java.util.Iterator;
  20. import java.util.Map;
  21. import java.util.StringTokenizer;
  22. import java.util.jar.Attributes;
  23. import java.util.jar.Manifest;
  24. /**
  25. * <p>Utility class that represents either an available "Optional Package"
  26. * (formerly known as "Standard Extension") as described in the manifest
  27. * of a JAR file, or the requirement for such an optional package.</p>
  28. *
  29. * <p>For more information about optional packages, see the document
  30. * <em>Optional Package Versioning</em> in the documentation bundle for your
  31. * Java2 Standard Edition package, in file
  32. * <code>guide/extensions/versioning.html</code>.</p>
  33. *
  34. * @version $Revision: 1.8.2.4 $ $Date: 2004/03/09 17:01:45 $
  35. */
  36. public final class Extension {
  37. /**
  38. * Manifest Attribute Name object for EXTENSION_LIST.
  39. * @see Attributes.Name#EXTENSION_LIST
  40. */
  41. public static final Attributes.Name EXTENSION_LIST
  42. = new Attributes.Name("Extension-List");
  43. /**
  44. * <code>Name</code> object for <code>Optional-Extension-List</code>
  45. * manifest attribute used for declaring optional dependencies on
  46. * installed extensions. Note that the dependencies declared by this method
  47. * are not required for the library to operate but if present will be used.
  48. * It is NOT part of the official "Optional Package" specification.
  49. *
  50. * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/spec.html#dependnecy">
  51. * Installed extension dependency</a>
  52. */
  53. public static final Attributes.Name OPTIONAL_EXTENSION_LIST
  54. = new Attributes.Name("Optional-Extension-List");
  55. /**
  56. * Manifest Attribute Name object for EXTENSION_NAME.
  57. * @see Attributes.Name#EXTENSION_NAME
  58. */
  59. public static final Attributes.Name EXTENSION_NAME =
  60. new Attributes.Name("Extension-Name");
  61. /**
  62. * Manifest Attribute Name object for SPECIFICATION_VERSION.
  63. * @see Attributes.Name#SPECIFICATION_VERSION
  64. */
  65. public static final Attributes.Name SPECIFICATION_VERSION
  66. = Attributes.Name.SPECIFICATION_VERSION;
  67. /**
  68. * Manifest Attribute Name object for SPECIFICATION_VENDOR.
  69. * @see Attributes.Name#SPECIFICATION_VENDOR
  70. */
  71. public static final Attributes.Name SPECIFICATION_VENDOR
  72. = Attributes.Name.SPECIFICATION_VENDOR;
  73. /**
  74. * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
  75. * @see Attributes.Name#IMPLEMENTATION_VERSION
  76. */
  77. public static final Attributes.Name IMPLEMENTATION_VERSION
  78. = Attributes.Name.IMPLEMENTATION_VERSION;
  79. /**
  80. * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
  81. * @see Attributes.Name#IMPLEMENTATION_VENDOR
  82. */
  83. public static final Attributes.Name IMPLEMENTATION_VENDOR
  84. = Attributes.Name.IMPLEMENTATION_VENDOR;
  85. /**
  86. * Manifest Attribute Name object for IMPLEMENTATION_URL.
  87. * @see Attributes.Name#IMPLEMENTATION_URL
  88. */
  89. public static final Attributes.Name IMPLEMENTATION_URL
  90. = new Attributes.Name("Implementation-URL");
  91. /**
  92. * Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID.
  93. * @see Attributes.Name#IMPLEMENTATION_VENDOR_ID
  94. */
  95. public static final Attributes.Name IMPLEMENTATION_VENDOR_ID
  96. = new Attributes.Name("Implementation-Vendor-Id");
  97. /**
  98. * Enum indicating that extension is compatible with other extension.
  99. */
  100. public static final Compatibility COMPATIBLE
  101. = new Compatibility("COMPATIBLE");
  102. /**
  103. * Enum indicating that extension requires an upgrade
  104. * of specification to be compatible with other extension.
  105. */
  106. public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE
  107. = new Compatibility("REQUIRE_SPECIFICATION_UPGRADE");
  108. /**
  109. * Enum indicating that extension requires a vendor
  110. * switch to be compatible with other extension.
  111. */
  112. public static final Compatibility REQUIRE_VENDOR_SWITCH
  113. = new Compatibility("REQUIRE_VENDOR_SWITCH");
  114. /**
  115. * Enum indicating that extension requires an upgrade
  116. * of implementation to be compatible with other extension.
  117. */
  118. public static final Compatibility REQUIRE_IMPLEMENTATION_UPGRADE
  119. = new Compatibility("REQUIRE_IMPLEMENTATION_UPGRADE");
  120. /**
  121. * Enum indicating that extension is incompatible with
  122. * other extension in ways other than other enums
  123. * indicate). For example the other extension may have
  124. * a different ID.
  125. */
  126. public static final Compatibility INCOMPATIBLE
  127. = new Compatibility("INCOMPATIBLE");
  128. /**
  129. * The name of the optional package being made available, or required.
  130. */
  131. private String extensionName;
  132. /**
  133. * The version number (dotted decimal notation) of the specification
  134. * to which this optional package conforms.
  135. */
  136. private DeweyDecimal specificationVersion;
  137. /**
  138. * The name of the company or organization that originated the
  139. * specification to which this optional package conforms.
  140. */
  141. private String specificationVendor;
  142. /**
  143. * The unique identifier of the company that produced the optional
  144. * package contained in this JAR file.
  145. */
  146. private String implementationVendorID;
  147. /**
  148. * The name of the company or organization that produced this
  149. * implementation of this optional package.
  150. */
  151. private String implementationVendor;
  152. /**
  153. * The version number (dotted decimal notation) for this implementation
  154. * of the optional package.
  155. */
  156. private DeweyDecimal implementationVersion;
  157. /**
  158. * The URL from which the most recent version of this optional package
  159. * can be obtained if it is not already installed.
  160. */
  161. private String implementationURL;
  162. /**
  163. * Return an array of <code>Extension</code> objects representing optional
  164. * packages that are available in the JAR file associated with the
  165. * specified <code>Manifest</code>. If there are no such optional
  166. * packages, a zero-length array is returned.
  167. *
  168. * @param manifest Manifest to be parsed
  169. * @return the "available" extensions in specified manifest
  170. */
  171. public static Extension[] getAvailable(final Manifest manifest) {
  172. if (null == manifest) {
  173. return new Extension[ 0 ];
  174. }
  175. final ArrayList results = new ArrayList();
  176. final Attributes mainAttributes = manifest.getMainAttributes();
  177. if (null != mainAttributes) {
  178. final Extension extension = getExtension("", mainAttributes);
  179. if (null != extension) {
  180. results.add(extension);
  181. }
  182. }
  183. final Map entries = manifest.getEntries();
  184. final Iterator keys = entries.keySet().iterator();
  185. while (keys.hasNext()) {
  186. final String key = (String) keys.next();
  187. final Attributes attributes = (Attributes) entries.get(key);
  188. final Extension extension = getExtension("", attributes);
  189. if (null != extension) {
  190. results.add(extension);
  191. }
  192. }
  193. return (Extension[]) results.toArray(new Extension[0]);
  194. }
  195. /**
  196. * Return the set of <code>Extension</code> objects representing optional
  197. * packages that are required by the application contained in the JAR
  198. * file associated with the specified <code>Manifest</code>. If there
  199. * are no such optional packages, a zero-length list is returned.
  200. *
  201. * @param manifest Manifest to be parsed
  202. * @return the dependencies that are specified in manifes
  203. */
  204. public static Extension[] getRequired(final Manifest manifest) {
  205. return getListed(manifest, Attributes.Name.EXTENSION_LIST);
  206. }
  207. /**
  208. * Return the set of <code>Extension</code> objects representing "Optional
  209. * Packages" that the application declares they will use if present. If
  210. * there are no such optional packages, a zero-length list is returned.
  211. *
  212. * @param manifest Manifest to be parsed
  213. * @return the optional dependencies that are specified in manifest
  214. */
  215. public static Extension[] getOptions(final Manifest manifest) {
  216. return getListed(manifest, OPTIONAL_EXTENSION_LIST);
  217. }
  218. /**
  219. * Add Extension to the specified manifest Attributes.
  220. *
  221. * @param attributes the attributes of manifest to add to
  222. * @param extension the extension
  223. */
  224. public static void addExtension(final Extension extension,
  225. final Attributes attributes) {
  226. addExtension(extension, "", attributes);
  227. }
  228. /**
  229. * Add Extension to the specified manifest Attributes.
  230. * Use the specified prefix so that dependencies can added
  231. * with a prefix such as "java3d-" etc.
  232. *
  233. * @param attributes the attributes of manifest to add to
  234. * @param extension the extension
  235. * @param prefix the name to prefix to extension
  236. */
  237. public static void addExtension(final Extension extension,
  238. final String prefix,
  239. final Attributes attributes) {
  240. attributes.putValue(prefix + EXTENSION_NAME,
  241. extension.getExtensionName());
  242. final String specificationVendor = extension.getSpecificationVendor();
  243. if (null != specificationVendor) {
  244. attributes.putValue(prefix + SPECIFICATION_VENDOR,
  245. specificationVendor);
  246. }
  247. final DeweyDecimal specificationVersion
  248. = extension.getSpecificationVersion();
  249. if (null != specificationVersion) {
  250. attributes.putValue(prefix + SPECIFICATION_VERSION,
  251. specificationVersion.toString());
  252. }
  253. final String implementationVendorID
  254. = extension.getImplementationVendorID();
  255. if (null != implementationVendorID) {
  256. attributes.putValue(prefix + IMPLEMENTATION_VENDOR_ID,
  257. implementationVendorID);
  258. }
  259. final String implementationVendor = extension.getImplementationVendor();
  260. if (null != implementationVendor) {
  261. attributes.putValue(prefix + IMPLEMENTATION_VENDOR,
  262. implementationVendor);
  263. }
  264. final DeweyDecimal implementationVersion
  265. = extension.getImplementationVersion();
  266. if (null != implementationVersion) {
  267. attributes.putValue(prefix + IMPLEMENTATION_VERSION,
  268. implementationVersion.toString());
  269. }
  270. final String implementationURL = extension.getImplementationURL();
  271. if (null != implementationURL) {
  272. attributes.putValue(prefix + IMPLEMENTATION_URL,
  273. implementationURL);
  274. }
  275. }
  276. /**
  277. * The constructor to create Extension object.
  278. * Note that every component is allowed to be specified
  279. * but only the extensionName is mandatory.
  280. *
  281. * @param extensionName the name of extension.
  282. * @param specificationVersion the specification Version of extension.
  283. * @param specificationVendor the specification Vendor of extension.
  284. * @param implementationVersion the implementation Version of extension.
  285. * @param implementationVendor the implementation Vendor of extension.
  286. * @param implementationVendorId the implementation VendorId of extension.
  287. * @param implementationURL the implementation URL of extension.
  288. */
  289. public Extension(final String extensionName,
  290. final String specificationVersion,
  291. final String specificationVendor,
  292. final String implementationVersion,
  293. final String implementationVendor,
  294. final String implementationVendorId,
  295. final String implementationURL) {
  296. this.extensionName = extensionName;
  297. this.specificationVendor = specificationVendor;
  298. if (null != specificationVersion) {
  299. try {
  300. this.specificationVersion
  301. = new DeweyDecimal(specificationVersion);
  302. } catch (final NumberFormatException nfe) {
  303. final String error = "Bad specification version format '"
  304. + specificationVersion + "' in '" + extensionName
  305. + "'. (Reason: " + nfe + ")";
  306. throw new IllegalArgumentException(error);
  307. }
  308. }
  309. this.implementationURL = implementationURL;
  310. this.implementationVendor = implementationVendor;
  311. this.implementationVendorID = implementationVendorId;
  312. if (null != implementationVersion) {
  313. try {
  314. this.implementationVersion
  315. = new DeweyDecimal(implementationVersion);
  316. } catch (final NumberFormatException nfe) {
  317. final String error = "Bad implementation version format '"
  318. + implementationVersion + "' in '" + extensionName
  319. + "'. (Reason: " + nfe + ")";
  320. throw new IllegalArgumentException(error);
  321. }
  322. }
  323. if (null == this.extensionName) {
  324. throw new NullPointerException("extensionName property is null");
  325. }
  326. }
  327. /**
  328. * Get the name of the extension.
  329. *
  330. * @return the name of the extension
  331. */
  332. public String getExtensionName() {
  333. return extensionName;
  334. }
  335. /**
  336. * Get the vendor of the extensions specification.
  337. *
  338. * @return the vendor of the extensions specification.
  339. */
  340. public String getSpecificationVendor() {
  341. return specificationVendor;
  342. }
  343. /**
  344. * Get the version of the extensions specification.
  345. *
  346. * @return the version of the extensions specification.
  347. */
  348. public DeweyDecimal getSpecificationVersion() {
  349. return specificationVersion;
  350. }
  351. /**
  352. * Get the url of the extensions implementation.
  353. *
  354. * @return the url of the extensions implementation.
  355. */
  356. public String getImplementationURL() {
  357. return implementationURL;
  358. }
  359. /**
  360. * Get the vendor of the extensions implementation.
  361. *
  362. * @return the vendor of the extensions implementation.
  363. */
  364. public String getImplementationVendor() {
  365. return implementationVendor;
  366. }
  367. /**
  368. * Get the vendorID of the extensions implementation.
  369. *
  370. * @return the vendorID of the extensions implementation.
  371. */
  372. public String getImplementationVendorID() {
  373. return implementationVendorID;
  374. }
  375. /**
  376. * Get the version of the extensions implementation.
  377. *
  378. * @return the version of the extensions implementation.
  379. */
  380. public DeweyDecimal getImplementationVersion() {
  381. return implementationVersion;
  382. }
  383. /**
  384. * Return a Compatibility enum indicating the relationship of this
  385. * <code>Extension</code> with the specified <code>Extension</code>.
  386. *
  387. * @param required Description of the required optional package
  388. * @return the enum indicating the compatibility (or lack thereof)
  389. * of specifed extension
  390. */
  391. public Compatibility getCompatibilityWith(final Extension required) {
  392. // Extension Name must match
  393. if (!extensionName.equals(required.getExtensionName())) {
  394. return INCOMPATIBLE;
  395. }
  396. // Available specification version must be >= required
  397. final DeweyDecimal specificationVersion
  398. = required.getSpecificationVersion();
  399. if (null != specificationVersion) {
  400. if (null == specificationVersion
  401. || !isCompatible(specificationVersion, specificationVersion)) {
  402. return REQUIRE_SPECIFICATION_UPGRADE;
  403. }
  404. }
  405. // Implementation Vendor ID must match
  406. final String implementationVendorId
  407. = required.getImplementationVendorID();
  408. if (null != implementationVendorId) {
  409. if (null == implementationVendorID
  410. || !implementationVendorID.equals(implementationVendorId)) {
  411. return REQUIRE_VENDOR_SWITCH;
  412. }
  413. }
  414. // Implementation version must be >= required
  415. final DeweyDecimal implementationVersion
  416. = required.getImplementationVersion();
  417. if (null != implementationVersion) {
  418. if (null == implementationVersion
  419. || !isCompatible(implementationVersion, implementationVersion)) {
  420. return REQUIRE_IMPLEMENTATION_UPGRADE;
  421. }
  422. }
  423. // This available optional package satisfies the requirements
  424. return COMPATIBLE;
  425. }
  426. /**
  427. * Return <code>true</code> if the specified <code>Extension</code>
  428. * (which represents an optional package required by an application)
  429. * is satisfied by this <code>Extension</code> (which represents an
  430. * optional package that is already installed. Otherwise, return
  431. * <code>false</code>.
  432. *
  433. * @param required Description of the required optional package
  434. * @return true if the specified extension is compatible with this extension
  435. */
  436. public boolean isCompatibleWith(final Extension required) {
  437. return (COMPATIBLE == getCompatibilityWith(required));
  438. }
  439. /**
  440. * Return a String representation of this object.
  441. *
  442. * @return string representation of object.
  443. */
  444. public String toString() {
  445. final String lineSeparator = System.getProperty("line.separator");
  446. final String brace = ": ";
  447. final StringBuffer sb = new StringBuffer(EXTENSION_NAME.toString());
  448. sb.append(brace);
  449. sb.append(extensionName);
  450. sb.append(lineSeparator);
  451. if (null != specificationVersion) {
  452. sb.append(SPECIFICATION_VERSION);
  453. sb.append(brace);
  454. sb.append(specificationVersion);
  455. sb.append(lineSeparator);
  456. }
  457. if (null != specificationVendor) {
  458. sb.append(SPECIFICATION_VENDOR);
  459. sb.append(brace);
  460. sb.append(specificationVendor);
  461. sb.append(lineSeparator);
  462. }
  463. if (null != implementationVersion) {
  464. sb.append(IMPLEMENTATION_VERSION);
  465. sb.append(brace);
  466. sb.append(implementationVersion);
  467. sb.append(lineSeparator);
  468. }
  469. if (null != implementationVendorID) {
  470. sb.append(IMPLEMENTATION_VENDOR_ID);
  471. sb.append(brace);
  472. sb.append(implementationVendorID);
  473. sb.append(lineSeparator);
  474. }
  475. if (null != implementationVendor) {
  476. sb.append(IMPLEMENTATION_VENDOR);
  477. sb.append(brace);
  478. sb.append(implementationVendor);
  479. sb.append(lineSeparator);
  480. }
  481. if (null != implementationURL) {
  482. sb.append(IMPLEMENTATION_URL);
  483. sb.append(brace);
  484. sb.append(implementationURL);
  485. sb.append(lineSeparator);
  486. }
  487. return sb.toString();
  488. }
  489. /**
  490. * Return <code>true</code> if the first version number is greater than
  491. * or equal to the second; otherwise return <code>false</code>.
  492. *
  493. * @param first First version number (dotted decimal)
  494. * @param second Second version number (dotted decimal)
  495. */
  496. private boolean isCompatible(final DeweyDecimal first,
  497. final DeweyDecimal second) {
  498. return first.isGreaterThanOrEqual(second);
  499. }
  500. /**
  501. * Retrieve all the extensions listed under a particular key
  502. * (Usually EXTENSION_LIST or OPTIONAL_EXTENSION_LIST).
  503. *
  504. * @param manifest the manifest to extract extensions from
  505. * @param listKey the key used to get list (Usually
  506. * EXTENSION_LIST or OPTIONAL_EXTENSION_LIST)
  507. * @return the list of listed extensions
  508. */
  509. private static Extension[] getListed(final Manifest manifest,
  510. final Attributes.Name listKey) {
  511. final ArrayList results = new ArrayList();
  512. final Attributes mainAttributes = manifest.getMainAttributes();
  513. if (null != mainAttributes) {
  514. getExtension(mainAttributes, results, listKey);
  515. }
  516. final Map entries = manifest.getEntries();
  517. final Iterator keys = entries.keySet().iterator();
  518. while (keys.hasNext()) {
  519. final String key = (String) keys.next();
  520. final Attributes attributes = (Attributes) entries.get(key);
  521. getExtension(attributes, results, listKey);
  522. }
  523. return (Extension[]) results.toArray(new Extension[ 0 ]);
  524. }
  525. /**
  526. * Add required optional packages defined in the specified
  527. * attributes entry, if any.
  528. *
  529. * @param attributes Attributes to be parsed
  530. * @param required list to add required optional packages to
  531. * @param listKey the key to use to lookup list, usually EXTENSION_LIST
  532. * or OPTIONAL_EXTENSION_LIST
  533. */
  534. private static void getExtension(final Attributes attributes,
  535. final ArrayList required,
  536. final Attributes.Name listKey) {
  537. final String names = attributes.getValue(listKey);
  538. if (null == names) {
  539. return;
  540. }
  541. final String[] extentions = split(names, " ");
  542. for (int i = 0; i < extentions.length; i++) {
  543. final String prefix = extentions[ i ] + "-";
  544. final Extension extension = getExtension(prefix, attributes);
  545. if (null != extension) {
  546. required.add(extension);
  547. }
  548. }
  549. }
  550. /**
  551. * Splits the string on every token into an array of strings.
  552. *
  553. * @param string the string
  554. * @param onToken the token
  555. * @return the resultant array
  556. */
  557. private static final String[] split(final String string,
  558. final String onToken) {
  559. final StringTokenizer tokenizer = new StringTokenizer(string, onToken);
  560. final String[] result = new String[ tokenizer.countTokens() ];
  561. for (int i = 0; i < result.length; i++) {
  562. result[ i ] = tokenizer.nextToken();
  563. }
  564. return result;
  565. }
  566. /**
  567. * Extract an Extension from Attributes.
  568. * Prefix indicates the prefix checked for each string.
  569. * Usually the prefix is <em>"<extension>-"</em> if looking for a
  570. * <b>Required</b> extension. If you are looking for an
  571. * <b>Available</b> extension
  572. * then the prefix is <em>""</em>.
  573. *
  574. * @param prefix the prefix for each attribute name
  575. * @param attributes Attributes to searched
  576. * @return the new Extension object, or null
  577. */
  578. private static Extension getExtension(final String prefix,
  579. final Attributes attributes) {
  580. //WARNING: We trim the values of all the attributes because
  581. //Some extension declarations are badly defined (ie have spaces
  582. //after version or vendorID)
  583. final String nameKey = prefix + EXTENSION_NAME;
  584. final String name = getTrimmedString(attributes.getValue(nameKey));
  585. if (null == name) {
  586. return null;
  587. }
  588. final String specVendorKey = prefix + SPECIFICATION_VENDOR;
  589. final String specVendor
  590. = getTrimmedString(attributes.getValue(specVendorKey));
  591. final String specVersionKey = prefix + SPECIFICATION_VERSION;
  592. final String specVersion
  593. = getTrimmedString(attributes.getValue(specVersionKey));
  594. final String impVersionKey = prefix + IMPLEMENTATION_VERSION;
  595. final String impVersion
  596. = getTrimmedString(attributes.getValue(impVersionKey));
  597. final String impVendorKey = prefix + IMPLEMENTATION_VENDOR;
  598. final String impVendor
  599. = getTrimmedString(attributes.getValue(impVendorKey));
  600. final String impVendorIDKey = prefix + IMPLEMENTATION_VENDOR_ID;
  601. final String impVendorId
  602. = getTrimmedString(attributes.getValue(impVendorIDKey));
  603. final String impURLKey = prefix + IMPLEMENTATION_URL;
  604. final String impURL = getTrimmedString(attributes.getValue(impURLKey));
  605. return new Extension(name, specVersion, specVendor, impVersion,
  606. impVendor, impVendorId, impURL);
  607. }
  608. /**
  609. * Trim the supplied string if the string is non-null
  610. *
  611. * @param value the string to trim or null
  612. * @return the trimmed string or null
  613. */
  614. private static String getTrimmedString(final String value) {
  615. if (null == value) {
  616. return null;
  617. } else {
  618. return value.trim();
  619. }
  620. }
  621. }