/*
 Copyright 2004-2008 Paul R. Holser, Jr.  All rights reserved.
 Licensed under the Academic Free License version 3.0
 */

package joptsimple.internal;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * <p>Helper methods for reflection.</p>
 *
 * @since 2.0
 * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
 * @version $Id: Reflection.java,v 1.1 2008/04/01 21:46:46 pholser Exp $
 */
public class Reflection {
    protected Reflection() {
        throw new UnsupportedOperationException();
    }

    /**
     * Finds an appropriate value converter for the given class.
     * 
     * @param clazz class to introspect on
     * @return a converter method or constructor
     */
    public static Member findConverter( Class clazz ) {
        Member method = findConverterMethod( clazz );
        if ( method != null )
            return method;

        Member constructor = findConverterConstructor( clazz );
        if ( constructor != null )
            return constructor;
        
        throw new IllegalArgumentException( clazz + " is not a value type" );
    }

    private static Method findConverterMethod( Class clazz ) {
        try {
            Method valueOf =
                clazz.getDeclaredMethod( "valueOf", new Class[] { String.class } );

            return matchesConverterRequirements( valueOf, clazz ) ? valueOf : null;
        }
        catch ( NoSuchMethodException ignored ) {
            return null;
        }
    }

    private static boolean matchesConverterRequirements( Method method,
        Class expectedReturnType ) {

        int modifiers = method.getModifiers();
        return Modifier.isPublic( modifiers )
            && Modifier.isStatic( modifiers )
            && expectedReturnType.equals( method.getReturnType() );
    }

    private static Constructor findConverterConstructor( Class clazz ) {
        try {
            return clazz.getConstructor( new Class[] { String.class } );
        }
        catch ( NoSuchMethodException ignored ) {
            return null;
        }
    }

    /**
     * Invokes the given constructor with the given args.
     *
     * @param constructor constructor to invoke
     * @param args arguments to hand to the constructor
     * @return the result of invoking the constructor
     * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
     */
    public static Object invokeQuietly( Constructor constructor, Object[] args ) {
        try {
            return constructor.newInstance( args );
        }
        catch ( InstantiationException ex ) {
            throw new ReflectionException( ex );
        }
        catch ( IllegalAccessException ex ) {
            throw new ReflectionException( ex );
        }
        catch ( InvocationTargetException ex ) {
            throw new ReflectionException( ex.getTargetException() );
        }
        catch ( IllegalArgumentException ex ) {
            throw new ReflectionException( ex );
        }
    }

    /**
     * Invokes the given static method with the given args.
     *
     * @param method method to invoke
     * @param args arguments to hand to the method
     * @return the result of invoking the method
     * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
     */
    public static Object invokeStaticQuietly( Method method, Object[] args ) {
        try {
            return method.invoke( null, args );
        }
        catch ( IllegalAccessException ex ) {
            throw new ReflectionException( ex );
        }
        catch ( InvocationTargetException ex ) {
            throw new ReflectionException( ex.getTargetException() );
        }
        catch ( IllegalArgumentException ex ) {
            throw new ReflectionException( ex );
        }
    }
}
