// Path: src/main/java/com/timgroup/stanislavski/recording/FinalCallHandler.java
// public interface FinalCallHandler<T> {
//     T handle(MethodCall closingCall, Iterable<MethodCall> callHistory);
// }
// 
// Path: src/main/java/com/timgroup/stanislavski/recording/InterceptingMethodCallRecorder.java
// public class InterceptingMethodCallRecorder<T> implements MethodCallHandler {
//     
//     private final Predicate<MethodCall> finalCallMatcher;
//     private final FinalCallHandler<T> finalCallHandler;
//     private final ProxyFactory proxyFactory;
//     private final MethodCallRecorder recorder;
//     
//     public static <I, T, R> I proxying(Class<I> interfaceType,
//                                        Predicate<MethodCall> finalCallMatcher,
//                                        FinalCallHandler<T> finalCallHandler) {
//         return new InterceptingMethodCallRecorder<T>(finalCallMatcher, finalCallHandler).proxyFactory.getProxy(interfaceType);
//     }
//     
//     private InterceptingMethodCallRecorder(Predicate<MethodCall> predicate,
//                                            FinalCallHandler<T> methodCallDispatcher) {
//         this.finalCallMatcher = predicate;
//         this.finalCallHandler = methodCallDispatcher;
//         this.proxyFactory = ProxyFactory.forMethodCallHandler(this);
//         this.recorder = new MethodCallRecorder();
//     }
//     
//     @Override public Object handle(MethodCall methodCall) {
//         if (finalCallMatcher.apply(methodCall)) {
//             return finalCallHandler.handle(methodCall, recorder.callHistory());
//         }
//         recorder.record(methodCall);
//         return proxyFactory.getProxy(methodCall.returnType());
//     }
// }
// 
// 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);
//     }
// }


import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
import com.google.common.base.Predicate;
import com.timgroup.stanislavski.recording.FinalCallHandler;
import com.timgroup.stanislavski.recording.InterceptingMethodCallRecorder;
import com.timgroup.stanislavski.reflection.MethodCall;
package com.timgroup.stanislavski.magic.matchers;



public final class MagicJavaBeanMatcher<T, I> implements Predicate<MethodCall>, FinalCallHandler<Object> {
    private final Class<T> targetClass;

    public static class MagicJavaBeanMatcherBuilder<T> {
        private Class<T> targetClass;
        private MagicJavaBeanMatcherBuilder(Class<T> targetClass) {
            this.targetClass = targetClass;
        }
        public <I> I using(Class<I> interfaceClass) {
            MagicJavaBeanMatcher<T, I> matcher = new MagicJavaBeanMatcher<T, I>(targetClass);
// Path: src/main/java/com/timgroup/stanislavski/recording/FinalCallHandler.java
// public interface FinalCallHandler<T> {
//     T handle(MethodCall closingCall, Iterable<MethodCall> callHistory);
// }
// 
// Path: src/main/java/com/timgroup/stanislavski/recording/InterceptingMethodCallRecorder.java
// public class InterceptingMethodCallRecorder<T> implements MethodCallHandler {
//     
//     private final Predicate<MethodCall> finalCallMatcher;
//     private final FinalCallHandler<T> finalCallHandler;
//     private final ProxyFactory proxyFactory;
//     private final MethodCallRecorder recorder;
//     
//     public static <I, T, R> I proxying(Class<I> interfaceType,
//                                        Predicate<MethodCall> finalCallMatcher,
//                                        FinalCallHandler<T> finalCallHandler) {
//         return new InterceptingMethodCallRecorder<T>(finalCallMatcher, finalCallHandler).proxyFactory.getProxy(interfaceType);
//     }
//     
//     private InterceptingMethodCallRecorder(Predicate<MethodCall> predicate,
//                                            FinalCallHandler<T> methodCallDispatcher) {
//         this.finalCallMatcher = predicate;
//         this.finalCallHandler = methodCallDispatcher;
//         this.proxyFactory = ProxyFactory.forMethodCallHandler(this);
//         this.recorder = new MethodCallRecorder();
//     }
//     
//     @Override public Object handle(MethodCall methodCall) {
//         if (finalCallMatcher.apply(methodCall)) {
//             return finalCallHandler.handle(methodCall, recorder.callHistory());
//         }
//         recorder.record(methodCall);
//         return proxyFactory.getProxy(methodCall.returnType());
//     }
// }
// 
// 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/magic/matchers/MagicJavaBeanMatcher.java
import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
import com.google.common.base.Predicate;
import com.timgroup.stanislavski.recording.FinalCallHandler;
import com.timgroup.stanislavski.recording.InterceptingMethodCallRecorder;
import com.timgroup.stanislavski.reflection.MethodCall;

package com.timgroup.stanislavski.magic.matchers;



public final class MagicJavaBeanMatcher<T, I> implements Predicate<MethodCall>, FinalCallHandler<Object> {
    private final Class<T> targetClass;

    public static class MagicJavaBeanMatcherBuilder<T> {
        private Class<T> targetClass;
        private MagicJavaBeanMatcherBuilder(Class<T> targetClass) {
            this.targetClass = targetClass;
        }
        public <I> I using(Class<I> interfaceClass) {
            MagicJavaBeanMatcher<T, I> matcher = new MagicJavaBeanMatcher<T, I>(targetClass);
            return InterceptingMethodCallRecorder.proxying(interfaceClass, matcher, matcher);