/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.expectations.mocking;
import java.util.*;
import javax.annotation.*;
import mockit.internal.*;
import mockit.internal.state.*;
import static mockit.internal.util.AutoBoxing.*;
import static mockit.internal.util.GeneratedClasses.*;
public final class DynamicPartialMocking extends BaseTypeRedefinition
{
@Nonnull public final List<Object> targetInstances;
@Nonnull private final Map<Class<?>, byte[]> modifiedClassfiles;
private boolean methodsOnly;
public DynamicPartialMocking()
{
targetInstances = new ArrayList<Object>(2);
modifiedClassfiles = new HashMap<Class<?>, byte[]>();
}
public void redefineTypes(@Nonnull Object[] classesOrInstancesToBePartiallyMocked)
{
for (Object classOrInstance : classesOrInstancesToBePartiallyMocked) {
redefineClassHierarchy(classOrInstance);
}
if (!modifiedClassfiles.isEmpty()) {
new RedefinitionEngine().redefineMethods(modifiedClassfiles);
modifiedClassfiles.clear();
}
}
private void redefineClassHierarchy(@Nonnull Object classOrInstance)
{
Object mockInstance;
if (classOrInstance instanceof Class) {
mockInstance = null;
targetClass = (Class<?>) classOrInstance;
CaptureOfNewInstances capture = TestRun.mockFixture().findCaptureOfImplementations(targetClass);
if (capture != null) {
capture.useDynamicMocking(targetClass);
return;
}
applyPartialMockingToGivenClass();
}
else {
mockInstance = classOrInstance;
targetClass = getMockedClass(classOrInstance);
applyPartialMockingToGivenInstance(classOrInstance);
}
InstanceFactory instanceFactory = createInstanceFactory(targetClass);
instanceFactory.lastInstance = mockInstance;
TestRun.mockFixture().registerInstanceFactoryForMockedType(targetClass, instanceFactory);
TestRun.getExecutingTest().getCascadingTypes().add(false, targetClass);
}
private void applyPartialMockingToGivenClass()
{
validateTargetClassType();
TestRun.ensureThatClassIsInitialized(targetClass);
methodsOnly = false;
redefineMethodsAndConstructorsInTargetType();
}
private void applyPartialMockingToGivenInstance(@Nonnull Object instance)
{
validateTargetClassType();
methodsOnly = true;
redefineMethodsAndConstructorsInTargetType();
targetInstances.add(instance);
}
private void validateTargetClassType()
{
if (
targetClass.isInterface() || targetClass.isAnnotation() ||
targetClass.isArray() || targetClass.isPrimitive() ||
isWrapperOfPrimitiveType(targetClass) ||
isGeneratedImplementationClass(targetClass)
) {
throw new IllegalArgumentException("Invalid type for partial mocking: " + targetClass);
}
}
@Override
void configureClassModifier(@Nonnull ExpectationsModifier modifier) { modifier.useDynamicMocking(methodsOnly); }
@Override
void applyClassRedefinition(@Nonnull Class<?> realClass, @Nonnull byte[] modifiedClass)
{
modifiedClassfiles.put(realClass, modifiedClass);
}
}