// Path: src/main/java/com/timgroup/stanislavski/interpreters/MethodCallInterpreter.java
// public interface MethodCallInterpreter<T> extends Function<MethodCall, T> { }
// 
// Path: src/main/java/com/timgroup/stanislavski/magic/MethodNameToPropertyNameTranslator.java
// public class MethodNameToPropertyNameTranslator implements Function<String, String> {
// 
//     public static String interpret(MethodCall methodCall) {
//         return ExtractorFor.theMethodName()
//                            .compose(new MethodNameToPropertyNameTranslator())
//                            .chain(AnnotationOverride.<AddressesProperty, String>obtainingValueOf(AddressesProperty.class))
//                            .apply(methodCall);
//     }
//     
//     private static final List<Pattern> NO_UNDERSCORE_PATTERNS = 
//         Patterns.matching(One.of("with", "having", "")
//                              .followedByOneOf("An", "A", "The", "")
//                              .followedByOneOf("([A-Z].*)Of", "([A-Z].*)"));
//     
//     private static final List<Pattern> UNDERSCORE_PATTERNS = 
//         Patterns.matching(One.of("with_", "having_", "")
//                              .followedByOneOf("an_", "a_", "the_", "")
//                              .followedByOneOf("([a-z].*)_of", "([a-z].*)"));
//     
//     @Override
//     public String apply(String methodName) {
//         if (methodName.contains("_")) {
//             return interpret(methodName,
//                              UNDERSCORE_PATTERNS,
//                              TargetNameParser.UNDERSCORE_SEPARATED);
//         }
//         
//         return interpret(methodName,
//                          NO_UNDERSCORE_PATTERNS,
//                          TargetNameParser.CAMEL_CASE);
//     }
//     
//     private String interpret(String methodName,
//                              List<Pattern> patterns,
//                              TargetNameParser parser) {
//         for (Pattern pattern : patterns) {
//             Matcher matcher = pattern.matcher(methodName);
//             if (matcher.matches()) {
//                 return parser.parse(matcher.group(1))
//                              .formatWith(LOWER_CAMEL_CASE);
//             }
//         }
//         return methodName;
//     }
// }
// 
// 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.Matchers;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.timgroup.karg.reference.Getter;
import com.timgroup.karg.reflection.ReflectiveAccessorFactory;
import com.timgroup.stanislavski.interpreters.MethodCallInterpreter;
import com.timgroup.stanislavski.magic.MethodNameToPropertyNameTranslator;
import com.timgroup.stanislavski.reflection.MethodCall;
                                       .value()
                                       .newInstance()
                                       .apply(methodCall);
        } catch (IllegalAccessException e) {
          throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        }
    }
    
    public <V> Matcher<? super T> make(MethodCall methodCall, Matcher<? super V> matcher) {
        return make(getPropertyName(methodCall), matcher);
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public <V> Matcher<? super T> make(String propertyName, Matcher<? super V> matcher) {
        Getter<T, V> getter = (Getter) getterProvider.apply(propertyName);
        return JavaBeanPropertyMatcher.matching(propertyName, getter, matcher);
    }
    
    @SuppressWarnings("unchecked")
    private <V> Matcher<? super V> getMatcher(MethodCall methodCall) {
        Object firstArgumentValue = methodCall.firstArgument().value();
        if (firstArgumentValue instanceof Matcher) {
            return Matchers.is((Matcher<? super V>) firstArgumentValue);
        }
        return Matchers.is(firstArgumentValue);
    }

    private String getPropertyName(MethodCall methodCall) {
// Path: src/main/java/com/timgroup/stanislavski/interpreters/MethodCallInterpreter.java
// public interface MethodCallInterpreter<T> extends Function<MethodCall, T> { }
// 
// Path: src/main/java/com/timgroup/stanislavski/magic/MethodNameToPropertyNameTranslator.java
// public class MethodNameToPropertyNameTranslator implements Function<String, String> {
// 
//     public static String interpret(MethodCall methodCall) {
//         return ExtractorFor.theMethodName()
//                            .compose(new MethodNameToPropertyNameTranslator())
//                            .chain(AnnotationOverride.<AddressesProperty, String>obtainingValueOf(AddressesProperty.class))
//                            .apply(methodCall);
//     }
//     
//     private static final List<Pattern> NO_UNDERSCORE_PATTERNS = 
//         Patterns.matching(One.of("with", "having", "")
//                              .followedByOneOf("An", "A", "The", "")
//                              .followedByOneOf("([A-Z].*)Of", "([A-Z].*)"));
//     
//     private static final List<Pattern> UNDERSCORE_PATTERNS = 
//         Patterns.matching(One.of("with_", "having_", "")
//                              .followedByOneOf("an_", "a_", "the_", "")
//                              .followedByOneOf("([a-z].*)_of", "([a-z].*)"));
//     
//     @Override
//     public String apply(String methodName) {
//         if (methodName.contains("_")) {
//             return interpret(methodName,
//                              UNDERSCORE_PATTERNS,
//                              TargetNameParser.UNDERSCORE_SEPARATED);
//         }
//         
//         return interpret(methodName,
//                          NO_UNDERSCORE_PATTERNS,
//                          TargetNameParser.CAMEL_CASE);
//     }
//     
//     private String interpret(String methodName,
//                              List<Pattern> patterns,
//                              TargetNameParser parser) {
//         for (Pattern pattern : patterns) {
//             Matcher matcher = pattern.matcher(methodName);
//             if (matcher.matches()) {
//                 return parser.parse(matcher.group(1))
//                              .formatWith(LOWER_CAMEL_CASE);
//             }
//         }
//         return methodName;
//     }
// }
// 
// 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/JavaBeanPropertyMatcherMaker.java
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.timgroup.karg.reference.Getter;
import com.timgroup.karg.reflection.ReflectiveAccessorFactory;
import com.timgroup.stanislavski.interpreters.MethodCallInterpreter;
import com.timgroup.stanislavski.magic.MethodNameToPropertyNameTranslator;
import com.timgroup.stanislavski.reflection.MethodCall;

                                       .value()
                                       .newInstance()
                                       .apply(methodCall);
        } catch (IllegalAccessException e) {
          throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        }
    }
    
    public <V> Matcher<? super T> make(MethodCall methodCall, Matcher<? super V> matcher) {
        return make(getPropertyName(methodCall), matcher);
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public <V> Matcher<? super T> make(String propertyName, Matcher<? super V> matcher) {
        Getter<T, V> getter = (Getter) getterProvider.apply(propertyName);
        return JavaBeanPropertyMatcher.matching(propertyName, getter, matcher);
    }
    
    @SuppressWarnings("unchecked")
    private <V> Matcher<? super V> getMatcher(MethodCall methodCall) {
        Object firstArgumentValue = methodCall.firstArgument().value();
        if (firstArgumentValue instanceof Matcher) {
            return Matchers.is((Matcher<? super V>) firstArgumentValue);
        }
        return Matchers.is(firstArgumentValue);
    }

    private String getPropertyName(MethodCall methodCall) {
        return MethodNameToPropertyNameTranslator.interpret(methodCall);