1 /* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 6 package org.mockito.internal.invocation; 7 8 import java.io.Serializable; 9 import java.lang.reflect.Method; 10 import java.util.Collections; 11 import java.util.LinkedList; 12 import java.util.List; 13 14 import org.hamcrest.Matcher; 15 import org.mockito.internal.matchers.CapturesArguments; 16 import org.mockito.internal.matchers.MatcherDecorator; 17 import org.mockito.internal.matchers.VarargMatcher; 18 import org.mockito.internal.reporting.PrintSettings; 19 import org.mockito.invocation.DescribedInvocation; 20 import org.mockito.invocation.Invocation; 21 import org.mockito.invocation.Location; 22 23 @SuppressWarnings("unchecked") 24 public 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 InvocationMatcher(Invocation invocation, List<Matcher> matchers)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 InvocationMatcher(Invocation invocation)39 public InvocationMatcher(Invocation invocation) { 40 this(invocation, Collections.<Matcher>emptyList()); 41 } 42 getMethod()43 public Method getMethod() { 44 return invocation.getMethod(); 45 } 46 getInvocation()47 public Invocation getInvocation() { 48 return this.invocation; 49 } 50 getMatchers()51 public List<Matcher> getMatchers() { 52 return this.matchers; 53 } 54 toString()55 public String toString() { 56 return new PrintSettings().print(matchers, invocation); 57 } 58 matches(Invocation actual)59 public boolean matches(Invocation actual) { 60 return invocation.getMock().equals(actual.getMock()) 61 && hasSameMethod(actual) 62 && new ArgumentsComparator().argumentsMatch(this, actual); 63 } 64 safelyArgumentsMatch(Object[] actualArgs)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 */ hasSimilarMethod(Invocation candidate)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 hasSameMethod(Invocation candidate)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 getLocation()116 public Location getLocation() { 117 return invocation.getLocation(); 118 } 119 captureArgumentsFrom(Invocation invocation)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 isVarargMatcher(Matcher matcher)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 isVariableArgument(Invocation invocation, int position)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 createFrom(List<Invocation> invocations)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 }