1 /* 2 * Copyright (c) 2016 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.invocation; 6 7 import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; 8 import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER; 9 import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT; 10 11 import java.util.ArrayList; 12 import java.util.List; 13 14 import org.mockito.ArgumentMatcher; 15 import org.mockito.internal.matchers.CapturingMatcher; 16 import org.mockito.internal.matchers.VarargMatcher; 17 import org.mockito.invocation.Invocation; 18 19 public class MatcherApplicationStrategy { 20 21 private final Invocation invocation; 22 private final List<ArgumentMatcher<?>> matchers; 23 private final MatcherApplicationType matchingType; 24 25 26 MatcherApplicationStrategy(Invocation invocation, List<ArgumentMatcher<?>> matchers, MatcherApplicationType matchingType)27 private MatcherApplicationStrategy(Invocation invocation, List<ArgumentMatcher<?>> matchers, MatcherApplicationType matchingType) { 28 this.invocation = invocation; 29 if (matchingType == MATCH_EACH_VARARGS_WITH_LAST_MATCHER) { 30 int times = varargLength(invocation); 31 this.matchers = appendLastMatcherNTimes(matchers, times); 32 } else { 33 this.matchers = matchers; 34 } 35 36 this.matchingType = matchingType; 37 } 38 39 /** 40 * Returns the {@link MatcherApplicationStrategy} that must be used to capture the 41 * arguments of the given <b>invocation</b> using the given <b>matchers</b>. 42 * 43 * @param invocation 44 * that contain the arguments to capture 45 * @param matchers 46 * that will be used to capture the arguments of the invocation, 47 * the passed {@link List} is not required to contain a 48 * {@link CapturingMatcher} 49 * @return never <code>null</code> 50 */ getMatcherApplicationStrategyFor(Invocation invocation, List<ArgumentMatcher<?>> matchers)51 public static MatcherApplicationStrategy getMatcherApplicationStrategyFor(Invocation invocation, List<ArgumentMatcher<?>> matchers) { 52 53 MatcherApplicationType type = getMatcherApplicationType(invocation, matchers); 54 return new MatcherApplicationStrategy(invocation, matchers, type); 55 } 56 57 /** 58 * Applies the given {@link ArgumentMatcherAction} to all arguments and 59 * corresponding matchers 60 * 61 * @param action 62 * must not be <code>null</code> 63 * @return 64 * <ul> 65 * <li><code>true</code> if the given <b>action</b> returned 66 * <code>true</code> for all arguments and matchers passed to it. 67 * <li><code>false</code> if the given <b>action</b> returned 68 * <code>false</code> for one of the passed arguments and matchers 69 * <li><code>false</code> if the given matchers don't fit to the given invocation 70 * because too many or to few matchers are available. 71 * </ul> 72 */ forEachMatcherAndArgument(ArgumentMatcherAction action)73 public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) { 74 if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS) 75 return false; 76 77 Object[] arguments = invocation.getArguments(); 78 for (int i = 0; i < arguments.length; i++) { 79 ArgumentMatcher<?> matcher = matchers.get(i); 80 Object argument = arguments[i]; 81 82 if (!action.apply(matcher, argument)) { 83 return false; 84 } 85 } 86 return true; 87 } 88 getMatcherApplicationType(Invocation invocation, List<ArgumentMatcher<?>> matchers)89 private static MatcherApplicationType getMatcherApplicationType(Invocation invocation, List<ArgumentMatcher<?>> matchers) { 90 final int rawArguments = invocation.getRawArguments().length; 91 final int expandedArguments = invocation.getArguments().length; 92 final int matcherCount = matchers.size(); 93 94 if (expandedArguments == matcherCount) { 95 return ONE_MATCHER_PER_ARGUMENT; 96 } 97 98 if (rawArguments == matcherCount && isLastMatcherVargargMatcher(matchers)) { 99 return MATCH_EACH_VARARGS_WITH_LAST_MATCHER; 100 } 101 102 return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; 103 } 104 isLastMatcherVargargMatcher(final List<ArgumentMatcher<?>> matchers)105 private static boolean isLastMatcherVargargMatcher(final List<ArgumentMatcher<?>> matchers) { 106 return lastMatcher(matchers) instanceof VarargMatcher; 107 } 108 appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher)109 private static List<ArgumentMatcher<?>> appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher) { 110 ArgumentMatcher<?> lastMatcher = lastMatcher(matchers); 111 112 List<ArgumentMatcher<?>> expandedMatchers = new ArrayList<ArgumentMatcher<?>>(matchers); 113 for (int i = 0; i < timesToAppendLastMatcher; i++) { 114 expandedMatchers.add(lastMatcher); 115 } 116 return expandedMatchers; 117 } 118 varargLength(Invocation invocation)119 private static int varargLength(Invocation invocation) { 120 int rawArgumentCount = invocation.getRawArguments().length; 121 int expandedArgumentCount = invocation.getArguments().length; 122 return expandedArgumentCount - rawArgumentCount; 123 } 124 lastMatcher(List<ArgumentMatcher<?>> matchers)125 private static ArgumentMatcher<?> lastMatcher(List<ArgumentMatcher<?>> matchers) { 126 return matchers.get(matchers.size() - 1); 127 } 128 129 enum MatcherApplicationType { 130 ONE_MATCHER_PER_ARGUMENT, MATCH_EACH_VARARGS_WITH_LAST_MATCHER, ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS; 131 } 132 } 133