blob: e085c25a32561aaa0994458e7bfc83350af4dea4 [file] [log] [blame]
/*
* Copyright (c) 2016 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.invocation;
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT;
import java.util.ArrayList;
import java.util.List;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.CapturingMatcher;
import org.mockito.internal.matchers.VarargMatcher;
import org.mockito.invocation.Invocation;
public class MatcherApplicationStrategy {
private final Invocation invocation;
private final List<ArgumentMatcher<?>> matchers;
private final MatcherApplicationType matchingType;
private MatcherApplicationStrategy(Invocation invocation, List<ArgumentMatcher<?>> matchers, MatcherApplicationType matchingType) {
this.invocation = invocation;
if (matchingType == MATCH_EACH_VARARGS_WITH_LAST_MATCHER) {
int times = varargLength(invocation);
this.matchers = appendLastMatcherNTimes(matchers, times);
} else {
this.matchers = matchers;
}
this.matchingType = matchingType;
}
/**
* Returns the {@link MatcherApplicationStrategy} that must be used to capture the
* arguments of the given <b>invocation</b> using the given <b>matchers</b>.
*
* @param invocation
* that contain the arguments to capture
* @param matchers
* that will be used to capture the arguments of the invocation,
* the passed {@link List} is not required to contain a
* {@link CapturingMatcher}
* @return never <code>null</code>
*/
public static MatcherApplicationStrategy getMatcherApplicationStrategyFor(Invocation invocation, List<ArgumentMatcher<?>> matchers) {
MatcherApplicationType type = getMatcherApplicationType(invocation, matchers);
return new MatcherApplicationStrategy(invocation, matchers, type);
}
/**
* Applies the given {@link ArgumentMatcherAction} to all arguments and
* corresponding matchers
*
* @param action
* must not be <code>null</code>
* @return
* <ul>
* <li><code>true</code> if the given <b>action</b> returned
* <code>true</code> for all arguments and matchers passed to it.
* <li><code>false</code> if the given <b>action</b> returned
* <code>false</code> for one of the passed arguments and matchers
* <li><code>false</code> if the given matchers don't fit to the given invocation
* because too many or to few matchers are available.
* </ul>
*/
public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS)
return false;
Object[] arguments = invocation.getArguments();
for (int i = 0; i < arguments.length; i++) {
ArgumentMatcher<?> matcher = matchers.get(i);
Object argument = arguments[i];
if (!action.apply(matcher, argument)) {
return false;
}
}
return true;
}
private static MatcherApplicationType getMatcherApplicationType(Invocation invocation, List<ArgumentMatcher<?>> matchers) {
final int rawArguments = invocation.getRawArguments().length;
final int expandedArguments = invocation.getArguments().length;
final int matcherCount = matchers.size();
if (expandedArguments == matcherCount) {
return ONE_MATCHER_PER_ARGUMENT;
}
if (rawArguments == matcherCount && isLastMatcherVargargMatcher(matchers)) {
return MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
}
return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
}
private static boolean isLastMatcherVargargMatcher(final List<ArgumentMatcher<?>> matchers) {
return lastMatcher(matchers) instanceof VarargMatcher;
}
private static List<ArgumentMatcher<?>> appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher) {
ArgumentMatcher<?> lastMatcher = lastMatcher(matchers);
List<ArgumentMatcher<?>> expandedMatchers = new ArrayList<ArgumentMatcher<?>>(matchers);
for (int i = 0; i < timesToAppendLastMatcher; i++) {
expandedMatchers.add(lastMatcher);
}
return expandedMatchers;
}
private static int varargLength(Invocation invocation) {
int rawArgumentCount = invocation.getRawArguments().length;
int expandedArgumentCount = invocation.getArguments().length;
return expandedArgumentCount - rawArgumentCount;
}
private static ArgumentMatcher<?> lastMatcher(List<ArgumentMatcher<?>> matchers) {
return matchers.get(matchers.size() - 1);
}
enum MatcherApplicationType {
ONE_MATCHER_PER_ARGUMENT, MATCH_EACH_VARARGS_WITH_LAST_MATCHER, ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
}
}