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