61 if (wrapper == Long.class) return Long.TYPE; |
45 if (wrapper == Long.class) return Long.TYPE; |
62 if (wrapper == Float.class) return Float.TYPE; |
46 if (wrapper == Float.class) return Float.TYPE; |
63 if (wrapper == Double.class) return Double.TYPE; |
47 if (wrapper == Double.class) return Double.TYPE; |
64 if (wrapper == Void.class) return Void.TYPE; |
48 if (wrapper == Void.class) return Void.TYPE; |
65 return null; |
49 return null; |
66 } |
|
67 |
|
68 /** |
|
69 * Tests each element on the class arrays for assignability. |
|
70 * |
|
71 * @param argClasses arguments to be tested |
|
72 * @param argTypes arguments from Method |
|
73 * @return true if each class in argTypes is assignable from the |
|
74 * corresponding class in argClasses. |
|
75 */ |
|
76 private static boolean matchArguments(Class[] argClasses, Class[] argTypes) { |
|
77 return matchArguments(argClasses, argTypes, false); |
|
78 } |
|
79 |
|
80 /** |
|
81 * Tests each element on the class arrays for equality. |
|
82 * |
|
83 * @param argClasses arguments to be tested |
|
84 * @param argTypes arguments from Method |
|
85 * @return true if each class in argTypes is equal to the |
|
86 * corresponding class in argClasses. |
|
87 */ |
|
88 private static boolean matchExplicitArguments(Class[] argClasses, Class[] argTypes) { |
|
89 return matchArguments(argClasses, argTypes, true); |
|
90 } |
|
91 |
|
92 private static boolean matchArguments(Class[] argClasses, |
|
93 Class[] argTypes, boolean explicit) { |
|
94 |
|
95 boolean match = (argClasses.length == argTypes.length); |
|
96 for(int j = 0; j < argClasses.length && match; j++) { |
|
97 Class argType = argTypes[j]; |
|
98 if (argType.isPrimitive()) { |
|
99 argType = PrimitiveWrapperMap.getType(argType.getName()); |
|
100 } |
|
101 if (explicit) { |
|
102 // Test each element for equality |
|
103 if (argClasses[j] != argType) { |
|
104 match = false; |
|
105 } |
|
106 } else { |
|
107 // Consider null an instance of all classes. |
|
108 if (argClasses[j] != null && |
|
109 !(argType.isAssignableFrom(argClasses[j]))) { |
|
110 match = false; |
|
111 } |
|
112 } |
|
113 } |
|
114 return match; |
|
115 } |
|
116 |
|
117 /** |
|
118 * @return the method which best matches the signature or throw an exception |
|
119 * if it can't be found or the method is ambiguous. |
|
120 */ |
|
121 static Method getPublicMethod(Class declaringClass, String methodName, |
|
122 Class[] argClasses) throws NoSuchMethodException { |
|
123 Method m; |
|
124 |
|
125 m = findPublicMethod(declaringClass, methodName, argClasses); |
|
126 if (m == null) |
|
127 throw new NoSuchMethodException(declaringClass.getName() + "." + methodName); |
|
128 return m; |
|
129 } |
|
130 |
|
131 /** |
|
132 * @return the method which best matches the signature or null if it cant be found or |
|
133 * the method is ambiguous. |
|
134 */ |
|
135 public static Method findPublicMethod(Class declaringClass, String methodName, |
|
136 Class[] argClasses) { |
|
137 // Many methods are "getters" which take no arguments. |
|
138 // This permits the following optimisation which |
|
139 // avoids the expensive call to getMethods(). |
|
140 if (argClasses.length == 0) { |
|
141 try { |
|
142 return MethodUtil.getMethod(declaringClass, methodName, argClasses); |
|
143 } |
|
144 catch (NoSuchMethodException e) { |
|
145 return null; |
|
146 } catch (SecurityException se) { |
|
147 // fall through |
|
148 } |
|
149 } |
|
150 Method[] methods = MethodUtil.getPublicMethods(declaringClass); |
|
151 List list = new ArrayList(); |
|
152 for(int i = 0; i < methods.length; i++) { |
|
153 // Collect all the methods which match the signature. |
|
154 Method method = methods[i]; |
|
155 if (method.getName().equals(methodName)) { |
|
156 if (matchArguments(argClasses, method.getParameterTypes())) { |
|
157 list.add(method); |
|
158 } |
|
159 } |
|
160 } |
|
161 if (list.size() > 0) { |
|
162 if (list.size() == 1) { |
|
163 return (Method)list.get(0); |
|
164 } |
|
165 else { |
|
166 ListIterator iterator = list.listIterator(); |
|
167 Method method; |
|
168 while (iterator.hasNext()) { |
|
169 method = (Method)iterator.next(); |
|
170 if (matchExplicitArguments(argClasses, method.getParameterTypes())) { |
|
171 return method; |
|
172 } |
|
173 } |
|
174 // There are more than one method which matches this signature. |
|
175 // try to return the most specific method. |
|
176 return getMostSpecificMethod(list, argClasses); |
|
177 } |
|
178 } |
|
179 return null; |
|
180 } |
|
181 |
|
182 /** |
|
183 * Return the most specific method from the list of methods which |
|
184 * matches the args. The most specific method will have the most |
|
185 * number of equal parameters or will be closest in the inheritance |
|
186 * heirarchy to the runtime execution arguments. |
|
187 * <p> |
|
188 * See the JLS section 15.12 |
|
189 * http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#20448 |
|
190 * |
|
191 * @param methods List of methods which already have the same param length |
|
192 * and arg types are assignable to param types |
|
193 * @param args an array of param types to match |
|
194 * @return method or null if a specific method cannot be determined |
|
195 */ |
|
196 private static Method getMostSpecificMethod(List methods, Class[] args) { |
|
197 Method method = null; |
|
198 |
|
199 int matches = 0; |
|
200 int lastMatch = matches; |
|
201 |
|
202 ListIterator iterator = methods.listIterator(); |
|
203 while (iterator.hasNext()) { |
|
204 Method m = (Method)iterator.next(); |
|
205 Class[] mArgs = m.getParameterTypes(); |
|
206 matches = 0; |
|
207 for (int i = 0; i < args.length; i++) { |
|
208 Class mArg = mArgs[i]; |
|
209 if (mArg.isPrimitive()) { |
|
210 mArg = PrimitiveWrapperMap.getType(mArg.getName()); |
|
211 } |
|
212 if (args[i] == mArg) { |
|
213 matches++; |
|
214 } |
|
215 } |
|
216 if (matches == 0 && lastMatch == 0) { |
|
217 if (method == null) { |
|
218 method = m; |
|
219 } else { |
|
220 // Test existing method. We already know that the args can |
|
221 // be assigned to all the method params. However, if the |
|
222 // current method parameters is higher in the inheritance |
|
223 // hierarchy then replace it. |
|
224 if (!matchArguments(method.getParameterTypes(), |
|
225 m.getParameterTypes())) { |
|
226 method = m; |
|
227 } |
|
228 } |
|
229 } else if (matches > lastMatch) { |
|
230 lastMatch = matches; |
|
231 method = m; |
|
232 } else if (matches == lastMatch) { |
|
233 // ambiguous method selection. |
|
234 method = null; |
|
235 } |
|
236 } |
|
237 return method; |
|
238 } |
|
239 |
|
240 /** |
|
241 * @return the method or null if it can't be found or is ambiguous. |
|
242 */ |
|
243 public static Method findMethod(Class targetClass, String methodName, |
|
244 Class[] argClasses) { |
|
245 Method m = findPublicMethod(targetClass, methodName, argClasses); |
|
246 if (m != null && Modifier.isPublic(m.getDeclaringClass().getModifiers())) { |
|
247 return m; |
|
248 } |
|
249 |
|
250 /* |
|
251 Search the interfaces for a public version of this method. |
|
252 |
|
253 Example: the getKeymap() method of a JTextField |
|
254 returns a package private implementation of the |
|
255 of the public Keymap interface. In the Keymap |
|
256 interface there are a number of "properties" one |
|
257 being the "resolveParent" property implied by the |
|
258 getResolveParent() method. This getResolveParent() |
|
259 cannot be called reflectively because the class |
|
260 itself is not public. Instead we search the class's |
|
261 interfaces and find the getResolveParent() |
|
262 method of the Keymap interface - on which invoke |
|
263 may be applied without error. |
|
264 |
|
265 So in :- |
|
266 |
|
267 JTextField o = new JTextField("Hello, world"); |
|
268 Keymap km = o.getKeymap(); |
|
269 Method m1 = km.getClass().getMethod("getResolveParent", new Class[0]); |
|
270 Method m2 = Keymap.class.getMethod("getResolveParent", new Class[0]); |
|
271 |
|
272 Methods m1 and m2 are different. The invocation of method |
|
273 m1 unconditionally throws an IllegalAccessException where |
|
274 the invocation of m2 will invoke the implementation of the |
|
275 method. Note that (ignoring the overloading of arguments) |
|
276 there is only one implementation of the named method which |
|
277 may be applied to this target. |
|
278 */ |
|
279 for(Class type = targetClass; type != null; type = type.getSuperclass()) { |
|
280 Class[] interfaces = type.getInterfaces(); |
|
281 for(int i = 0; i < interfaces.length; i++) { |
|
282 m = findPublicMethod(interfaces[i], methodName, argClasses); |
|
283 if (m != null) { |
|
284 return m; |
|
285 } |
|
286 } |
|
287 } |
|
288 return null; |
|
289 } |
|
290 |
|
291 /** |
|
292 * A class that represents the unique elements of a method that will be a |
|
293 * key in the method cache. |
|
294 */ |
|
295 private static class Signature { |
|
296 private Class targetClass; |
|
297 private String methodName; |
|
298 private Class[] argClasses; |
|
299 |
|
300 private volatile int hashCode = 0; |
|
301 |
|
302 public Signature(Class targetClass, String methodName, Class[] argClasses) { |
|
303 this.targetClass = targetClass; |
|
304 this.methodName = methodName; |
|
305 this.argClasses = argClasses; |
|
306 } |
|
307 |
|
308 public boolean equals(Object o2) { |
|
309 if (this == o2) { |
|
310 return true; |
|
311 } |
|
312 Signature that = (Signature)o2; |
|
313 if (!(targetClass == that.targetClass)) { |
|
314 return false; |
|
315 } |
|
316 if (!(methodName.equals(that.methodName))) { |
|
317 return false; |
|
318 } |
|
319 if (argClasses.length != that.argClasses.length) { |
|
320 return false; |
|
321 } |
|
322 for (int i = 0; i < argClasses.length; i++) { |
|
323 if (!(argClasses[i] == that.argClasses[i])) { |
|
324 return false; |
|
325 } |
|
326 } |
|
327 return true; |
|
328 } |
|
329 |
|
330 /** |
|
331 * Hash code computed using algorithm suggested in |
|
332 * Effective Java, Item 8. |
|
333 */ |
|
334 public int hashCode() { |
|
335 if (hashCode == 0) { |
|
336 int result = 17; |
|
337 result = 37 * result + targetClass.hashCode(); |
|
338 result = 37 * result + methodName.hashCode(); |
|
339 if (argClasses != null) { |
|
340 for (int i = 0; i < argClasses.length; i++) { |
|
341 result = 37 * result + ((argClasses[i] == null) ? 0 : |
|
342 argClasses[i].hashCode()); |
|
343 } |
|
344 } |
|
345 hashCode = result; |
|
346 } |
|
347 return hashCode; |
|
348 } |
|
349 } |
|
350 |
|
351 /** |
|
352 * A wrapper to findMethod(), which will search or populate the method |
|
353 * in a cache. |
|
354 * @throws exception if the method is ambiguios. |
|
355 */ |
|
356 public static synchronized Method getMethod(Class targetClass, |
|
357 String methodName, |
|
358 Class[] argClasses) { |
|
359 Object signature = new Signature(targetClass, methodName, argClasses); |
|
360 |
|
361 Method method = null; |
|
362 Map methodCache = null; |
|
363 boolean cache = false; |
|
364 if (ReflectUtil.isPackageAccessible(targetClass)) { |
|
365 cache = true; |
|
366 } |
|
367 |
|
368 if (cache && methodCacheRef != null && |
|
369 (methodCache = (Map)methodCacheRef.get()) != null) { |
|
370 method = (Method)methodCache.get(signature); |
|
371 if (method != null) { |
|
372 return method; |
|
373 } |
|
374 } |
|
375 method = findMethod(targetClass, methodName, argClasses); |
|
376 if (cache && method != null) { |
|
377 if (methodCache == null) { |
|
378 methodCache = new HashMap(); |
|
379 methodCacheRef = new SoftReference(methodCache); |
|
380 } |
|
381 methodCache.put(signature, method); |
|
382 } |
|
383 return method; |
|
384 } |
|
385 |
|
386 /** |
|
387 * Return a constructor on the class with the arguments. |
|
388 * |
|
389 * @throws exception if the method is ambiguios. |
|
390 */ |
|
391 public static Constructor getConstructor(Class cls, Class[] args) { |
|
392 Constructor constructor = null; |
|
393 |
|
394 // PENDING: Implement the resolutuion of ambiguities properly. |
|
395 Constructor[] ctors = ConstructorUtil.getConstructors(cls); |
|
396 for(int i = 0; i < ctors.length; i++) { |
|
397 if (matchArguments(args, ctors[i].getParameterTypes())) { |
|
398 constructor = ctors[i]; |
|
399 } |
|
400 } |
|
401 return constructor; |
|
402 } |
|
403 |
|
404 public static Object getPrivateField(Object instance, Class cls, String name) { |
|
405 return getPrivateField(instance, cls, name, null); |
|
406 } |
50 } |
407 |
51 |
408 /** |
52 /** |
409 * Returns the value of a private field. |
53 * Returns the value of a private field. |
410 * |
54 * |