001: /*
002: * @(#)Package.java 1.38 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.lang;
029:
030: import java.io.InputStream;
031: import java.util.Enumeration;
032:
033: import java.util.StringTokenizer;
034: import java.io.File;
035: import java.io.FileInputStream;
036: import java.io.FileNotFoundException;
037: import java.io.IOException;
038: import java.net.URL;
039: import java.net.MalformedURLException;
040: import java.security.AccessController;
041: import java.security.PrivilegedAction;
042:
043: import java.util.jar.JarInputStream;
044: import java.util.jar.Manifest;
045: import java.util.jar.Attributes;
046: import java.util.jar.Attributes.Name;
047: import java.util.jar.JarException;
048: import java.util.Map;
049: import java.util.HashMap;
050: import java.util.Iterator;
051:
052: import sun.net.www.ParseUtil;
053:
054: /**
055: * <code>Package</code> objects contain version information
056: * about the implementation and specification of a Java package.
057: * This versioning information is retrieved and made available
058: * by the {@link ClassLoader <code>ClassLoader</code>} instance that
059: * loaded the class(es). Typically, it is stored in the manifest that is
060: * distributed with the classes.
061: *
062: * <p>The set of classes that make up the package may implement a
063: * particular specification and if so the specification title, version number,
064: * and vendor strings identify that specification.
065: * An application can ask if the package is
066: * compatible with a particular version, see the {@link #isCompatibleWith
067: * <code>isCompatibleWith</code>} method for details.
068: *
069: * <p>Specification version numbers use a syntax that consists of positive
070: * decimal integers separated by periods ".", for example "2.0" or
071: * "1.2.3.4.5.6.7". This allows an extensible number to be used to represent
072: * major, minor, micro, etc. versions. The version specification is described
073: * by the following formal grammar:
074: * <blockquote>
075: * <dl>
076: * <dt><i>SpecificationVersion:
077: * <dd>Digits RefinedVersion<sub>opt</sub></i>
078:
079: * <p><dt><i>RefinedVersion:</i>
080: * <dd><code>.</code> <i>Digits</i>
081: * <dd><code>.</code> <i>Digits RefinedVersion</i>
082: *
083: * <p><dt><i>Digits:
084: * <dd>Digit
085: * <dd>Digits</i>
086: *
087: * <p><dt><i>Digit:</i>
088: * <dd>any character for which {@link Character#isDigit} returns <code>true</code>,
089: * e.g. 0, 1, 2, ...
090: * </dl>
091: * </blockquote>
092: *
093: * <p>The implementation title, version, and vendor strings identify an
094: * implementation and are made available conveniently to enable accurate
095: * reporting of the packages involved when a problem occurs. The contents
096: * all three implementation strings are vendor specific. The
097: * implementation version strings have no specified syntax and should
098: * only be compared for equality with desired version identifers.
099: *
100: * <p>Within each <code>ClassLoader</code> instance all classes from the same
101: * java package have the same Package object. The static methods allow a package
102: * to be found by name or the set of all packages known to the current class
103: * loader to be found.
104: *
105: * @see ClassLoader#definePackage
106: */
107: public class Package {
108: /**
109: * Return the name of this package.
110: *
111: * @return The name of this package using the Java language dot notation
112: * for the package. i.e java.lang
113: */
114: public String getName() {
115: return pkgName;
116: }
117:
118: /**
119: * Return the title of the specification that this package implements.
120: * @return the specification title, null is returned if it is not known.
121: */
122: public String getSpecificationTitle() {
123: return specTitle;
124: }
125:
126: /**
127: * Returns the version number of the specification
128: * that this package implements.
129: * This version string must be a sequence of positive decimal
130: * integers separated by "."'s and may have leading zeros.
131: * When version strings are compared the most significant
132: * numbers are compared.
133: * @return the specification version, null is returned if it is not known.
134: */
135: public String getSpecificationVersion() {
136: return specVersion;
137: }
138:
139: /**
140: * Return the name of the organization, vendor,
141: * or company that owns and maintains the specification
142: * of the classes that implement this package.
143: * @return the specification vendor, null is returned if it is not known.
144: */
145: public String getSpecificationVendor() {
146: return specVendor;
147: }
148:
149: /**
150: * Return the title of this package.
151: * @return the title of the implementation, null is returned if it is not known.
152: */
153: public String getImplementationTitle() {
154: return implTitle;
155: }
156:
157: /**
158: * Return the version of this implementation. It consists of any string
159: * assigned by the vendor of this implementation and does
160: * not have any particular syntax specified or expected by the Java
161: * runtime. It may be compared for equality with other
162: * package version strings used for this implementation
163: * by this vendor for this package.
164: * @return the version of the implementation, null is returned if it is not known.
165: */
166: public String getImplementationVersion() {
167: return implVersion;
168: }
169:
170: /**
171: * Returns the name of the organization,
172: * vendor or company that provided this implementation.
173: * @return the vendor that implemented this package..
174: */
175: public String getImplementationVendor() {
176: return implVendor;
177: }
178:
179: /**
180: * Returns true if this package is sealed.
181: *
182: * @return true if the package is sealed, false otherwise
183: */
184: public boolean isSealed() {
185: return sealBase != null;
186: }
187:
188: /**
189: * Returns true if this package is sealed with respect to the specified
190: * code source url.
191: *
192: * @param url the code source url
193: * @return true if this package is sealed with respect to url
194: */
195: public boolean isSealed(URL url) {
196: return url.equals(sealBase);
197: }
198:
199: /**
200: * Compare this package's specification version with a
201: * desired version. It returns true if
202: * this packages specification version number is greater than or equal
203: * to the desired version number. <p>
204: *
205: * Version numbers are compared by sequentially comparing corresponding
206: * components of the desired and specification strings.
207: * Each component is converted as a decimal integer and the values
208: * compared.
209: * If the specification value is greater than the desired
210: * value true is returned. If the value is less false is returned.
211: * If the values are equal the period is skipped and the next pair of
212: * components is compared.
213: *
214: * @param desired the version string of the desired version.
215: * @return true if this package's version number is greater
216: * than or equal to the desired version number
217: *
218: * @exception NumberFormatException if the desired or current version
219: * is not of the correct dotted form.
220: */
221: public boolean isCompatibleWith(String desired)
222: throws NumberFormatException {
223: if (specVersion == null || specVersion.length() < 1) {
224: throw new NumberFormatException("Empty version string");
225: }
226:
227: //String [] sa = specVersion.split("\\.", -1);
228: String[] sa = new String[0];
229:
230: int[] si = new int[sa.length];
231: for (int i = 0; i < sa.length; i++) {
232: si[i] = Integer.parseInt(sa[i]);
233: if (si[i] < 0)
234: throw NumberFormatException.forInputString("" + si[i]);
235: }
236:
237: //String [] da = desired.split("\\.", -1);
238: String[] da = new String[0];
239: int[] di = new int[da.length];
240: for (int i = 0; i < da.length; i++) {
241: di[i] = Integer.parseInt(da[i]);
242: if (di[i] < 0)
243: throw NumberFormatException.forInputString("" + di[i]);
244: }
245:
246: int len = Math.max(di.length, si.length);
247: for (int i = 0; i < len; i++) {
248: int d = (i < di.length ? di[i] : 0);
249: int s = (i < si.length ? si[i] : 0);
250: if (s < d)
251: return false;
252: if (s > d)
253: return true;
254: }
255: return true;
256: }
257:
258: /**
259: * Find a package by name in the callers <code>ClassLoader</code> instance.
260: * The callers <code>ClassLoader</code> instance is used to find the package
261: * instance corresponding to the named class. If the callers
262: * <code>ClassLoader</code> instance is null then the set of packages loaded
263: * by the system <code>ClassLoader</code> instance is searched to find the
264: * named package. <p>
265: *
266: * Packages have attributes for versions and specifications only if the class
267: * loader created the package instance with the appropriate attributes. Typically,
268: * those attributes are defined in the manifests that accompany the classes.
269: *
270: * @param name a package name, for example, java.lang.
271: * @return the package of the requested name. It may be null if no package
272: * information is available from the archive or codebase.
273: */
274: public static Package getPackage(String name) {
275: ClassLoader l = ClassLoader.getCallerClassLoader();
276: if (l != null) {
277: return l.getPackage(name);
278: } else {
279: return getSystemPackage(name);
280: }
281: }
282:
283: /**
284: * Get all the packages currently known for the caller's <code>ClassLoader</code>
285: * instance. Those packages correspond to classes loaded via or accessible by
286: * name to that <code>ClassLoader</code> instance. If the caller's
287: * <code>ClassLoader</code> instance is the bootstrap <code>ClassLoader</code>
288: * instance, which may be represented by <code>null</code> in some implementations,
289: * only packages corresponding to classes loaded by the bootstrap
290: * <code>ClassLoader</code> instance will be returned.
291: *
292: * @return a new array of packages known to the callers <code>ClassLoader</code>
293: * instance. An zero length array is returned if none are known.
294: */
295: public static Package[] getPackages() {
296: ClassLoader l = ClassLoader.getCallerClassLoader();
297: if (l != null) {
298: return l.getPackages();
299: } else {
300: return getSystemPackages();
301: }
302: }
303:
304: /**
305: * Get the package for the specified class.
306: * The class's class loader is used to find the package instance
307: * corresponding to the specified class. If the class loader
308: * is the bootstrap class loader, which may be represented by
309: * <code>null</code> in some implementations, then the set of packages
310: * loaded by the bootstrap class loader is searched to find the package.
311: * <p>
312: * Packages have attributes for versions and specifications only
313: * if the class loader created the package
314: * instance with the appropriate attributes. Typically those
315: * attributes are defined in the manifests that accompany
316: * the classes.
317: *
318: * @param class the class to get the package of.
319: * @return the package of the class. It may be null if no package
320: * information is available from the archive or codebase. */
321: static Package getPackage(Class c) {
322: String name = c.getName();
323: int i = name.lastIndexOf('.');
324: if (i != -1) {
325: name = name.substring(0, i);
326: ClassLoader cl = c.getClassLoader();
327: if (cl != null) {
328: return cl.getPackage(name);
329: } else {
330: return getSystemPackage(name);
331: }
332: } else {
333: return null;
334: }
335: }
336:
337: /**
338: * Return the hash code computed from the package name.
339: * @return the hash code computed from the package name.
340: */
341: public int hashCode() {
342: return pkgName.hashCode();
343: }
344:
345: /**
346: * Returns the string representation of this Package.
347: * Its value is the string "package " and the package name.
348: * If the package title is defined it is appended.
349: * If the package version is defined it is appended.
350: * @return the string representation of the package.
351: */
352: public String toString() {
353: String spec = specTitle;
354: String ver = specVersion;
355: if (spec != null && spec.length() > 0)
356: spec = ", " + spec;
357: else
358: spec = "";
359: if (ver != null && ver.length() > 0)
360: ver = ", version " + ver;
361: else
362: ver = "";
363: return "package " + pkgName + spec + ver;
364: }
365:
366: /**
367: * Construct a package instance with the specified version
368: * information.
369: * @param pkgName the name of the package
370: * @param spectitle the title of the specification
371: * @param specversion the version of the specification
372: * @param specvendor the organization that maintains the specification
373: * @param impltitle the title of the implementation
374: * @param implversion the version of the implementation
375: * @param implvendor the organization that maintains the implementation
376: * @return a new package for containing the specified information.
377: */
378: Package(String name, String spectitle, String specversion,
379: String specvendor, String impltitle, String implversion,
380: String implvendor, URL sealbase) {
381: pkgName = name;
382: implTitle = impltitle;
383: implVersion = implversion;
384: implVendor = implvendor;
385: specTitle = spectitle;
386: specVersion = specversion;
387: specVendor = specvendor;
388: sealBase = sealbase;
389: }
390:
391: /*
392: * Construct a package using the attributes from the specified manifest.
393: *
394: * @param name the package name
395: * @param man the optional manifest for the package
396: * @param url the optional code source url for the package
397: */
398: private Package(String name, Manifest man, URL url) {
399: String path = name.replace('.', '/').concat("/");
400: String sealed = null;
401: Attributes attr = man.getAttributes(path);
402: if (attr != null) {
403: specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
404: specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
405: specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
406: implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
407: implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
408: implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
409: sealed = attr.getValue(Name.SEALED);
410: }
411: attr = man.getMainAttributes();
412: if (attr != null) {
413: if (specTitle == null) {
414: specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
415: }
416: if (specVersion == null) {
417: specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
418: }
419: if (specVendor == null) {
420: specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
421: }
422: if (implTitle == null) {
423: implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
424: }
425: if (implVersion == null) {
426: implVersion = attr
427: .getValue(Name.IMPLEMENTATION_VERSION);
428: }
429: if (implVendor == null) {
430: implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
431: }
432: if (sealed == null) {
433: sealed = attr.getValue(Name.SEALED);
434: }
435: }
436: if ("true".equalsIgnoreCase(sealed)) {
437: sealBase = url;
438: }
439: pkgName = name;
440: }
441:
442: /*
443: * Returns the loaded system package for the specified name.
444: */
445: static Package getSystemPackage(String name) {
446: synchronized (pkgs) {
447: Package pkg = (Package) pkgs.get(name);
448: if (pkg == null) {
449: name = name.replace('.', '/').concat("/");
450: String fn = getSystemPackage0(name);
451: if (fn != null) {
452: pkg = defineSystemPackage(name, fn);
453: }
454: }
455: return pkg;
456: }
457: }
458:
459: /*
460: * Return an array of loaded system packages.
461: */
462: static Package[] getSystemPackages() {
463: // First, update the system package map with new package names
464: String[] names = getSystemPackages0();
465: synchronized (pkgs) {
466: for (int i = 0; i < names.length; i++) {
467: defineSystemPackage(names[i],
468: getSystemPackage0(names[i]));
469: }
470: return (Package[]) pkgs.values().toArray(
471: new Package[pkgs.size()]);
472: }
473: }
474:
475: private static Package defineSystemPackage(final String iname,
476: final String fn) {
477: return (Package) AccessController
478: .doPrivileged(new PrivilegedAction() {
479: public Object run() {
480: String name = iname;
481: // Get the cached code source url for the file name
482: URL url = (URL) urls.get(fn);
483: if (url == null) {
484: // URL not found, so create one
485: File file = new File(fn);
486: try {
487: url = ParseUtil.fileToEncodedURL(file);
488: } catch (MalformedURLException e) {
489: }
490: if (url != null) {
491: urls.put(fn, url);
492: // If loading a JAR file, then also cache the manifest
493: if (file.isFile()) {
494: mans.put(fn, loadManifest(fn));
495: }
496: }
497: }
498: // Convert to "."-separated package name
499: name = name.substring(0, name.length() - 1)
500: .replace('/', '.');
501: Package pkg;
502: Manifest man = (Manifest) mans.get(fn);
503: if (man != null) {
504: pkg = new Package(name, man, url);
505: } else {
506: pkg = new Package(name, null, null, null,
507: null, null, null, null);
508: }
509: pkgs.put(name, pkg);
510: return pkg;
511: }
512: });
513: }
514:
515: /*
516: * Returns the Manifest for the specified JAR file name.
517: */
518: private static Manifest loadManifest(String fn) {
519: try {
520: FileInputStream fis = new FileInputStream(fn);
521: JarInputStream jis = new JarInputStream(fis, false);
522: Manifest man = jis.getManifest();
523: jis.close();
524: return man;
525: } catch (IOException e) {
526: return null;
527: }
528: }
529:
530: // The map of loaded system packages
531: private static Map pkgs = new HashMap(31);
532:
533: // Maps each directory or zip file name to its corresponding url
534: private static Map urls = new HashMap(10);
535:
536: // Maps each code source url for a jar file to its manifest
537: private static Map mans = new HashMap(10);
538:
539: private static native String getSystemPackage0(String name);
540:
541: private static native String[] getSystemPackages0();
542:
543: /*
544: * Private storage for the package name and attributes.
545: */
546: private String pkgName;
547: private String specTitle;
548: private String specVersion;
549: private String specVendor;
550: private String implTitle;
551: private String implVersion;
552: private String implVendor;
553: private URL sealBase;
554: }
|