// Path: src/main/java/com/timgroup/karg/naming/TargetName.java
// public class TargetName {
// 
//     public static TargetName fromMethodName(String methodName) {
//         if (methodName.contains("_")) {
//             return TargetNameParser.UNDERSCORE_SEPARATED.parse(methodName);
//         }
//         return TargetNameParser.CAMEL_CASE.parse(methodName);
//     }
//     
//     private final Iterable<String> words;
//     
//     public TargetName(Iterable<String> words) {
//         this.words = words;
//     }
//     
//     public TargetName prefixedWith(String prefix) {
//         return new TargetName(Iterables.concat(Lists.newArrayList(prefix), words));
//     }
//     
//     public TargetName withSuffix(String suffix) {
//         return new TargetName(Iterables.concat(words, Lists.newArrayList(suffix)));
//     }
//     
//     public boolean hasPrefix(String prefix) {
//         return prefix.equals(Iterables.getFirst(words, null));
//     }
//     
//     public TargetName withoutPrefix() {
//         return new TargetName(Iterables.skip(words, 1));
//     }
//     
//     public String formatWith(TargetNameFormatter formatter) {
//         return formatter.format(words);
//     }
// }
// 
// Path: src/main/java/com/timgroup/karg/naming/TargetNameFormatter.java
// public interface TargetNameFormatter {
//     
//     static final TargetNameFormatter LOWER_CAMEL_CASE = new LowerCamelCaseFormatter();
//     static final TargetNameFormatter UPPER_CAMEL_CASE = new UpperCamelCaseFormatter();
//     static final TargetNameFormatter UNDERSCORE_SEPARATED = new UnderscoreSeparatedFormatter();
//     
//     static final class LowerCamelCaseFormatter implements TargetNameFormatter {
//         
//         @Override
//         public String format(Iterable<String> words) {
//             Iterator<String> lowercased = transform(words.iterator(), LOWERCASE_TRIM);
//             if (!lowercased.hasNext()) {
//                 return "";
//             }
//             Iterator<String> lowerCamelCased = concat(singletonIterator(lowercased.next()),
//                                                       transform(lowercased, CAPITALISE));
//             return Joiner.on("").join(Lists.newArrayList(lowerCamelCased));
//         }
//     }
//     
//     static final class UpperCamelCaseFormatter implements TargetNameFormatter {
//         @Override
//         public String format(Iterable<String> words) {
//             Iterator<String> upperCamelCased = transform(words.iterator(),
//                                                          compose(CAPITALISE, LOWERCASE_TRIM));
//             return Joiner.on("").join(Lists.newArrayList(upperCamelCased));
//         }
//     }
//     
//     static final class UnderscoreSeparatedFormatter implements TargetNameFormatter {
//         @Override
//         public String format(Iterable<String> words) {
//             Iterator<String> upperCamelCased = transform(words.iterator(), LOWERCASE_TRIM);
//             return Joiner.on("_").join(newArrayList(upperCamelCased));
//         }
//     }
//     
//     public abstract String format(Iterable<String> words);
//     
// }
// 
// Path: src/main/java/com/timgroup/karg/reference/Setter.java
// public interface Setter<O, T> {
//     T set(O object, T newValue);
// }


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.google.common.base.Preconditions;
import com.timgroup.karg.naming.TargetName;
import com.timgroup.karg.naming.TargetNameFormatter;
import com.timgroup.karg.reference.Setter;
package com.timgroup.karg.reflection;



public class SetterMethod<C, V> implements Setter<C, V>, PropertyMethod {
    
    private final String propertyName;
    private final Method method;

    public static interface PropertyNameBinder {
        <C, V> SetterMethod<C, V> ofClass(Class<C> owningClass);
    }
    
    public static PropertyNameBinder forProperty(final String propertyName) {
        return new PropertyNameBinder() {
            @Override public <C, V> SetterMethod<C, V> ofClass(Class<C> owningClass) {
                Method setterMethod = ClassInspector.forClass(owningClass).findSetterMethod(propertyName);
                Preconditions.checkNotNull(setterMethod,
                                           "No setter method found for property %s",
                                           propertyName);
                return forMethod(propertyName, setterMethod);
            }
        };
    }
    
    public static <C, V> SetterMethod<C, V> forMethod(String propertyName, Method method) {
        return new SetterMethod<C, V>(propertyName, method);
    }
    
    public static <C, V> SetterMethod<C, V> forMethod(Method method) {
        return new SetterMethod<C, V>(getPropertyName(method), method);
    }

    private static String getPropertyName(Method method) {
// Path: src/main/java/com/timgroup/karg/naming/TargetName.java
// public class TargetName {
// 
//     public static TargetName fromMethodName(String methodName) {
//         if (methodName.contains("_")) {
//             return TargetNameParser.UNDERSCORE_SEPARATED.parse(methodName);
//         }
//         return TargetNameParser.CAMEL_CASE.parse(methodName);
//     }
//     
//     private final Iterable<String> words;
//     
//     public TargetName(Iterable<String> words) {
//         this.words = words;
//     }
//     
//     public TargetName prefixedWith(String prefix) {
//         return new TargetName(Iterables.concat(Lists.newArrayList(prefix), words));
//     }
//     
//     public TargetName withSuffix(String suffix) {
//         return new TargetName(Iterables.concat(words, Lists.newArrayList(suffix)));
//     }
//     
//     public boolean hasPrefix(String prefix) {
//         return prefix.equals(Iterables.getFirst(words, null));
//     }
//     
//     public TargetName withoutPrefix() {
//         return new TargetName(Iterables.skip(words, 1));
//     }
//     
//     public String formatWith(TargetNameFormatter formatter) {
//         return formatter.format(words);
//     }
// }
// 
// Path: src/main/java/com/timgroup/karg/naming/TargetNameFormatter.java
// public interface TargetNameFormatter {
//     
//     static final TargetNameFormatter LOWER_CAMEL_CASE = new LowerCamelCaseFormatter();
//     static final TargetNameFormatter UPPER_CAMEL_CASE = new UpperCamelCaseFormatter();
//     static final TargetNameFormatter UNDERSCORE_SEPARATED = new UnderscoreSeparatedFormatter();
//     
//     static final class LowerCamelCaseFormatter implements TargetNameFormatter {
//         
//         @Override
//         public String format(Iterable<String> words) {
//             Iterator<String> lowercased = transform(words.iterator(), LOWERCASE_TRIM);
//             if (!lowercased.hasNext()) {
//                 return "";
//             }
//             Iterator<String> lowerCamelCased = concat(singletonIterator(lowercased.next()),
//                                                       transform(lowercased, CAPITALISE));
//             return Joiner.on("").join(Lists.newArrayList(lowerCamelCased));
//         }
//     }
//     
//     static final class UpperCamelCaseFormatter implements TargetNameFormatter {
//         @Override
//         public String format(Iterable<String> words) {
//             Iterator<String> upperCamelCased = transform(words.iterator(),
//                                                          compose(CAPITALISE, LOWERCASE_TRIM));
//             return Joiner.on("").join(Lists.newArrayList(upperCamelCased));
//         }
//     }
//     
//     static final class UnderscoreSeparatedFormatter implements TargetNameFormatter {
//         @Override
//         public String format(Iterable<String> words) {
//             Iterator<String> upperCamelCased = transform(words.iterator(), LOWERCASE_TRIM);
//             return Joiner.on("_").join(newArrayList(upperCamelCased));
//         }
//     }
//     
//     public abstract String format(Iterable<String> words);
//     
// }
// 
// Path: src/main/java/com/timgroup/karg/reference/Setter.java
// public interface Setter<O, T> {
//     T set(O object, T newValue);
// }


// Path: src/main/java/com/timgroup/karg/reflection/SetterMethod.java
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.google.common.base.Preconditions;
import com.timgroup.karg.naming.TargetName;
import com.timgroup.karg.naming.TargetNameFormatter;
import com.timgroup.karg.reference.Setter;

package com.timgroup.karg.reflection;



public class SetterMethod<C, V> implements Setter<C, V>, PropertyMethod {
    
    private final String propertyName;
    private final Method method;

    public static interface PropertyNameBinder {
        <C, V> SetterMethod<C, V> ofClass(Class<C> owningClass);
    }
    
    public static PropertyNameBinder forProperty(final String propertyName) {
        return new PropertyNameBinder() {
            @Override public <C, V> SetterMethod<C, V> ofClass(Class<C> owningClass) {
                Method setterMethod = ClassInspector.forClass(owningClass).findSetterMethod(propertyName);
                Preconditions.checkNotNull(setterMethod,
                                           "No setter method found for property %s",
                                           propertyName);
                return forMethod(propertyName, setterMethod);
            }
        };
    }
    
    public static <C, V> SetterMethod<C, V> forMethod(String propertyName, Method method) {
        return new SetterMethod<C, V>(propertyName, method);
    }
    
    public static <C, V> SetterMethod<C, V> forMethod(Method method) {
        return new SetterMethod<C, V>(getPropertyName(method), method);
    }

    private static String getPropertyName(Method method) {
        return TargetName.fromMethodName(method.getName())