/*
*
* * Copyright 2010, Unitils.org
* *
* * 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.unitils.mock.core.matching;
import org.unitils.core.UnitilsException;
import org.unitils.mock.Mock;
import org.unitils.mock.argumentmatcher.ArgumentMatcher;
import org.unitils.mock.argumentmatcher.ArgumentMatcherRepository;
import org.unitils.mock.argumentmatcher.impl.DefaultArgumentMatcher;
import org.unitils.mock.core.proxy.ProxyInvocation;
import org.unitils.mock.core.proxy.ProxyInvocationHandler;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.unitils.core.util.ObjectFormatter.MOCK_NAME_CHAIN_SEPARATOR;
import static org.unitils.mock.argumentmatcher.ArgumentMatcherPositionFinder.getArgumentMatcherIndexes;
import static org.unitils.mock.core.proxy.ProxyFactory.createUninitializedProxy;
import static org.unitils.mock.core.proxy.StackTraceUtils.getInvocationStackTrace;
import static org.unitils.mock.core.proxy.StackTraceUtils.getStackTraceStartingFrom;
/**
* @author Filip Neven
* @author Tim Ducheyne
*/
public class MatchingInvocationBuilder {
protected String currentMockName;
protected String definingMethodName;
protected StackTraceElement[] invokedAt;
protected MatchingInvocationHandler matchingInvocationHandler;
public synchronized <T> T startMatchingInvocation(String mockName, Class<T> mockedType, MatchingInvocationHandler matchingInvocationHandler) {
assertNotExpectingInvocation();
this.currentMockName = mockName;
this.matchingInvocationHandler = matchingInvocationHandler;
this.invokedAt = getInvocationStackTrace(Mock.class);
this.definingMethodName = invokedAt[0].getMethodName();
ArgumentMatcherRepository.getInstance().registerStartOfMatchingInvocation(invokedAt[1].getLineNumber());
return createUninitializedProxy(mockName, new InvocationHandler(matchingInvocationHandler), mockedType);
}
public synchronized void reset() {
this.currentMockName = null;
this.invokedAt = null;
this.definingMethodName = null;
}
public synchronized void assertNotExpectingInvocation() {
if (currentMockName != null && !isChainedMock()) {
UnitilsException exception = new UnitilsException("Invalid syntax. " + currentMockName + "." + definingMethodName + "() must be followed by a method invocation on the returned proxy. E.g. " + currentMockName + "." + definingMethodName + "().myMethod();");
exception.setStackTrace(getStackTraceStartingFrom(invokedAt, 1));
reset();
throw exception;
}
reset();
}
protected boolean isChainedMock() {
return currentMockName.contains(MOCK_NAME_CHAIN_SEPARATOR);
}
protected Object handleProxyInvocation(ProxyInvocation proxyInvocation, MatchingInvocationHandler matchingInvocationHandler) throws Throwable {
ArgumentMatcherRepository.getInstance().registerEndOfMatchingInvocation(proxyInvocation.getLineNumber(), proxyInvocation.getMethod().getName());
reset();
List<ArgumentMatcher> argumentMatchers = createArgumentMatchers(proxyInvocation);
Object result = matchingInvocationHandler.handleInvocation(proxyInvocation, argumentMatchers);
ArgumentMatcherRepository.getInstance().reset();
return result;
}
protected List<ArgumentMatcher> createArgumentMatchers(ProxyInvocation proxyInvocation) {
List<ArgumentMatcher> result = new ArrayList<ArgumentMatcher>();
ArgumentMatcherRepository argumentMatcherRepository = ArgumentMatcherRepository.getInstance();
int matchInvocationStartLineNr = argumentMatcherRepository.getMatchInvocationStartLineNr();
int matchInvocationEndLineNr = argumentMatcherRepository.getMatchInvocationEndLineNr();
int matchInvocationIndex = argumentMatcherRepository.getMatchInvocationIndex();
List<Integer> argumentMatcherIndexes = getArgumentMatcherIndexes(proxyInvocation, matchInvocationStartLineNr, matchInvocationEndLineNr, matchInvocationIndex);
int argumentIndex = 0;
Iterator<ArgumentMatcher> argumentMatcherIterator = ArgumentMatcherRepository.getInstance().getArgumentMatchers().iterator();
for (Object argument : proxyInvocation.getArguments()) {
if (argumentMatcherIndexes.contains(argumentIndex++)) {
result.add(argumentMatcherIterator.next());
} else {
result.add(new DefaultArgumentMatcher(argument));
}
}
argumentMatcherRepository.reset();
return result;
}
protected class InvocationHandler implements ProxyInvocationHandler {
private MatchingInvocationHandler matchingInvocationHandler;
public InvocationHandler(MatchingInvocationHandler matchingInvocationHandler) {
this.matchingInvocationHandler = matchingInvocationHandler;
}
public Object handleInvocation(ProxyInvocation proxyInvocation) throws Throwable {
return handleProxyInvocation(proxyInvocation, matchingInvocationHandler);
}
}
}