LANG-1433: MethodUtils will throw a NPE if invokeMethod() is called for a var-args method (#407)
* LANG-1433: MethodUtils will throw a NPE if invokeMethod() is called for a var-args method with null parameter value * LANG-1433: Result of invokeMethod() is not deterministic for overloaded methods that can not be uniquly resolved from parameter types * LANG-1433: Fixed checkstyle errors
This commit is contained in:
parent
d3112e4306
commit
553a0474ea
|
@ -162,7 +162,7 @@ private static float getTotalTransformationCost(final Class<?>[] srcArgs, final
|
|||
// When isVarArgs is true, srcArgs and dstArgs may differ in length.
|
||||
// There are two special cases to consider:
|
||||
final boolean noVarArgsPassed = srcArgs.length < destArgs.length;
|
||||
final boolean explicitArrayForVarags = srcArgs.length == destArgs.length && srcArgs[srcArgs.length-1].isArray();
|
||||
final boolean explicitArrayForVarags = srcArgs.length == destArgs.length && srcArgs[srcArgs.length-1] != null && srcArgs[srcArgs.length-1].isArray();
|
||||
|
||||
final float varArgsCost = 0.001f;
|
||||
final Class<?> destClass = destArgs[destArgs.length-1].getComponentType();
|
||||
|
@ -227,6 +227,9 @@ private static float getObjectTransformationCost(Class<?> srcClass, final Class<
|
|||
* @return The cost of promoting the primitive
|
||||
*/
|
||||
private static float getPrimitivePromotionCost(final Class<?> srcClass, final Class<?> destClass) {
|
||||
if (srcClass == null) {
|
||||
return 1.5f;
|
||||
}
|
||||
float cost = 0.0f;
|
||||
Class<?> cls = srcClass;
|
||||
if (!cls.isPrimitive()) {
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
@ -59,6 +61,13 @@
|
|||
*/
|
||||
public class MethodUtils {
|
||||
|
||||
private static final Comparator<Method> METHOD_BY_SIGNATURE = new Comparator<Method>() {
|
||||
@Override
|
||||
public int compare(final Method m1, final Method m2) {
|
||||
return m1.toString ().compareTo (m2.toString ());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* <p>{@link MethodUtils} instances should NOT be constructed in standard programming.
|
||||
* Instead, the class should be used as
|
||||
|
@ -462,8 +471,8 @@ private static Object[] toVarArgs(final Method method, Object[] args) {
|
|||
* @since 3.5
|
||||
*/
|
||||
static Object[] getVarArgs(final Object[] args, final Class<?>[] methodParameterTypes) {
|
||||
if (args.length == methodParameterTypes.length
|
||||
&& args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1])) {
|
||||
if (args.length == methodParameterTypes.length && (args[args.length - 1] == null ||
|
||||
args[args.length - 1].getClass().equals(methodParameterTypes[methodParameterTypes.length - 1]))) {
|
||||
// The args array is already in the canonical form for the method.
|
||||
return args;
|
||||
}
|
||||
|
@ -679,12 +688,21 @@ public static Method getMatchingAccessibleMethod(final Class<?> cls,
|
|||
} catch (final NoSuchMethodException e) { // NOPMD - Swallow the exception
|
||||
}
|
||||
// search through all methods
|
||||
Method bestMatch = null;
|
||||
final Method[] methods = cls.getMethods();
|
||||
final List<Method> matchingMethods = new ArrayList<>();
|
||||
for (final Method method : methods) {
|
||||
// compare name and parameters
|
||||
if (method.getName().equals(methodName) &&
|
||||
MemberUtils.isMatchingMethod(method, parameterTypes)) {
|
||||
matchingMethods.add (method);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort methods by signature to force deterministic result
|
||||
Collections.sort (matchingMethods, METHOD_BY_SIGNATURE);
|
||||
|
||||
Method bestMatch = null;
|
||||
for (final Method method : matchingMethods) {
|
||||
// get accessible version of method
|
||||
final Method accessibleMethod = getAccessibleMethod(method);
|
||||
if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareMethodFit(
|
||||
|
@ -694,7 +712,6 @@ public static Method getMatchingAccessibleMethod(final Class<?> cls,
|
|||
bestMatch = accessibleMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestMatch != null) {
|
||||
MemberUtils.setAccessibleWorkaround(bestMatch);
|
||||
}
|
||||
|
@ -703,10 +720,12 @@ public static Method getMatchingAccessibleMethod(final Class<?> cls,
|
|||
final Class<?>[] methodParameterTypes = bestMatch.getParameterTypes();
|
||||
final Class<?> methodParameterComponentType = methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
|
||||
final String methodParameterComponentTypeName = ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
|
||||
final String parameterTypeName = parameterTypes[parameterTypes.length - 1].getName();
|
||||
final String parameterTypeSuperClassName = parameterTypes[parameterTypes.length - 1].getSuperclass().getName();
|
||||
|
||||
if (!methodParameterComponentTypeName.equals(parameterTypeName)
|
||||
final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
|
||||
final String parameterTypeName = (lastParameterType==null) ? null : lastParameterType.getName();
|
||||
final String parameterTypeSuperClassName = (lastParameterType==null) ? null : lastParameterType.getSuperclass().getName();
|
||||
|
||||
if (parameterTypeName!= null && parameterTypeSuperClassName != null && !methodParameterComponentTypeName.equals(parameterTypeName)
|
||||
&& !methodParameterComponentTypeName.equals(parameterTypeSuperClassName)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -446,6 +446,22 @@ public void testInvokeMethod() throws Exception {
|
|||
MethodUtils.invokeMethod(testBean, "varOverloadEcho", 17, 23, 42));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMethod_VarArgsWithNullValues() throws Exception {
|
||||
assertEquals("String...", MethodUtils.invokeMethod(testBean, "varOverload",
|
||||
"a", null, "c"));
|
||||
assertEquals("String...", MethodUtils.invokeMethod(testBean, "varOverload",
|
||||
"a", "b", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeMethod_VarArgsNotUniqueResolvable() throws Exception {
|
||||
assertEquals("Boolean...", MethodUtils.invokeMethod(testBean, "varOverload",
|
||||
new Object[] {null}));
|
||||
assertEquals("Object...", MethodUtils.invokeMethod(testBean, "varOverload",
|
||||
(Object[]) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeExactMethod() throws Exception {
|
||||
assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
|
||||
|
|
Loading…
Reference in New Issue