| /* |
| * Copyright (c) 2007 Mockito contributors |
| * This program is made available under the terms of the MIT License. |
| */ |
| |
| package org.mockito.internal.invocation; |
| |
| import org.hamcrest.Matcher; |
| import org.mockito.internal.matchers.CapturesArguments; |
| import org.mockito.internal.matchers.MatcherDecorator; |
| import org.mockito.internal.matchers.VarargMatcher; |
| import org.mockito.internal.reporting.PrintSettings; |
| import org.mockito.invocation.DescribedInvocation; |
| import org.mockito.invocation.Invocation; |
| import org.mockito.invocation.Location; |
| |
| import java.io.Serializable; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Method; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| @SuppressWarnings("unchecked") |
| public class InvocationMatcher implements DescribedInvocation, CapturesArgumensFromInvocation, Serializable { |
| |
| private static final long serialVersionUID = -3047126096857467610L; |
| private final Invocation invocation; |
| private final List<Matcher> matchers; |
| |
| public InvocationMatcher(Invocation invocation, List<Matcher> matchers) { |
| this.invocation = invocation; |
| if (matchers.isEmpty()) { |
| this.matchers = ArgumentsProcessor.argumentsToMatchers(invocation.getArguments()); |
| } else { |
| this.matchers = matchers; |
| } |
| } |
| |
| public InvocationMatcher(Invocation invocation) { |
| this(invocation, Collections.<Matcher>emptyList()); |
| } |
| |
| public Method getMethod() { |
| return invocation.getMethod(); |
| } |
| |
| public Invocation getInvocation() { |
| return this.invocation; |
| } |
| |
| public List<Matcher> getMatchers() { |
| return this.matchers; |
| } |
| |
| public String toString() { |
| return new PrintSettings().print(matchers, invocation); |
| } |
| |
| public boolean matches(Invocation actual) { |
| return invocation.getMock().equals(actual.getMock()) |
| && hasSameMethod(actual) |
| && new ArgumentsComparator().argumentsMatch(this, actual); |
| } |
| |
| private boolean safelyArgumentsMatch(Object[] actualArgs) { |
| try { |
| return new ArgumentsComparator().argumentsMatch(this, actualArgs); |
| } catch (Throwable t) { |
| return false; |
| } |
| } |
| |
| /** |
| * similar means the same method name, same mock, unverified |
| * and: if arguments are the same cannot be overloaded |
| */ |
| public boolean hasSimilarMethod(Invocation candidate) { |
| String wantedMethodName = getMethod().getName(); |
| String currentMethodName = candidate.getMethod().getName(); |
| |
| final boolean methodNameEquals = wantedMethodName.equals(currentMethodName); |
| final boolean isUnverified = !candidate.isVerified(); |
| final boolean mockIsTheSame = getInvocation().getMock() == candidate.getMock(); |
| final boolean methodEquals = hasSameMethod(candidate); |
| |
| if (!methodNameEquals || !isUnverified || !mockIsTheSame) { |
| return false; |
| } |
| |
| final boolean overloadedButSameArgs = !methodEquals && safelyArgumentsMatch(candidate.getArguments()); |
| |
| return !overloadedButSameArgs; |
| } |
| |
| public boolean hasSameMethod(Invocation candidate) { |
| //not using method.equals() for 1 good reason: |
| //sometimes java generates forwarding methods when generics are in play see JavaGenericsForwardingMethodsTest |
| Method m1 = invocation.getMethod(); |
| Method m2 = candidate.getMethod(); |
| |
| if (m1.getName() != null && m1.getName().equals(m2.getName())) { |
| /* Avoid unnecessary cloning */ |
| Class[] params1 = m1.getParameterTypes(); |
| Class[] params2 = m2.getParameterTypes(); |
| if (params1.length == params2.length) { |
| for (int i = 0; i < params1.length; i++) { |
| if (params1[i] != params2[i]) |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public Location getLocation() { |
| return invocation.getLocation(); |
| } |
| |
| public void captureArgumentsFrom(Invocation invocation) { |
| for (int position = 0; position < matchers.size(); position++) { |
| Matcher m = matchers.get(position); |
| if (m instanceof CapturesArguments && invocation.getRawArguments().length > position) { |
| //TODO SF - this whole lot can be moved captureFrom implementation |
| if(isVariableArgument(invocation, position) && isVarargMatcher(m)) { |
| Object array = invocation.getRawArguments()[position]; |
| for (int i = 0; i < Array.getLength(array); i++) { |
| ((CapturesArguments) m).captureFrom(Array.get(array, i)); |
| } |
| //since we've captured all varargs already, it does not make sense to process other matchers. |
| return; |
| } else { |
| ((CapturesArguments) m).captureFrom(invocation.getRawArguments()[position]); |
| } |
| } |
| } |
| } |
| |
| private boolean isVarargMatcher(Matcher matcher) { |
| Matcher actualMatcher = matcher; |
| if (actualMatcher instanceof MatcherDecorator) { |
| actualMatcher = ((MatcherDecorator) actualMatcher).getActualMatcher(); |
| } |
| return actualMatcher instanceof VarargMatcher; |
| } |
| |
| private boolean isVariableArgument(Invocation invocation, int position) { |
| return invocation.getRawArguments().length - 1 == position |
| && invocation.getRawArguments()[position] != null |
| && invocation.getRawArguments()[position].getClass().isArray() |
| && invocation.getMethod().isVarArgs(); |
| } |
| |
| public static List<InvocationMatcher> createFrom(List<Invocation> invocations) { |
| LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>(); |
| |
| for (Invocation i : invocations) { |
| out.add(new InvocationMatcher(i)); |
| } |
| |
| return out; |
| } |
| } |