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     * Returns 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 array of this package's annotations.
96     */
97    public Annotation[] getAnnotations() {
98        try {
99            Class<?> c = Class.forName(getName() + ".package-info");
100            return c.getAnnotations();
101        } catch (Exception ex) {
102            return NO_ANNOTATIONS;
103        }
104    }
105
106    /**
107     * Returns an array of this package's declared annotations. Package annotations aren't
108     * inherited, so this is equivalent to {@link #getAnnotations}.
109     */
110    public Annotation[] getDeclaredAnnotations() {
111        return getAnnotations();
112    }
113
114    /**
115     * Indicates whether the specified annotation is present.
116     *
117     * @param annotationType
118     *            the annotation type to look for.
119     * @return {@code true} if the annotation is present; {@code false}
120     *         otherwise.
121     * @see java.lang.reflect.AnnotatedElement#isAnnotationPresent(java.lang.Class)
122     */
123    public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
124        return getAnnotation(annotationType) != null;
125    }
126
127    /**
128     * Returns the title of the implementation of this package, or {@code null}
129     * if this is unknown. The format of this string is unspecified.
130     *
131     * @return the implementation title, may be {@code null}.
132     */
133    public String getImplementationTitle() {
134        return implTitle;
135    }
136
137    /**
138     * Returns the name of the vendor or organization that provides this
139     * implementation of the package, or {@code null} if this is unknown. The
140     * format of this string is unspecified.
141     *
142     * @return the implementation vendor name, may be {@code null}.
143     */
144    public String getImplementationVendor() {
145        return implVendor;
146    }
147
148    /**
149     * Returns the version of the implementation of this package, or {@code
150     * null} if this is unknown. The format of this string is unspecified.
151     *
152     * @return the implementation version, may be {@code null}.
153     */
154    public String getImplementationVersion() {
155        return implVersion;
156    }
157
158    /**
159     * Returns the name of this package in the standard dot notation; for
160     * example: "java.lang".
161     *
162     * @return the name of this package.
163     */
164    public String getName() {
165        return name;
166    }
167
168    /**
169     * Attempts to locate the requested package in the caller's class loader. If
170     * no package information can be located, {@code null} is returned.
171     *
172     * @param packageName
173     *            the name of the package to find.
174     * @return the requested package, or {@code null}.
175     * @see ClassLoader#getPackage(java.lang.String)
176     */
177    public static Package getPackage(String packageName) {
178        ClassLoader classloader = VMStack.getCallingClassLoader();
179        if (classloader == null) {
180            classloader = ClassLoader.getSystemClassLoader();
181        }
182        return classloader.getPackage(packageName);
183    }
184
185    /**
186     * Returns all the packages known to the caller's class loader.
187     *
188     * @return all the packages known to the caller's class loader.
189     * @see ClassLoader#getPackages
190     */
191    public static Package[] getPackages() {
192        ClassLoader classloader = VMStack.getCallingClassLoader();
193        if (classloader == null) {
194            classloader = ClassLoader.getSystemClassLoader();
195        }
196        return classloader.getPackages();
197    }
198
199    /**
200     * Returns the title of the specification this package implements, or
201     * {@code null} if this is unknown.
202     *
203     * @return the specification title, may be {@code null}.
204     */
205    public String getSpecificationTitle() {
206        return specTitle;
207    }
208
209    /**
210     * Returns the name of the vendor or organization that owns and maintains
211     * the specification this package implements, or {@code null} if this is
212     * unknown.
213     *
214     * @return the specification vendor name, may be {@code null}.
215     */
216    public String getSpecificationVendor() {
217        return specVendor;
218    }
219
220    /**
221     * Returns the version of the specification this package implements, or
222     * {@code null} if this is unknown. The version string is a sequence of
223     * non-negative integers separated by dots; for example: "1.2.3".
224     *
225     * @return the specification version string, may be {@code null}.
226     */
227    public String getSpecificationVersion() {
228        return specVersion;
229    }
230
231    @Override
232    public int hashCode() {
233        return name.hashCode();
234    }
235
236    /**
237     * Indicates whether this package's specification version is compatible with
238     * the specified version string. Version strings are compared by comparing
239     * each dot separated part of the version as an integer.
240     *
241     * @param version
242     *            the version string to compare against.
243     * @return {@code true} if the package versions are compatible; {@code
244     *         false} otherwise.
245     * @throws NumberFormatException
246     *             if this package's version string or the one provided are not
247     *             in the correct format.
248     */
249    public boolean isCompatibleWith(String version) throws NumberFormatException {
250        String[] requested = version.split("\\.");
251        String[] provided = specVersion.split("\\.");
252
253        for (int i = 0; i < Math.min(requested.length, provided.length); i++) {
254            int reqNum = Integer.parseInt(requested[i]);
255            int provNum = Integer.parseInt(provided[i]);
256
257            if (reqNum > provNum) {
258                return false;
259            } else if (reqNum < provNum) {
260                return true;
261            }
262        }
263
264        if (requested.length > provided.length) {
265            return false;
266        }
267
268        return true;
269    }
270
271    /**
272     * Indicates whether this package is sealed.
273     *
274     * @return {@code true} if this package is sealed; {@code false} otherwise.
275     */
276    public boolean isSealed() {
277        return sealBase != null;
278    }
279
280    /**
281     * Indicates whether this package is sealed with respect to the specified
282     * URL.
283     *
284     * @param url
285     *            the URL to check.
286     * @return {@code true} if this package is sealed with {@code url}; {@code
287     *         false} otherwise
288     */
289    public boolean isSealed(URL url) {
290        return sealBase != null && sealBase.sameFile(url);
291    }
292
293    @Override
294    public String toString() {
295        return "package " + name;
296    }
297}
298