1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/*
18 * Copyright (C) 2008 The Android Open Source Project
19 *
20 * Licensed under the Apache License, Version 2.0 (the "License");
21 * you may not use this file except in compliance with the License.
22 * You may obtain a copy of the License at
23 *
24 *      http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS,
28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 * See the License for the specific language governing permissions and
30 * limitations under the License.
31 */
32
33package java.lang;
34
35import dalvik.system.VMStack;
36import java.lang.annotation.Annotation;
37import java.lang.reflect.AnnotatedElement;
38import java.net.URL;
39
40/**
41 * Contains information about a Java package. This includes implementation and
42 * specification versions. Typically this information is retrieved from the
43 * manifest.
44 * <p>
45 * Packages are managed by class loaders. All classes loaded by the same loader
46 * from the same package share a {@code Package} instance.
47 * </p>
48 *
49 * @see java.lang.ClassLoader
50 */
51public class Package implements AnnotatedElement {
52    private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
53
54    private final String name;
55    private final String specTitle;
56    private final String specVersion;
57    private final String specVendor;
58    private final String implTitle;
59    private final String implVersion;
60    private final String implVendor;
61    private final URL sealBase;
62
63    Package(String name, String specTitle, String specVersion, String specVendor,
64            String implTitle, String implVersion, String implVendor, URL sealBase) {
65        this.name = name;
66        this.specTitle = specTitle;
67        this.specVersion = specVersion;
68        this.specVendor = specVendor;
69        this.implTitle = implTitle;
70        this.implVersion = implVersion;
71        this.implVendor = implVendor;
72        this.sealBase = sealBase;
73    }
74
75    /**
76     * Gets the annotation associated with the specified annotation type and
77     * this package, if present.
78     *
79     * @param annotationType
80     *            the annotation type to look for.
81     * @return an instance of {@link Annotation} or {@code null}.
82     * @see java.lang.reflect.AnnotatedElement#getAnnotation(java.lang.Class)
83     */
84    @SuppressWarnings("unchecked")
85    public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
86        for (Annotation annotation : getAnnotations()) {
87            if (annotationType.isInstance(annotation)) {
88                return (A) annotation;
89            }
90        }
91        return null;
92    }
93
94    /**
95     * Returns an empty array. Package annotations are not supported on Android.
96     */
97    public Annotation[] getAnnotations() {
98        return NO_ANNOTATIONS;
99    }
100
101    /**
102     * Returns an empty array. Package annotations are not supported on Android.
103     */
104    public Annotation[] getDeclaredAnnotations() {
105        return NO_ANNOTATIONS;
106    }
107
108    /**
109     * Indicates whether the specified annotation is present.
110     *
111     * @param annotationType
112     *            the annotation type to look for.
113     * @return {@code true} if the annotation is present; {@code false}
114     *         otherwise.
115     * @see java.lang.reflect.AnnotatedElement#isAnnotationPresent(java.lang.Class)
116     */
117    public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
118        return getAnnotation(annotationType) != null;
119    }
120
121    /**
122     * Returns the title of the implementation of this package, or {@code null}
123     * if this is unknown. The format of this string is unspecified.
124     *
125     * @return the implementation title, may be {@code null}.
126     */
127    public String getImplementationTitle() {
128        return implTitle;
129    }
130
131    /**
132     * Returns the name of the vendor or organization that provides this
133     * implementation of the package, or {@code null} if this is unknown. The
134     * format of this string is unspecified.
135     *
136     * @return the implementation vendor name, may be {@code null}.
137     */
138    public String getImplementationVendor() {
139        return implVendor;
140    }
141
142    /**
143     * Returns the version of the implementation of this package, or {@code
144     * null} if this is unknown. The format of this string is unspecified.
145     *
146     * @return the implementation version, may be {@code null}.
147     */
148    public String getImplementationVersion() {
149        return implVersion;
150    }
151
152    /**
153     * Returns the name of this package in the standard dot notation; for
154     * example: "java.lang".
155     *
156     * @return the name of this package.
157     */
158    public String getName() {
159        return name;
160    }
161
162    /**
163     * Attempts to locate the requested package in the caller's class loader. If
164     * no package information can be located, {@code null} is returned.
165     *
166     * @param packageName
167     *            the name of the package to find.
168     * @return the requested package, or {@code null}.
169     * @see ClassLoader#getPackage(java.lang.String)
170     */
171    public static Package getPackage(String packageName) {
172        ClassLoader classloader = VMStack.getCallingClassLoader();
173        if (classloader == null) {
174            classloader = ClassLoader.getSystemClassLoader();
175        }
176        return classloader.getPackage(packageName);
177    }
178
179    /**
180     * Returns all the packages known to the caller's class loader.
181     *
182     * @return all the packages known to the caller's class loader.
183     * @see ClassLoader#getPackages
184     */
185    public static Package[] getPackages() {
186        ClassLoader classloader = VMStack.getCallingClassLoader();
187        if (classloader == null) {
188            classloader = ClassLoader.getSystemClassLoader();
189        }
190        return classloader.getPackages();
191    }
192
193    /**
194     * Returns the title of the specification this package implements, or
195     * {@code null} if this is unknown.
196     *
197     * @return the specification title, may be {@code null}.
198     */
199    public String getSpecificationTitle() {
200        return specTitle;
201    }
202
203    /**
204     * Returns the name of the vendor or organization that owns and maintains
205     * the specification this package implements, or {@code null} if this is
206     * unknown.
207     *
208     * @return the specification vendor name, may be {@code null}.
209     */
210    public String getSpecificationVendor() {
211        return specVendor;
212    }
213
214    /**
215     * Returns the version of the specification this package implements, or
216     * {@code null} if this is unknown. The version string is a sequence of
217     * non-negative integers separated by dots; for example: "1.2.3".
218     *
219     * @return the specification version string, may be {@code null}.
220     */
221    public String getSpecificationVersion() {
222        return specVersion;
223    }
224
225    @Override
226    public int hashCode() {
227        return name.hashCode();
228    }
229
230    /**
231     * Indicates whether this package's specification version is compatible with
232     * the specified version string. Version strings are compared by comparing
233     * each dot separated part of the version as an integer.
234     *
235     * @param version
236     *            the version string to compare against.
237     * @return {@code true} if the package versions are compatible; {@code
238     *         false} otherwise.
239     * @throws NumberFormatException
240     *             if this package's version string or the one provided are not
241     *             in the correct format.
242     */
243    public boolean isCompatibleWith(String version) throws NumberFormatException {
244        String[] requested = version.split("\\.");
245        String[] provided = specVersion.split("\\.");
246
247        for (int i = 0; i < Math.min(requested.length, provided.length); i++) {
248            int reqNum = Integer.parseInt(requested[i]);
249            int provNum = Integer.parseInt(provided[i]);
250
251            if (reqNum > provNum) {
252                return false;
253            } else if (reqNum < provNum) {
254                return true;
255            }
256        }
257
258        if (requested.length > provided.length) {
259            return false;
260        }
261
262        return true;
263    }
264
265    /**
266     * Indicates whether this package is sealed.
267     *
268     * @return {@code true} if this package is sealed; {@code false} otherwise.
269     */
270    public boolean isSealed() {
271        return sealBase != null;
272    }
273
274    /**
275     * Indicates whether this package is sealed with respect to the specified
276     * URL.
277     *
278     * @param url
279     *            the URL to check.
280     * @return {@code true} if this package is sealed with {@code url}; {@code
281     *         false} otherwise
282     */
283    public boolean isSealed(URL url) {
284        return sealBase != null && sealBase.sameFile(url);
285    }
286
287    @Override
288    public String toString() {
289        return "package " + name;
290    }
291}
292