001: /*
002: * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.lang;
027:
028: import java.io.InputStream;
029: import java.util.Enumeration;
030:
031: import java.util.StringTokenizer;
032: import java.io.File;
033: import java.io.FileInputStream;
034: import java.io.FileNotFoundException;
035: import java.io.IOException;
036: import java.net.URL;
037: import java.net.MalformedURLException;
038: import java.security.AccessController;
039: import java.security.PrivilegedAction;
040:
041: import java.util.jar.JarInputStream;
042: import java.util.jar.Manifest;
043: import java.util.jar.Attributes;
044: import java.util.jar.Attributes.Name;
045: import java.util.jar.JarException;
046: import java.util.Map;
047: import java.util.HashMap;
048: import java.util.Iterator;
049:
050: import sun.net.www.ParseUtil;
051:
052: import java.lang.annotation.Annotation;
053:
054: /**
055: * {@code Package} 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} 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
067: * #isCompatibleWith isCompatibleWith}
068: * method for details.
069: *
070: * <p>Specification version numbers use a syntax that consists of nonnegative
071: * decimal integers separated by periods ".", for example "2.0" or
072: * "1.2.3.4.5.6.7". This allows an extensible number to be used to represent
073: * major, minor, micro, etc. versions. The version specification is described
074: * by the following formal grammar:
075: * <blockquote>
076: * <dl>
077: * <dt><i>SpecificationVersion:
078: * <dd>Digits RefinedVersion<sub>opt</sub></i>
079:
080: * <p><dt><i>RefinedVersion:</i>
081: * <dd>{@code .} <i>Digits</i>
082: * <dd>{@code .} <i>Digits RefinedVersion</i>
083: *
084: * <p><dt><i>Digits:
085: * <dd>Digit
086: * <dd>Digits</i>
087: *
088: * <p><dt><i>Digit:</i>
089: * <dd>any character for which {@link Character#isDigit} returns {@code true},
090: * e.g. 0, 1, 2, ...
091: * </dl>
092: * </blockquote>
093: *
094: * <p>The implementation title, version, and vendor strings identify an
095: * implementation and are made available conveniently to enable accurate
096: * reporting of the packages involved when a problem occurs. The contents
097: * all three implementation strings are vendor specific. The
098: * implementation version strings have no specified syntax and should
099: * only be compared for equality with desired version identifiers.
100: *
101: * <p>Within each {@code ClassLoader} instance all classes from the same
102: * java package have the same Package object. The static methods allow a package
103: * to be found by name or the set of all packages known to the current class
104: * loader to be found.
105: *
106: * @see ClassLoader#definePackage
107: */
108: public class Package implements java.lang.reflect.AnnotatedElement {
109: /**
110: * Return the name of this package.
111: *
112: * @return The fully-qualified name of this package as defined in the
113: * <em>Java Language Specification, Third Edition</em>
114: * <a href="http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.5.3">
115: * §6.5.3</a>, for example, {@code java.lang}
116: */
117: public String getName() {
118: return pkgName;
119: }
120:
121: /**
122: * Return the title of the specification that this package implements.
123: * @return the specification title, null is returned if it is not known.
124: */
125: public String getSpecificationTitle() {
126: return specTitle;
127: }
128:
129: /**
130: * Returns the version number of the specification
131: * that this package implements.
132: * This version string must be a sequence of nonnegative decimal
133: * integers separated by "."'s and may have leading zeros.
134: * When version strings are compared the most significant
135: * numbers are compared.
136: * @return the specification version, null is returned if it is not known.
137: */
138: public String getSpecificationVersion() {
139: return specVersion;
140: }
141:
142: /**
143: * Return the name of the organization, vendor,
144: * or company that owns and maintains the specification
145: * of the classes that implement this package.
146: * @return the specification vendor, null is returned if it is not known.
147: */
148: public String getSpecificationVendor() {
149: return specVendor;
150: }
151:
152: /**
153: * Return the title of this package.
154: * @return the title of the implementation, null is returned if it is not known.
155: */
156: public String getImplementationTitle() {
157: return implTitle;
158: }
159:
160: /**
161: * Return the version of this implementation. It consists of any string
162: * assigned by the vendor of this implementation and does
163: * not have any particular syntax specified or expected by the Java
164: * runtime. It may be compared for equality with other
165: * package version strings used for this implementation
166: * by this vendor for this package.
167: * @return the version of the implementation, null is returned if it is not known.
168: */
169: public String getImplementationVersion() {
170: return implVersion;
171: }
172:
173: /**
174: * Returns the name of the organization,
175: * vendor or company that provided this implementation.
176: * @return the vendor that implemented this package..
177: */
178: public String getImplementationVendor() {
179: return implVendor;
180: }
181:
182: /**
183: * Returns true if this package is sealed.
184: *
185: * @return true if the package is sealed, false otherwise
186: */
187: public boolean isSealed() {
188: return sealBase != null;
189: }
190:
191: /**
192: * Returns true if this package is sealed with respect to the specified
193: * code source url.
194: *
195: * @param url the code source url
196: * @return true if this package is sealed with respect to url
197: */
198: public boolean isSealed(URL url) {
199: return url.equals(sealBase);
200: }
201:
202: /**
203: * Compare this package's specification version with a
204: * desired version. It returns true if
205: * this packages specification version number is greater than or equal
206: * to the desired version number. <p>
207: *
208: * Version numbers are compared by sequentially comparing corresponding
209: * components of the desired and specification strings.
210: * Each component is converted as a decimal integer and the values
211: * compared.
212: * If the specification value is greater than the desired
213: * value true is returned. If the value is less false is returned.
214: * If the values are equal the period is skipped and the next pair of
215: * components is compared.
216: *
217: * @param desired the version string of the desired version.
218: * @return true if this package's version number is greater
219: * than or equal to the desired version number
220: *
221: * @exception NumberFormatException if the desired or current version
222: * is not of the correct dotted form.
223: */
224: public boolean isCompatibleWith(String desired)
225: throws NumberFormatException {
226: if (specVersion == null || specVersion.length() < 1) {
227: throw new NumberFormatException("Empty version string");
228: }
229:
230: String[] sa = specVersion.split("\\.", -1);
231: int[] si = new int[sa.length];
232: for (int i = 0; i < sa.length; i++) {
233: si[i] = Integer.parseInt(sa[i]);
234: if (si[i] < 0)
235: throw NumberFormatException.forInputString("" + si[i]);
236: }
237:
238: String[] da = desired.split("\\.", -1);
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} instance.
260: * The callers {@code ClassLoader} instance is used to find the package
261: * instance corresponding to the named class. If the callers
262: * {@code ClassLoader} instance is null then the set of packages loaded
263: * by the system {@code ClassLoader} 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}
285: * instance. Those packages correspond to classes loaded via or accessible by
286: * name to that {@code ClassLoader} instance. If the caller's
287: * {@code ClassLoader} instance is the bootstrap {@code ClassLoader}
288: * instance, which may be represented by {@code null} in some implementations,
289: * only packages corresponding to classes loaded by the bootstrap
290: * {@code ClassLoader} instance will be returned.
291: *
292: * @return a new array of packages known to the callers {@code ClassLoader}
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} 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: private Class<?> getPackageInfo() {
367: if (packageInfo == null) {
368: try {
369: packageInfo = Class.forName(pkgName + ".package-info",
370: false, loader);
371: } catch (ClassNotFoundException ex) {
372: // store a proxy for the package info that has no annotations
373: class PackageInfoProxy {
374: }
375: packageInfo = PackageInfoProxy.class;
376: }
377: }
378: return packageInfo;
379: }
380:
381: /**
382: * @throws NullPointerException {@inheritDoc}
383: * @since 1.5
384: */
385: public <A extends Annotation> A getAnnotation(
386: Class<A> annotationClass) {
387: return getPackageInfo().getAnnotation(annotationClass);
388: }
389:
390: /**
391: * @throws NullPointerException {@inheritDoc}
392: * @since 1.5
393: */
394: public boolean isAnnotationPresent(
395: Class<? extends Annotation> annotationClass) {
396: return getPackageInfo().isAnnotationPresent(annotationClass);
397: }
398:
399: /**
400: * @since 1.5
401: */
402: public Annotation[] getAnnotations() {
403: return getPackageInfo().getAnnotations();
404: }
405:
406: /**
407: * @since 1.5
408: */
409: public Annotation[] getDeclaredAnnotations() {
410: return getPackageInfo().getDeclaredAnnotations();
411: }
412:
413: /**
414: * Construct a package instance with the specified version
415: * information.
416: * @param pkgName the name of the package
417: * @param spectitle the title of the specification
418: * @param specversion the version of the specification
419: * @param specvendor the organization that maintains the specification
420: * @param impltitle the title of the implementation
421: * @param implversion the version of the implementation
422: * @param implvendor the organization that maintains the implementation
423: * @return a new package for containing the specified information.
424: */
425: Package(String name, String spectitle, String specversion,
426: String specvendor, String impltitle, String implversion,
427: String implvendor, URL sealbase, ClassLoader loader) {
428: pkgName = name;
429: implTitle = impltitle;
430: implVersion = implversion;
431: implVendor = implvendor;
432: specTitle = spectitle;
433: specVersion = specversion;
434: specVendor = specvendor;
435: sealBase = sealbase;
436: this .loader = loader;
437: }
438:
439: /*
440: * Construct a package using the attributes from the specified manifest.
441: *
442: * @param name the package name
443: * @param man the optional manifest for the package
444: * @param url the optional code source url for the package
445: */
446: private Package(String name, Manifest man, URL url,
447: ClassLoader loader) {
448: String path = name.replace('.', '/').concat("/");
449: String sealed = null;
450: String specTitle = null;
451: String specVersion = null;
452: String specVendor = null;
453: String implTitle = null;
454: String implVersion = null;
455: String implVendor = null;
456: URL sealBase = null;
457: Attributes attr = man.getAttributes(path);
458: if (attr != null) {
459: specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
460: specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
461: specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
462: implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
463: implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
464: implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
465: sealed = attr.getValue(Name.SEALED);
466: }
467: attr = man.getMainAttributes();
468: if (attr != null) {
469: if (specTitle == null) {
470: specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
471: }
472: if (specVersion == null) {
473: specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
474: }
475: if (specVendor == null) {
476: specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
477: }
478: if (implTitle == null) {
479: implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
480: }
481: if (implVersion == null) {
482: implVersion = attr
483: .getValue(Name.IMPLEMENTATION_VERSION);
484: }
485: if (implVendor == null) {
486: implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
487: }
488: if (sealed == null) {
489: sealed = attr.getValue(Name.SEALED);
490: }
491: }
492: if ("true".equalsIgnoreCase(sealed)) {
493: sealBase = url;
494: }
495: pkgName = name;
496: this .specTitle = specTitle;
497: this .specVersion = specVersion;
498: this .specVendor = specVendor;
499: this .implTitle = implTitle;
500: this .implVersion = implVersion;
501: this .implVendor = implVendor;
502: this .sealBase = sealBase;
503: this .loader = loader;
504: }
505:
506: /*
507: * Returns the loaded system package for the specified name.
508: */
509: static Package getSystemPackage(String name) {
510: synchronized (pkgs) {
511: Package pkg = (Package) pkgs.get(name);
512: if (pkg == null) {
513: name = name.replace('.', '/').concat("/");
514: String fn = getSystemPackage0(name);
515: if (fn != null) {
516: pkg = defineSystemPackage(name, fn);
517: }
518: }
519: return pkg;
520: }
521: }
522:
523: /*
524: * Return an array of loaded system packages.
525: */
526: static Package[] getSystemPackages() {
527: // First, update the system package map with new package names
528: String[] names = getSystemPackages0();
529: synchronized (pkgs) {
530: for (int i = 0; i < names.length; i++) {
531: defineSystemPackage(names[i],
532: getSystemPackage0(names[i]));
533: }
534: return (Package[]) pkgs.values().toArray(
535: new Package[pkgs.size()]);
536: }
537: }
538:
539: private static Package defineSystemPackage(final String iname,
540: final String fn) {
541: return (Package) AccessController
542: .doPrivileged(new PrivilegedAction() {
543: public Object run() {
544: String name = iname;
545: // Get the cached code source url for the file name
546: URL url = (URL) urls.get(fn);
547: if (url == null) {
548: // URL not found, so create one
549: File file = new File(fn);
550: try {
551: url = ParseUtil.fileToEncodedURL(file);
552: } catch (MalformedURLException e) {
553: }
554: if (url != null) {
555: urls.put(fn, url);
556: // If loading a JAR file, then also cache the manifest
557: if (file.isFile()) {
558: mans.put(fn, loadManifest(fn));
559: }
560: }
561: }
562: // Convert to "."-separated package name
563: name = name.substring(0, name.length() - 1)
564: .replace('/', '.');
565: Package pkg;
566: Manifest man = (Manifest) mans.get(fn);
567: if (man != null) {
568: pkg = new Package(name, man, url, null);
569: } else {
570: pkg = new Package(name, null, null, null,
571: null, null, null, null, null);
572: }
573: pkgs.put(name, pkg);
574: return pkg;
575: }
576: });
577: }
578:
579: /*
580: * Returns the Manifest for the specified JAR file name.
581: */
582: private static Manifest loadManifest(String fn) {
583: try {
584: FileInputStream fis = new FileInputStream(fn);
585: JarInputStream jis = new JarInputStream(fis, false);
586: Manifest man = jis.getManifest();
587: jis.close();
588: return man;
589: } catch (IOException e) {
590: return null;
591: }
592: }
593:
594: // The map of loaded system packages
595: private static Map pkgs = new HashMap(31);
596:
597: // Maps each directory or zip file name to its corresponding url
598: private static Map urls = new HashMap(10);
599:
600: // Maps each code source url for a jar file to its manifest
601: private static Map mans = new HashMap(10);
602:
603: private static native String getSystemPackage0(String name);
604:
605: private static native String[] getSystemPackages0();
606:
607: /*
608: * Private storage for the package name and attributes.
609: */
610: private final String pkgName;
611: private final String specTitle;
612: private final String specVersion;
613: private final String specVendor;
614: private final String implTitle;
615: private final String implVersion;
616: private final String implVendor;
617: private final URL sealBase;
618: private transient final ClassLoader loader;
619: private transient Class packageInfo;
620: }
|