/* * Copyright (c) 2006-2013 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 mockit.external.asm4.*; import mockit.internal.capturing.*; import mockit.internal.state.*; import mockit.internal.util.*; import static mockit.internal.util.FieldReflection.*; class CaptureOfNewInstances extends CaptureOfImplementations { static final class Capture { final MockedType typeMetadata; private Object originalMockInstance; private final List<Object> instancesCaptured; private Capture(MockedType typeMetadata, Object originalMockInstance) { this.typeMetadata = typeMetadata; this.originalMockInstance = originalMockInstance; instancesCaptured = new ArrayList<Object>(4); } private boolean isInstanceAlreadyCaptured(Object mock) { return Utilities.containsReference(instancesCaptured, mock); } private boolean captureInstance(Object fieldOwner, Object instance) { if (instancesCaptured.size() < typeMetadata.getMaxInstancesToCapture()) { if (fieldOwner != null && originalMockInstance == null) { originalMockInstance = getFieldValue(typeMetadata.field, fieldOwner); } instancesCaptured.add(instance); return true; } return false; } void reset() { originalMockInstance = null; instancesCaptured.clear(); } } final Map<Class<?>, List<Capture>> baseTypeToCaptures; private MockedType typeMetadata; Capture captureFound; CaptureOfNewInstances() { baseTypeToCaptures = new HashMap<Class<?>, List<Capture>>(); } @Override public final ClassVisitor createModifier(ClassLoader cl, ClassReader cr, String baseTypeDesc) { ExpectationsModifier modifier = new ExpectationsModifier(cl, cr, typeMetadata); modifier.setClassNameForCapturedInstanceMethods(baseTypeDesc); if (typeMetadata.injectable) { modifier.useDynamicMockingForInstanceMethods(typeMetadata); } return modifier; } final void registerCaptureOfNewInstances(MockedType typeMetadata, Object mockInstance) { this.typeMetadata = typeMetadata; Class<?> baseType = typeMetadata.getClassType(); if (!typeMetadata.isFinalFieldOrParameter()) { makeSureAllSubtypesAreModified(baseType, typeMetadata.fieldFromTestClass); } List<Capture> captures = baseTypeToCaptures.get(baseType); if (captures == null) { captures = new ArrayList<Capture>(); baseTypeToCaptures.put(baseType, captures); } captures.add(new Capture(typeMetadata, mockInstance)); } final boolean captureNewInstance(Object fieldOwner, Object mock) { captureFound = null; Class<?> mockedClass = mock.getClass(); List<Capture> captures = baseTypeToCaptures.get(mockedClass); boolean constructorModifiedForCaptureOnly = captures == null; if (constructorModifiedForCaptureOnly) { captures = findCaptures(mockedClass); if (captures == null) { return false; } } for (Capture capture : captures) { if (capture.isInstanceAlreadyCaptured(mock)) { break; } else if (capture.captureInstance(fieldOwner, mock)) { captureFound = capture; break; } } if (typeMetadata.injectable) { if (captureFound != null) { TestRun.getExecutingTest().addCapturedInstanceForInjectableMock(captureFound.originalMockInstance, mock); } constructorModifiedForCaptureOnly = true; } else if (captureFound != null) { TestRun.getExecutingTest().addCapturedInstance(captureFound.originalMockInstance, mock); } return constructorModifiedForCaptureOnly; } private List<Capture> findCaptures(Class<?> mockedClass) { Class<?>[] interfaces = mockedClass.getInterfaces(); for (Class<?> anInterface : interfaces) { List<Capture> found = baseTypeToCaptures.get(anInterface); if (found != null) { return found; } } Class<?> superclass = mockedClass.getSuperclass(); if (superclass == Object.class) { return null; } List<Capture> found = baseTypeToCaptures.get(superclass); return found != null ? found : findCaptures(superclass); } @Override public final void cleanUp() { super.cleanUp(); baseTypeToCaptures.clear(); } }