/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal; import java.lang.reflect.*; import javax.annotation.*; import mockit.*; import mockit.internal.reflection.*; /** * Base class for encapsulating state and logic needed by both the Expectations and Mockups APIs, but which should not * go into {@link Invocation} in order to keep the published API clean. */ public abstract class BaseInvocation extends Invocation { @Nullable protected Member realMember; @Nullable protected BaseInvocation previousInvocation; protected BaseInvocation(@Nullable Object invokedInstance, @Nonnull Object[] invokedArguments, int invocationCount) { super(invokedInstance, invokedArguments, invocationCount); } @Nonnull public final Member getRealMember() { if (realMember == null) { realMember = findRealMember(); } return realMember; } @Nonnull protected abstract Member findRealMember(); @Nullable public final <T> T doProceed(@Nullable Object[] replacementArguments) { Member memberToInvoke = getRealMember(); if (memberToInvoke instanceof Constructor) { prepareToProceed(); return null; } prepareToProceed(); Method realMethod = (Method) memberToInvoke; Object[] actualArgs = getInvokedArguments(); if (replacementArguments != null && replacementArguments.length > 0) { actualArgs = realMethod.isVarArgs() ? createArgumentsArrayWithVarargs(actualArgs.length, replacementArguments) : replacementArguments; } try { return MethodReflection.invoke(getInvokedInstance(), realMethod, actualArgs); } finally { cleanUpAfterProceed(); } } @Nonnull private static Object[] createArgumentsArrayWithVarargs(int numInvokedArgs, @Nonnull Object[] replacementArguments) { int m = numInvokedArgs - 1; Object[] actualArgs = new Object[numInvokedArgs]; System.arraycopy(replacementArguments, 0, actualArgs, 0, m); Object[] replacementVarargs = new Object[replacementArguments.length - m]; System.arraycopy(replacementArguments, m, replacementVarargs, 0, replacementVarargs.length); actualArgs[m] = replacementVarargs; return actualArgs; } public abstract void prepareToProceed(); protected abstract void cleanUpAfterProceed(); @Nullable public final BaseInvocation getPrevious() { return previousInvocation; } public final void setPrevious(@Nonnull BaseInvocation previous) { previousInvocation = previous; } public final boolean isMethodInSuperclass(@Nullable Object mock, @Nonnull String classDesc) { if (mock != null && mock == getInvokedInstance() && getInvokedMember() instanceof Method) { Method methodToInvoke = getInvokedMember(); String invokedClassDesc = methodToInvoke.getDeclaringClass().getName().replace('.', '/'); return !invokedClassDesc.equals(classDesc); } return previousInvocation != null && previousInvocation.isMethodInSuperclass(mock, classDesc); } }