1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
202
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
215 if (superclass != null) {
216 overriddenMethod = getAccessibleMethodFrom(superclass, methodName, parameterTypes);
217 if (overriddenMethod != null) {
218 return overriddenMethod;
219 }
220 }
221
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
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 }