001    /* Package.java -- information about a package
002       Copyright (C) 2000, 2001, 2002, 2003, 2005, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    package java.lang;
040    
041    import gnu.classpath.VMStackWalker;
042    
043    import java.lang.annotation.Annotation;
044    import java.lang.reflect.AnnotatedElement;
045    import java.net.URL;
046    import java.util.NoSuchElementException;
047    import java.util.StringTokenizer;
048    
049    
050    /**
051     * Everything you ever wanted to know about a package. This class makes it
052     * possible to attach specification and implementation information to a
053     * package as explained in the
054     * <a href="http://java.sun.com/products/jdk/1.3/docs/guide/versioning/spec/VersioningSpecification.html#PackageVersionSpecification">Package Versioning Specification</a>
055     * section of the
056     * <a href="http://java.sun.com/products/jdk/1.3/docs/guide/versioning/spec/VersioningSpecification.html">Product Versioning Specification</a>.
057     * It also allows packages to be sealed with respect to the originating URL.
058     *
059     * <p>The most useful method is the <code>isCompatibleWith()</code> method that
060     * compares a desired version of a specification with the version of the
061     * specification as implemented by a package. A package is considered
062     * compatible with another version if the version of the specification is
063     * equal or higher then the requested version. Version numbers are represented
064     * as strings of positive numbers separated by dots (e.g. "1.2.0").
065     * The first number is called the major number, the second the minor,
066     * the third the micro, etc. A version is considered higher then another
067     * version if it has a bigger major number then the another version or when
068     * the major numbers of the versions are equal if it has a bigger minor number
069     * then the other version, etc. (If a version has no minor, micro, etc numbers
070     * then they are considered the be 0.)
071     *
072     * @author Mark Wielaard (mark@klomp.org)
073     * @see ClassLoader#definePackage(String, String, String, String, String,
074     *      String, String, URL)
075     * @since 1.2
076     * @status updated to 1.5
077     */
078    public class Package
079      implements AnnotatedElement
080    {
081      /** The name of the Package */
082      private final String name;
083    
084      /** The name if the implementation */
085      private final String implTitle;
086    
087      /** The vendor that wrote this implementation */
088      private final String implVendor;
089    
090      /** The version of this implementation */
091      private final String implVersion;
092    
093      /** The name of the specification */
094      private final String specTitle;
095    
096      /** The name of the specification designer */
097      private final String specVendor;
098    
099      /** The version of this specification */
100      private final String specVersion;
101    
102      /** If sealed the origin of the package classes, otherwise null */
103      private final URL sealed;
104    
105      /** The class loader that defined this package */
106      private ClassLoader loader;
107    
108      /** @deprecated Please use the other constructor that takes the class loader
109       *              that defines the Package.
110       */
111      Package(String name,
112              String specTitle, String specVendor, String specVersion,
113              String implTitle, String implVendor, String implVersion, URL sealed)
114      {
115        this(name, specTitle, specVendor, specVersion, implTitle, implVendor,
116             implVersion, sealed, null);
117      }
118    
119      /**
120       * A package local constructor for the Package class. All parameters except
121       * the <code>name</code> of the package may be <code>null</code>.
122       * There are no public constructors defined for Package; this is a package
123       * local constructor that is used by java.lang.Classloader.definePackage().
124       *
125       * @param name The name of the Package
126       * @param specTitle The name of the specification
127       * @param specVendor The name of the specification designer
128       * @param specVersion The version of this specification
129       * @param implTitle The name of the implementation
130       * @param implVendor The vendor that wrote this implementation
131       * @param implVersion The version of this implementation
132       * @param sealed If sealed the origin of the package classes
133       */
134      Package(String name,
135              String specTitle, String specVendor, String specVersion,
136              String implTitle, String implVendor, String implVersion, URL sealed,
137              ClassLoader loader)
138      {
139        if (name == null)
140          throw new IllegalArgumentException("null Package name");
141    
142        this.name = name;
143        this.implTitle = implTitle;
144        this.implVendor = implVendor;
145        this.implVersion = implVersion;
146        this.specTitle = specTitle;
147        this.specVendor = specVendor;
148        this.specVersion = specVersion;
149        this.sealed = sealed;
150        this.loader = loader;
151      }
152    
153      /**
154       * Returns the Package name in dot-notation.
155       *
156       * @return the non-null package name
157       */
158      public String getName()
159      {
160        return name;
161      }
162    
163      /**
164       * Returns the name of the specification, or null if unknown.
165       *
166       * @return the specification title
167       */
168      public String getSpecificationTitle()
169      {
170        return specTitle;
171      }
172    
173      /**
174       * Returns the version of the specification, or null if unknown.
175       *
176       * @return the specification version
177       */
178      public String getSpecificationVersion()
179      {
180        return specVersion;
181      }
182    
183      /**
184       * Returns the name of the specification designer, or null if unknown.
185       *
186       * @return the specification vendor
187       */
188      public String getSpecificationVendor()
189      {
190        return specVendor;
191      }
192    
193      /**
194       * Returns the name of the implementation, or null if unknown.
195       *
196       * @return the implementation title
197       */
198      public String getImplementationTitle()
199      {
200        return implTitle;
201      }
202    
203      /**
204       * Returns the version of this implementation, or null if unknown.
205       *
206       * @return the implementation version
207       */
208      public String getImplementationVersion()
209      {
210        return implVersion;
211      }
212    
213      /**
214       * Returns the vendor that wrote this implementation, or null if unknown.
215       *
216       * @return the implementation vendor
217       */
218      public String getImplementationVendor()
219      {
220        return implVendor;
221      }
222    
223      /**
224       * Returns true if this Package is sealed.
225       *
226       * @return true if the package is sealed
227       */
228      public boolean isSealed()
229      {
230        return sealed != null;
231      }
232    
233      /**
234       * Returns true if this Package is sealed and the origin of the classes is
235       * the given URL.
236       *
237       * @param url the URL to test
238       * @return true if the package is sealed by this URL
239       * @throws NullPointerException if url is null
240       */
241      public boolean isSealed(URL url)
242      {
243        return url.equals(sealed);
244      }
245    
246      /**
247       * Checks if the version of the specification is higher or at least as high
248       * as the desired version. Comparison is done by sequentially comparing
249       * dotted decimal numbers from the parameter and from
250       * <code>getSpecificationVersion</code>.
251       *
252       * @param version the (minimal) desired version of the specification
253       *
254       * @return true if the version is compatible, false otherwise
255       *
256       * @throws NumberFormatException if either version string is invalid
257       * @throws NullPointerException if either version string is null
258       */
259      public boolean isCompatibleWith(String version)
260      {
261        StringTokenizer versionTokens = new StringTokenizer(version, ".");
262        StringTokenizer specTokens = new StringTokenizer(specVersion, ".");
263        try
264          {
265            while (versionTokens.hasMoreElements())
266              {
267                int vers = Integer.parseInt(versionTokens.nextToken());
268                int spec = Integer.parseInt(specTokens.nextToken());
269                if (spec < vers)
270                  return false;
271                else if (spec > vers)
272                  return true;
273                // They must be equal, next Token please!
274              }
275          }
276        catch (NoSuchElementException e)
277          {
278            // This must have been thrown by spec.nextToken() so return false.
279            return false;
280          }
281        // They must have been exactly the same version.
282        // Or the specVersion has more subversions. That is also good.
283        return true;
284      }
285    
286      /**
287       * Returns the named package if it is known by the callers class loader.
288       * It may return null if the package is unknown, when there is no
289       * information on that particular package available or when the callers
290       * classloader is null.
291       *
292       * @param name the name of the desired package
293       * @return the package by that name in the current ClassLoader
294       */
295      public static Package getPackage(String name)
296      {
297        // Get the caller's classloader
298        ClassLoader cl = VMStackWalker.getCallingClassLoader();
299        return cl != null ? cl.getPackage(name) : VMClassLoader.getPackage(name);
300      }
301    
302      /**
303       * Returns all the packages that are known to the callers class loader.
304       * It may return an empty array if the classloader of the caller is null.
305       *
306       * @return an array of all known packages
307       */
308      public static Package[] getPackages()
309      {
310        // Get the caller's classloader
311        ClassLoader cl = VMStackWalker.getCallingClassLoader();
312        return cl != null ? cl.getPackages() : VMClassLoader.getPackages();
313      }
314    
315      /**
316       * Returns the hashCode of the name of this package.
317       *
318       * @return the hash code
319       */
320      public int hashCode()
321      {
322        return name.hashCode();
323      }
324    
325      /**
326       * Returns a string representation of this package. It is specified to
327       * be <code>"package " + getName() + (getSpecificationTitle() == null
328       * ? "" : ", " + getSpecificationTitle()) + (getSpecificationVersion()
329       * == null ? "" : ", version " + getSpecificationVersion())</code>.
330       *
331       * @return the string representation of the package
332       */
333      public String toString()
334      {
335        return ("package " + name + (specTitle == null ? "" : ", " + specTitle)
336                + (specVersion == null ? "" : ", version " + specVersion));
337      }
338    
339      /**
340       * Returns this package's annotation for the specified annotation type,
341       * or <code>null</code> if no such annotation exists.
342       *
343       * @param annotationClass the type of annotation to look for.
344       * @return this package's annotation for the specified type, or
345       *         <code>null</code> if no such annotation exists.
346       * @since 1.5
347       */
348      public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
349      {
350        A foundAnnotation = null;
351        Annotation[] annotations = getAnnotations();
352        for (Annotation annotation : annotations)
353          if (annotation.annotationType() == annotationClass)
354            foundAnnotation = (A) annotation;
355        return foundAnnotation;
356      }
357    
358      /**
359       * Returns all annotations associated with this package.  If there are
360       * no annotations associated with this package, then a zero-length array
361       * will be returned.  The returned array may be modified by the client
362       * code, but this will have no effect on the annotation content of this
363       * package, and hence no effect on the return value of this method for
364       * future callers.
365       *
366       * @return this package' annotations.
367       * @since 1.5
368       */
369      public Annotation[] getAnnotations()
370      {
371        /** All a package's annotations are declared within it. */
372        return getDeclaredAnnotations();
373      }
374    
375      /**
376       * Returns all annotations directly defined by this package.  If there are
377       * no annotations associated with this package, then a zero-length array
378       * will be returned.  The returned array may be modified by the client
379       * code, but this will have no effect on the annotation content of this
380       * package, and hence no effect on the return value of this method for
381       * future callers.
382       *
383       * @return the annotations directly defined by this package.
384       * @since 1.5
385       */
386      public Annotation[] getDeclaredAnnotations()
387      {
388        try
389          {
390            Class pkgInfo = Class.forName(name + ".package-info", false, loader);
391            return pkgInfo.getDeclaredAnnotations();
392          }
393        catch (ClassNotFoundException _)
394          {
395            return new Annotation[0];
396          }
397      }
398    
399      /**
400       * Returns true if an annotation for the specified type is associated
401       * with this package.  This is primarily a short-hand for using marker
402       * annotations.
403       *
404       * @param annotationClass the type of annotation to look for.
405       * @return true if an annotation exists for the specified type.
406       * @since 1.5
407       */
408      public boolean isAnnotationPresent(Class<? extends Annotation>
409                                         annotationClass)
410      {
411        return getAnnotation(annotationClass) != null;
412      }
413    
414    } // class Package