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 }
|