// Path: src/main/java/com/timgroup/stanislavski/reflection/MethodCall.java
// public class MethodCall {
//     private final Method method;
//     private final Object[] rawArgs;
//     private final List<MethodCallArgument<?>> arguments;
//     
//     public static MethodCall create(Method method, Object...args) {
//         return new MethodCall(method, args);
//     }
// 
//     private MethodCall(Method method, Object[] rawArgs) {
//         this.method = method;
//         this.rawArgs = rawArgs;
//         this.arguments = MethodCallArgument.wrapArguments(method, rawArgs);
//     }
// 
//     public Class<?> targetClass() {
//         return method.getDeclaringClass();
//     }
//     
//     public Class<?> returnType() {
//         return method.getReturnType();
//     }
// 
//     public String name() {
//         return method.getName();
//     }
// 
//     public List<MethodCallArgument<?>> arguments() {
//         return arguments;
//     }
// 
//     public boolean hasAnnotation(Class<? extends Annotation> annotationClass) {
//         return method.isAnnotationPresent(annotationClass);
//     }
//     
//     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
//         return method.getAnnotation(annotationClass);
//     }
// 
//     public MethodCallArgument<?> firstArgument() {
//         return arguments.get(0);
//     }
//     
//     public Object applyTo(Object target) throws Throwable {
//         try {
//             return this.method.invoke(target, rawArgs);
//         } catch (InvocationTargetException e) {
//             throw e.getCause();
//         }
//     }
//     
//     @Override public String toString() {
//         return String.format("%s: %s", method, arguments);
//     }
// 
//     public List<Object> argumentValues() {
//         return Lists.transform(arguments, MethodCallArgument.TO_VALUE);
//     }
// }
// 
// Path: src/main/java/com/timgroup/stanislavski/reflection/MethodCallArgument.java
// public final class MethodCallArgument<F> {
// 
//     public static final Function<MethodCallArgument<?>, Object> TO_VALUE =
//         new Function<MethodCallArgument<?>, Object>() {
//             @Override public Object apply(MethodCallArgument<?> arg0) {
//                 return arg0.value();
//             }
//         };
//         
//     public static List<MethodCallArgument<?>> wrapArguments(Method method, Object[] args) {
//         if (args == null) {
//             return Collections.emptyList();
//         }
//         Builder<MethodCallArgument<?>> builder = ImmutableList.builder();
//         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
//         for (int i=0; i<args.length; i++) {
//             MethodCallArgument<?> argument = create(args[i], parameterAnnotations[i]);
//             builder.add(argument);
//         }
//         return builder.build();
//     }
//     
//     private final F value;
//     private final Class<F> type;
//     private List<Annotation> parameterAnnotations;
// 
//     @SuppressWarnings("unchecked")
//     public static <F> MethodCallArgument<F> create(F argument, Annotation...parameterAnnotations) {
//         List<Annotation> parameterAnnotationList = Lists.newArrayList(parameterAnnotations);
//         return new MethodCallArgument<F>(argument, argument == null ? null : (Class<F>) argument.getClass(), parameterAnnotationList);
//     }
//     
//     private MethodCallArgument(F value, Class<F> type, List<Annotation> parameterAnnotations) {
//         this.value = value;
//         this.type = type;
//         this.parameterAnnotations = parameterAnnotations;
//     }
// 
//     public Class<F> type() {
//         return type;
//     }
// 
//     public F value() {
//         return value;
//     }
//     
//     public List<Annotation> parameterAnnotations() {
//         return parameterAnnotations;
//     }
//     
//     public boolean hasAnnotation(Class<? extends Annotation> annotationClass) {
//         return Iterables.any(parameterAnnotations, Predicates.instanceOf(annotationClass));
//     }
//     
//     @SuppressWarnings("unchecked")
//     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
//         return (A) Iterables.find(parameterAnnotations, Predicates.instanceOf(annotationClass));
//     }
//     
//     @Override public String toString() {
//         return String.format("A %s of %s, annotated with %s", type, value, parameterAnnotations);
//     }
// }


import java.util.Collection;
import java.util.Iterator;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import com.google.common.collect.Lists;
import com.timgroup.stanislavski.reflection.MethodCall;
import com.timgroup.stanislavski.reflection.MethodCallArgument;
        return this;
    }
    
    @Override
    public void describeTo(Description description) {
        description.appendText(" a method call to the method ").appendValue(methodName).appendText(" on class ").appendValue(targetClass);
        if (arguments.size() > 0) {
            description.appendText(" with arguments ").appendValueList("[", ",", "]", arguments);
        }
    }

    @Override
    protected boolean matchesSafely(MethodCall item, Description mismatchDescription) {
        if (!item.targetClass().equals(targetClass)) {
            mismatchDescription.appendText(" the target class was ").appendValue(targetClass);
            return false;
        }
        if (!item.name().equals(methodName)) {
            mismatchDescription.appendText(" the method called was ").appendText(methodName);
            return false;
        }
        if (arguments.size() != item.arguments().size()) {
            mismatchDescription.appendText(" the number of arguments was ").appendValue(item.arguments().size());
            return false;
        }
        Iterator<AnArgument<?>> matcherIterator = arguments.iterator();
        Iterator<MethodCallArgument<?>> argumentIterator = item.arguments().iterator();
        int argumentIndex = 0;
        while (matcherIterator.hasNext()) {
            AnArgument<?> matcher = matcherIterator.next();
// Path: src/main/java/com/timgroup/stanislavski/reflection/MethodCall.java
// public class MethodCall {
//     private final Method method;
//     private final Object[] rawArgs;
//     private final List<MethodCallArgument<?>> arguments;
//     
//     public static MethodCall create(Method method, Object...args) {
//         return new MethodCall(method, args);
//     }
// 
//     private MethodCall(Method method, Object[] rawArgs) {
//         this.method = method;
//         this.rawArgs = rawArgs;
//         this.arguments = MethodCallArgument.wrapArguments(method, rawArgs);
//     }
// 
//     public Class<?> targetClass() {
//         return method.getDeclaringClass();
//     }
//     
//     public Class<?> returnType() {
//         return method.getReturnType();
//     }
// 
//     public String name() {
//         return method.getName();
//     }
// 
//     public List<MethodCallArgument<?>> arguments() {
//         return arguments;
//     }
// 
//     public boolean hasAnnotation(Class<? extends Annotation> annotationClass) {
//         return method.isAnnotationPresent(annotationClass);
//     }
//     
//     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
//         return method.getAnnotation(annotationClass);
//     }
// 
//     public MethodCallArgument<?> firstArgument() {
//         return arguments.get(0);
//     }
//     
//     public Object applyTo(Object target) throws Throwable {
//         try {
//             return this.method.invoke(target, rawArgs);
//         } catch (InvocationTargetException e) {
//             throw e.getCause();
//         }
//     }
//     
//     @Override public String toString() {
//         return String.format("%s: %s", method, arguments);
//     }
// 
//     public List<Object> argumentValues() {
//         return Lists.transform(arguments, MethodCallArgument.TO_VALUE);
//     }
// }
// 
// Path: src/main/java/com/timgroup/stanislavski/reflection/MethodCallArgument.java
// public final class MethodCallArgument<F> {
// 
//     public static final Function<MethodCallArgument<?>, Object> TO_VALUE =
//         new Function<MethodCallArgument<?>, Object>() {
//             @Override public Object apply(MethodCallArgument<?> arg0) {
//                 return arg0.value();
//             }
//         };
//         
//     public static List<MethodCallArgument<?>> wrapArguments(Method method, Object[] args) {
//         if (args == null) {
//             return Collections.emptyList();
//         }
//         Builder<MethodCallArgument<?>> builder = ImmutableList.builder();
//         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
//         for (int i=0; i<args.length; i++) {
//             MethodCallArgument<?> argument = create(args[i], parameterAnnotations[i]);
//             builder.add(argument);
//         }
//         return builder.build();
//     }
//     
//     private final F value;
//     private final Class<F> type;
//     private List<Annotation> parameterAnnotations;
// 
//     @SuppressWarnings("unchecked")
//     public static <F> MethodCallArgument<F> create(F argument, Annotation...parameterAnnotations) {
//         List<Annotation> parameterAnnotationList = Lists.newArrayList(parameterAnnotations);
//         return new MethodCallArgument<F>(argument, argument == null ? null : (Class<F>) argument.getClass(), parameterAnnotationList);
//     }
//     
//     private MethodCallArgument(F value, Class<F> type, List<Annotation> parameterAnnotations) {
//         this.value = value;
//         this.type = type;
//         this.parameterAnnotations = parameterAnnotations;
//     }
// 
//     public Class<F> type() {
//         return type;
//     }
// 
//     public F value() {
//         return value;
//     }
//     
//     public List<Annotation> parameterAnnotations() {
//         return parameterAnnotations;
//     }
//     
//     public boolean hasAnnotation(Class<? extends Annotation> annotationClass) {
//         return Iterables.any(parameterAnnotations, Predicates.instanceOf(annotationClass));
//     }
//     
//     @SuppressWarnings("unchecked")
//     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
//         return (A) Iterables.find(parameterAnnotations, Predicates.instanceOf(annotationClass));
//     }
//     
//     @Override public String toString() {
//         return String.format("A %s of %s, annotated with %s", type, value, parameterAnnotations);
//     }
// }


// Path: src/test/java/com/timgroup/stanislavski/matchers/AMethodCall.java
import java.util.Collection;
import java.util.Iterator;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import com.google.common.collect.Lists;
import com.timgroup.stanislavski.reflection.MethodCall;
import com.timgroup.stanislavski.reflection.MethodCallArgument;

        return this;
    }
    
    @Override
    public void describeTo(Description description) {
        description.appendText(" a method call to the method ").appendValue(methodName).appendText(" on class ").appendValue(targetClass);
        if (arguments.size() > 0) {
            description.appendText(" with arguments ").appendValueList("[", ",", "]", arguments);
        }
    }

    @Override
    protected boolean matchesSafely(MethodCall item, Description mismatchDescription) {
        if (!item.targetClass().equals(targetClass)) {
            mismatchDescription.appendText(" the target class was ").appendValue(targetClass);
            return false;
        }
        if (!item.name().equals(methodName)) {
            mismatchDescription.appendText(" the method called was ").appendText(methodName);
            return false;
        }
        if (arguments.size() != item.arguments().size()) {
            mismatchDescription.appendText(" the number of arguments was ").appendValue(item.arguments().size());
            return false;
        }
        Iterator<AnArgument<?>> matcherIterator = arguments.iterator();
        Iterator<MethodCallArgument<?>> argumentIterator = item.arguments().iterator();
        int argumentIndex = 0;
        while (matcherIterator.hasNext()) {
            AnArgument<?> matcher = matcherIterator.next();
            MethodCallArgument<?> argument = argumentIterator.next();