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 java.io.Serializable; 9import java.lang.reflect.Method; 10import java.util.Collections; 11import java.util.LinkedList; 12import java.util.List; 13 14import org.hamcrest.Matcher; 15import org.mockito.internal.matchers.CapturesArguments; 16import org.mockito.internal.matchers.MatcherDecorator; 17import org.mockito.internal.matchers.VarargMatcher; 18import org.mockito.internal.reporting.PrintSettings; 19import org.mockito.invocation.DescribedInvocation; 20import org.mockito.invocation.Invocation; 21import org.mockito.invocation.Location; 22 23@SuppressWarnings("unchecked") 24public class InvocationMatcher implements DescribedInvocation, CapturesArgumensFromInvocation, Serializable { 25 26 private static final long serialVersionUID = -3047126096857467610L; 27 private final Invocation invocation; 28 private final List<Matcher> matchers; 29 30 public InvocationMatcher(Invocation invocation, List<Matcher> matchers) { 31 this.invocation = invocation; 32 if (matchers.isEmpty()) { 33 this.matchers = ArgumentsProcessor.argumentsToMatchers(invocation.getArguments()); 34 } else { 35 this.matchers = matchers; 36 } 37 } 38 39 public InvocationMatcher(Invocation invocation) { 40 this(invocation, Collections.<Matcher>emptyList()); 41 } 42 43 public Method getMethod() { 44 return invocation.getMethod(); 45 } 46 47 public Invocation getInvocation() { 48 return this.invocation; 49 } 50 51 public List<Matcher> getMatchers() { 52 return this.matchers; 53 } 54 55 public String toString() { 56 return new PrintSettings().print(matchers, invocation); 57 } 58 59 public boolean matches(Invocation actual) { 60 return invocation.getMock().equals(actual.getMock()) 61 && hasSameMethod(actual) 62 && new ArgumentsComparator().argumentsMatch(this, actual); 63 } 64 65 private boolean safelyArgumentsMatch(Object[] actualArgs) { 66 try { 67 return new ArgumentsComparator().argumentsMatch(this, actualArgs); 68 } catch (Throwable t) { 69 return false; 70 } 71 } 72 73 /** 74 * similar means the same method name, same mock, unverified 75 * and: if arguments are the same cannot be overloaded 76 */ 77 public boolean hasSimilarMethod(Invocation candidate) { 78 String wantedMethodName = getMethod().getName(); 79 String currentMethodName = candidate.getMethod().getName(); 80 81 final boolean methodNameEquals = wantedMethodName.equals(currentMethodName); 82 final boolean isUnverified = !candidate.isVerified(); 83 final boolean mockIsTheSame = getInvocation().getMock() == candidate.getMock(); 84 final boolean methodEquals = hasSameMethod(candidate); 85 86 if (!methodNameEquals || !isUnverified || !mockIsTheSame) { 87 return false; 88 } 89 90 final boolean overloadedButSameArgs = !methodEquals && safelyArgumentsMatch(candidate.getArguments()); 91 92 return !overloadedButSameArgs; 93 } 94 95 public boolean hasSameMethod(Invocation candidate) { 96 //not using method.equals() for 1 good reason: 97 //sometimes java generates forwarding methods when generics are in play see JavaGenericsForwardingMethodsTest 98 Method m1 = invocation.getMethod(); 99 Method m2 = candidate.getMethod(); 100 101 if (m1.getName() != null && m1.getName().equals(m2.getName())) { 102 /* Avoid unnecessary cloning */ 103 Class[] params1 = m1.getParameterTypes(); 104 Class[] params2 = m2.getParameterTypes(); 105 if (params1.length == params2.length) { 106 for (int i = 0; i < params1.length; i++) { 107 if (params1[i] != params2[i]) 108 return false; 109 } 110 return true; 111 } 112 } 113 return false; 114 } 115 116 public Location getLocation() { 117 return invocation.getLocation(); 118 } 119 120 public void captureArgumentsFrom(Invocation invocation) { 121 for (int position = 0; position < matchers.size(); position++) { 122 Matcher m = matchers.get(position); 123 if (m instanceof CapturesArguments && invocation.getArguments().length > position) { 124 if(isVariableArgument(invocation, position) && isVarargMatcher(m)) { 125 ((CapturesArguments) m).captureFrom(invocation.getRawArguments()[position]); 126 } else { 127 ((CapturesArguments) m).captureFrom(invocation.getArguments()[position]); 128 } 129 } 130 } 131 } 132 133 private boolean isVarargMatcher(Matcher matcher) { 134 Matcher actualMatcher = matcher; 135 if (actualMatcher instanceof MatcherDecorator) { 136 actualMatcher = ((MatcherDecorator) actualMatcher).getActualMatcher(); 137 } 138 return actualMatcher instanceof VarargMatcher; 139 } 140 141 private boolean isVariableArgument(Invocation invocation, int position) { 142 return invocation.getRawArguments().length - 1 == position 143 && invocation.getRawArguments()[position] != null 144 && invocation.getRawArguments()[position].getClass().isArray() 145 && invocation.getMethod().isVarArgs(); 146 } 147 148 public static List<InvocationMatcher> createFrom(List<Invocation> invocations) { 149 LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>(); 150 151 for (Invocation i : invocations) { 152 out.add(new InvocationMatcher(i)); 153 } 154 155 return out; 156 } 157}