blob: 80af5d9bc46fe6d85268c2cd6d18f7781ed6bec2 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.lang;
27
28import java.io.InputStream;
29import java.util.Enumeration;
30
31import java.util.StringTokenizer;
32import java.io.File;
33import java.io.FileInputStream;
34import java.io.FileNotFoundException;
35import java.io.IOException;
36import java.net.URL;
37import java.net.MalformedURLException;
38import java.security.AccessController;
39import java.security.PrivilegedAction;
40
41import java.util.jar.JarInputStream;
42import java.util.jar.Manifest;
43import java.util.jar.Attributes;
44import java.util.jar.Attributes.Name;
45import java.util.jar.JarException;
46import java.util.Map;
47import java.util.HashMap;
48import java.util.Iterator;
49
50import sun.net.www.ParseUtil;
51
52import java.lang.annotation.Annotation;
53
54/**
55 * {@code Package} objects contain version information
56 * about the implementation and specification of a Java package.
57 * This versioning information is retrieved and made available
58 * by the {@link ClassLoader} instance that
59 * loaded the class(es). Typically, it is stored in the manifest that is
60 * distributed with the classes.
61 *
62 * <p>The set of classes that make up the package may implement a
63 * particular specification and if so the specification title, version number,
64 * and vendor strings identify that specification.
65 * An application can ask if the package is
66 * compatible with a particular version, see the {@link
67 * #isCompatibleWith isCompatibleWith}
68 * method for details.
69 *
70 * <p>Specification version numbers use a syntax that consists of nonnegative
71 * decimal integers separated by periods ".", for example "2.0" or
72 * "1.2.3.4.5.6.7". This allows an extensible number to be used to represent
73 * major, minor, micro, etc. versions. The version specification is described
74 * by the following formal grammar:
75 * <blockquote>
76 * <dl>
77 * <dt><i>SpecificationVersion:
78 * <dd>Digits RefinedVersion<sub>opt</sub></i>
79
80 * <p><dt><i>RefinedVersion:</i>
81 * <dd>{@code .} <i>Digits</i>
82 * <dd>{@code .} <i>Digits RefinedVersion</i>
83 *
84 * <p><dt><i>Digits:
85 * <dd>Digit
86 * <dd>Digits</i>
87 *
88 * <p><dt><i>Digit:</i>
89 * <dd>any character for which {@link Character#isDigit} returns {@code true},
90 * e.g. 0, 1, 2, ...
91 * </dl>
92 * </blockquote>
93 *
94 * <p>The implementation title, version, and vendor strings identify an
95 * implementation and are made available conveniently to enable accurate
96 * reporting of the packages involved when a problem occurs. The contents
97 * all three implementation strings are vendor specific. The
98 * implementation version strings have no specified syntax and should
99 * 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 */
108public 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 * &sect;6.5.3</a>, for example, {@code java.lang}
116 */
117 public String getName() {
118 return pkgName;
119 }
120
121
122 /**
123 * Return the title of the specification that this package implements.
124 * @return the specification title, null is returned if it is not known.
125 */
126 public String getSpecificationTitle() {
127 return specTitle;
128 }
129
130 /**
131 * Returns the version number of the specification
132 * that this package implements.
133 * This version string must be a sequence of nonnegative decimal
134 * integers separated by "."'s and may have leading zeros.
135 * When version strings are compared the most significant
136 * numbers are compared.
137 * @return the specification version, null is returned if it is not known.
138 */
139 public String getSpecificationVersion() {
140 return specVersion;
141 }
142
143 /**
144 * Return the name of the organization, vendor,
145 * or company that owns and maintains the specification
146 * of the classes that implement this package.
147 * @return the specification vendor, null is returned if it is not known.
148 */
149 public String getSpecificationVendor() {
150 return specVendor;
151 }
152
153 /**
154 * Return the title of this package.
155 * @return the title of the implementation, null is returned if it is not known.
156 */
157 public String getImplementationTitle() {
158 return implTitle;
159 }
160
161 /**
162 * Return the version of this implementation. It consists of any string
163 * assigned by the vendor of this implementation and does
164 * not have any particular syntax specified or expected by the Java
165 * runtime. It may be compared for equality with other
166 * package version strings used for this implementation
167 * by this vendor for this package.
168 * @return the version of the implementation, null is returned if it is not known.
169 */
170 public String getImplementationVersion() {
171 return implVersion;
172 }
173
174 /**
175 * Returns the name of the organization,
176 * vendor or company that provided this implementation.
177 * @return the vendor that implemented this package..
178 */
179 public String getImplementationVendor() {
180 return implVendor;
181 }
182
183 /**
184 * Returns true if this package is sealed.
185 *
186 * @return true if the package is sealed, false otherwise
187 */
188 public boolean isSealed() {
189 return sealBase != null;
190 }
191
192 /**
193 * Returns true if this package is sealed with respect to the specified
194 * code source url.
195 *
196 * @param url the code source url
197 * @return true if this package is sealed with respect to url
198 */
199 public boolean isSealed(URL url) {
200 return url.equals(sealBase);
201 }
202
203 /**
204 * Compare this package's specification version with a
205 * desired version. It returns true if
206 * this packages specification version number is greater than or equal
207 * to the desired version number. <p>
208 *
209 * Version numbers are compared by sequentially comparing corresponding
210 * components of the desired and specification strings.
211 * Each component is converted as a decimal integer and the values
212 * compared.
213 * If the specification value is greater than the desired
214 * value true is returned. If the value is less false is returned.
215 * If the values are equal the period is skipped and the next pair of
216 * components is compared.
217 *
218 * @param desired the version string of the desired version.
219 * @return true if this package's version number is greater
220 * than or equal to the desired version number
221 *
222 * @exception NumberFormatException if the desired or current version
223 * is not of the correct dotted form.
224 */
225 public boolean isCompatibleWith(String desired)
226 throws NumberFormatException
227 {
228 if (specVersion == null || specVersion.length() < 1) {
229 throw new NumberFormatException("Empty version string");
230 }
231
232 String [] sa = specVersion.split("\\.", -1);
233 int [] si = new int[sa.length];
234 for (int i = 0; i < sa.length; i++) {
235 si[i] = Integer.parseInt(sa[i]);
236 if (si[i] < 0)
237 throw NumberFormatException.forInputString("" + si[i]);
238 }
239
240 String [] da = desired.split("\\.", -1);
241 int [] di = new int[da.length];
242 for (int i = 0; i < da.length; i++) {
243 di[i] = Integer.parseInt(da[i]);
244 if (di[i] < 0)
245 throw NumberFormatException.forInputString("" + di[i]);
246 }
247
248 int len = Math.max(di.length, si.length);
249 for (int i = 0; i < len; i++) {
250 int d = (i < di.length ? di[i] : 0);
251 int s = (i < si.length ? si[i] : 0);
252 if (s < d)
253 return false;
254 if (s > d)
255 return true;
256 }
257 return true;
258 }
259
260 /**
261 * Find a package by name in the callers {@code ClassLoader} instance.
262 * The callers {@code ClassLoader} instance is used to find the package
263 * instance corresponding to the named class. If the callers
264 * {@code ClassLoader} instance is null then the set of packages loaded
265 * by the system {@code ClassLoader} instance is searched to find the
266 * named package. <p>
267 *
268 * Packages have attributes for versions and specifications only if the class
269 * loader created the package instance with the appropriate attributes. Typically,
270 * those attributes are defined in the manifests that accompany the classes.
271 *
272 * @param name a package name, for example, java.lang.
273 * @return the package of the requested name. It may be null if no package
274 * information is available from the archive or codebase.
275 */
276 public static Package getPackage(String name) {
277 ClassLoader l = ClassLoader.getCallerClassLoader();
278 if (l != null) {
279 return l.getPackage(name);
280 } else {
281 return getSystemPackage(name);
282 }
283 }
284
285 /**
286 * Get all the packages currently known for the caller's {@code ClassLoader}
287 * instance. Those packages correspond to classes loaded via or accessible by
288 * name to that {@code ClassLoader} instance. If the caller's
289 * {@code ClassLoader} instance is the bootstrap {@code ClassLoader}
290 * instance, which may be represented by {@code null} in some implementations,
291 * only packages corresponding to classes loaded by the bootstrap
292 * {@code ClassLoader} instance will be returned.
293 *
294 * @return a new array of packages known to the callers {@code ClassLoader}
295 * instance. An zero length array is returned if none are known.
296 */
297 public static Package[] getPackages() {
298 ClassLoader l = ClassLoader.getCallerClassLoader();
299 if (l != null) {
300 return l.getPackages();
301 } else {
302 return getSystemPackages();
303 }
304 }
305
306 /**
307 * Get the package for the specified class.
308 * The class's class loader is used to find the package instance
309 * corresponding to the specified class. If the class loader
310 * is the bootstrap class loader, which may be represented by
311 * {@code null} in some implementations, then the set of packages
312 * loaded by the bootstrap class loader is searched to find the package.
313 * <p>
314 * Packages have attributes for versions and specifications only
315 * if the class loader created the package
316 * instance with the appropriate attributes. Typically those
317 * attributes are defined in the manifests that accompany
318 * the classes.
319 *
320 * @param class the class to get the package of.
321 * @return the package of the class. It may be null if no package
322 * information is available from the archive or codebase. */
323 static Package getPackage(Class c) {
324 String name = c.getName();
325 int i = name.lastIndexOf('.');
326 if (i != -1) {
327 name = name.substring(0, i);
328 ClassLoader cl = c.getClassLoader();
329 if (cl != null) {
330 return cl.getPackage(name);
331 } else {
332 return getSystemPackage(name);
333 }
334 } else {
335 return null;
336 }
337 }
338
339 /**
340 * Return the hash code computed from the package name.
341 * @return the hash code computed from the package name.
342 */
343 public int hashCode(){
344 return pkgName.hashCode();
345 }
346
347 /**
348 * Returns the string representation of this Package.
349 * Its value is the string "package " and the package name.
350 * If the package title is defined it is appended.
351 * If the package version is defined it is appended.
352 * @return the string representation of the package.
353 */
354 public String toString() {
355 String spec = specTitle;
356 String ver = specVersion;
357 if (spec != null && spec.length() > 0)
358 spec = ", " + spec;
359 else
360 spec = "";
361 if (ver != null && ver.length() > 0)
362 ver = ", version " + ver;
363 else
364 ver = "";
365 return "package " + pkgName + spec + ver;
366 }
367
368 private Class<?> getPackageInfo() {
369 if (packageInfo == null) {
370 try {
371 packageInfo = Class.forName(pkgName + ".package-info", false, loader);
372 } catch (ClassNotFoundException ex) {
373 // store a proxy for the package info that has no annotations
374 class PackageInfoProxy {}
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(Class<A> annotationClass) {
386 return getPackageInfo().getAnnotation(annotationClass);
387 }
388
389 /**
390 * @throws NullPointerException {@inheritDoc}
391 * @since 1.5
392 */
393 public boolean isAnnotationPresent(
394 Class<? extends Annotation> annotationClass) {
395 return getPackageInfo().isAnnotationPresent(annotationClass);
396 }
397
398 /**
399 * @since 1.5
400 */
401 public Annotation[] getAnnotations() {
402 return getPackageInfo().getAnnotations();
403 }
404
405 /**
406 * @since 1.5
407 */
408 public Annotation[] getDeclaredAnnotations() {
409 return getPackageInfo().getDeclaredAnnotations();
410 }
411
412 /**
413 * Construct a package instance with the specified version
414 * information.
415 * @param pkgName the name of the package
416 * @param spectitle the title of the specification
417 * @param specversion the version of the specification
418 * @param specvendor the organization that maintains the specification
419 * @param impltitle the title of the implementation
420 * @param implversion the version of the implementation
421 * @param implvendor the organization that maintains the implementation
422 * @return a new package for containing the specified information.
423 */
424 Package(String name,
425 String spectitle, String specversion, String specvendor,
426 String impltitle, String implversion, String implvendor,
427 URL sealbase, ClassLoader loader)
428 {
429 pkgName = name;
430 implTitle = impltitle;
431 implVersion = implversion;
432 implVendor = implvendor;
433 specTitle = spectitle;
434 specVersion = specversion;
435 specVendor = specvendor;
436 sealBase = sealbase;
437 this.loader = loader;
438 }
439
440 /*
441 * Construct a package using the attributes from the specified manifest.
442 *
443 * @param name the package name
444 * @param man the optional manifest for the package
445 * @param url the optional code source url for the package
446 */
447 private Package(String name, Manifest man, URL url, 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.getValue(Name.IMPLEMENTATION_VERSION);
483 }
484 if (implVendor == null) {
485 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
486 }
487 if (sealed == null) {
488 sealed = attr.getValue(Name.SEALED);
489 }
490 }
491 if ("true".equalsIgnoreCase(sealed)) {
492 sealBase = url;
493 }
494 pkgName = name;
495 this.specTitle = specTitle;
496 this.specVersion = specVersion;
497 this.specVendor = specVendor;
498 this.implTitle = implTitle;
499 this.implVersion = implVersion;
500 this.implVendor = implVendor;
501 this.sealBase = sealBase;
502 this.loader = loader;
503 }
504
505 /*
506 * Returns the loaded system package for the specified name.
507 */
508 static Package getSystemPackage(String name) {
509 synchronized (pkgs) {
510 Package pkg = (Package)pkgs.get(name);
511 if (pkg == null) {
512 name = name.replace('.', '/').concat("/");
513 String fn = getSystemPackage0(name);
514 if (fn != null) {
515 pkg = defineSystemPackage(name, fn);
516 }
517 }
518 return pkg;
519 }
520 }
521
522 /*
523 * Return an array of loaded system packages.
524 */
525 static Package[] getSystemPackages() {
526 // First, update the system package map with new package names
527 String[] names = getSystemPackages0();
528 synchronized (pkgs) {
529 for (int i = 0; i < names.length; i++) {
530 defineSystemPackage(names[i], getSystemPackage0(names[i]));
531 }
532 return (Package[])pkgs.values().toArray(new Package[pkgs.size()]);
533 }
534 }
535
536 private static Package defineSystemPackage(final String iname,
537 final String fn)
538 {
539 return (Package) AccessController.doPrivileged(new PrivilegedAction() {
540 public Object run() {
541 String name = iname;
542 // Get the cached code source url for the file name
543 URL url = (URL)urls.get(fn);
544 if (url == null) {
545 // URL not found, so create one
546 File file = new File(fn);
547 try {
548 url = ParseUtil.fileToEncodedURL(file);
549 } catch (MalformedURLException e) {
550 }
551 if (url != null) {
552 urls.put(fn, url);
553 // If loading a JAR file, then also cache the manifest
554 if (file.isFile()) {
555 mans.put(fn, loadManifest(fn));
556 }
557 }
558 }
559 // Convert to "."-separated package name
560 name = name.substring(0, name.length() - 1).replace('/', '.');
561 Package pkg;
562 Manifest man = (Manifest)mans.get(fn);
563 if (man != null) {
564 pkg = new Package(name, man, url, null);
565 } else {
566 pkg = new Package(name, null, null, null,
567 null, null, null, null, null);
568 }
569 pkgs.put(name, pkg);
570 return pkg;
571 }
572 });
573 }
574
575 /*
576 * Returns the Manifest for the specified JAR file name.
577 */
578 private static Manifest loadManifest(String fn) {
579 try {
580 FileInputStream fis = new FileInputStream(fn);
581 JarInputStream jis = new JarInputStream(fis, false);
582 Manifest man = jis.getManifest();
583 jis.close();
584 return man;
585 } catch (IOException e) {
586 return null;
587 }
588 }
589
590 // The map of loaded system packages
591 private static Map pkgs = new HashMap(31);
592
593 // Maps each directory or zip file name to its corresponding url
594 private static Map urls = new HashMap(10);
595
596 // Maps each code source url for a jar file to its manifest
597 private static Map mans = new HashMap(10);
598
599 private static native String getSystemPackage0(String name);
600 private static native String[] getSystemPackages0();
601
602 /*
603 * Private storage for the package name and attributes.
604 */
605 private final String pkgName;
606 private final String specTitle;
607 private final String specVersion;
608 private final String specVendor;
609 private final String implTitle;
610 private final String implVersion;
611 private final String implVendor;
612 private final URL sealBase;
613 private transient final ClassLoader loader;
614 private transient Class packageInfo;
615}