View Javadoc

1   /*
2    * This file is part of Domingo
3    * an Open Source Java-API to Lotus Notes/Domino
4    * hosted at http://domingo.sourceforge.net
5    *
6    * Copyright (c) 2003-2007 Beck et al. projects GmbH Munich, Germany (http://www.bea.de)
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2.1 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21   */
22  
23  /***
24   * Origin:
25   * From <http://www.adtmag.com/java/article.aspx?id=4276>
26   * Original license- public domain? code published in article
27   */
28  
29  package de.bea.domingo.map;
30  
31  import java.lang.reflect.Method;
32  import java.lang.reflect.Modifier;
33  import java.util.HashMap;
34  import java.util.HashSet;
35  import java.util.Map;
36  import java.util.Set;
37  
38  /***
39   * Utility methods for querying Class objects.
40   */
41  final class ClassUtilities {
42  
43      /*** Mapping from primitive wrapper Classes to their corresponding primitive Classes. */
44      private static final Map OBJECT_PRIMITIVE_MAP = new HashMap(13);
45  
46      static {
47          OBJECT_PRIMITIVE_MAP.put(Boolean.class, Boolean.TYPE);
48          OBJECT_PRIMITIVE_MAP.put(Byte.class, Byte.TYPE);
49          OBJECT_PRIMITIVE_MAP.put(Character.class, Character.TYPE);
50          OBJECT_PRIMITIVE_MAP.put(Double.class, Double.TYPE);
51          OBJECT_PRIMITIVE_MAP.put(Float.class, Float.TYPE);
52          OBJECT_PRIMITIVE_MAP.put(Integer.class, Integer.TYPE);
53          OBJECT_PRIMITIVE_MAP.put(Long.class, Long.TYPE);
54          OBJECT_PRIMITIVE_MAP.put(Short.class, Short.TYPE);
55      }
56  
57      /*** Mapping from primitive names to primitive Classes. */
58      private static final Map PRIMITIVE_NAME_MAP = new HashMap(13);
59  
60      static {
61          PRIMITIVE_NAME_MAP.put("boolean", Boolean.TYPE);
62          PRIMITIVE_NAME_MAP.put("byte", Byte.TYPE);
63          PRIMITIVE_NAME_MAP.put("char", Character.TYPE);
64          PRIMITIVE_NAME_MAP.put("double", Double.TYPE);
65          PRIMITIVE_NAME_MAP.put("float", Float.TYPE);
66          PRIMITIVE_NAME_MAP.put("int", Integer.TYPE);
67          PRIMITIVE_NAME_MAP.put("long", Long.TYPE);
68          PRIMITIVE_NAME_MAP.put("short", Short.TYPE);
69          PRIMITIVE_NAME_MAP.put("null", Void.TYPE);
70          PRIMITIVE_NAME_MAP.put("void", Void.TYPE);
71          PRIMITIVE_NAME_MAP.put("", Void.TYPE);
72      }
73  
74      /***
75       * Mapping from primitive wrapper Classes to the sets of primitive classes
76       * whose instances can be assigned an instance of the first.
77       */
78      private static final Map PRIMITIVE_WIDENINGS_MAP = new HashMap(11);
79  
80      static {
81          Set set = new HashSet();
82          set.add(Short.TYPE);
83          set.add(Integer.TYPE);
84          set.add(Long.TYPE);
85          set.add(Float.TYPE);
86          set.add(Double.TYPE);
87          PRIMITIVE_WIDENINGS_MAP.put(Byte.TYPE, set);
88  
89          set = new HashSet();
90          set.add(Integer.TYPE);
91          set.add(Long.TYPE);
92          set.add(Float.TYPE);
93          set.add(Double.TYPE);
94          PRIMITIVE_WIDENINGS_MAP.put(Short.TYPE, set);
95          PRIMITIVE_WIDENINGS_MAP.put(Character.TYPE, set);
96  
97          set = new HashSet();
98          set.add(Long.TYPE);
99          set.add(Float.TYPE);
100         set.add(Double.TYPE);
101         PRIMITIVE_WIDENINGS_MAP.put(Integer.TYPE, set);
102 
103         set = new HashSet();
104         set.add(Float.TYPE);
105         set.add(Double.TYPE);
106         PRIMITIVE_WIDENINGS_MAP.put(Long.TYPE, set);
107 
108         set = new HashSet();
109         set.add(Double.TYPE);
110         PRIMITIVE_WIDENINGS_MAP.put(Float.TYPE, set);
111     }
112 
113     /***
114      * Do not instantiate. Static methods only.
115      */
116     private ClassUtilities() {
117     }
118 
119     /***
120      * @param name FQN of a class, or the name of a primitive type
121      * @param loader a ClassLoader
122      * @return the Class for the name given. Primitive types are converted to
123      *         their particular Class object. null, the empty string, "null",
124      *         and "void" yield Void.TYPE. If any classes require loading
125      *         because of this operation, the loading is done by the given class
126      *         loader. Such classes are not initialized, however.
127      * @exception ClassNotFoundException if name names an unknown class or
128      *                primitive
129      */
130     public static Class classForNameOrPrimitive(final String name, final ClassLoader loader) throws ClassNotFoundException {
131         Class clazz = (Class) PRIMITIVE_NAME_MAP.get(name == null ? "" : name);
132         return clazz != null ? clazz : Class.forName(name, false, loader);
133     }
134 
135     /***
136      * @param aClass a Class
137      * @return true if the class is accessible, false otherwise. Presently
138      *         returns true if the class is declared public.
139      */
140     public static boolean classIsAccessible(final Class aClass) {
141         return Modifier.isPublic(aClass.getModifiers());
142     }
143 
144     /***
145      * Tells whether instances of the classes in the 'rhs' array could be used
146      * as parameters to a reflective method invocation whose parameter list has
147      * types denoted by the 'lhs' array.
148      *
149      * @param lhs Class array representing the types of the formal parameters of
150      *            a method
151      * @param rhs Class array representing the types of the actual parameters of
152      *            a method. A null value or Void.TYPE is considered to match a
153      *            corresponding Object or array class in lhs, but not a
154      *            primitive.
155      * @return true if compatible, false otherwise
156      */
157     public static boolean compatibleClasses(final Class[] lhs, final Class[] rhs) {
158         if (lhs.length != rhs.length) {
159             return false;
160         }
161         for (int i = 0; i < lhs.length; ++i) {
162             if (rhs[i] == null || rhs[i].equals(Void.TYPE)) {
163                 if (lhs[i].isPrimitive()) {
164                     return false;
165                 } else {
166                     continue;
167                 }
168             }
169             if (!lhs[i].isAssignableFrom(rhs[i])) {
170                 Class lhsPrimEquiv = primitiveEquivalentOf(lhs[i]);
171                 Class rhsPrimEquiv = primitiveEquivalentOf(rhs[i]);
172                 if (!primitiveIsAssignableFrom(lhsPrimEquiv, rhsPrimEquiv)) {
173                     return false;
174                 }
175             }
176         }
177         return true;
178     }
179 
180     /***
181      * @param aClass a Class
182      * @param methodName name of a method
183      * @param parameterTypes Class array representing the types of a method's formal
184      *            parameters
185      * @return the Method with the given name and formal parameter types that is
186      *         in the nearest accessible class in the class hierarchy, starting
187      *         with aClass's superclass. The superclass and implemented
188      *         interfaces of aClass are searched, then their superclasses, etc.
189      *         until a method is found. Returns null if there is no such method.
190      */
191     public static Method getAccessibleMethodFrom(final Class aClass, final String methodName, final Class[] parameterTypes) {
192         // Look for overridden method in the superclass.
193         Class superclass = aClass.getSuperclass();
194         Method overriddenMethod = null;
195         if (superclass != null && classIsAccessible(superclass)) {
196             overriddenMethod = getMethod(methodName, parameterTypes, superclass);
197             if (overriddenMethod != null) {
198                 return overriddenMethod;
199             }
200         }
201         // If here, then aClass represents Object, or an interface, or
202         // the superclass did not have an override. Check implemented interfaces.
203         Class[] interfaces = aClass.getInterfaces();
204         for (int i = 0; i < interfaces.length; ++i) {
205             overriddenMethod = null;
206             if (classIsAccessible(interfaces[i])) {
207                 overriddenMethod = getMethod(methodName, parameterTypes, interfaces[i]);
208                 if (overriddenMethod != null) {
209                     return overriddenMethod;
210                 }
211             }
212         }
213         overriddenMethod = null;
214         // Try superclass's superclass and implemented interfaces.
215         if (superclass != null) {
216             overriddenMethod = getAccessibleMethodFrom(superclass, methodName, parameterTypes);
217             if (overriddenMethod != null) {
218                 return overriddenMethod;
219             }
220         }
221         // Try implemented interfaces' extended interfaces...
222         for (int i = 0; i < interfaces.length; ++i) {
223             overriddenMethod = getAccessibleMethodFrom(interfaces[i], methodName, parameterTypes);
224             if (overriddenMethod != null) {
225                 return overriddenMethod;
226             }
227         }
228         // Give up.
229         return null;
230     }
231 
232     private static Method getMethod(final String methodName, final Class[] parameterTypes, final Class superclass) {
233         try {
234             return superclass.getMethod(methodName, parameterTypes);
235         } catch (NoSuchMethodException e) {
236         }
237         return null;
238     }
239 
240     /***
241      * @param aClass a Class
242      * @return the class's primitive equivalent, if aClass is a primitive
243      *         wrapper. If aClass is primitive, returns aClass. Otherwise,
244      *         returns null.
245      */
246     public static Class primitiveEquivalentOf(final Class aClass) {
247         return aClass.isPrimitive() ? aClass : (Class) OBJECT_PRIMITIVE_MAP.get(aClass);
248     }
249 
250     /***
251      * Tells whether an instance of the primitive class represented by 'rhs' can
252      * be assigned to an instance of the primitive class represented by 'lhs'.
253      *
254      * @param lhs assignee class
255      * @param rhs assigned class
256      * @return true if compatible, false otherwise. If either argument is
257      *         <code>null</code>, or one of the parameters does not represent
258      *         a primitive (e.g. Byte.TYPE), returns false.
259      */
260     public static boolean primitiveIsAssignableFrom(final Class lhs, final Class rhs) {
261         if (lhs == null || rhs == null) {
262             return false;
263         }
264         if (!(lhs.isPrimitive() && rhs.isPrimitive())) {
265             return false;
266         }
267         if (lhs.equals(rhs)) {
268             return true;
269         }
270         Set wideningSet = (Set) PRIMITIVE_WIDENINGS_MAP.get(rhs);
271         if (wideningSet == null) {
272             return false;
273         }
274         return wideningSet.contains(lhs);
275     }
276 }