1/* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 6package org.mockito.internal.invocation; 7 8import static org.mockito.internal.invocation.ArgumentsProcessor.argumentsToMatchers; 9import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor; 10import static org.mockito.internal.invocation.TypeSafeMatching.matchesTypeSafe; 11 12import java.io.Serializable; 13import java.lang.reflect.Method; 14import java.util.Arrays; 15import java.util.Collections; 16import java.util.LinkedList; 17import java.util.List; 18import org.mockito.ArgumentMatcher; 19import org.mockito.internal.matchers.CapturesArguments; 20import org.mockito.internal.reporting.PrintSettings; 21import org.mockito.invocation.DescribedInvocation; 22import org.mockito.invocation.Invocation; 23import org.mockito.invocation.Location; 24import org.mockito.invocation.MatchableInvocation; 25 26/** 27 * In addition to all content of the invocation, the invocation matcher contains the argument matchers. Invocation matcher is used during verification and stubbing. In those cases, the user can provide argument matchers instead of 'raw' arguments. Raw arguments are converted to 'equals' matchers anyway. 28 */ 29@SuppressWarnings("serial") 30public class InvocationMatcher implements MatchableInvocation, DescribedInvocation, Serializable { 31 32 private final Invocation invocation; 33 private final List<ArgumentMatcher<?>> matchers; 34 35 @SuppressWarnings({ "rawtypes", "unchecked" }) 36 public InvocationMatcher(Invocation invocation, List<ArgumentMatcher> matchers) { 37 this.invocation = invocation; 38 if (matchers.isEmpty()) { 39 this.matchers = (List) argumentsToMatchers(invocation.getArguments()); 40 } else { 41 this.matchers = (List) matchers; 42 } 43 } 44 45 @SuppressWarnings("rawtypes") 46 public InvocationMatcher(Invocation invocation) { 47 this(invocation, Collections.<ArgumentMatcher> emptyList()); 48 } 49 50 public static List<InvocationMatcher> createFrom(List<Invocation> invocations) { 51 LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>(); 52 for (Invocation i : invocations) { 53 out.add(new InvocationMatcher(i)); 54 } 55 return out; 56 } 57 58 public Method getMethod() { 59 return invocation.getMethod(); 60 } 61 62 @Override 63 public Invocation getInvocation() { 64 return invocation; 65 } 66 67 @Override 68 @SuppressWarnings({ "unchecked", "rawtypes" }) 69 public List<ArgumentMatcher> getMatchers() { 70 return (List) matchers; 71 } 72 73 @Override 74 @SuppressWarnings({ "unchecked", "rawtypes" }) 75 public String toString() { 76 return new PrintSettings().print((List) matchers, invocation); 77 } 78 79 @Override 80 public boolean matches(Invocation candidate) { 81 return invocation.getMock().equals(candidate.getMock()) && hasSameMethod(candidate) && argumentsMatch(candidate); 82 } 83 84 /** 85 * similar means the same method name, same mock, unverified and: if arguments are the same cannot be overloaded 86 */ 87 @Override 88 public boolean hasSimilarMethod(Invocation candidate) { 89 String wantedMethodName = getMethod().getName(); 90 String candidateMethodName = candidate.getMethod().getName(); 91 92 if (!wantedMethodName.equals(candidateMethodName)) { 93 return false; 94 } 95 if (candidate.isVerified()) { 96 return false; 97 } 98 if (getInvocation().getMock() != candidate.getMock()) { 99 return false; 100 } 101 if (hasSameMethod(candidate)) { 102 return true; 103 } 104 105 return !argumentsMatch(candidate); 106 } 107 108 @Override 109 public boolean hasSameMethod(Invocation candidate) { 110 // not using method.equals() for 1 good reason: 111 // sometimes java generates forwarding methods when generics are in play see JavaGenericsForwardingMethodsTest 112 Method m1 = invocation.getMethod(); 113 Method m2 = candidate.getMethod(); 114 115 if (m1.getName() != null && m1.getName().equals(m2.getName())) { 116 /* Avoid unnecessary cloning */ 117 Class<?>[] params1 = m1.getParameterTypes(); 118 Class<?>[] params2 = m2.getParameterTypes(); 119 return Arrays.equals(params1, params2); 120 } 121 return false; 122 } 123 124 @Override 125 public Location getLocation() { 126 return invocation.getLocation(); 127 } 128 129 @Override 130 public void captureArgumentsFrom(Invocation invocation) { 131 MatcherApplicationStrategy strategy = getMatcherApplicationStrategyFor(invocation, matchers); 132 strategy.forEachMatcherAndArgument(captureArgument()); 133 } 134 135 private ArgumentMatcherAction captureArgument() { 136 return new ArgumentMatcherAction() { 137 138 @Override 139 public boolean apply(ArgumentMatcher<?> matcher, Object argument) { 140 if (matcher instanceof CapturesArguments) { 141 ((CapturesArguments) matcher).captureFrom(argument); 142 } 143 144 return true; 145 } 146 }; 147 } 148 149 @SuppressWarnings({ "rawtypes", "unchecked" }) 150 private boolean argumentsMatch(Invocation actual) { 151 List matchers = getMatchers(); 152 return getMatcherApplicationStrategyFor(actual, matchers).forEachMatcherAndArgument( matchesTypeSafe()); 153 } 154} 155