/* | |
* Copyright 2001-2009 OFFIS, Tammo Freese | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.easymock.internal; | |
import java.io.Serializable; | |
import java.lang.reflect.Method; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import org.easymock.IAnswer; | |
import org.easymock.IArgumentMatcher; | |
public class RecordState implements IMocksControlState, Serializable { | |
private static final long serialVersionUID = -5418279681566430252L; | |
private ExpectedInvocation lastInvocation = null; | |
private boolean lastInvocationUsed = true; | |
private Result lastResult; | |
private final IMocksBehavior behavior; | |
private static Map<Class<?>, Object> emptyReturnValues = new HashMap<Class<?>, Object>(); | |
static { | |
emptyReturnValues.put(Void.TYPE, null); | |
emptyReturnValues.put(Boolean.TYPE, Boolean.FALSE); | |
emptyReturnValues.put(Byte.TYPE, Byte.valueOf((byte) 0)); | |
emptyReturnValues.put(Short.TYPE, Short.valueOf((short) 0)); | |
emptyReturnValues.put(Character.TYPE, Character.valueOf((char)0)); | |
emptyReturnValues.put(Integer.TYPE, Integer.valueOf(0)); | |
emptyReturnValues.put(Long.TYPE, Long.valueOf(0)); | |
emptyReturnValues.put(Float.TYPE, Float.valueOf(0)); | |
emptyReturnValues.put(Double.TYPE, Double.valueOf(0)); | |
} | |
private static Map<Class<?>, Class<?>> primitiveToWrapperType = new HashMap<Class<?>, Class<?>>(); | |
static { | |
primitiveToWrapperType.put(Boolean.TYPE, Boolean.class); | |
primitiveToWrapperType.put(Byte.TYPE, Byte.class); | |
primitiveToWrapperType.put(Short.TYPE, Short.class); | |
primitiveToWrapperType.put(Character.TYPE, Character.class); | |
primitiveToWrapperType.put(Integer.TYPE, Integer.class); | |
primitiveToWrapperType.put(Long.TYPE, Long.class); | |
primitiveToWrapperType.put(Float.TYPE, Float.class); | |
primitiveToWrapperType.put(Double.TYPE, Double.class); | |
} | |
public RecordState(IMocksBehavior behavior) { | |
this.behavior = behavior; | |
} | |
public void assertRecordState() { | |
} | |
public java.lang.Object invoke(Invocation invocation) { | |
closeMethod(); | |
List<IArgumentMatcher> lastMatchers = LastControl.pullMatchers(); | |
lastInvocation = new ExpectedInvocation(invocation, lastMatchers); | |
lastInvocationUsed = false; | |
return emptyReturnValueFor(invocation.getMethod().getReturnType()); | |
} | |
public void replay() { | |
closeMethod(); | |
if (LastControl.pullMatchers() != null) { | |
throw new IllegalStateException("matcher calls were used outside expectations"); | |
} | |
} | |
public void verify() { | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"calling verify is not allowed in record state")); | |
} | |
public void andReturn(Object value) { | |
requireMethodCall("return value"); | |
value = convertNumberClassIfNeccessary(value); | |
requireAssignable(value); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
lastResult = Result.createReturnResult(value); | |
} | |
public void andThrow(Throwable throwable) { | |
requireMethodCall("Throwable"); | |
requireValidThrowable(throwable); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
lastResult = Result.createThrowResult(throwable); | |
} | |
public void andAnswer(IAnswer<?> answer) { | |
requireMethodCall("answer"); | |
requireValidAnswer(answer); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
lastResult = Result.createAnswerResult(answer); | |
} | |
public void andDelegateTo(Object delegateTo) { | |
requireMethodCall("delegate"); | |
requireValidDelegation(delegateTo); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
lastResult = Result.createDelegatingResult(delegateTo); | |
} | |
public void andStubReturn(Object value) { | |
requireMethodCall("stub return value"); | |
value = convertNumberClassIfNeccessary(value); | |
requireAssignable(value); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
behavior.addStub(lastInvocation, Result.createReturnResult(value)); | |
lastInvocationUsed = true; | |
} | |
@SuppressWarnings("deprecation") | |
public void setDefaultReturnValue(Object value) { | |
requireMethodCall("default return value"); | |
value = convertNumberClassIfNeccessary(value); | |
requireAssignable(value); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
behavior.addStub( | |
lastInvocation.withMatcher(org.easymock.MockControl.ALWAYS_MATCHER), Result | |
.createReturnResult(value)); | |
lastInvocationUsed = true; | |
} | |
public void asStub() { | |
requireMethodCall("stub behavior"); | |
requireVoidMethod(); | |
behavior.addStub(lastInvocation, Result.createReturnResult(null)); | |
lastInvocationUsed = true; | |
} | |
@SuppressWarnings("deprecation") | |
public void setDefaultVoidCallable() { | |
requireMethodCall("default void callable"); | |
requireVoidMethod(); | |
behavior.addStub( | |
lastInvocation.withMatcher(org.easymock.MockControl.ALWAYS_MATCHER), Result | |
.createReturnResult(null)); | |
lastInvocationUsed = true; | |
} | |
public void andStubThrow(Throwable throwable) { | |
requireMethodCall("stub Throwable"); | |
requireValidThrowable(throwable); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
behavior.addStub(lastInvocation, Result.createThrowResult(throwable)); | |
lastInvocationUsed = true; | |
} | |
@SuppressWarnings("deprecation") | |
public void setDefaultThrowable(Throwable throwable) { | |
requireMethodCall("default Throwable"); | |
requireValidThrowable(throwable); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
behavior.addStub( | |
lastInvocation.withMatcher(org.easymock.MockControl.ALWAYS_MATCHER), Result | |
.createThrowResult(throwable)); | |
lastInvocationUsed = true; | |
} | |
public void andStubAnswer(IAnswer<?> answer) { | |
requireMethodCall("stub answer"); | |
requireValidAnswer(answer); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
behavior.addStub(lastInvocation, Result.createAnswerResult(answer)); | |
lastInvocationUsed = true; | |
} | |
public void andStubDelegateTo(Object delegateTo) { | |
requireMethodCall("stub delegate"); | |
requireValidDelegation(delegateTo); | |
if (lastResult != null) { | |
times(MocksControl.ONCE); | |
} | |
behavior.addStub(lastInvocation, Result | |
.createDelegatingResult(delegateTo)); | |
lastInvocationUsed = true; | |
} | |
public void times(Range range) { | |
requireMethodCall("times"); | |
requireLastResultOrVoidMethod(); | |
behavior.addExpected(lastInvocation, lastResult != null ? lastResult | |
: Result.createReturnResult(null), range); | |
lastInvocationUsed = true; | |
lastResult = null; | |
} | |
private Object createNumberObject(Object value, Class<?> returnType) { | |
if (!(value instanceof Number)) { | |
return value; | |
} | |
Number number = (Number) value; | |
if (returnType.equals(Byte.TYPE)) { | |
return number.byteValue(); | |
} else if (returnType.equals(Short.TYPE)) { | |
return number.shortValue(); | |
} else if (returnType.equals(Character.TYPE)) { | |
return (char) number.intValue(); | |
} else if (returnType.equals(Integer.TYPE)) { | |
return number.intValue(); | |
} else if (returnType.equals(Long.TYPE)) { | |
return number.longValue(); | |
} else if (returnType.equals(Float.TYPE)) { | |
return number.floatValue(); | |
} else if (returnType.equals(Double.TYPE)) { | |
return number.doubleValue(); | |
} else { | |
return number; | |
} | |
} | |
private Object convertNumberClassIfNeccessary(Object o) { | |
Class<?> returnType = lastInvocation.getMethod().getReturnType(); | |
return createNumberObject(o, returnType); | |
} | |
@SuppressWarnings("deprecation") | |
private void closeMethod() { | |
if (lastInvocationUsed && lastResult == null) { | |
return; | |
} | |
if (!isLastResultOrVoidMethod()) { | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"missing behavior definition for the preceding method call " | |
+ lastInvocation.toString())); | |
} | |
this.times(org.easymock.MockControl.ONE); | |
} | |
public static Object emptyReturnValueFor(Class<?> type) { | |
return type.isPrimitive() ? emptyReturnValues.get(type) : null; | |
} | |
private void requireMethodCall(String failMessage) { | |
if (lastInvocation == null) { | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"method call on the mock needed before setting " | |
+ failMessage)); | |
} | |
} | |
private void requireAssignable(Object returnValue) { | |
if (lastMethodIsVoidMethod()) { | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"void method cannot return a value")); | |
} | |
if (returnValue == null) { | |
return; | |
} | |
Class<?> returnedType = lastInvocation.getMethod().getReturnType(); | |
if (returnedType.isPrimitive()) { | |
returnedType = primitiveToWrapperType.get(returnedType); | |
} | |
if (!returnedType.isAssignableFrom(returnValue.getClass())) { | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"incompatible return value type")); | |
} | |
} | |
private void requireValidThrowable(Throwable throwable) { | |
if (throwable == null) | |
throw new RuntimeExceptionWrapper(new NullPointerException( | |
"null cannot be thrown")); | |
if (isValidThrowable(throwable)) | |
return; | |
throw new RuntimeExceptionWrapper(new IllegalArgumentException( | |
"last method called on mock cannot throw " | |
+ throwable.getClass().getName())); | |
} | |
private void requireValidAnswer(IAnswer<?> answer) { | |
if (answer == null) | |
throw new RuntimeExceptionWrapper(new NullPointerException( | |
"answer object must not be null")); | |
} | |
private void requireValidDelegation(Object delegateTo) { | |
if (delegateTo == null) | |
throw new RuntimeExceptionWrapper(new NullPointerException( | |
"delegated to object must not be null")); | |
// Would be nice to validate delegateTo is implementing the mock | |
// interface (not possible right now) | |
} | |
private void requireLastResultOrVoidMethod() { | |
if (isLastResultOrVoidMethod()) { | |
return; | |
} | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"last method called on mock is not a void method")); | |
} | |
private void requireVoidMethod() { | |
if (lastMethodIsVoidMethod()) { | |
return; | |
} | |
throw new RuntimeExceptionWrapper(new IllegalStateException( | |
"last method called on mock is not a void method")); | |
} | |
private boolean isLastResultOrVoidMethod() { | |
return lastResult != null || lastMethodIsVoidMethod(); | |
} | |
private boolean lastMethodIsVoidMethod() { | |
Class<?> returnType = lastInvocation.getMethod().getReturnType(); | |
return returnType.equals(Void.TYPE); | |
} | |
private boolean isValidThrowable(Throwable throwable) { | |
if (throwable instanceof RuntimeException) { | |
return true; | |
} | |
if (throwable instanceof Error) { | |
return true; | |
} | |
Class<?>[] exceptions = lastInvocation.getMethod().getExceptionTypes(); | |
Class<?> throwableClass = throwable.getClass(); | |
for (Class<?> exception : exceptions) { | |
if (exception.isAssignableFrom(throwableClass)) | |
return true; | |
} | |
return false; | |
} | |
public void checkOrder(boolean value) { | |
closeMethod(); | |
behavior.checkOrder(value); | |
} | |
public void makeThreadSafe(boolean threadSafe) { | |
behavior.makeThreadSafe(threadSafe); | |
} | |
public void checkIsUsedInOneThread(boolean shouldBeUsedInOneThread) { | |
behavior.shouldBeUsedInOneThread(shouldBeUsedInOneThread); | |
} | |
@SuppressWarnings("deprecation") | |
public void setDefaultMatcher(org.easymock.ArgumentsMatcher matcher) { | |
behavior.setDefaultMatcher(matcher); | |
} | |
@SuppressWarnings("deprecation") | |
public void setMatcher(Method method, org.easymock.ArgumentsMatcher matcher) { | |
requireMethodCall("matcher"); | |
behavior.setMatcher(lastInvocation.getMethod(), matcher); | |
} | |
} |