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      */
getName()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      */
getSpecificationTitle()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      */
getSpecificationVersion()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      */
getSpecificationVendor()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      */
getImplementationTitle()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      */
getImplementationVersion()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      */
getImplementationVendor()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      */
isSealed()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      */
isSealed(URL url)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      */
isCompatibleWith(String desired)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
getPackage(String name)281     public static Package getPackage(String name) {
282         ClassLoader l = VMStack.getCallingClassLoader();
283         if (l != null) {
284             return l.getPackage(name);
285         } else {
286             return getSystemPackage(name);
287         }
288     }
289 
290     /**
291      * Get all the packages currently known for the caller's {@code ClassLoader}
292      * instance.  Those packages correspond to classes loaded via or accessible by
293      * name to that {@code ClassLoader} instance.  If the caller's
294      * {@code ClassLoader} instance is the bootstrap {@code ClassLoader}
295      * instance, which may be represented by {@code null} in some implementations,
296      * only packages corresponding to classes loaded by the bootstrap
297      * {@code ClassLoader} instance will be returned.
298      *
299      * @return a new array of packages known to the callers {@code ClassLoader}
300      * instance.  An zero length array is returned if none are known.
301      */
302     @CallerSensitive
getPackages()303     public static Package[] getPackages() {
304         ClassLoader l = VMStack.getCallingClassLoader();
305         if (l != null) {
306             return l.getPackages();
307         } else {
308             return getSystemPackages();
309         }
310     }
311 
312     /**
313      * Get the package for the specified class.
314      * The class's class loader is used to find the package instance
315      * corresponding to the specified class. If the class loader
316      * is the bootstrap class loader, which may be represented by
317      * {@code null} in some implementations, then the set of packages
318      * loaded by the bootstrap class loader is searched to find the package.
319      * <p>
320      * Packages have attributes for versions and specifications only
321      * if the class loader created the package
322      * instance with the appropriate attributes. Typically those
323      * attributes are defined in the manifests that accompany
324      * the classes.
325      *
326      * @param c the class to get the package of.
327      * @return the package of the class. It may be null if no package
328      *          information is available from the archive or codebase.  */
getPackage(Class<?> c)329     static Package getPackage(Class<?> c) {
330         String name = c.getName();
331         int i = name.lastIndexOf('.');
332         if (i != -1) {
333             name = name.substring(0, i);
334             ClassLoader cl = c.getClassLoader();
335             if (cl != null) {
336                 return cl.getPackage(name);
337             } else {
338                 return getSystemPackage(name);
339             }
340         } else {
341             return null;
342         }
343     }
344 
345     /**
346      * Return the hash code computed from the package name.
347      * @return the hash code computed from the package name.
348      */
hashCode()349     public int hashCode(){
350         return pkgName.hashCode();
351     }
352 
353     /**
354      * Returns the string representation of this Package.
355      * Its value is the string "package " and the package name.
356      * If the package title is defined it is appended.
357      * If the package version is defined it is appended.
358      * @return the string representation of the package.
359      */
toString()360     public String toString() {
361         // Android-changed start
362         // Several apps try to parse the output of toString(). This is a really
363         // bad idea - especially when there's a Package.getName() function as well as a
364         // Class.getName() function that can be used instead.
365         // Starting from the API level 25 the proper output is generated.
366         final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
367         if (targetSdkVersion > 0 && targetSdkVersion <= 24) {
368             return "package " + pkgName;
369         }
370         // Android-changed end
371 
372         String spec = specTitle;
373         String ver =  specVersion;
374         if (spec != null && spec.length() > 0)
375             spec = ", " + spec;
376         else
377             spec = "";
378         if (ver != null && ver.length() > 0)
379             ver = ", version " + ver;
380         else
381             ver = "";
382         return "package " + pkgName + spec + ver;
383     }
384 
getPackageInfo()385     private Class<?> getPackageInfo() {
386         if (packageInfo == null) {
387             try {
388                 packageInfo = Class.forName(pkgName + ".package-info", false, loader);
389             } catch (ClassNotFoundException ex) {
390                 // store a proxy for the package info that has no annotations
391                 class PackageInfoProxy {}
392                 packageInfo = PackageInfoProxy.class;
393             }
394         }
395         return packageInfo;
396     }
397 
398     /**
399      * @throws NullPointerException {@inheritDoc}
400      * @since 1.5
401      */
getAnnotation(Class<A> annotationClass)402     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
403         return getPackageInfo().getAnnotation(annotationClass);
404     }
405 
406     /**
407      * {@inheritDoc}
408      * @throws NullPointerException {@inheritDoc}
409      * @since 1.5
410      */
411     @Override
isAnnotationPresent(Class<? extends Annotation> annotationClass)412     public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
413         return AnnotatedElement.super.isAnnotationPresent(annotationClass);
414     }
415 
416     /**
417      * @throws NullPointerException {@inheritDoc}
418      * @since 1.8
419      */
420     @Override
getAnnotationsByType(Class<A> annotationClass)421     public  <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) {
422         return getPackageInfo().getAnnotationsByType(annotationClass);
423     }
424 
425     /**
426      * @since 1.5
427      */
getAnnotations()428     public Annotation[] getAnnotations() {
429         return getPackageInfo().getAnnotations();
430     }
431 
432     /**
433      * @throws NullPointerException {@inheritDoc}
434      * @since 1.8
435      */
436     @Override
getDeclaredAnnotation(Class<A> annotationClass)437     public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
438         return getPackageInfo().getDeclaredAnnotation(annotationClass);
439     }
440 
441     /**
442      * @throws NullPointerException {@inheritDoc}
443      * @since 1.8
444      */
445     @Override
getDeclaredAnnotationsByType(Class<A> annotationClass)446     public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) {
447         return getPackageInfo().getDeclaredAnnotationsByType(annotationClass);
448     }
449 
450     /**
451      * @since 1.5
452      */
getDeclaredAnnotations()453     public Annotation[] getDeclaredAnnotations()  {
454         return getPackageInfo().getDeclaredAnnotations();
455     }
456 
457     /**
458      * Construct a package instance with the specified version
459      * information.
460      * @param name the name of the package
461      * @param spectitle the title of the specification
462      * @param specversion the version of the specification
463      * @param specvendor the organization that maintains the specification
464      * @param impltitle the title of the implementation
465      * @param implversion the version of the implementation
466      * @param implvendor the organization that maintains the implementation
467      */
Package(String name, String spectitle, String specversion, String specvendor, String impltitle, String implversion, String implvendor, URL sealbase, ClassLoader loader)468     Package(String name,
469             String spectitle, String specversion, String specvendor,
470             String impltitle, String implversion, String implvendor,
471             URL sealbase, ClassLoader loader)
472     {
473         pkgName = name;
474         implTitle = impltitle;
475         implVersion = implversion;
476         implVendor = implvendor;
477         specTitle = spectitle;
478         specVersion = specversion;
479         specVendor = specvendor;
480         sealBase = sealbase;
481         this.loader = loader;
482     }
483 
484     /*
485      * Construct a package using the attributes from the specified manifest.
486      *
487      * @param name the package name
488      * @param man the optional manifest for the package
489      * @param url the optional code source url for the package
490      */
Package(String name, Manifest man, URL url, ClassLoader loader)491     private Package(String name, Manifest man, URL url, ClassLoader loader) {
492         String path = name.replace('.', '/').concat("/");
493         String sealed = null;
494         String specTitle= null;
495         String specVersion= null;
496         String specVendor= null;
497         String implTitle= null;
498         String implVersion= null;
499         String implVendor= null;
500         URL sealBase= null;
501         Attributes attr = man.getAttributes(path);
502         if (attr != null) {
503             specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
504             specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
505             specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
506             implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
507             implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
508             implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
509             sealed      = attr.getValue(Name.SEALED);
510         }
511         attr = man.getMainAttributes();
512         if (attr != null) {
513             if (specTitle == null) {
514                 specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
515             }
516             if (specVersion == null) {
517                 specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
518             }
519             if (specVendor == null) {
520                 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
521             }
522             if (implTitle == null) {
523                 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
524             }
525             if (implVersion == null) {
526                 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
527             }
528             if (implVendor == null) {
529                 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
530             }
531             if (sealed == null) {
532                 sealed = attr.getValue(Name.SEALED);
533             }
534         }
535         if ("true".equalsIgnoreCase(sealed)) {
536             sealBase = url;
537         }
538         pkgName = name;
539         this.specTitle = specTitle;
540         this.specVersion = specVersion;
541         this.specVendor = specVendor;
542         this.implTitle = implTitle;
543         this.implVersion = implVersion;
544         this.implVendor = implVendor;
545         this.sealBase = sealBase;
546         this.loader = loader;
547     }
548 
549     /*
550      * Returns the loaded system package for the specified name.
551      */
getSystemPackage(String name)552     static Package getSystemPackage(String name) {
553         synchronized (pkgs) {
554             Package pkg = pkgs.get(name);
555             if (pkg == null) {
556                 name = name.replace('.', '/').concat("/");
557                 String fn = getSystemPackage0(name);
558                 if (fn != null) {
559                     pkg = defineSystemPackage(name, fn);
560                 }
561             }
562             return pkg;
563         }
564     }
565 
566     /*
567      * Return an array of loaded system packages.
568      */
getSystemPackages()569     static Package[] getSystemPackages() {
570         // First, update the system package map with new package names
571         String[] names = getSystemPackages0();
572         synchronized (pkgs) {
573             for (int i = 0; i < names.length; i++) {
574                 defineSystemPackage(names[i], getSystemPackage0(names[i]));
575             }
576             return pkgs.values().toArray(new Package[pkgs.size()]);
577         }
578     }
579 
defineSystemPackage(final String iname, final String fn)580     private static Package defineSystemPackage(final String iname,
581                                                final String fn)
582     {
583         return AccessController.doPrivileged(new PrivilegedAction<Package>() {
584             public Package run() {
585                 String name = iname;
586                 // Get the cached code source url for the file name
587                 URL url = urls.get(fn);
588                 if (url == null) {
589                     // URL not found, so create one
590                     File file = new File(fn);
591                     try {
592                         url = ParseUtil.fileToEncodedURL(file);
593                     } catch (MalformedURLException e) {
594                     }
595                     if (url != null) {
596                         urls.put(fn, url);
597                         // If loading a JAR file, then also cache the manifest
598                         if (file.isFile()) {
599                             mans.put(fn, loadManifest(fn));
600                         }
601                     }
602                 }
603                 // Convert to "."-separated package name
604                 name = name.substring(0, name.length() - 1).replace('/', '.');
605                 Package pkg;
606                 Manifest man = mans.get(fn);
607                 if (man != null) {
608                     pkg = new Package(name, man, url, null);
609                 } else {
610                     pkg = new Package(name, null, null, null,
611                                       null, null, null, null, null);
612                 }
613                 pkgs.put(name, pkg);
614                 return pkg;
615             }
616         });
617     }
618 
619     /*
620      * Returns the Manifest for the specified JAR file name.
621      */
622     private static Manifest loadManifest(String fn) {
623         try (FileInputStream fis = new FileInputStream(fn);
624              JarInputStream jis = new JarInputStream(fis, false))
625         {
626             return jis.getManifest();
627         } catch (IOException e) {
628             return null;
629         }
630     }
631 
632     // The map of loaded system packages
633     private static Map<String, Package> pkgs = new HashMap<>(31);
634 
635     // Maps each directory or zip file name to its corresponding url
636     private static Map<String, URL> urls = new HashMap<>(10);
637 
638     // Maps each code source url for a jar file to its manifest
639     private static Map<String, Manifest> mans = new HashMap<>(10);
640 
641     private static native String getSystemPackage0(String name);
642     private static native String[] getSystemPackages0();
643 
644     /*
645      * Private storage for the package name and attributes.
646      */
647     private final String pkgName;
648     private final String specTitle;
649     private final String specVersion;
650     private final String specVendor;
651     private final String implTitle;
652     private final String implVersion;
653     private final String implVendor;
654     private final URL sealBase;
655     private transient final ClassLoader loader;
656     private transient Class<?> packageInfo;
657 }
658