`MethodUtils.java`
`0001 /* 0002 * Licensed to the Apache Software Foundation (ASF) under one or more 0003 * contributor license agreements. See the NOTICE file distributed with 0004 * this work for additional information regarding copyright ownership. 0005 * The ASF licenses this file to You under the Apache License, Version 2.0 0006 * (the "License"); you may not use this file except in compliance with 0007 * the License. You may obtain a copy of the License at 0008 * 0009 * http://www.apache.org/licenses/LICENSE-2.0 0010 * 0011 * Unless required by applicable law or agreed to in writing, software 0012 * distributed under the License is distributed on an "AS IS" BASIS, 0013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0014 * See the License for the specific language governing permissions and 0015 * limitations under the License. 0016 */ 0017 0018 package griffon.util; 0019 0020 0021 import java.lang.ref.Reference; 0022 import java.lang.ref.WeakReference; 0023 import java.lang.reflect.InvocationTargetException; 0024 import java.lang.reflect.Method; 0025 import java.lang.reflect.Modifier; 0026 import java.util.Collections; 0027 import java.util.Map; 0028 import java.util.WeakHashMap; 0029 0030 0031 /** 0032 * <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p> 0033 * <p/> 0034 * <p><b>Copied from commons-beanutils, removed dependencies to commons-logging</b></p> 0035 * <p/> 0036 * <h3>Known Limitations</h3> 0037 * <h4>Accessing Public Methods In A Default Access Superclass</h4> 0038 * <p>There is an issue when invoking public methods contained in a default access superclass. 0039 * Reflection locates these methods fine and correctly assigns them as public. 0040 * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p> 0041 * <p/> 0042 * <p><code>MethodUtils</code> contains a workaround for this situation. 0043 * It will attempt to call <code>setAccessible</code> on this method. 0044 * If this call succeeds, then the method can be invoked as normal. 0045 * This call will only succeed when the application has sufficient security privilages. 0046 * 0047 * @author Craig R. McClanahan 0048 * @author Ralph Schaer 0049 * @author Chris Audley 0050 * @author Rey Fran&#231;ois 0051 * @author Gregor Ra&#253;man 0052 * @author Jan Sorensen 0053 * @author Robert Burrell Donkin 0054 */ 0055 0056 public class MethodUtils { 0057 0058 // --------------------------------------------------------- Private Methods 0059 0060 /** 0061 * Indicates whether methods should be cached for improved performance. 0062 * <p/> 0063 * Note that when this class is deployed via a shared classloader in 0064 * a container, this will affect all webapps. However making this 0065 * configurable per webapp would mean having a map keyed by context classloader 0066 * which may introduce memory-leak problems. 0067 */ 0068 private static boolean CACHE_METHODS = true; 0069 0070 /** 0071 * An empty class array 0072 */ 0073 private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0]; 0074 /** 0075 * An empty object array 0076 */ 0077 private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; 0078 0079 /** 0080 * Stores a cache of MethodDescriptor -> Method in a WeakHashMap. 0081 * <p/> 0082 * The keys into this map only ever exist as temporary variables within 0083 * methods of this class, and are never exposed to users of this class. 0084 * This means that the WeakHashMap is used only as a mechanism for 0085 * limiting the size of the cache, ie a way to tell the garbage collector 0086 * that the contents of the cache can be completely garbage-collected 0087 * whenever it needs the memory. Whether this is a good approach to 0088 * this problem is doubtful; something like the commons-collections 0089 * LRUMap may be more appropriate (though of course selecting an 0090 * appropriate size is an issue). 0091 * <p/> 0092 * This static variable is safe even when this code is deployed via a 0093 * shared classloader because it is keyed via a MethodDescriptor object 0094 * which has a Class as one of its members and that member is used in 0095 * the MethodDescriptor.equals method. So two components that load the same 0096 * class via different classloaders will generate non-equal MethodDescriptor 0097 * objects and hence end up with different entries in the map. 0098 */ 0099 private static final Map cache = Collections.synchronizedMap(new WeakHashMap()); 0100 0101 // --------------------------------------------------------- Public Methods 0102 0103 /** 0104 * Set whether methods should be cached for greater performance or not, 0105 * default is <code>true</code>. 0106 * 0107 * @param cacheMethods <code>true</code> if methods should be 0108 * cached for greater performance, otherwise <code>false</code> 0109 * @since 1.8.0 0110 */ 0111 public static synchronized void setCacheMethods(boolean cacheMethods) { 0112 CACHE_METHODS = cacheMethods; 0113 if (!CACHE_METHODS) { 0114 clearCache(); 0115 } 0116 } 0117 0118 /** 0119 * Clear the method cache. 0120 * 0121 * @return the number of cached methods cleared 0122 * @since 1.8.0 0123 */ 0124 public static synchronized int clearCache() { 0125 int size = cache.size(); 0126 cache.clear(); 0127 return size; 0128 } 0129 0130 /** 0131 * <p>Invoke a named method whose parameter type matches the object type.</p> 0132 * <p/> 0133 * <p>The behaviour of this method is less deterministic 0134 * than <code>invokeExactMethod()</code>. 0135 * It loops through all methods with names that match 0136 * and then executes the first it finds with compatable parameters.</p> 0137 * <p/> 0138 * <p>This method supports calls to methods taking primitive parameters 0139 * via passing in wrapping classes. So, for example, a <code>Boolean</code> class 0140 * would match a <code>boolean</code> primitive.</p> 0141 * <p/> 0142 * <p> This is a convenient wrapper for 0143 * {@link #invokeMethod(Object object, String methodName, Object [] args)}. 0144 * </p> 0145 * 0146 * @param object invoke method on this object 0147 * @param methodName get method with this name 0148 * @param arg use this argument 0149 * @return The value returned by the invoked method 0150 * @throws NoSuchMethodException if there is no such accessible method 0151 * @throws InvocationTargetException wraps an exception thrown by the 0152 * method invoked 0153 * @throws IllegalAccessException if the requested method is not accessible 0154 * via reflection 0155 */ 0156 public static Object invokeMethod( 0157 Object object, 0158 String methodName, 0159 Object arg) 0160 throws 0161 NoSuchMethodException, 0162 IllegalAccessException, 0163 InvocationTargetException { 0164 0165 Object[] args = {arg}; 0166 return invokeMethod(object, methodName, args); 0167 0168 } 0169 0170 0171 /** 0172 * <p>Invoke a named method whose parameter type matches the object type.</p> 0173 * <p/> 0174 * <p>The behaviour of this method is less deterministic 0175 * than {@link #invokeExactMethod(Object object, String methodName, Object [] args)}. 0176 * It loops through all methods with names that match 0177 * and then executes the first it finds with compatable parameters.</p> 0178 * <p/> 0179 * <p>This method supports calls to methods taking primitive parameters 0180 * via passing in wrapping classes. So, for example, a <code>Boolean</code> class 0181 * would match a <code>boolean</code> primitive.</p> 0182 * <p/> 0183 * <p> This is a convenient wrapper for 0184 * {@link #invokeMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}. 0185 * </p> 0186 * 0187 * @param object invoke method on this object 0188 * @param methodName get method with this name 0189 * @param args use these arguments - treat null as empty array 0190 * @return The value returned by the invoked method 0191 * @throws NoSuchMethodException if there is no such accessible method 0192 * @throws InvocationTargetException wraps an exception thrown by the 0193 * method invoked 0194 * @throws IllegalAccessException if the requested method is not accessible 0195 * via reflection 0196 */ 0197 public static Object invokeMethod( 0198 Object object, 0199 String methodName, 0200 Object[] args) 0201 throws 0202 NoSuchMethodException, 0203 IllegalAccessException, 0204 InvocationTargetException { 0205 0206 if (args == null) { 0207 args = EMPTY_OBJECT_ARRAY; 0208 } 0209 int arguments = args.length; 0210 Class[] parameterTypes = new Class[arguments]; 0211 for (int i = 0; i < arguments; i++) { 0212 parameterTypes[i] = args[i].getClass(); 0213 } 0214 return invokeMethod(object, methodName, args, parameterTypes); 0215 0216 } 0217 0218 0219 /** 0220 * <p>Invoke a named method whose parameter type matches the object type.</p> 0221 * <p/> 0222 * <p>The behaviour of this method is less deterministic 0223 * than {@link 0224 * #invokeExactMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}. 0225 * It loops through all methods with names that match 0226 * and then executes the first it finds with compatable parameters.</p> 0227 * <p/> 0228 * <p>This method supports calls to methods taking primitive parameters 0229 * via passing in wrapping classes. So, for example, a <code>Boolean</code> class 0230 * would match a <code>boolean</code> primitive.</p> 0231 * 0232 * @param object invoke method on this object 0233 * @param methodName get method with this name 0234 * @param args use these arguments - treat null as empty array 0235 * @param parameterTypes match these parameters - treat null as empty array 0236 * @return The value returned by the invoked method 0237 * @throws NoSuchMethodException if there is no such accessible method 0238 * @throws InvocationTargetException wraps an exception thrown by the 0239 * method invoked 0240 * @throws IllegalAccessException if the requested method is not accessible 0241 * via reflection 0242 */ 0243 public static Object invokeMethod( 0244 Object object, 0245 String methodName, 0246 Object[] args, 0247 Class[] parameterTypes) 0248 throws 0249 NoSuchMethodException, 0250 IllegalAccessException, 0251 InvocationTargetException { 0252 0253 if (parameterTypes == null) { 0254 parameterTypes = EMPTY_CLASS_PARAMETERS; 0255 } 0256 if (args == null) { 0257 args = EMPTY_OBJECT_ARRAY; 0258 } 0259 0260 Method method = getMatchingAccessibleMethod( 0261 object.getClass(), 0262 methodName, 0263 parameterTypes); 0264 if (method == null) { 0265 throw new NoSuchMethodException("No such accessible method: " + 0266 methodName + "() on object: " + object.getClass().getName()); 0267 } 0268 return method.invoke(object, args); 0269 } 0270 0271 0272 /** 0273 * <p>Invoke a method whose parameter type matches exactly the object 0274 * type.</p> 0275 * <p/> 0276 * <p> This is a convenient wrapper for 0277 * {@link #invokeExactMethod(Object object, String methodName, Object [] args)}. 0278 * </p> 0279 * 0280 * @param object invoke method on this object 0281 * @param methodName get method with this name 0282 * @param arg use this argument 0283 * @return The value returned by the invoked method 0284 * @throws NoSuchMethodException if there is no such accessible method 0285 * @throws InvocationTargetException wraps an exception thrown by the 0286 * method invoked 0287 * @throws IllegalAccessException if the requested method is not accessible 0288 * via reflection 0289 */ 0290 public static Object invokeExactMethod( 0291 Object object, 0292 String methodName, 0293 Object arg) 0294 throws 0295 NoSuchMethodException, 0296 IllegalAccessException, 0297 InvocationTargetException { 0298 0299 Object[] args = {arg}; 0300 return invokeExactMethod(object, methodName, args); 0301 0302 } 0303 0304 0305 /** 0306 * <p>Invoke a method whose parameter types match exactly the object 0307 * types.</p> 0308 * <p/> 0309 * <p> This uses reflection to invoke the method obtained from a call to 0310 * <code>getAccessibleMethod()</code>.</p> 0311 * 0312 * @param object invoke method on this object 0313 * @param methodName get method with this name 0314 * @param args use these arguments - treat null as empty array 0315 * @return The value returned by the invoked method 0316 * @throws NoSuchMethodException if there is no such accessible method 0317 * @throws InvocationTargetException wraps an exception thrown by the 0318 * method invoked 0319 * @throws IllegalAccessException if the requested method is not accessible 0320 * via reflection 0321 */ 0322 public static Object invokeExactMethod( 0323 Object object, 0324 String methodName, 0325 Object[] args) 0326 throws 0327 NoSuchMethodException, 0328 IllegalAccessException, 0329 InvocationTargetException { 0330 if (args == null) { 0331 args = EMPTY_OBJECT_ARRAY; 0332 } 0333 int arguments = args.length; 0334 Class[] parameterTypes = new Class[arguments]; 0335 for (int i = 0; i < arguments; i++) { 0336 parameterTypes[i] = args[i].getClass(); 0337 } 0338 return invokeExactMethod(object, methodName, args, parameterTypes); 0339 0340 } 0341 0342 0343 /** 0344 * <p>Invoke a method whose parameter types match exactly the parameter 0345 * types given.</p> 0346 * <p/> 0347 * <p>This uses reflection to invoke the method obtained from a call to 0348 * <code>getAccessibleMethod()</code>.</p> 0349 * 0350 * @param object invoke method on this object 0351 * @param methodName get method with this name 0352 * @param args use these arguments - treat null as empty array 0353 * @param parameterTypes match these parameters - treat null as empty array 0354 * @return The value returned by the invoked method 0355 * @throws NoSuchMethodException if there is no such accessible method 0356 * @throws InvocationTargetException wraps an exception thrown by the 0357 * method invoked 0358 * @throws IllegalAccessException if the requested method is not accessible 0359 * via reflection 0360 */ 0361 public static Object invokeExactMethod( 0362 Object object, 0363 String methodName, 0364 Object[] args, 0365 Class[] parameterTypes) 0366 throws 0367 NoSuchMethodException, 0368 IllegalAccessException, 0369 InvocationTargetException { 0370 0371 if (args == null) { 0372 args = EMPTY_OBJECT_ARRAY; 0373 } 0374 0375 if (parameterTypes == null) { 0376 parameterTypes = EMPTY_CLASS_PARAMETERS; 0377 } 0378 0379 Method method = getAccessibleMethod( 0380 object.getClass(), 0381 methodName, 0382 parameterTypes); 0383 if (method == null) { 0384 throw new NoSuchMethodException("No such accessible method: " + 0385 methodName + "() on object: " + object.getClass().getName()); 0386 } 0387 return method.invoke(object, args); 0388 0389 } 0390 0391 /** 0392 * <p>Invoke a static method whose parameter types match exactly the parameter 0393 * types given.</p> 0394 * <p/> 0395 * <p>This uses reflection to invoke the method obtained from a call to 0396 * {@link #getAccessibleMethod(Class, String, Class[])}.</p> 0397 * 0398 * @param objectClass invoke static method on this class 0399 * @param methodName get method with this name 0400 * @param args use these arguments - treat null as empty array 0401 * @param parameterTypes match these parameters - treat null as empty array 0402 * @return The value returned by the invoked method 0403 * @throws NoSuchMethodException if there is no such accessible method 0404 * @throws InvocationTargetException wraps an exception thrown by the 0405 * method invoked 0406 * @throws IllegalAccessException if the requested method is not accessible 0407 * via reflection 0408 * @since 1.8.0 0409 */ 0410 public static Object invokeExactStaticMethod( 0411 Class objectClass, 0412 String methodName, 0413 Object[] args, 0414 Class[] parameterTypes) 0415 throws 0416 NoSuchMethodException, 0417 IllegalAccessException, 0418 InvocationTargetException { 0419 0420 if (args == null) { 0421 args = EMPTY_OBJECT_ARRAY; 0422 } 0423 0424 if (parameterTypes == null) { 0425 parameterTypes = EMPTY_CLASS_PARAMETERS; 0426 } 0427 0428 Method method = getAccessibleMethod( 0429 objectClass, 0430 methodName, 0431 parameterTypes); 0432 if (method == null) { 0433 throw new NoSuchMethodException("No such accessible method: " + 0434 methodName + "() on class: " + objectClass.getName()); 0435 } 0436 return method.invoke(null, args); 0437 0438 } 0439 0440 /** 0441 * <p>Invoke a named static method whose parameter type matches the object type.</p> 0442 * <p/> 0443 * <p>The behaviour of this method is less deterministic 0444 * than {@link #invokeExactMethod(Object, String, Object[], Class[])}. 0445 * It loops through all methods with names that match 0446 * and then executes the first it finds with compatable parameters.</p> 0447 * <p/> 0448 * <p>This method supports calls to methods taking primitive parameters 0449 * via passing in wrapping classes. So, for example, a <code>Boolean</code> class 0450 * would match a <code>boolean</code> primitive.</p> 0451 * <p/> 0452 * <p> This is a convenient wrapper for 0453 * {@link #invokeStaticMethod(Class objectClass, String methodName, Object [] args)}. 0454 * </p> 0455 * 0456 * @param objectClass invoke static method on this class 0457 * @param methodName get method with this name 0458 * @param arg use this argument 0459 * @return The value returned by the invoked method 0460 * @throws NoSuchMethodException if there is no such accessible method 0461 * @throws InvocationTargetException wraps an exception thrown by the 0462 * method invoked 0463 * @throws IllegalAccessException if the requested method is not accessible 0464 * via reflection 0465 * @since 1.8.0 0466 */ 0467 public static Object invokeStaticMethod( 0468 Class objectClass, 0469 String methodName, 0470 Object arg) 0471 throws 0472 NoSuchMethodException, 0473 IllegalAccessException, 0474 InvocationTargetException { 0475 0476 Object[] args = {arg}; 0477 return invokeStaticMethod(objectClass, methodName, args); 0478 0479 } 0480 0481 0482 /** 0483 * <p>Invoke a named static method whose parameter type matches the object type.</p> 0484 * <p/> 0485 * <p>The behaviour of this method is less deterministic 0486 * than {@link #invokeExactMethod(Object object, String methodName, Object [] args)}. 0487 * It loops through all methods with names that match 0488 * and then executes the first it finds with compatable parameters.</p> 0489 * <p/> 0490 * <p>This method supports calls to methods taking primitive parameters 0491 * via passing in wrapping classes. So, for example, a <code>Boolean</code> class 0492 * would match a <code>boolean</code> primitive.</p> 0493 * <p/> 0494 * <p> This is a convenient wrapper for 0495 * {@link #invokeStaticMethod(Class objectClass, String methodName, Object [] args, Class[] parameterTypes)}. 0496 * </p> 0497 * 0498 * @param objectClass invoke static method on this class 0499 * @param methodName get method with this name 0500 * @param args use these arguments - treat null as empty array 0501 * @return The value returned by the invoked method 0502 * @throws NoSuchMethodException if there is no such accessible method 0503 * @throws InvocationTargetException wraps an exception thrown by the 0504 * method invoked 0505 * @throws IllegalAccessException if the requested method is not accessible 0506 * via reflection 0507 * @since 1.8.0 0508 */ 0509 public static Object invokeStaticMethod( 0510 Class objectClass, 0511 String methodName, 0512 Object[] args) 0513 throws 0514 NoSuchMethodException, 0515 IllegalAccessException, 0516 InvocationTargetException { 0517 0518 if (args == null) { 0519 args = EMPTY_OBJECT_ARRAY; 0520 } 0521 int arguments = args.length; 0522 Class[] parameterTypes = new Class[arguments]; 0523 for (int i = 0; i < arguments; i++) { 0524 parameterTypes[i] = args[i].getClass(); 0525 } 0526 return invokeStaticMethod(objectClass, methodName, args, parameterTypes); 0527 0528 } 0529 0530 0531 /** 0532 * <p>Invoke a named static method whose parameter type matches the object type.</p> 0533 * <p/> 0534 * <p>The behaviour of this method is less deterministic 0535 * than {@link 0536 * #invokeExactStaticMethod(Class objectClass, String methodName, Object [] args, Class[] parameterTypes)}. 0537 * It loops through all methods with names that match 0538 * and then executes the first it finds with compatable parameters.</p> 0539 * <p/> 0540 * <p>This method supports calls to methods taking primitive parameters 0541 * via passing in wrapping classes. So, for example, a <code>Boolean</code> class 0542 * would match a <code>boolean</code> primitive.</p> 0543 * 0544 * @param objectClass invoke static method on this class 0545 * @param methodName get method with this name 0546 * @param args use these arguments - treat null as empty array 0547 * @param parameterTypes match these parameters - treat null as empty array 0548 * @return The value returned by the invoked method 0549 * @throws NoSuchMethodException if there is no such accessible method 0550 * @throws InvocationTargetException wraps an exception thrown by the 0551 * method invoked 0552 * @throws IllegalAccessException if the requested method is not accessible 0553 * via reflection 0554 * @since 1.8.0 0555 */ 0556 public static Object invokeStaticMethod( 0557 Class objectClass, 0558 String methodName, 0559 Object[] args, 0560 Class[] parameterTypes) 0561 throws 0562 NoSuchMethodException, 0563 IllegalAccessException, 0564 InvocationTargetException { 0565 0566 if (parameterTypes == null) { 0567 parameterTypes = EMPTY_CLASS_PARAMETERS; 0568 } 0569 if (args == null) { 0570 args = EMPTY_OBJECT_ARRAY; 0571 } 0572 0573 Method method = getMatchingAccessibleMethod( 0574 objectClass, 0575 methodName, 0576 parameterTypes); 0577 if (method == null) { 0578 throw new NoSuchMethodException("No such accessible method: " + 0579 methodName + "() on class: " + objectClass.getName()); 0580 } 0581 return method.invoke(null, args); 0582 } 0583 0584 0585 /** 0586 * <p>Invoke a static method whose parameter type matches exactly the object 0587 * type.</p> 0588 * <p/> 0589 * <p> This is a convenient wrapper for 0590 * {@link #invokeExactStaticMethod(Class objectClass, String methodName, Object [] args)}. 0591 * </p> 0592 * 0593 * @param objectClass invoke static method on this class 0594 * @param methodName get method with this name 0595 * @param arg use this argument 0596 * @return The value returned by the invoked method 0597 * @throws NoSuchMethodException if there is no such accessible method 0598 * @throws InvocationTargetException wraps an exception thrown by the 0599 * method invoked 0600 * @throws IllegalAccessException if the requested method is not accessible 0601 * via reflection 0602 * @since 1.8.0 0603 */ 0604 public static Object invokeExactStaticMethod( 0605 Class objectClass, 0606 String methodName, 0607 Object arg) 0608 throws 0609 NoSuchMethodException, 0610 IllegalAccessException, 0611 InvocationTargetException { 0612 0613 Object[] args = {arg}; 0614 return invokeExactStaticMethod(objectClass, methodName, args); 0615 0616 } 0617 0618 0619 /** 0620 * <p>Invoke a static method whose parameter types match exactly the object 0621 * types.</p> 0622 * <p/> 0623 * <p> This uses reflection to invoke the method obtained from a call to 0624 * {@link #getAccessibleMethod(Class, String, Class[])}.</p> 0625 * 0626 * @param objectClass invoke static method on this class 0627 * @param methodName get method with this name 0628 * @param args use these arguments - treat null as empty array 0629 * @return The value returned by the invoked method 0630 * @throws NoSuchMethodException if there is no such accessible method 0631 * @throws InvocationTargetException wraps an exception thrown by the 0632 * method invoked 0633 * @throws IllegalAccessException if the requested method is not accessible 0634 * via reflection 0635 * @since 1.8.0 0636 */ 0637 public static Object invokeExactStaticMethod( 0638 Class objectClass, 0639 String methodName, 0640 Object[] args) 0641 throws 0642 NoSuchMethodException, 0643 IllegalAccessException, 0644 InvocationTargetException { 0645 if (args == null) { 0646 args = EMPTY_OBJECT_ARRAY; 0647 } 0648 int arguments = args.length; 0649 Class[] parameterTypes = new Class[arguments]; 0650 for (int i = 0; i < arguments; i++) { 0651 parameterTypes[i] = args[i].getClass(); 0652 } 0653 return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes); 0654 0655 } 0656 0657 0658 /** 0659 * <p>Return an accessible method (that is, one that can be invoked via 0660 * reflection) with given name and a single parameter. If no such method 0661 * can be found, return <code>null</code>. 0662 * Basically, a convenience wrapper that constructs a <code>Class</code> 0663 * array for you.</p> 0664 * 0665 * @param clazz get method from this class 0666 * @param methodName get method with this name 0667 * @param parameterType taking this type of parameter 0668 * @return The accessible method 0669 */ 0670 public static Method getAccessibleMethod( 0671 Class clazz, 0672 String methodName, 0673 Class parameterType) { 0674 0675 Class[] parameterTypes = {parameterType}; 0676 return getAccessibleMethod(clazz, methodName, parameterTypes); 0677 0678 } 0679 0680 0681 /** 0682 * <p>Return an accessible method (that is, one that can be invoked via 0683 * reflection) with given name and parameters. If no such method 0684 * can be found, return <code>null</code>. 0685 * This is just a convenient wrapper for 0686 * {@link #getAccessibleMethod(Method method)}.</p> 0687 * 0688 * @param clazz get method from this class 0689 * @param methodName get method with this name 0690 * @param parameterTypes with these parameters types 0691 * @return The accessible method 0692 */ 0693 public static Method getAccessibleMethod( 0694 Class clazz, 0695 String methodName, 0696 Class[] parameterTypes) { 0697 0698 try { 0699 MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true); 0700 // Check the cache first 0701 Method method = getCachedMethod(md); 0702 if (method != null) { 0703 return method; 0704 } 0705 0706 method = getAccessibleMethod 0707 (clazz, clazz.getMethod(methodName, parameterTypes)); 0708 cacheMethod(md, method); 0709 return method; 0710 } catch (NoSuchMethodException e) { 0711 return (null); 0712 } 0713 0714 } 0715 0716 0717 /** 0718 * <p>Return an accessible method (that is, one that can be invoked via 0719 * reflection) that implements the specified Method. If no such method 0720 * can be found, return <code>null</code>.</p> 0721 * 0722 * @param method The method that we wish to call 0723 * @return The accessible method 0724 */ 0725 public static Method getAccessibleMethod(Method method) { 0726 0727 // Make sure we have a method to check 0728 if (method == null) { 0729 return (null); 0730 } 0731 0732 return getAccessibleMethod(method.getDeclaringClass(), method); 0733 0734 } 0735 0736 0737 /** 0738 * <p>Return an accessible method (that is, one that can be invoked via 0739 * reflection) that implements the specified Method. If no such method 0740 * can be found, return <code>null</code>.</p> 0741 * 0742 * @param clazz The class of the object 0743 * @param method The method that we wish to call 0744 * @return The accessible method 0745 * @since 1.8.0 0746 */ 0747 public static Method getAccessibleMethod(Class clazz, Method method) { 0748 0749 // Make sure we have a method to check 0750 if (method == null) { 0751 return (null); 0752 } 0753 0754 // If the requested method is not public we cannot call it 0755 if (!Modifier.isPublic(method.getModifiers())) { 0756 return (null); 0757 } 0758 0759 boolean sameClass = true; 0760 if (clazz == null) { 0761 clazz = method.getDeclaringClass(); 0762 } else { 0763 sameClass = clazz.equals(method.getDeclaringClass()); 0764 if (!method.getDeclaringClass().isAssignableFrom(clazz)) { 0765 throw new IllegalArgumentException(clazz.getName() + 0766 " is not assignable from " + method.getDeclaringClass().getName()); 0767 } 0768 } 0769 0770 // If the class is public, we are done 0771 if (Modifier.isPublic(clazz.getModifiers())) { 0772 if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { 0773 setMethodAccessible(method); // Default access superclass workaround 0774 } 0775 return (method); 0776 } 0777 0778 String methodName = method.getName(); 0779 Class[] parameterTypes = method.getParameterTypes(); 0780 0781 // Check the implemented interfaces and subinterfaces 0782 method = 0783 getAccessibleMethodFromInterfaceNest(clazz, 0784 methodName, 0785 parameterTypes); 0786 0787 // Check the superclass chain 0788 if (method == null) { 0789 method = getAccessibleMethodFromSuperclass(clazz, 0790 methodName, 0791 parameterTypes); 0792 } 0793 0794 return (method); 0795 0796 } 0797 0798 0799 // -------------------------------------------------------- Private Methods 0800 0801 /** 0802 * <p>Return an accessible method (that is, one that can be invoked via 0803 * reflection) by scanning through the superclasses. If no such method 0804 * can be found, return <code>null</code>.</p> 0805 * 0806 * @param clazz Class to be checked 0807 * @param methodName Method name of the method we wish to call 0808 * @param parameterTypes The parameter type signatures 0809 */ 0810 private static Method getAccessibleMethodFromSuperclass 0811 (Class clazz, String methodName, Class[] parameterTypes) { 0812 0813 Class parentClazz = clazz.getSuperclass(); 0814 while (parentClazz != null) { 0815 if (Modifier.isPublic(parentClazz.getModifiers())) { 0816 try { 0817 return parentClazz.getMethod(methodName, parameterTypes); 0818 } catch (NoSuchMethodException e) { 0819 return null; 0820 } 0821 } 0822 parentClazz = parentClazz.getSuperclass(); 0823 } 0824 return null; 0825 } 0826 0827 /** 0828 * <p>Return an accessible method (that is, one that can be invoked via 0829 * reflection) that implements the specified method, by scanning through 0830 * all implemented interfaces and subinterfaces. If no such method 0831 * can be found, return <code>null</code>.</p> 0832 * <p/> 0833 * <p> There isn't any good reason why this method must be private. 0834 * It is because there doesn't seem any reason why other classes should 0835 * call this rather than the higher level methods.</p> 0836 * 0837 * @param clazz Parent class for the interfaces to be checked 0838 * @param methodName Method name of the method we wish to call 0839 * @param parameterTypes The parameter type signatures 0840 */ 0841 private static Method getAccessibleMethodFromInterfaceNest 0842 (Class clazz, String methodName, Class[] parameterTypes) { 0843 0844 Method method = null; 0845 0846 // Search up the superclass chain 0847 for (; clazz != null; clazz = clazz.getSuperclass()) { 0848 0849 // Check the implemented interfaces of the parent class 0850 Class[] interfaces = clazz.getInterfaces(); 0851 for (int i = 0; i < interfaces.length; i++) { 0852 0853 // Is this interface public? 0854 if (!Modifier.isPublic(interfaces[i].getModifiers())) { 0855 continue; 0856 } 0857 0858 // Does the method exist on this interface? 0859 try { 0860 method = interfaces[i].getDeclaredMethod(methodName, 0861 parameterTypes); 0862 } catch (NoSuchMethodException e) { 0863 /* Swallow, if no method is found after the loop then this 0864 * method returns null. 0865 */ 0866 } 0867 if (method != null) { 0868 return method; 0869 } 0870 0871 // Recursively check our parent interfaces 0872 method = 0873 getAccessibleMethodFromInterfaceNest(interfaces[i], 0874 methodName, 0875 parameterTypes); 0876 if (method != null) { 0877 return method; 0878 } 0879 0880 } 0881 0882 } 0883 0884 // We did not find anything 0885 return (null); 0886 0887 } 0888 0889 /** 0890 * <p>Find an accessible method that matches the given name and has compatible parameters. 0891 * Compatible parameters mean that every method parameter is assignable from 0892 * the given parameters. 0893 * In other words, it finds a method with the given name 0894 * that will take the parameters given.<p> 0895 * <p/> 0896 * <p>This method is slightly undeterminstic since it loops 0897 * through methods names and return the first matching method.</p> 0898 * <p/> 0899 * <p>This method is used by 0900 * {@link 0901 * #invokeMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}. 0902 * <p/> 0903 * <p>This method can match primitive parameter by passing in wrapper classes. 0904 * For example, a <code>Boolean</code> will match a primitive <code>boolean</code> 0905 * parameter. 0906 * 0907 * @param clazz find method in this class 0908 * @param methodName find method with this name 0909 * @param parameterTypes find method with compatible parameters 0910 * @return The accessible method 0911 */ 0912 public static Method getMatchingAccessibleMethod( 0913 Class clazz, 0914 String methodName, 0915 Class[] parameterTypes) { 0916 MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false); 0917 0918 // see if we can find the method directly 0919 // most of the time this works and it's much faster 0920 try { 0921 // Check the cache first 0922 Method method = getCachedMethod(md); 0923 if (method != null) { 0924 return method; 0925 } 0926 0927 method = clazz.getMethod(methodName, parameterTypes); 0928 0929 setMethodAccessible(method); // Default access superclass workaround 0930 0931 cacheMethod(md, method); 0932 return method; 0933 0934 } catch (NoSuchMethodException e) { /* SWALLOW */ } 0935 0936 // search through all methods 0937 int paramSize = parameterTypes.length; 0938 Method bestMatch = null; 0939 Method[] methods = clazz.getMethods(); 0940 float bestMatchCost = Float.MAX_VALUE; 0941 float myCost = Float.MAX_VALUE; 0942 for (int i = 0, size = methods.length; i < size; i++) { 0943 if (methods[i].getName().equals(methodName)) { 0944 // compare parameters 0945 Class[] methodsParams = methods[i].getParameterTypes(); 0946 int methodParamSize = methodsParams.length; 0947 if (methodParamSize == paramSize) { 0948 boolean match = true; 0949 for (int n = 0; n < methodParamSize; n++) { 0950 if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) { 0951 match = false; 0952 break; 0953 } 0954 } 0955 0956 if (match) { 0957 // get accessible version of method 0958 Method method = getAccessibleMethod(clazz, methods[i]); 0959 if (method != null) { 0960 setMethodAccessible(method); // Default access superclass workaround 0961 myCost = getTotalTransformationCost(parameterTypes, method.getParameterTypes()); 0962 if (myCost < bestMatchCost) { 0963 bestMatch = method; 0964 bestMatchCost = myCost; 0965 } 0966 } 0967 } 0968 } 0969 } 0970 } 0971 if (bestMatch != null) { //find a match 0972 cacheMethod(md, bestMatch); 0973 } 0974 return bestMatch; 0975 } 0976 0977 /** 0978 * Try to make the method accessible 0979 * 0980 * @param method The source arguments 0981 */ 0982 private static void setMethodAccessible(Method method) { 0983 try { 0984 // 0985 // XXX Default access superclass workaround 0986 // 0987 // When a public class has a default access superclass 0988 // with public methods, these methods are accessible. 0989 // Calling them from compiled code works fine. 0990 // 0991 // Unfortunately, using reflection to invoke these methods 0992 // seems to (wrongly) to prevent access even when the method 0993 // modifer is public. 0994 // 0995 // The following workaround solves the problem but will only 0996 // work from sufficiently privilages code. 0997 // 0998 // Better workarounds would be greatfully accepted. 0999 // 1000 if (!method.isAccessible()) { 1001 method.setAccessible(true); 1002 } 1003 1004 } catch (SecurityException se) { 1005 // ignore 1006 } 1007 } 1008 1009 /** 1010 * Returns the sum of the object transformation cost for each class in the source 1011 * argument list. 1012 * 1013 * @param srcArgs The source arguments 1014 * @param destArgs The destination arguments 1015 * @return The total transformation cost 1016 */ 1017 private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) { 1018 1019 float totalCost = 0.0f; 1020 for (int i = 0; i < srcArgs.length; i++) { 1021 Class srcClass, destClass; 1022 srcClass = srcArgs[i]; 1023 destClass = destArgs[i]; 1024 totalCost += getObjectTransformationCost(srcClass, destClass); 1025 } 1026 1027 return totalCost; 1028 } 1029 1030 /** 1031 * Gets the number of steps required needed to turn the source class into the 1032 * destination class. This represents the number of steps in the object hierarchy 1033 * graph. 1034 * 1035 * @param srcClass The source class 1036 * @param destClass The destination class 1037 * @return The cost of transforming an object 1038 */ 1039 private static float getObjectTransformationCost(Class srcClass, Class destClass) { 1040 float cost = 0.0f; 1041 while (destClass != null && !destClass.equals(srcClass)) { 1042 if (destClass.isInterface() && isAssignmentCompatible(destClass, srcClass)) { 1043 // slight penalty for interface match. 1044 // we still want an exact match to override an interface match, but 1045 // an interface match should override anything where we have to get a 1046 // superclass. 1047 cost += 0.25f; 1048 break; 1049 } 1050 cost++; 1051 destClass = destClass.getSuperclass(); 1052 } 1053 1054 /* 1055 * If the destination class is null, we've travelled all the way up to 1056 * an Object match. We'll penalize this by adding 1.5 to the cost. 1057 */ 1058 if (destClass == null) { 1059 cost += 1.5f; 1060 } 1061 1062 return cost; 1063 } 1064 1065 1066 /** 1067 * <p>Determine whether a type can be used as a parameter in a method invocation. 1068 * This method handles primitive conversions correctly.</p> 1069 * <p/> 1070 * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>, 1071 * a <code>Long</code> to a <code>long</code>, 1072 * a <code>Float</code> to a <code>float</code>, 1073 * a <code>Integer</code> to a <code>int</code>, 1074 * and a <code>Double</code> to a <code>double</code>. 1075 * Now logic widening matches are allowed. 1076 * For example, a <code>Long</code> will not match a <code>int</code>. 1077 * 1078 * @param parameterType the type of parameter accepted by the method 1079 * @param parameterization the type of parameter being tested 1080 * @return true if the assignement is compatible. 1081 */ 1082 public static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) { 1083 // try plain assignment 1084 if (parameterType.isAssignableFrom(parameterization)) { 1085 return true; 1086 } 1087 1088 if (parameterType.isPrimitive()) { 1089 // this method does *not* do widening - you must specify exactly 1090 // is this the right behaviour? 1091 Class parameterWrapperClazz = getPrimitiveWrapper(parameterType); 1092 if (parameterWrapperClazz != null) { 1093 return parameterWrapperClazz.equals(parameterization); 1094 } 1095 } 1096 1097 return false; 1098 } 1099 1100 /** 1101 * Gets the wrapper object class for the given primitive type class. 1102 * For example, passing <code>boolean.class</code> returns <code>Boolean.class</code> 1103 * 1104 * @param primitiveType the primitive type class for which a match is to be found 1105 * @return the wrapper type associated with the given primitive 1106 * or null if no match is found 1107 */ 1108 public static Class getPrimitiveWrapper(Class primitiveType) { 1109 // does anyone know a better strategy than comparing names? 1110 if (boolean.class.equals(primitiveType)) { 1111 return Boolean.class; 1112 } else if (float.class.equals(primitiveType)) { 1113 return Float.class; 1114 } else if (long.class.equals(primitiveType)) { 1115 return Long.class; 1116 } else if (int.class.equals(primitiveType)) { 1117 return Integer.class; 1118 } else if (short.class.equals(primitiveType)) { 1119 return Short.class; 1120 } else if (byte.class.equals(primitiveType)) { 1121 return Byte.class; 1122 } else if (double.class.equals(primitiveType)) { 1123 return Double.class; 1124 } else if (char.class.equals(primitiveType)) { 1125 return Character.class; 1126 } else { 1127 1128 return null; 1129 } 1130 } 1131 1132 /** 1133 * Gets the class for the primitive type corresponding to the primitive wrapper class given. 1134 * For example, an instance of <code>Boolean.class</code> returns a <code>boolean.class</code>. 1135 * 1136 * @param wrapperType the 1137 * @return the primitive type class corresponding to the given wrapper class, 1138 * null if no match is found 1139 */ 1140 public static Class getPrimitiveType(Class wrapperType) { 1141 // does anyone know a better strategy than comparing names? 1142 if (Boolean.class.equals(wrapperType)) { 1143 return boolean.class; 1144 } else if (Float.class.equals(wrapperType)) { 1145 return float.class; 1146 } else if (Long.class.equals(wrapperType)) { 1147 return long.class; 1148 } else if (Integer.class.equals(wrapperType)) { 1149 return int.class; 1150 } else if (Short.class.equals(wrapperType)) { 1151 return short.class; 1152 } else if (Byte.class.equals(wrapperType)) { 1153 return byte.class; 1154 } else if (Double.class.equals(wrapperType)) { 1155 return double.class; 1156 } else if (Character.class.equals(wrapperType)) { 1157 return char.class; 1158 } else { 1159 return null; 1160 } 1161 } 1162 1163 /** 1164 * Find a non primitive representation for given primitive class. 1165 * 1166 * @param clazz the class to find a representation for, not null 1167 * @return the original class if it not a primitive. Otherwise the wrapper class. Not null 1168 */ 1169 public static Class toNonPrimitiveClass(Class clazz) { 1170 if (clazz.isPrimitive()) { 1171 Class primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz); 1172 // the above method returns 1173 if (primitiveClazz != null) { 1174 return primitiveClazz; 1175 } else { 1176 return clazz; 1177 } 1178 } else { 1179 return clazz; 1180 } 1181 } 1182 1183 1184 /** 1185 * Return the method from the cache, if present. 1186 * 1187 * @param md The method descriptor 1188 * @return The cached method 1189 */ 1190 private static Method getCachedMethod(MethodDescriptor md) { 1191 if (CACHE_METHODS) { 1192 Reference methodRef = (Reference) cache.get(md); 1193 if (methodRef != null) { 1194 return (Method) methodRef.get(); 1195 } 1196 } 1197 return null; 1198 } 1199 1200 /** 1201 * Add a method to the cache. 1202 * 1203 * @param md The method descriptor 1204 * @param method The method to cache 1205 */ 1206 private static void cacheMethod(MethodDescriptor md, Method method) { 1207 if (CACHE_METHODS) { 1208 if (method != null) { 1209 cache.put(md, new WeakReference(method)); 1210 } 1211 } 1212 } 1213 1214 /** 1215 * Represents the key to looking up a Method by reflection. 1216 */ 1217 private static class MethodDescriptor { 1218 private Class cls; 1219 private String methodName; 1220 private Class[] paramTypes; 1221 private boolean exact; 1222 private int hashCode; 1223 1224 /** 1225 * The sole constructor. 1226 * 1227 * @param cls the class to reflect, must not be null 1228 * @param methodName the method name to obtain 1229 * @param paramTypes the array of classes representing the paramater types 1230 * @param exact whether the match has to be exact. 1231 */ 1232 public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) { 1233 if (cls == null) { 1234 throw new IllegalArgumentException("Class cannot be null"); 1235 } 1236 if (methodName == null) { 1237 throw new IllegalArgumentException("Method Name cannot be null"); 1238 } 1239 if (paramTypes == null) { 1240 paramTypes = EMPTY_CLASS_PARAMETERS; 1241 } 1242 1243 this.cls = cls; 1244 this.methodName = methodName; 1245 this.paramTypes = paramTypes; 1246 this.exact = exact; 1247 1248 this.hashCode = methodName.length(); 1249 } 1250 1251 /** 1252 * Checks for equality. 1253 * 1254 * @param obj object to be tested for equality 1255 * @return true, if the object describes the same Method. 1256 */ 1257 public boolean equals(Object obj) { 1258 if (!(obj instanceof MethodDescriptor)) { 1259 return false; 1260 } 1261 MethodDescriptor md = (MethodDescriptor) obj; 1262 1263 return ( 1264 exact == md.exact && 1265 methodName.equals(md.methodName) && 1266 cls.equals(md.cls) && 1267 java.util.Arrays.equals(paramTypes, md.paramTypes) 1268 ); 1269 } 1270 1271 /** 1272 * Returns the string length of method name. I.e. if the 1273 * hashcodes are different, the objects are different. If the 1274 * hashcodes are the same, need to use the equals method to 1275 * determine equality. 1276 * 1277 * @return the string length of method name. 1278 */ 1279 public int hashCode() { 1280 return hashCode; 1281 } 1282 } 1283 }`