1. /*
  2. * @(#)Package.java 1.38 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.lang;
  8. import java.io.InputStream;
  9. import java.util.Enumeration;
  10. import java.util.StringTokenizer;
  11. import java.io.File;
  12. import java.io.FileInputStream;
  13. import java.io.FileNotFoundException;
  14. import java.io.IOException;
  15. import java.net.URL;
  16. import java.net.MalformedURLException;
  17. import java.security.AccessController;
  18. import java.security.PrivilegedAction;
  19. import java.util.jar.JarInputStream;
  20. import java.util.jar.Manifest;
  21. import java.util.jar.Attributes;
  22. import java.util.jar.Attributes.Name;
  23. import java.util.jar.JarException;
  24. import java.util.Map;
  25. import java.util.HashMap;
  26. import java.util.Iterator;
  27. import sun.net.www.ParseUtil;
  28. /**
  29. * <code>Package</code> objects contain version information
  30. * about the implementation and specification of a Java package.
  31. * This versioning information is retrieved and made available
  32. * by the {@link ClassLoader <code>ClassLoader</code>} instance that
  33. * loaded the class(es). Typically, it is stored in the manifest that is
  34. * distributed with the classes.
  35. *
  36. * <p>The set of classes that make up the package may implement a
  37. * particular specification and if so the specification title, version number,
  38. * and vendor strings identify that specification.
  39. * An application can ask if the package is
  40. * compatible with a particular version, see the {@link #isCompatibleWith
  41. * <code>isCompatibleWith</code>} method for details.
  42. *
  43. * <p>Specification version numbers use a syntax that consists of positive
  44. * decimal integers separated by periods ".", for example "2.0" or
  45. * "1.2.3.4.5.6.7". This allows an extensible number to be used to represent
  46. * major, minor, micro, etc. versions. The version specification is described
  47. * by the following formal grammar:
  48. * <blockquote>
  49. * <dl>
  50. * <dt><i>SpecificationVersion:
  51. * <dd>Digits RefinedVersion<sub>opt</sub></i>
  52. * <p><dt><i>RefinedVersion:</i>
  53. * <dd><code>.</code> <i>Digits</i>
  54. * <dd><code>.</code> <i>Digits RefinedVersion</i>
  55. *
  56. * <p><dt><i>Digits:
  57. * <dd>Digit
  58. * <dd>Digits</i>
  59. *
  60. * <p><dt><i>Digit:</i>
  61. * <dd>any character for which {@link Character#isDigit} returns <code>true</code>,
  62. * e.g. 0, 1, 2, ...
  63. * </dl>
  64. * </blockquote>
  65. *
  66. * <p>The implementation title, version, and vendor strings identify an
  67. * implementation and are made available conveniently to enable accurate
  68. * reporting of the packages involved when a problem occurs. The contents
  69. * all three implementation strings are vendor specific. The
  70. * implementation version strings have no specified syntax and should
  71. * only be compared for equality with desired version identifers.
  72. *
  73. * <p>Within each <code>ClassLoader</code> instance all classes from the same
  74. * java package have the same Package object. The static methods allow a package
  75. * to be found by name or the set of all packages known to the current class
  76. * loader to be found.
  77. *
  78. * @see ClassLoader#definePackage
  79. */
  80. public class Package {
  81. /**
  82. * Return the name of this package.
  83. *
  84. * @return The name of this package using the Java language dot notation
  85. * for the package. i.e java.lang
  86. */
  87. public String getName() {
  88. return pkgName;
  89. }
  90. /**
  91. * Return the title of the specification that this package implements.
  92. * @return the specification title, null is returned if it is not known.
  93. */
  94. public String getSpecificationTitle() {
  95. return specTitle;
  96. }
  97. /**
  98. * Returns the version number of the specification
  99. * that this package implements.
  100. * This version string must be a sequence of positive decimal
  101. * integers separated by "."'s and may have leading zeros.
  102. * When version strings are compared the most significant
  103. * numbers are compared.
  104. * @return the specification version, null is returned if it is not known.
  105. */
  106. public String getSpecificationVersion() {
  107. return specVersion;
  108. }
  109. /**
  110. * Return the name of the organization, vendor,
  111. * or company that owns and maintains the specification
  112. * of the classes that implement this package.
  113. * @return the specification vendor, null is returned if it is not known.
  114. */
  115. public String getSpecificationVendor() {
  116. return specVendor;
  117. }
  118. /**
  119. * Return the title of this package.
  120. * @return the title of the implementation, null is returned if it is not known.
  121. */
  122. public String getImplementationTitle() {
  123. return implTitle;
  124. }
  125. /**
  126. * Return the version of this implementation. It consists of any string
  127. * assigned by the vendor of this implementation and does
  128. * not have any particular syntax specified or expected by the Java
  129. * runtime. It may be compared for equality with other
  130. * package version strings used for this implementation
  131. * by this vendor for this package.
  132. * @return the version of the implementation, null is returned if it is not known.
  133. */
  134. public String getImplementationVersion() {
  135. return implVersion;
  136. }
  137. /**
  138. * Returns the name of the organization,
  139. * vendor or company that provided this implementation.
  140. * @return the vendor that implemented this package..
  141. */
  142. public String getImplementationVendor() {
  143. return implVendor;
  144. }
  145. /**
  146. * Returns true if this package is sealed.
  147. *
  148. * @return true if the package is sealed, false otherwise
  149. */
  150. public boolean isSealed() {
  151. return sealBase != null;
  152. }
  153. /**
  154. * Returns true if this package is sealed with respect to the specified
  155. * code source url.
  156. *
  157. * @param url the code source url
  158. * @return true if this package is sealed with respect to url
  159. */
  160. public boolean isSealed(URL url) {
  161. return url.equals(sealBase);
  162. }
  163. /**
  164. * Compare this package's specification version with a
  165. * desired version. It returns true if
  166. * this packages specification version number is greater than or equal
  167. * to the desired version number. <p>
  168. *
  169. * Version numbers are compared by sequentially comparing corresponding
  170. * components of the desired and specification strings.
  171. * Each component is converted as a decimal integer and the values
  172. * compared.
  173. * If the specification value is greater than the desired
  174. * value true is returned. If the value is less false is returned.
  175. * If the values are equal the period is skipped and the next pair of
  176. * components is compared.
  177. *
  178. * @param desired the version string of the desired version.
  179. * @return true if this package's version number is greater
  180. * than or equal to the desired version number
  181. *
  182. * @exception NumberFormatException if the desired or current version
  183. * is not of the correct dotted form.
  184. */
  185. public boolean isCompatibleWith(String desired)
  186. throws NumberFormatException
  187. {
  188. if (specVersion == null || specVersion.length() < 1) {
  189. throw new NumberFormatException("Empty version string");
  190. }
  191. String [] sa = specVersion.split("\\.", -1);
  192. int [] si = new int[sa.length];
  193. for (int i = 0; i < sa.length; i++) {
  194. si[i] = Integer.parseInt(sa[i]);
  195. if (si[i] < 0)
  196. throw NumberFormatException.forInputString("" + si[i]);
  197. }
  198. String [] da = desired.split("\\.", -1);
  199. int [] di = new int[da.length];
  200. for (int i = 0; i < da.length; i++) {
  201. di[i] = Integer.parseInt(da[i]);
  202. if (di[i] < 0)
  203. throw NumberFormatException.forInputString("" + di[i]);
  204. }
  205. int len = Math.max(di.length, si.length);
  206. for (int i = 0; i < len; i++) {
  207. int d = (i < di.length ? di[i] : 0);
  208. int s = (i < si.length ? si[i] : 0);
  209. if (s < d)
  210. return false;
  211. if (s > d)
  212. return true;
  213. }
  214. return true;
  215. }
  216. /**
  217. * Find a package by name in the callers <code>ClassLoader</code> instance.
  218. * The callers <code>ClassLoader</code> instance is used to find the package
  219. * instance corresponding to the named class. If the callers
  220. * <code>ClassLoader</code> instance is null then the set of packages loaded
  221. * by the system <code>ClassLoader</code> instance is searched to find the
  222. * named package. <p>
  223. *
  224. * Packages have attributes for versions and specifications only if the class
  225. * loader created the package instance with the appropriate attributes. Typically,
  226. * those attributes are defined in the manifests that accompany the classes.
  227. *
  228. * @param name a package name, for example, java.lang.
  229. * @return the package of the requested name. It may be null if no package
  230. * information is available from the archive or codebase.
  231. */
  232. public static Package getPackage(String name) {
  233. ClassLoader l = ClassLoader.getCallerClassLoader();
  234. if (l != null) {
  235. return l.getPackage(name);
  236. } else {
  237. return getSystemPackage(name);
  238. }
  239. }
  240. /**
  241. * Get all the packages currently known for the caller's <code>ClassLoader</code>
  242. * instance. Those packages correspond to classes loaded via or accessible by
  243. * name to that <code>ClassLoader</code> instance. If the caller's
  244. * <code>ClassLoader</code> instance is the bootstrap <code>ClassLoader</code>
  245. * instance, which may be represented by <code>null</code> in some implementations,
  246. * only packages corresponding to classes loaded by the bootstrap
  247. * <code>ClassLoader</code> instance will be returned.
  248. *
  249. * @return a new array of packages known to the callers <code>ClassLoader</code>
  250. * instance. An zero length array is returned if none are known.
  251. */
  252. public static Package[] getPackages() {
  253. ClassLoader l = ClassLoader.getCallerClassLoader();
  254. if (l != null) {
  255. return l.getPackages();
  256. } else {
  257. return getSystemPackages();
  258. }
  259. }
  260. /**
  261. * Get the package for the specified class.
  262. * The class's class loader is used to find the package instance
  263. * corresponding to the specified class. If the class loader
  264. * is the bootstrap class loader, which may be represented by
  265. * <code>null</code> in some implementations, then the set of packages
  266. * loaded by the bootstrap class loader is searched to find the package.
  267. * <p>
  268. * Packages have attributes for versions and specifications only
  269. * if the class loader created the package
  270. * instance with the appropriate attributes. Typically those
  271. * attributes are defined in the manifests that accompany
  272. * the classes.
  273. *
  274. * @param class the class to get the package of.
  275. * @return the package of the class. It may be null if no package
  276. * information is available from the archive or codebase. */
  277. static Package getPackage(Class c) {
  278. String name = c.getName();
  279. int i = name.lastIndexOf('.');
  280. if (i != -1) {
  281. name = name.substring(0, i);
  282. ClassLoader cl = c.getClassLoader();
  283. if (cl != null) {
  284. return cl.getPackage(name);
  285. } else {
  286. return getSystemPackage(name);
  287. }
  288. } else {
  289. return null;
  290. }
  291. }
  292. /**
  293. * Return the hash code computed from the package name.
  294. * @return the hash code computed from the package name.
  295. */
  296. public int hashCode(){
  297. return pkgName.hashCode();
  298. }
  299. /**
  300. * Returns the string representation of this Package.
  301. * Its value is the string "package " and the package name.
  302. * If the package title is defined it is appended.
  303. * If the package version is defined it is appended.
  304. * @return the string representation of the package.
  305. */
  306. public String toString() {
  307. String spec = specTitle;
  308. String ver = specVersion;
  309. if (spec != null && spec.length() > 0)
  310. spec = ", " + spec;
  311. else
  312. spec = "";
  313. if (ver != null && ver.length() > 0)
  314. ver = ", version " + ver;
  315. else
  316. ver = "";
  317. return "package " + pkgName + spec + ver;
  318. }
  319. /**
  320. * Construct a package instance with the specified version
  321. * information.
  322. * @param pkgName the name of the package
  323. * @param spectitle the title of the specification
  324. * @param specversion the version of the specification
  325. * @param specvendor the organization that maintains the specification
  326. * @param impltitle the title of the implementation
  327. * @param implversion the version of the implementation
  328. * @param implvendor the organization that maintains the implementation
  329. * @return a new package for containing the specified information.
  330. */
  331. Package(String name,
  332. String spectitle, String specversion, String specvendor,
  333. String impltitle, String implversion, String implvendor,
  334. URL sealbase)
  335. {
  336. pkgName = name;
  337. implTitle = impltitle;
  338. implVersion = implversion;
  339. implVendor = implvendor;
  340. specTitle = spectitle;
  341. specVersion = specversion;
  342. specVendor = specvendor;
  343. sealBase = sealbase;
  344. }
  345. /*
  346. * Construct a package using the attributes from the specified manifest.
  347. *
  348. * @param name the package name
  349. * @param man the optional manifest for the package
  350. * @param url the optional code source url for the package
  351. */
  352. private Package(String name, Manifest man, URL url) {
  353. String path = name.replace('.', '/').concat("/");
  354. String sealed = null;
  355. Attributes attr = man.getAttributes(path);
  356. if (attr != null) {
  357. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  358. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  359. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  360. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  361. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  362. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  363. sealed = attr.getValue(Name.SEALED);
  364. }
  365. attr = man.getMainAttributes();
  366. if (attr != null) {
  367. if (specTitle == null) {
  368. specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  369. }
  370. if (specVersion == null) {
  371. specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  372. }
  373. if (specVendor == null) {
  374. specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  375. }
  376. if (implTitle == null) {
  377. implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  378. }
  379. if (implVersion == null) {
  380. implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  381. }
  382. if (implVendor == null) {
  383. implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  384. }
  385. if (sealed == null) {
  386. sealed = attr.getValue(Name.SEALED);
  387. }
  388. }
  389. if ("true".equalsIgnoreCase(sealed)) {
  390. sealBase = url;
  391. }
  392. pkgName = name;
  393. }
  394. /*
  395. * Returns the loaded system package for the specified name.
  396. */
  397. static Package getSystemPackage(String name) {
  398. synchronized (pkgs) {
  399. Package pkg = (Package)pkgs.get(name);
  400. if (pkg == null) {
  401. name = name.replace('.', '/').concat("/");
  402. String fn = getSystemPackage0(name);
  403. if (fn != null) {
  404. pkg = defineSystemPackage(name, fn);
  405. }
  406. }
  407. return pkg;
  408. }
  409. }
  410. /*
  411. * Return an array of loaded system packages.
  412. */
  413. static Package[] getSystemPackages() {
  414. // First, update the system package map with new package names
  415. String[] names = getSystemPackages0();
  416. synchronized (pkgs) {
  417. for (int i = 0; i < names.length; i++) {
  418. defineSystemPackage(names[i], getSystemPackage0(names[i]));
  419. }
  420. return (Package[])pkgs.values().toArray(new Package[pkgs.size()]);
  421. }
  422. }
  423. private static Package defineSystemPackage(final String iname,
  424. final String fn)
  425. {
  426. return (Package) AccessController.doPrivileged(new PrivilegedAction() {
  427. public Object run() {
  428. String name = iname;
  429. // Get the cached code source url for the file name
  430. URL url = (URL)urls.get(fn);
  431. if (url == null) {
  432. // URL not found, so create one
  433. File file = new File(fn);
  434. try {
  435. url = ParseUtil.fileToEncodedURL(file);
  436. } catch (MalformedURLException e) {
  437. }
  438. if (url != null) {
  439. urls.put(fn, url);
  440. // If loading a JAR file, then also cache the manifest
  441. if (file.isFile()) {
  442. mans.put(fn, loadManifest(fn));
  443. }
  444. }
  445. }
  446. // Convert to "."-separated package name
  447. name = name.substring(0, name.length() - 1).replace('/', '.');
  448. Package pkg;
  449. Manifest man = (Manifest)mans.get(fn);
  450. if (man != null) {
  451. pkg = new Package(name, man, url);
  452. } else {
  453. pkg = new Package(name, null, null, null,
  454. null, null, null, null);
  455. }
  456. pkgs.put(name, pkg);
  457. return pkg;
  458. }
  459. });
  460. }
  461. /*
  462. * Returns the Manifest for the specified JAR file name.
  463. */
  464. private static Manifest loadManifest(String fn) {
  465. try {
  466. FileInputStream fis = new FileInputStream(fn);
  467. JarInputStream jis = new JarInputStream(fis, false);
  468. Manifest man = jis.getManifest();
  469. jis.close();
  470. return man;
  471. } catch (IOException e) {
  472. return null;
  473. }
  474. }
  475. // The map of loaded system packages
  476. private static Map pkgs = new HashMap(31);
  477. // Maps each directory or zip file name to its corresponding url
  478. private static Map urls = new HashMap(10);
  479. // Maps each code source url for a jar file to its manifest
  480. private static Map mans = new HashMap(10);
  481. private static native String getSystemPackage0(String name);
  482. private static native String[] getSystemPackages0();
  483. /*
  484. * Private storage for the package name and attributes.
  485. */
  486. private String pkgName;
  487. private String specTitle;
  488. private String specVersion;
  489. private String specVendor;
  490. private String implTitle;
  491. private String implVersion;
  492. private String implVendor;
  493. private URL sealBase;
  494. }