![JAR search and dependency download from the Maven repository](/logo.png)
mockit.internal.util.Utilities Maven / Gradle / Ivy
/*
* JMockit
* Copyright (c) 2006-2010 Rogério Liesenfeld
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package mockit.internal.util;
import java.lang.reflect.*;
import static java.lang.reflect.Modifier.*;
import java.util.*;
import mockit.*;
/**
* Miscellaneous utility methods which don't fit into any other class, most of them related to the
* use of Reflection.
*/
@SuppressWarnings({"unchecked", "ClassWithTooManyMethods", "OverlyComplexClass"})
public final class Utilities
{
public static final String GENERATED_SUBCLASS_PREFIX = "$Subclass_";
private static final Class>[] PRIMITIVE_TYPES = {
null, boolean.class, char.class, byte.class, short.class, int.class, float.class, long.class,
double.class
};
private static final Class>[] NO_PARAMETERS = new Class>[0];
private Utilities() {}
public static Class loadClass(String className)
{
try {
return (Class) Class.forName(className);
}
catch (LinkageError e) {
e.printStackTrace();
throw e;
}
catch (ClassNotFoundException ignore) {
//noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException
throw new IllegalArgumentException("No class with name \"" + className + "\" found");
}
}
public static T newInstance(Class aClass)
{
return newInstance(aClass, NO_PARAMETERS);
}
public static T newInstance(Class aClass, Class>[] parameterTypes, Object... initArgs)
{
Constructor> constructor = findSpecifiedConstructor(aClass, parameterTypes);
return (T) invoke(constructor, initArgs);
}
private static Constructor> findSpecifiedConstructor(Class> theClass, Class>[] paramTypes)
{
for (Constructor> declaredConstructor : theClass.getDeclaredConstructors()) {
if (matchesParameterTypes(declaredConstructor.getParameterTypes(), paramTypes)) {
return declaredConstructor;
}
}
String paramTypesDesc = getParameterTypesDescription(paramTypes);
throw new IllegalArgumentException("Specified constructor not found: " + paramTypesDesc);
}
private static String getParameterTypesDescription(Class>[] paramTypes)
{
String paramTypesDesc = Arrays.asList(paramTypes).toString();
return paramTypesDesc.replace("class ", "").replace('[', '(').replace(']', ')');
}
public static T newInstance(String className, Class>[] parameterTypes, Object... initArgs)
{
Class theClass = loadClass(className);
return newInstance(theClass, parameterTypes, initArgs);
}
public static T newInstance(String className, Object... nonNullArgs)
{
Class>[] paramTypes = getParameterTypesFromArguments(nonNullArgs);
return (T) newInstance(className, paramTypes, nonNullArgs);
}
public static Class>[] getParameterTypesFromArguments(Object... args)
{
Class>[] paramTypes = new Class>[args.length];
for (int i = 0; i < args.length; i++) {
Class> argType = getParameterTypeFromArgument(i, args);
paramTypes[i] = argType;
}
return paramTypes;
}
private static Class> getParameterTypeFromArgument(int i, Object[] args)
{
Object arg = args[i];
if (arg == null) {
throw new IllegalArgumentException(
"Invalid null value passed as argument " + i +
" (instead of null, provide the Class object for the parameter type)");
}
Class> argType;
if (arg instanceof Class) {
argType = (Class>) arg;
args[i] = null;
}
else {
argType = arg.getClass();
if (Proxy.isProxyClass(argType)) {
// Assumes that the proxy class implements a single interface.
argType = argType.getInterfaces()[0];
}
else if (argType.getSimpleName().startsWith(GENERATED_SUBCLASS_PREFIX)) {
argType = argType.getSuperclass();
}
}
return argType;
}
public static T newInnerInstance(
String innerClassName, Object outerInstance, Object... nonNullArgs)
{
String className = outerInstance.getClass().getName() + '$' + innerClassName;
Class>[] parameterTypes = new Class>[1 + nonNullArgs.length];
parameterTypes[0] = outerInstance.getClass();
Object[] initArgs = new Object[1 + nonNullArgs.length];
initArgs[0] = outerInstance;
for (int i = 0; i < nonNullArgs.length; i++) {
parameterTypes[1 + i] = getParameterTypeFromArgument(i, nonNullArgs);
initArgs[1 + i] = nonNullArgs[i];
}
return (T) newInstance(className, parameterTypes, initArgs);
}
public static T invoke(Object targetInstance, String methodName, Object... methodArgs)
{
return (T) invoke(targetInstance.getClass(), targetInstance, methodName, methodArgs);
}
public static T invoke(
Class> theClass, Object targetInstance, String methodName, Object... methodArgs)
{
Class>[] paramTypes = getParameterTypesFromArguments(methodArgs);
Method method = findCompatibleMethod(theClass, methodName, paramTypes);
T result = (T) invoke(targetInstance, method, methodArgs);
return result;
}
public static Method findCompatibleMethod(
Object targetInstance, String methodName, Object[] args)
{
Class>[] paramTypes = getParameterTypesFromArguments(args);
return findCompatibleMethod(targetInstance.getClass(), methodName, paramTypes);
}
private static Method findCompatibleMethod(
Class> theClass, String methodName, Class>[] argTypes)
{
while (true) {
Method methodFound = findCompatibleMethodInClass(theClass, methodName, argTypes);
if (methodFound != null) {
return methodFound;
}
Class> superClass = theClass.getSuperclass();
if (superClass == null || superClass == Object.class) {
break;
}
//noinspection AssignmentToMethodParameter
theClass = superClass;
}
String argTypesDesc = getParameterTypesDescription(argTypes);
throw new IllegalArgumentException(
"No compatible method found: " + methodName + argTypesDesc);
}
private static Method findCompatibleMethodInClass(
Class> theClass, String methodName, Class>[] argTypes)
{
for (Method declaredMethod : theClass.getDeclaredMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class>[] declaredParamTypes = declaredMethod.getParameterTypes();
if (
matchesParameterTypes(declaredParamTypes, argTypes) ||
acceptsArgumentTypes(declaredParamTypes, argTypes)
) {
return declaredMethod;
}
}
}
return null;
}
private static boolean acceptsArgumentTypes(Class>[] paramTypes, Class>[] argTypes)
{
int i0 = 0;
if (paramTypes.length != argTypes.length)
{
if (paramTypes.length > 0 && paramTypes[0] == Invocation.class) {
i0 = 1;
}
else {
return false;
}
}
for (int i = i0; i < paramTypes.length; i++) {
Class> parType = paramTypes[i];
Class> argType = argTypes[i - i0];
if (isSameTypeIgnoringAutoBoxing(parType, argType) || parType.isAssignableFrom(argType)) {
// OK, move to next parameter.
}
else {
return false;
}
}
return true;
}
public static Method findNonPrivateHandlerMethod(Object handler)
{
Method[] declaredMethods = handler.getClass().getDeclaredMethods();
Method nonPrivateMethod = null;
for (Method declaredMethod : declaredMethods) {
if (!Modifier.isPrivate(declaredMethod.getModifiers())) {
if (nonPrivateMethod != null) {
throw new IllegalArgumentException(
"More than one non-private invocation handler method found");
}
nonPrivateMethod = declaredMethod;
}
}
if (nonPrivateMethod == null) {
throw new IllegalArgumentException("No non-private invocation handler method found");
}
return nonPrivateMethod;
}
public static T invoke(
Class> theClass, Object targetInstance, String methodName, Class>[] paramTypes,
Object... methodArgs)
{
Method method = findSpecifiedMethod(theClass, methodName, paramTypes);
T result = (T) invoke(targetInstance, method, methodArgs);
return result;
}
public static T invoke(Object targetInstance, Method method, Object... methodArgs)
{
ensureThatMemberIsAccessible(method);
try {
return (T) method.invoke(targetInstance, methodArgs);
}
catch (IllegalAccessException e) {
assert false : "Not expected to happen because the method was made accessible";
throw new RuntimeException(e);
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Error) {
throw (Error) cause;
}
else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
else {
throwCheckedException((Exception) cause);
return null;
}
}
}
private static void ensureThatMemberIsAccessible(AccessibleObject classMember)
{
if (!classMember.isAccessible()) {
classMember.setAccessible(true);
}
}
private static Method findSpecifiedMethod(
Class> theClass, String methodName, Class>[] paramTypes)
{
for (Method declaredMethod : theClass.getDeclaredMethods()) {
if (
declaredMethod.getName().equals(methodName) &&
matchesParameterTypes(declaredMethod.getParameterTypes(), paramTypes)
) {
return declaredMethod;
}
}
Class> superClass = theClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
return findSpecifiedMethod(superClass, methodName, paramTypes);
}
String paramTypesDesc = getParameterTypesDescription(paramTypes);
throw new IllegalArgumentException(
"Specified method not found: " + methodName + paramTypesDesc);
}
private static boolean matchesParameterTypes(Class>[] declaredTypes, Class>[] specifiedTypes)
{
int i0 = 0;
if (declaredTypes.length != specifiedTypes.length)
{
if (declaredTypes.length > 0 && declaredTypes[0] == Invocation.class) {
i0 = 1;
}
else {
return false;
}
}
for (int i = i0; i < declaredTypes.length; i++) {
Class> declaredType = declaredTypes[i];
Class> specifiedType = specifiedTypes[i - i0];
if (isSameTypeIgnoringAutoBoxing(declaredType, specifiedType)) {
// OK, move to next parameter.
}
else {
return false;
}
}
return true;
}
private static boolean isSameTypeIgnoringAutoBoxing(Class> firstType, Class> secondType)
{
return
firstType == secondType ||
firstType.isPrimitive() && isWrapperOfPrimitiveType(firstType, secondType) ||
secondType.isPrimitive() && isWrapperOfPrimitiveType(secondType, firstType);
}
private static boolean isWrapperOfPrimitiveType(Class> primitiveType, Class> otherType)
{
return
primitiveType == int.class && otherType == Integer.class ||
primitiveType == long.class && otherType == Long.class ||
primitiveType == short.class && otherType == Short.class ||
primitiveType == byte.class && otherType == Byte.class ||
primitiveType == double.class && otherType == Double.class ||
primitiveType == float.class && otherType == Float.class ||
primitiveType == char.class && otherType == Character.class ||
primitiveType == boolean.class && otherType == Boolean.class;
}
public static Method findPublicVoidMethod(Class> aClass, String methodName)
{
for (Method method : aClass.getDeclaredMethods()) {
if (
isPublic(method.getModifiers()) && method.getReturnType() == void.class &&
methodName.equals(method.getName())
) {
return method;
}
}
return null;
}
private static Object[] getDefaultParameterValues(Constructor> constructor)
{
Class>[] parameterTypes = constructor.getParameterTypes();
int numParameters = parameterTypes.length;
Object[] defaultArgs = new Object[numParameters];
for (int i = 0; i < numParameters; i++) {
Class> paramType = parameterTypes[i];
defaultArgs[i] = DefaultValues.computeForType(paramType);
}
return defaultArgs;
}
public static Object invoke(Constructor> constructor, Object... initArgs)
{
ensureThatMemberIsAccessible(constructor);
Object[] args = initArgs != null? initArgs : getDefaultParameterValues(constructor);
try {
return constructor.newInstance(args);
}
catch (InstantiationException e) {
assert false : "Not expected to happen because the class is expected to be concrete";
throw new RuntimeException(e);
}
catch (IllegalAccessException e) {
assert false : "Not expected to happen because the constructor was made accessible";
throw new RuntimeException(e);
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Error) {
throw (Error) cause;
}
else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
else {
throwCheckedException((Exception) cause);
return null;
}
}
}
public static Object getField(Class> theClass, String fieldName, Object targetObject)
{
Field field = getDeclaredField(theClass, fieldName);
return getFieldValue(field, targetObject);
}
public static Field getDeclaredField(Class> theClass, String fieldName)
{
try {
return theClass.getDeclaredField(fieldName);
}
catch (NoSuchFieldException ignore) {
Class> superClass = theClass.getSuperclass();
if (superClass != null) {
return getDeclaredField(superClass, fieldName);
}
//noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException
throw new IllegalArgumentException(
"No instance field of name \"" + fieldName + "\" found in " + theClass);
}
}
public static T getField(Class> theClass, Class fieldType, Object targetObject)
{
Field field = getDeclaredField(theClass, fieldType, targetObject != null, false);
return (T) getFieldValue(field, targetObject);
}
public static T getField(Class> theClass, Type fieldType, Object targetObject)
{
Field field = getDeclaredField(theClass, fieldType, targetObject != null, false);
return (T) getFieldValue(field, targetObject);
}
private static Field getDeclaredField(
Class> theClass, Type desiredType, boolean instanceField, boolean forAssignment)
{
Field found =
getDeclaredFieldInSingleClass(theClass, desiredType, instanceField, forAssignment);
if (found == null) {
Class> superClass = theClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
return getDeclaredField(superClass, desiredType, instanceField, forAssignment);
}
throw new IllegalArgumentException(
(instanceField ? "Instance" : "Static") + " field of " + desiredType +
" not found in " + theClass);
}
return found;
}
private static Field getDeclaredFieldInSingleClass(
Class> theClass, Type desiredType, boolean instanceField, boolean forAssignment)
{
Field found = null;
for (Field field : theClass.getDeclaredFields()) {
if (!field.isSynthetic()) {
Type fieldType = field.getGenericType();
if (
instanceField != isStatic(field.getModifiers()) &&
isCompatibleFieldType(fieldType, desiredType, forAssignment)
) {
if (found != null) {
String message =
errorMessageForMoreThanOneFieldFound(
desiredType, instanceField, forAssignment, found, field);
throw new IllegalArgumentException(message);
}
found = field;
}
}
}
return found;
}
private static String errorMessageForMoreThanOneFieldFound(
Type desiredFieldType, boolean instanceField, boolean forAssignment,
Field firstField, Field secondField)
{
StringBuilder message = new StringBuilder("More than one ");
message.append(instanceField ? "instance" : "static").append(" field ");
message.append(forAssignment ? "to which a value of " : "from which a value of ");
message.append(desiredFieldType);
message.append(forAssignment ? " can be assigned" : " can be read");
message.append(" exists in ").append(secondField.getDeclaringClass()).append(": ");
message.append(firstField.getName()).append(", ").append(secondField.getName());
return message.toString();
}
private static boolean isCompatibleFieldType(
Type fieldType, Type desiredType, boolean forAssignment)
{
Class> fieldClass = getClassType(fieldType);
Class> desiredClass = getClassType(desiredType);
if (isSameTypeIgnoringAutoBoxing(desiredClass, fieldClass)) {
return true;
}
else if (forAssignment) {
return fieldClass.isAssignableFrom(desiredClass);
}
return desiredClass.isAssignableFrom(fieldClass) || fieldClass.isAssignableFrom(desiredClass);
}
private static Class> getClassType(Type declaredType)
{
if (declaredType instanceof ParameterizedType) {
return (Class>) ((ParameterizedType) declaredType).getRawType();
}
return (Class>) declaredType;
}
public static Object getFieldValue(Field field, Object targetObject)
{
ensureThatMemberIsAccessible(field);
try {
return field.get(targetObject);
}
catch (IllegalAccessException e) {
assert false : "Not expected to happen because the field was made accessible";
throw new RuntimeException(e);
}
}
public static Field setField(
Class> theClass, Object targetObject, String fieldName, Object fieldValue)
{
Field field =
fieldName == null ?
getDeclaredField(theClass, fieldValue.getClass(), targetObject != null, true) :
getDeclaredField(theClass, fieldName);
setFieldValue(field, targetObject, fieldValue);
return field;
}
public static void setFieldValue(Field field, Object targetObject, Object value)
{
ensureThatMemberIsAccessible(field);
try {
field.set(targetObject, value);
}
catch (IllegalAccessException e) {
assert false : "Not expected to happen because the field was made accessible";
throw new RuntimeException(e);
}
}
public static Class>[] getParameterTypes(String mockDesc)
{
mockit.external.asm.Type[] paramTypes = mockit.external.asm.Type.getArgumentTypes(mockDesc);
Class>[] paramClasses = new Class>[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
paramClasses[i] = getClassForType(paramTypes[i]);
}
return paramClasses;
}
public static Class> getClassForType(mockit.external.asm.Type type)
{
int elementSort = type.getSort();
if (elementSort < PRIMITIVE_TYPES.length) {
return PRIMITIVE_TYPES[elementSort];
}
String className =
elementSort == mockit.external.asm.Type.ARRAY ?
type.getDescriptor().replace('/', '.') : type.getClassName();
return loadClass(className);
}
public static void filterStackTrace(Throwable t)
{
StackTraceElement[] originalST = t.getStackTrace();
List filteredST = new ArrayList(originalST.length);
for (StackTraceElement ste : originalST) {
if (ste.getFileName() != null) {
String where = ste.getClassName();
if (
(!where.startsWith("sun.") || ste.isNativeMethod()) &&
!where.startsWith("org.junit.") && !where.startsWith("junit.") &&
!where.startsWith("org.testng.")
) {
if (!where.startsWith("mockit.") || ste.getFileName().endsWith("Test.java")) {
filteredST.add(ste);
}
}
}
}
t.setStackTrace(filteredST.toArray(new StackTraceElement[filteredST.size()]));
}
public static void throwCheckedException(Exception exceptionToThrow)
{
synchronized (ThrowOfCheckedException.class) {
ThrowOfCheckedException.exceptionToThrow = exceptionToThrow;
try {
//noinspection ClassNewInstance
ThrowOfCheckedException.class.newInstance();
}
catch (InstantiationException ignore) {}
catch (IllegalAccessException ignored) {}
}
}
private static final class ThrowOfCheckedException
{
static Exception exceptionToThrow;
ThrowOfCheckedException() throws Exception { throw exceptionToThrow; }
}
/**
* This method was created to work around an issue in the standard
* {@link Class#isAnonymousClass()} method, which causes a sibling nested class to be loaded
* when called on a nested class. If that sibling nested class is not in the classpath, a
* ClassNotFoundException
would result.
*
* This method checks only the given class name, never causing any other classes to be loaded.
*/
public static boolean isAnonymousClass(Class> aClass)
{
String className = aClass.getName();
int p = className.lastIndexOf('$');
if (p > 0 && p + 1 < className.length()) {
char c = className.charAt(p + 1);
return isPositiveDigit(c);
}
return false;
}
public static boolean isPositiveDigit(char c)
{
return c >= '1' && c <= '9';
}
public static String objectIdentity(Object obj)
{
return obj.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(obj));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy