/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.reflection.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import net.ranides.assira.trace.ExceptionUtils;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class MethodUtils {
    
    private MethodUtils() {
        // utility class
    }

    /**
     * Sprawdza, czy podana metoda jest nadpisana przez metodę (override). Cały dowcip
     * to obsługa metod package-scope, które może przeładować klasa z tego samego
     * pakietu, a z innego nie.
     * @param candidate 
     * @param original 
     * @return 
     */
    public static boolean overrides(Method original, Method candidate) {
        if( !original.getName().equals(candidate.getName()) ) {
            return false;
        }
        if( MemberTraits.isPrivate(original) ) {
            return false;
        }
        Class<?> otype = original.getDeclaringClass();
        Class<?> ctype = candidate.getDeclaringClass();
        if(!otype.isAssignableFrom(ctype)) {
            return false;
        }

        // package scope check
        if( MemberTraits.isPackage(original)) {
            Package opackage = otype.getPackage();
            Package cpackage = ctype.getPackage();
            if( !opackage.equals(cpackage) ) {
                return false;
            }
        }
        return Arrays.equals(original.getParameterTypes(), candidate.getParameterTypes());
    }
    
    /**
     * Wywołuje na rzecz obiektu podaną metodę. 
     * @param that
     * @param method
     * @param arguments
     * @return
     * @throws InspectException
     */
    public static Object invoke(Method method, Object that, Object... arguments) {
        if(that == null && !MemberTraits.isStatic(method)) {
            throw new IllegalArgumentException("non-static method require this!=null");
        }

        try {
            return method.invoke(that, arguments);
        } catch (InvocationTargetException ex) {
            throw ExceptionUtils.rethrow(ex.getTargetException());
        } catch (ReflectiveOperationException cause) {
            throw ExceptionUtils.rethrow(cause);
        } 
    }
    
    public static Object invoke(Constructor<?> ctor, Object... arguments) {
        try {
            return ctor.newInstance(arguments);
        } catch (InvocationTargetException ex) {
            throw ExceptionUtils.rethrow(ex.getTargetException());
        } catch (ReflectiveOperationException cause) {
            throw ExceptionUtils.rethrow(cause);
        } 
    }
	
	@SuppressWarnings("unchecked")
	public static <T> T $invoke(Method method, Object that, Object... arguments) {
		return (T)invoke(method, that, arguments);
	}
    
}
