package ngp.util;

import java.net.*;
import java.awt.*;
import java.applet.*;
import java.lang.reflect.*;


/**
 * A utility class that contains methods for creating different 
 * kinds of objects from Strings.  All methods are static so it 
 * is not necessary to ever create an instance of this class.
 *
 * <p>
 * Note, these methods are typically convenience wrappers around
 * one or two-line methods that already exist in Java.  To avoid
 * dealing with exceptions early on, all these functions ignore 
 * errors and attempt to return invalid values.  This is not 
 * always reasonable, so use the standard Java functions directly
 * if you want to handle the exceptions smartly.
 *
 * @author Robert C. Duvall
 */
public final class FromString
{
    /**
     * Given a String representing an integer value, returns that 
     * value.  Returns 0 if string is not formatted properly.
     */
    public static int toInt (final String number)
    {
        try
        {
            return Integer.parseInt(number);
        }
        catch (Exception e)
        {
            System.err.println("*** badly formatted number: " + number);
            return 0;
        }
    }


    /**
     * Given a String representing an double value, returns that 
     * value.  Returns 0 if string is not formatted properly.
     */
    public static double toDouble (final String number)
    {
        try
        {
            return Double.parseDouble(number);
        }
        catch (NumberFormatException e)
        {
            System.err.println("*** badly formatted number: " + number);
            return 0;
        }
    }


    /**
     * Given a String representing the name of an image in the context
     * of an application, returns that image.  Returns null if image 
     * cannot be found.
     */
    public static Image toImage (final String imageName, Component c)
    {
        try
        {
            // try name as url, this will work for file:/xyz and http://xzy
            return c.getToolkit().getImage(FromString.toURL(imageName));
        }
        catch (Exception e)
        {
            try
            {
                // try it as a file name instead of a URL
                Image result = c.getToolkit().getImage(imageName);
                if (result == null)
                {
                    System.err.println("*** badly named image: " + imageName);
                }
                return result;
            }
            catch (Exception ee)
            {
                // should never get here
                return null;
            }
        }
    }


    /**
     * Given a String representing the name of an image in the context 
     * of an applet, returns that image.  Return null if image cannot 
     * be found.
     */
    public static Image toImage (final String imageName, Applet a)
    {
        try
        {
            // try name as url, this will work for file:/xyz and http://xzy
            return a.getImage(FromString.toURL(imageName, a));
        }
        catch (Exception e)
        {
            // give up
            System.err.println("*** badly named image: " + imageName);
            return a.createImage(25, 25);
        }
    }


    /**
     * Given a String representing a URL, try to convert it into a
     * valid URL.  Return null if not possible.
     */
    public static URL toURL (final String spec)
    {
        try
        {
            // try name as url, this will work for file:/xyz and http://xzy
            return new URL(spec);
        }
        catch (MalformedURLException e)
        {
            try
            {
                // check to see if file protocol was left off
                return new URL("file:" + spec);
            }
            catch (MalformedURLException ee)
            {
                try
                {
                    // check to see if http protocol was left off
                    return new URL("http://" + spec);
                }
                catch (MalformedURLException eee)
                {
                    // give up
                    System.err.println("*** badly formed URL: " + spec);
                    return null;
                }
            }
        }
    }

    /**
     * Given a String representing a URL, try to convert it into a
     * valid URL.  Return null if not possible.
     */
    public static URL toURL (final String spec, URL context)
    {
        try
        {
            // try name as url, this will work for any absolute URL
            return new URL(spec);
        }
        catch (MalformedURLException e)
        {
            try
            {
                // check to see if this URL is relative to another
                return new URL(context, spec);
            }
            catch (MalformedURLException ee)
            {
                // give up
                System.err.println("*** badly formed URL: " + spec);
                return null;
            }
        }
    }

    /**
     * Given a String representing a URL, try to convert it into a
     * valid URL.  Return null if not possible.
     */
    public static URL toURL (final String spec, Applet context)
    {
        try
        {
            // try name as url, this will work for any absolute URL
            return new URL(spec);
        }
        catch (MalformedURLException e)
        {
            try
            {
                // try url relative to html document base, i.e., user defaults
                return new URL(context.getDocumentBase(), spec);
            }
            catch (MalformedURLException ee)
            {
                try
                {
                    // try url relative to html code base, i.e., program defaults
                    return new URL(context.getCodeBase(), spec);
                }
                catch (MalformedURLException eee)
                {
                    // give up
                    System.err.println("*** badly formed URL: " + spec);
                    return null;
                }
            }
        }
    }


    /**
     * Given a String representing the fully qualified name of a 
     * class, returns an initialized instance of the corresponding 
     * class using default constructor.  Returns null if string 
     * does not name a valid class.
     */
    public static Object toNewInstance (final String className)
    {
        try
        {
            return Class.forName(className).newInstance();
        }
        catch (Exception e)
        {
            System.err.println("*** badly named class: " + className);
            return null;
        }
    }


    /**
     * Given String representing fully qualified name of a class and 
     * array of actual parameters, returns initialized instance of 
     * the corresponding class using matching constructor.  Returns 
     * null if string does not name a valid class or arguments do not 
     * match any possible constructors.
     */
    public static Object toNewInstance (final String className, Object[] args)
    {
        try
        {
            Class c = Class.forName(className);
            Constructor[] ctors = c.getDeclaredConstructors();
            for (int k = 0; k < ctors.length; k++)
            {
                Class[] params = ctors[k].getParameterTypes();
                if (params.length == args.length && isMatch(params, args))
                {
                    return ctors[k].newInstance(args);
                }
            }
            return null;
        }
        catch (Exception e)
        {
            System.err.println("*** badly named class: " + className);
            return null;
        }
    }

    // are parameters compatible types and in same order?
    private static boolean isMatch (Class[] formals, Object[] actuals)
    {
        for (int k = 0; k < formals.length; k++)
        {
            if (! isMatch(formals[k], actuals[k].getClass()))
            {
                return false;
            }
        }
        return true;
    }

    // is actual derived from formal class?
    private static boolean isMatch (Class formal, Class actual)
    {
        while (actual != null)
        {
            if (formal.getName().equals(actual.getName()))
            {
                return true;
            }
            actual = actual.getSuperclass();
        }
        return false;
    }
}
