1 /*
   2  * Copyright 1997-2008 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 
  26 package java.lang;
  27 
  28 import java.io.InputStream;
  29 import java.util.Enumeration;
  30 
  31 import java.util.StringTokenizer;
  32 import java.io.File;
  33 import java.io.FileInputStream;
  34 import java.io.FileNotFoundException;
  35 import java.io.IOException;
  36 import java.net.URL;
  37 import java.net.MalformedURLException;
  38 import java.security.AccessController;
  39 import java.security.PrivilegedAction;
  40 
  41 import java.util.jar.JarInputStream;
  42 import java.util.jar.Manifest;
  43 import java.util.jar.Attributes;
  44 import java.util.jar.Attributes.Name;
  45 import java.util.jar.JarException;
  46 import java.util.Map;
  47 import java.util.HashMap;
  48 import java.util.Iterator;
  49 
  50 import sun.net.www.ParseUtil;
  51 
  52 import 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  */
 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      *          &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 = 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 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 AccessController.doPrivileged(new PrivilegedAction<Package>() {
 540             public Package run() {
 541                 String name = iname;
 542                 // Get the cached code source url for the file name
 543                 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 = 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<String, Package> pkgs
 592         = new HashMap<String, Package>(31);
 593 
 594     // Maps each directory or zip file name to its corresponding url
 595     private static Map<String, URL> urls
 596         = new HashMap<String, URL>(10);
 597 
 598     // Maps each code source url for a jar file to its manifest
 599     private static Map<String, Manifest> mans
 600         = new HashMap<String, Manifest>(10);
 601 
 602     private static native String getSystemPackage0(String name);
 603     private static native String[] getSystemPackages0();
 604 
 605     /*
 606      * Private storage for the package name and attributes.
 607      */
 608     private final String pkgName;
 609     private final String specTitle;
 610     private final String specVersion;
 611     private final String specVendor;
 612     private final String implTitle;
 613     private final String implVersion;
 614     private final String implVendor;
 615     private final URL sealBase;
 616     private transient final ClassLoader loader;
 617     private transient Class packageInfo;
 618 }