001package org.andromda.core.common;
002
003import java.lang.reflect.Constructor;
004import java.lang.reflect.Method;
005import java.util.HashMap;
006import java.util.Map;
007import org.apache.log4j.Logger;
008
009/**
010 * A class used for converting simple types to other types (i.e.
011 * java.lang.String to java.lang.Integer, etc).
012 *
013 * @author Chad Brandon
014 * @author Bob Fields
015 */
016public class Converter
017{
018    /**
019     * The prefix of the 'valueOf' method available on wrapper classes.
020     */
021    private static final String VALUE_OF_METHOD_NAME = "valueOf";
022
023    /**
024     * The logger instance.
025     */
026    private static final Logger LOGGER = Logger.getLogger(Converter.class);
027
028    /**
029     * Attempts to convert the <code>object</code> to the <code>expectedType</code>.
030     *
031     * @param object the object to convert.
032     * @param expectedType the type to which it should be converted.
033     * @return the converted object
034     */
035    public static Object convert(
036        Object object,
037        Class expectedType)
038    {
039        Method method = null;
040        try
041        {
042            if (expectedType == String.class)
043            {
044                object = object.toString();
045            }
046            else if (expectedType == Class.class)
047            {
048                object = ClassUtils.loadClass(object.toString());
049            }
050            else
051            {
052                final Class originalType = expectedType;
053                if (expectedType.isPrimitive())
054                {
055                    expectedType = (Class)primitiveWrappers.get(expectedType);
056                }
057                try
058                {
059                    method = expectedType.getDeclaredMethod(
060                            VALUE_OF_METHOD_NAME,
061                            new Class[] {object.getClass()});
062                    object = method.invoke(
063                            expectedType,
064                            object);
065                }
066                catch (final NoSuchMethodException exception)
067                {
068                    // - ignore
069                }
070
071                // - if we couldn't find the method try with the constructor
072                if (method == null)
073                {
074                    Constructor constructor;
075                    try
076                    {
077                        constructor = expectedType.getConstructor(new Class[] {originalType});
078                        object = constructor.newInstance(object);
079                    }
080                    catch (final NoSuchMethodException exception)
081                    {
082                        throw new IntrospectorException("Could not convert '" + object + "' to type '" +
083                            expectedType.getName() + '\'');
084                    }
085                }
086            }
087        }
088        catch (Throwable throwable)
089        {
090            if (throwable.getCause()!=null)
091            {
092                throwable = throwable.getCause();
093            }
094            // At least output the location where the error happened, not the entire stack trace.
095            StackTraceElement[] trace = throwable.getStackTrace();
096            String location = " AT " + trace[0].getClassName() + '.' + trace[0].getMethodName() + ':' + trace[0].getLineNumber();
097            if (throwable.getMessage()!=null)
098            {
099                location += ' ' + throwable.getMessage();
100            }
101            LOGGER.error("Converter " + throwable + " invoking " + object + " METHOD " + method + " WITH " + expectedType.getName() + location);
102            throw new IntrospectorException(throwable);
103        }
104        return object;
105    }
106
107    /**
108     * Stores each primitive and its associated wrapper class.
109     */
110    private static final Map primitiveWrappers = new HashMap();
111
112    /**
113     * Initialize the primitiveWrappers.
114     */
115    static
116    {
117        primitiveWrappers.put(
118            boolean.class,
119            Boolean.class);
120        primitiveWrappers.put(
121            int.class,
122            Integer.class);
123        primitiveWrappers.put(
124            long.class,
125            Long.class);
126        primitiveWrappers.put(
127            short.class,
128            Short.class);
129        primitiveWrappers.put(
130            byte.class,
131            Byte.class);
132        primitiveWrappers.put(
133            float.class,
134            Float.class);
135        primitiveWrappers.put(
136            double.class,
137            Double.class);
138        primitiveWrappers.put(
139            char.class,
140            Character.class);
141    }
142}