/* * Copyright (c) 2006-2011 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.expectations.mocking; import java.lang.reflect.*; import java.util.*; import mockit.external.asm.*; import mockit.internal.*; import mockit.internal.util.*; public final class DynamicPartialMocking { private final List<Class<?>> targetClasses; private final Map<Class<?>, byte[]> modifiedClassfiles; public DynamicPartialMocking() { targetClasses = new ArrayList<Class<?>>(2); modifiedClassfiles = new HashMap<Class<?>, byte[]>(); } public List<Class<?>> getTargetClasses() { return targetClasses; } public void redefineTypes(Object[] classesOrInstancesToBePartiallyMocked) { for (Object classOrInstance : classesOrInstancesToBePartiallyMocked) { redefineTargetType(classOrInstance); } new RedefinitionEngine().redefineMethods(modifiedClassfiles); modifiedClassfiles.clear(); } private void redefineTargetType(Object classOrInstance) { Class<?> targetClass; if (classOrInstance instanceof Class) { targetClass = (Class<?>) classOrInstance; validateTargetClassType(targetClass); redefineClass(targetClass, false); } else { targetClass = classOrInstance.getClass(); validateTargetClassType(targetClass); redefineClassAndItsSuperClasses(targetClass); } targetClasses.add(targetClass); } private void validateTargetClassType(Class<?> targetClass) { if ( targetClass.isInterface() || targetClass.isAnnotation() || targetClass.isArray() || targetClass.isPrimitive() || Utilities.isWrapperOfPrimitiveType(targetClass) || Utilities.isGeneratedImplementationClass(targetClass) ) { throw new IllegalArgumentException("Invalid type for dynamic mocking: " + targetClass); } } private void redefineClassAndItsSuperClasses(Class<?> realClass) { redefineClass(realClass, true); Class<?> superClass = realClass.getSuperclass(); if (superClass != Object.class && superClass != Proxy.class) { redefineClassAndItsSuperClasses(superClass); } } private void redefineClass(Class<?> realClass, boolean methodsOnly) { ClassReader classReader = new ClassFile(realClass, false).getReader(); ExpectationsModifier modifier = new ExpectationsModifier(realClass.getClassLoader(), classReader, null); modifier.useDynamicMocking(methodsOnly); classReader.accept(modifier, false); byte[] modifiedClass = modifier.toByteArray(); modifiedClassfiles.put(realClass, modifiedClass); } }