// Path: library/src/main/java/com/danikula/aibolit/InjectingException.java
// public class InjectingException extends RuntimeException {
// 
//     private static final long serialVersionUID = -1L;
// 
//     public InjectingException(String message) {
//         super(message);
//     }
// 
//     public InjectingException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public InjectingException(Throwable cause) {
//         super(cause);
//     }
// }
// 
// Path: library/src/main/java/com/danikula/aibolit/InjectionContext.java
// public interface InjectionContext {
// 
//     View getRootView();
// 
//     Context getAndroidContext();
// 
// }
// 
// Path: library/src/main/java/com/danikula/aibolit/MethodInvocationHandler.java
// public class MethodInvocationHandler implements InvocationHandler {
// 
//     private Object methodOwner;
// 
//     private Method targetMethod;
// 
//     private Method sourceMethod;
// 
//     /**
//      * Constructs invokation handler. 
//      * 
//      * @param methodOwner Object object that contains fields or methods that should be injected
//      * @param sourceMethod Method method to be called
//      * @param targetMethod Method method of listener
//      */
//     public MethodInvocationHandler(Object methodOwner, Method sourceMethod, Method targetMethod) {
//         this.methodOwner = methodOwner;
//         this.targetMethod = targetMethod;
//         this.sourceMethod = sourceMethod;
//     }
// 
//     @Override
//     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//         if (method.getName().equals(targetMethod.getName())) {
//             sourceMethod.setAccessible(true);
//             return sourceMethod.invoke(methodOwner, args);
//         }
//         // call only one target method, ignore other
//         return null;
//     }
// }


import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import android.view.View;
import com.danikula.aibolit.InjectingException;
import com.danikula.aibolit.InjectionContext;
import com.danikula.aibolit.MethodInvocationHandler;
    protected void checkMethodSignature(Method sourceMethod, Method targetMethod) {
        Class<?>[] sourceMethodArgTypes = sourceMethod.getParameterTypes();
        Class<?>[] targetMethodArgTypes = targetMethod.getParameterTypes();

        if (!Arrays.equals(sourceMethodArgTypes, targetMethodArgTypes)) {
            throw new InjectingException(String.format("Method has incorrect parameters: %s. Expected: %s",
                    Arrays.toString(targetMethodArgTypes), Arrays.toString(sourceMethodArgTypes)));
        }
        if (!sourceMethod.getReturnType().equals(targetMethod.getReturnType())) {
            throw new InjectingException(String.format("Method has incorrect return type: %s. Expected: %s",
                    targetMethod.getReturnType(), sourceMethod.getReturnType()));
        }
    }

    protected Method getMethod(Class<?> methodOwner, String methodName, Class<?>[] argsTypes, Method sourceMethod) {
        String errorPattern = "Error getting method named '%s' from class %s";
        try {
            Method method = methodOwner.getMethod(methodName, argsTypes);
            checkMethodSignature(method, sourceMethod);
            return method;
        }
        catch (SecurityException e) {
            throw new IllegalArgumentException(String.format(errorPattern, methodName, methodOwner.getName()), e);
        }
        catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(String.format(errorPattern, methodName, methodOwner.getName()), e);
        }
    }

    protected <H> H createInvokationProxy(Class<H> proxyClass, Object methodOwner, Method sourceMethod, Method targetMethod) {
// Path: library/src/main/java/com/danikula/aibolit/InjectingException.java
// public class InjectingException extends RuntimeException {
// 
//     private static final long serialVersionUID = -1L;
// 
//     public InjectingException(String message) {
//         super(message);
//     }
// 
//     public InjectingException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public InjectingException(Throwable cause) {
//         super(cause);
//     }
// }
// 
// Path: library/src/main/java/com/danikula/aibolit/InjectionContext.java
// public interface InjectionContext {
// 
//     View getRootView();
// 
//     Context getAndroidContext();
// 
// }
// 
// Path: library/src/main/java/com/danikula/aibolit/MethodInvocationHandler.java
// public class MethodInvocationHandler implements InvocationHandler {
// 
//     private Object methodOwner;
// 
//     private Method targetMethod;
// 
//     private Method sourceMethod;
// 
//     /**
//      * Constructs invokation handler. 
//      * 
//      * @param methodOwner Object object that contains fields or methods that should be injected
//      * @param sourceMethod Method method to be called
//      * @param targetMethod Method method of listener
//      */
//     public MethodInvocationHandler(Object methodOwner, Method sourceMethod, Method targetMethod) {
//         this.methodOwner = methodOwner;
//         this.targetMethod = targetMethod;
//         this.sourceMethod = sourceMethod;
//     }
// 
//     @Override
//     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//         if (method.getName().equals(targetMethod.getName())) {
//             sourceMethod.setAccessible(true);
//             return sourceMethod.invoke(methodOwner, args);
//         }
//         // call only one target method, ignore other
//         return null;
//     }
// }


// Path: library/src/main/java/com/danikula/aibolit/injector/AbstractMethodInjector.java
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import android.view.View;
import com.danikula.aibolit.InjectingException;
import com.danikula.aibolit.InjectionContext;
import com.danikula.aibolit.MethodInvocationHandler;

    protected void checkMethodSignature(Method sourceMethod, Method targetMethod) {
        Class<?>[] sourceMethodArgTypes = sourceMethod.getParameterTypes();
        Class<?>[] targetMethodArgTypes = targetMethod.getParameterTypes();

        if (!Arrays.equals(sourceMethodArgTypes, targetMethodArgTypes)) {
            throw new InjectingException(String.format("Method has incorrect parameters: %s. Expected: %s",
                    Arrays.toString(targetMethodArgTypes), Arrays.toString(sourceMethodArgTypes)));
        }
        if (!sourceMethod.getReturnType().equals(targetMethod.getReturnType())) {
            throw new InjectingException(String.format("Method has incorrect return type: %s. Expected: %s",
                    targetMethod.getReturnType(), sourceMethod.getReturnType()));
        }
    }

    protected Method getMethod(Class<?> methodOwner, String methodName, Class<?>[] argsTypes, Method sourceMethod) {
        String errorPattern = "Error getting method named '%s' from class %s";
        try {
            Method method = methodOwner.getMethod(methodName, argsTypes);
            checkMethodSignature(method, sourceMethod);
            return method;
        }
        catch (SecurityException e) {
            throw new IllegalArgumentException(String.format(errorPattern, methodName, methodOwner.getName()), e);
        }
        catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(String.format(errorPattern, methodName, methodOwner.getName()), e);
        }
    }

    protected <H> H createInvokationProxy(Class<H> proxyClass, Object methodOwner, Method sourceMethod, Method targetMethod) {
        MethodInvocationHandler methodInvocationHandler = new MethodInvocationHandler(methodOwner, sourceMethod, targetMethod);