rev 10175 : 8047721: @since should have JDK version
Reviewed-by:

   1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang;
  27 
  28 import java.lang.reflect.AnnotatedElement;
  29 import java.io.InputStream;
  30 import java.util.Enumeration;
  31 
  32 import java.util.StringTokenizer;
  33 import java.io.File;
  34 import java.io.FileInputStream;
  35 import java.io.FileNotFoundException;
  36 import java.io.IOException;
  37 import java.net.URL;
  38 import java.net.MalformedURLException;
  39 import java.security.AccessController;
  40 import java.security.PrivilegedAction;
  41 
  42 import java.util.jar.JarInputStream;
  43 import java.util.jar.Manifest;
  44 import java.util.jar.Attributes;
  45 import java.util.jar.Attributes.Name;
  46 import java.util.jar.JarException;
  47 import java.util.Map;
  48 import java.util.HashMap;
  49 import java.util.Iterator;
  50 
  51 import sun.net.www.ParseUtil;
  52 import sun.reflect.CallerSensitive;
  53 import sun.reflect.Reflection;
  54 
  55 import java.lang.annotation.Annotation;
  56 
  57 /**
  58  * {@code Package} objects contain version information
  59  * about the implementation and specification of a Java package.
  60  * This versioning information is retrieved and made available
  61  * by the {@link ClassLoader} instance that
  62  * loaded the class(es).  Typically, it is stored in the manifest that is
  63  * distributed with the classes.
  64  *
  65  * <p>The set of classes that make up the package may implement a
  66  * particular specification and if so the specification title, version number,
  67  * and vendor strings identify that specification.
  68  * An application can ask if the package is
  69  * compatible with a particular version, see the {@link
  70  * #isCompatibleWith isCompatibleWith}
  71  * method for details.
  72  *
  73  * <p>Specification version numbers use a syntax that consists of nonnegative
  74  * decimal integers separated by periods ".", for example "2.0" or
  75  * "1.2.3.4.5.6.7".  This allows an extensible number to be used to represent
  76  * major, minor, micro, etc. versions.  The version specification is described
  77  * by the following formal grammar:
  78  * <blockquote>
  79  * <dl>
  80  * <dt><i>SpecificationVersion:</i>
  81  * <dd><i>Digits RefinedVersion<sub>opt</sub></i>
  82 
  83  * <dt><i>RefinedVersion:</i>
  84  * <dd>{@code .} <i>Digits</i>
  85  * <dd>{@code .} <i>Digits RefinedVersion</i>
  86  *
  87  * <dt><i>Digits:</i>
  88  * <dd><i>Digit</i>
  89  * <dd><i>Digits</i>
  90  *
  91  * <dt><i>Digit:</i>
  92  * <dd>any character for which {@link Character#isDigit} returns {@code true},
  93  * e.g. 0, 1, 2, ...
  94  * </dl>
  95  * </blockquote>
  96  *
  97  * <p>The implementation title, version, and vendor strings identify an
  98  * implementation and are made available conveniently to enable accurate
  99  * reporting of the packages involved when a problem occurs. The contents
 100  * all three implementation strings are vendor specific. The
 101  * implementation version strings have no specified syntax and should
 102  * only be compared for equality with desired version identifiers.
 103  *
 104  * <p>Within each {@code ClassLoader} instance all classes from the same
 105  * java package have the same Package object.  The static methods allow a package
 106  * to be found by name or the set of all packages known to the current class
 107  * loader to be found.
 108  *
 109  * @see ClassLoader#definePackage

 110  */
 111 public class Package implements java.lang.reflect.AnnotatedElement {
 112     /**
 113      * Return the name of this package.
 114      *
 115      * @return  The fully-qualified name of this package as defined in section 6.5.3 of
 116      *          <cite>The Java&trade; Language Specification</cite>,
 117      *          for example, {@code java.lang}
 118      */
 119     public String getName() {
 120         return pkgName;
 121     }
 122 
 123 
 124     /**
 125      * Return the title of the specification that this package implements.
 126      * @return the specification title, null is returned if it is not known.
 127      */
 128     public String getSpecificationTitle() {
 129         return specTitle;
 130     }
 131 
 132     /**
 133      * Returns the version number of the specification
 134      * that this package implements.
 135      * This version string must be a sequence of nonnegative decimal
 136      * integers separated by "."'s and may have leading zeros.
 137      * When version strings are compared the most significant
 138      * numbers are compared.
 139      * @return the specification version, null is returned if it is not known.
 140      */
 141     public String getSpecificationVersion() {
 142         return specVersion;
 143     }
 144 
 145     /**
 146      * Return the name of the organization, vendor,
 147      * or company that owns and maintains the specification
 148      * of the classes that implement this package.
 149      * @return the specification vendor, null is returned if it is not known.
 150      */
 151     public String getSpecificationVendor() {
 152         return specVendor;
 153     }
 154 
 155     /**
 156      * Return the title of this package.
 157      * @return the title of the implementation, null is returned if it is not known.
 158      */
 159     public String getImplementationTitle() {
 160         return implTitle;
 161     }
 162 
 163     /**
 164      * Return the version of this implementation. It consists of any string
 165      * assigned by the vendor of this implementation and does
 166      * not have any particular syntax specified or expected by the Java
 167      * runtime. It may be compared for equality with other
 168      * package version strings used for this implementation
 169      * by this vendor for this package.
 170      * @return the version of the implementation, null is returned if it is not known.
 171      */
 172     public String getImplementationVersion() {
 173         return implVersion;
 174     }
 175 
 176     /**
 177      * Returns the name of the organization,
 178      * vendor or company that provided this implementation.
 179      * @return the vendor that implemented this package..
 180      */
 181     public String getImplementationVendor() {
 182         return implVendor;
 183     }
 184 
 185     /**
 186      * Returns true if this package is sealed.
 187      *
 188      * @return true if the package is sealed, false otherwise
 189      */
 190     public boolean isSealed() {
 191         return sealBase != null;
 192     }
 193 
 194     /**
 195      * Returns true if this package is sealed with respect to the specified
 196      * code source url.
 197      *
 198      * @param url the code source url
 199      * @return true if this package is sealed with respect to url
 200      */
 201     public boolean isSealed(URL url) {
 202         return url.equals(sealBase);
 203     }
 204 
 205     /**
 206      * Compare this package's specification version with a
 207      * desired version. It returns true if
 208      * this packages specification version number is greater than or equal
 209      * to the desired version number. <p>
 210      *
 211      * Version numbers are compared by sequentially comparing corresponding
 212      * components of the desired and specification strings.
 213      * Each component is converted as a decimal integer and the values
 214      * compared.
 215      * If the specification value is greater than the desired
 216      * value true is returned. If the value is less false is returned.
 217      * If the values are equal the period is skipped and the next pair of
 218      * components is compared.
 219      *
 220      * @param desired the version string of the desired version.
 221      * @return true if this package's version number is greater
 222      *          than or equal to the desired version number
 223      *
 224      * @exception NumberFormatException if the desired or current version
 225      *          is not of the correct dotted form.
 226      */
 227     public boolean isCompatibleWith(String desired)
 228         throws NumberFormatException
 229     {
 230         if (specVersion == null || specVersion.length() < 1) {
 231             throw new NumberFormatException("Empty version string");
 232         }
 233 
 234         String [] sa = specVersion.split("\\.", -1);
 235         int [] si = new int[sa.length];
 236         for (int i = 0; i < sa.length; i++) {
 237             si[i] = Integer.parseInt(sa[i]);
 238             if (si[i] < 0)
 239                 throw NumberFormatException.forInputString("" + si[i]);
 240         }
 241 
 242         String [] da = desired.split("\\.", -1);
 243         int [] di = new int[da.length];
 244         for (int i = 0; i < da.length; i++) {
 245             di[i] = Integer.parseInt(da[i]);
 246             if (di[i] < 0)
 247                 throw NumberFormatException.forInputString("" + di[i]);
 248         }
 249 
 250         int len = Math.max(di.length, si.length);
 251         for (int i = 0; i < len; i++) {
 252             int d = (i < di.length ? di[i] : 0);
 253             int s = (i < si.length ? si[i] : 0);
 254             if (s < d)
 255                 return false;
 256             if (s > d)
 257                 return true;
 258         }
 259         return true;
 260     }
 261 
 262     /**
 263      * Find a package by name in the callers {@code ClassLoader} instance.
 264      * The callers {@code ClassLoader} instance is used to find the package
 265      * instance corresponding to the named class. If the callers
 266      * {@code ClassLoader} instance is null then the set of packages loaded
 267      * by the system {@code ClassLoader} instance is searched to find the
 268      * named package. <p>
 269      *
 270      * Packages have attributes for versions and specifications only if the class
 271      * loader created the package instance with the appropriate attributes. Typically,
 272      * those attributes are defined in the manifests that accompany the classes.
 273      *
 274      * @param name a package name, for example, java.lang.
 275      * @return the package of the requested name. It may be null if no package
 276      *          information is available from the archive or codebase.
 277      */
 278     @CallerSensitive
 279     public static Package getPackage(String name) {
 280         ClassLoader l = ClassLoader.getClassLoader(Reflection.getCallerClass());
 281         if (l != null) {
 282             return l.getPackage(name);
 283         } else {
 284             return getSystemPackage(name);
 285         }
 286     }
 287 
 288     /**
 289      * Get all the packages currently known for the caller's {@code ClassLoader}
 290      * instance.  Those packages correspond to classes loaded via or accessible by
 291      * name to that {@code ClassLoader} instance.  If the caller's
 292      * {@code ClassLoader} instance is the bootstrap {@code ClassLoader}
 293      * instance, which may be represented by {@code null} in some implementations,
 294      * only packages corresponding to classes loaded by the bootstrap
 295      * {@code ClassLoader} instance will be returned.
 296      *
 297      * @return a new array of packages known to the callers {@code ClassLoader}
 298      * instance.  An zero length array is returned if none are known.
 299      */
 300     @CallerSensitive
 301     public static Package[] getPackages() {
 302         ClassLoader l = ClassLoader.getClassLoader(Reflection.getCallerClass());
 303         if (l != null) {
 304             return l.getPackages();
 305         } else {
 306             return getSystemPackages();
 307         }
 308     }
 309 
 310     /**
 311      * Get the package for the specified class.
 312      * The class's class loader is used to find the package instance
 313      * corresponding to the specified class. If the class loader
 314      * is the bootstrap class loader, which may be represented by
 315      * {@code null} in some implementations, then the set of packages
 316      * loaded by the bootstrap class loader is searched to find the package.
 317      * <p>
 318      * Packages have attributes for versions and specifications only
 319      * if the class loader created the package
 320      * instance with the appropriate attributes. Typically those
 321      * attributes are defined in the manifests that accompany
 322      * the classes.
 323      *
 324      * @param c the class to get the package of.
 325      * @return the package of the class. It may be null if no package
 326      *          information is available from the archive or codebase.  */
 327     static Package getPackage(Class<?> c) {
 328         String name = c.getName();
 329         int i = name.lastIndexOf('.');
 330         if (i != -1) {
 331             name = name.substring(0, i);
 332             ClassLoader cl = c.getClassLoader();
 333             if (cl != null) {
 334                 return cl.getPackage(name);
 335             } else {
 336                 return getSystemPackage(name);
 337             }
 338         } else {
 339             return null;
 340         }
 341     }
 342 
 343     /**
 344      * Return the hash code computed from the package name.
 345      * @return the hash code computed from the package name.
 346      */
 347     public int hashCode(){
 348         return pkgName.hashCode();
 349     }
 350 
 351     /**
 352      * Returns the string representation of this Package.
 353      * Its value is the string "package " and the package name.
 354      * If the package title is defined it is appended.
 355      * If the package version is defined it is appended.
 356      * @return the string representation of the package.
 357      */
 358     public String toString() {
 359         String spec = specTitle;
 360         String ver =  specVersion;
 361         if (spec != null && spec.length() > 0)
 362             spec = ", " + spec;
 363         else
 364             spec = "";
 365         if (ver != null && ver.length() > 0)
 366             ver = ", version " + ver;
 367         else
 368             ver = "";
 369         return "package " + pkgName + spec + ver;
 370     }
 371 
 372     private Class<?> getPackageInfo() {
 373         if (packageInfo == null) {
 374             try {
 375                 packageInfo = Class.forName(pkgName + ".package-info", false, loader);
 376             } catch (ClassNotFoundException ex) {
 377                 // store a proxy for the package info that has no annotations
 378                 class PackageInfoProxy {}
 379                 packageInfo = PackageInfoProxy.class;
 380             }
 381         }
 382         return packageInfo;
 383     }
 384 
 385     /**
 386      * @throws NullPointerException {@inheritDoc}
 387      * @since 1.5
 388      */
 389     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
 390         return getPackageInfo().getAnnotation(annotationClass);
 391     }
 392 
 393     /**
 394      * {@inheritDoc}
 395      * @throws NullPointerException {@inheritDoc}
 396      * @since 1.5
 397      */
 398     @Override
 399     public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
 400         return AnnotatedElement.super.isAnnotationPresent(annotationClass);
 401     }
 402 
 403     /**
 404      * @throws NullPointerException {@inheritDoc}
 405      * @since 1.8
 406      */
 407     @Override
 408     public  <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) {
 409         return getPackageInfo().getAnnotationsByType(annotationClass);
 410     }
 411 
 412     /**
 413      * @since 1.5
 414      */
 415     public Annotation[] getAnnotations() {
 416         return getPackageInfo().getAnnotations();
 417     }
 418 
 419     /**
 420      * @throws NullPointerException {@inheritDoc}
 421      * @since 1.8
 422      */
 423     @Override
 424     public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
 425         return getPackageInfo().getDeclaredAnnotation(annotationClass);
 426     }
 427 
 428     /**
 429      * @throws NullPointerException {@inheritDoc}
 430      * @since 1.8
 431      */
 432     @Override
 433     public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) {
 434         return getPackageInfo().getDeclaredAnnotationsByType(annotationClass);
 435     }
 436 
 437     /**
 438      * @since 1.5
 439      */
 440     public Annotation[] getDeclaredAnnotations()  {
 441         return getPackageInfo().getDeclaredAnnotations();
 442     }
 443 
 444     /**
 445      * Construct a package instance with the specified version
 446      * information.
 447      * @param name the name of the package
 448      * @param spectitle the title of the specification
 449      * @param specversion the version of the specification
 450      * @param specvendor the organization that maintains the specification
 451      * @param impltitle the title of the implementation
 452      * @param implversion the version of the implementation
 453      * @param implvendor the organization that maintains the implementation
 454      */
 455     Package(String name,
 456             String spectitle, String specversion, String specvendor,
 457             String impltitle, String implversion, String implvendor,
 458             URL sealbase, ClassLoader loader)
 459     {
 460         pkgName = name;
 461         implTitle = impltitle;
 462         implVersion = implversion;
 463         implVendor = implvendor;
 464         specTitle = spectitle;
 465         specVersion = specversion;
 466         specVendor = specvendor;
 467         sealBase = sealbase;
 468         this.loader = loader;
 469     }
 470 
 471     /*
 472      * Construct a package using the attributes from the specified manifest.
 473      *
 474      * @param name the package name
 475      * @param man the optional manifest for the package
 476      * @param url the optional code source url for the package
 477      */
 478     private Package(String name, Manifest man, URL url, ClassLoader loader) {
 479         String path = name.replace('.', '/').concat("/");
 480         String sealed = null;
 481         String specTitle= null;
 482         String specVersion= null;
 483         String specVendor= null;
 484         String implTitle= null;
 485         String implVersion= null;
 486         String implVendor= null;
 487         URL sealBase= null;
 488         Attributes attr = man.getAttributes(path);
 489         if (attr != null) {
 490             specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
 491             specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
 492             specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
 493             implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
 494             implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
 495             implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
 496             sealed      = attr.getValue(Name.SEALED);
 497         }
 498         attr = man.getMainAttributes();
 499         if (attr != null) {
 500             if (specTitle == null) {
 501                 specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
 502             }
 503             if (specVersion == null) {
 504                 specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
 505             }
 506             if (specVendor == null) {
 507                 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
 508             }
 509             if (implTitle == null) {
 510                 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
 511             }
 512             if (implVersion == null) {
 513                 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
 514             }
 515             if (implVendor == null) {
 516                 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
 517             }
 518             if (sealed == null) {
 519                 sealed = attr.getValue(Name.SEALED);
 520             }
 521         }
 522         if ("true".equalsIgnoreCase(sealed)) {
 523             sealBase = url;
 524         }
 525         pkgName = name;
 526         this.specTitle = specTitle;
 527         this.specVersion = specVersion;
 528         this.specVendor = specVendor;
 529         this.implTitle = implTitle;
 530         this.implVersion = implVersion;
 531         this.implVendor = implVendor;
 532         this.sealBase = sealBase;
 533         this.loader = loader;
 534     }
 535 
 536     /*
 537      * Returns the loaded system package for the specified name.
 538      */
 539     static Package getSystemPackage(String name) {
 540         synchronized (pkgs) {
 541             Package pkg = pkgs.get(name);
 542             if (pkg == null) {
 543                 name = name.replace('.', '/').concat("/");
 544                 String fn = getSystemPackage0(name);
 545                 if (fn != null) {
 546                     pkg = defineSystemPackage(name, fn);
 547                 }
 548             }
 549             return pkg;
 550         }
 551     }
 552 
 553     /*
 554      * Return an array of loaded system packages.
 555      */
 556     static Package[] getSystemPackages() {
 557         // First, update the system package map with new package names
 558         String[] names = getSystemPackages0();
 559         synchronized (pkgs) {
 560             for (String name : names) {
 561                 defineSystemPackage(name, getSystemPackage0(name));
 562             }
 563             return pkgs.values().toArray(new Package[pkgs.size()]);
 564         }
 565     }
 566 
 567     private static Package defineSystemPackage(final String iname,
 568                                                final String fn)
 569     {
 570         return AccessController.doPrivileged(new PrivilegedAction<Package>() {
 571             public Package run() {
 572                 String name = iname;
 573                 // Get the cached code source url for the file name
 574                 URL url = urls.get(fn);
 575                 if (url == null) {
 576                     // URL not found, so create one
 577                     File file = new File(fn);
 578                     try {
 579                         url = ParseUtil.fileToEncodedURL(file);
 580                     } catch (MalformedURLException e) {
 581                     }
 582                     if (url != null) {
 583                         urls.put(fn, url);
 584                         // If loading a JAR file, then also cache the manifest
 585                         if (file.isFile()) {
 586                             mans.put(fn, loadManifest(fn));
 587                         }
 588                     }
 589                 }
 590                 // Convert to "."-separated package name
 591                 name = name.substring(0, name.length() - 1).replace('/', '.');
 592                 Package pkg;
 593                 Manifest man = mans.get(fn);
 594                 if (man != null) {
 595                     pkg = new Package(name, man, url, null);
 596                 } else {
 597                     pkg = new Package(name, null, null, null,
 598                                       null, null, null, null, null);
 599                 }
 600                 pkgs.put(name, pkg);
 601                 return pkg;
 602             }
 603         });
 604     }
 605 
 606     /*
 607      * Returns the Manifest for the specified JAR file name.
 608      */
 609     private static Manifest loadManifest(String fn) {
 610         try (FileInputStream fis = new FileInputStream(fn);
 611              JarInputStream jis = new JarInputStream(fis, false))
 612         {
 613             return jis.getManifest();
 614         } catch (IOException e) {
 615             return null;
 616         }
 617     }
 618 
 619     // The map of loaded system packages
 620     private static Map<String, Package> pkgs = new HashMap<>(31);
 621 
 622     // Maps each directory or zip file name to its corresponding url
 623     private static Map<String, URL> urls = new HashMap<>(10);
 624 
 625     // Maps each code source url for a jar file to its manifest
 626     private static Map<String, Manifest> mans = new HashMap<>(10);
 627 
 628     private static native String getSystemPackage0(String name);
 629     private static native String[] getSystemPackages0();
 630 
 631     /*
 632      * Private storage for the package name and attributes.
 633      */
 634     private final String pkgName;
 635     private final String specTitle;
 636     private final String specVersion;
 637     private final String specVendor;
 638     private final String implTitle;
 639     private final String implVersion;
 640     private final String implVendor;
 641     private final URL sealBase;
 642     private transient final ClassLoader loader;
 643     private transient Class<?> packageInfo;
 644 }
--- EOF ---