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