ClassUtils.java

package org.andromda.core.common;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

// TODO: FindBugs: This class has a simple name that is identical to that of its superclass. This can be exceptionally confusing.
/**
 * Contains utilities for dealing with classes.
 *
 * @author Chad Brandon
 * @author Bob Fields
 */
public class ClassUtils
    extends org.apache.commons.lang.ClassUtils
{
    /**
     * Creates a new instance of the class having the given <code>className</code>.
     *
     * @param className the name of the class to instantiate.
     * @return Object the new instance
     */
    public static Object newInstance(final String className)
    {
        try
        {
            return loadClass(className).newInstance();
        }
        catch (final Throwable throwable)
        {
            throw new ClassUtilsException(throwable);
        }
    }

    /**
     * Creates a new instance of the class given the <code>type</code>.
     *
     * @param type the type from which to instantiate the new instance.
     * @return Object the new instance
     */
    public static Object newInstance(final Class type)
    {
        try
        {
            return type != null ? type.newInstance() : null;
        }
        catch (final Throwable throwable)
        {
            throw new ClassUtilsException(throwable);
        }
    }

    /**
     * Loads and returns the class having the className. Will load but normal classes and the classes representing
     * primitives.
     *
     * @param className the name of the class to load.
     * @return Class the loaded class
     */
    public static Class loadClass(String className)
    {
        ExceptionUtils.checkEmpty(
            "className",
            className);
        className = StringUtils.trimToNull(className);

        // get rid of any array notation
        className = StringUtils.replace(
                className,
                "[]",
                "");

        final ClassLoader loader = getClassLoader();
        Class loadedClass;
        try
        {
            // check and see if it's a primitive and if so convert it
            if (ClassUtils.isPrimitiveType(className))
            {
                loadedClass = getPrimitiveClass(
                        className,
                        loader);
            }
            else
            {
                loadedClass = loader.loadClass(className);
            }
        }
        catch (final Throwable throwable)
        {
            throw new ClassUtilsException(throwable);
        }
        return loadedClass;
    }

    /**
     * Gets the appropriate class loader instance.
     *
     * @return the class loader.
     */
    public static ClassLoader getClassLoader()
    {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null)
        {
            loader = ClassUtils.class.getClassLoader();
            Thread.currentThread().setContextClassLoader(loader);
        }
        return loader;
    }

    /**
     * <p> Returns the type class name for a Java primitive.
     * </p>
     *
     * @param name a <code>String</code> with the name of the type
     * @param loader the loader to use.
     * @return a <code>String</code> with the name of the corresponding
     *         java.lang wrapper class if <code>name</code> is a Java
     *         primitive type; <code>false</code> if not
     */
    protected static Class getPrimitiveClass(
        final String name,
        final ClassLoader loader)
    {
        ExceptionUtils.checkEmpty(
            "name",
            name);
        ExceptionUtils.checkNull(
            "loader",
            loader);

        Class primitiveClass = null;
        if (isPrimitiveType(name) && !"void".equals(name))
        {
            final String className;
            if ("char".equals(name))
            {
                className = "Character";
            }
            else if ("int".equals(name))
            {
                className = "Integer";
            }
            else
            {
                className = StringUtils.capitalize(name);
            }

            try
            {
                if (StringUtils.isNotBlank(className))
                {
                    Field field = loader.loadClass(className).getField("TYPE");
                    primitiveClass = (Class)field.get(null);
                }
            }
            catch (final Exception exception)
            {
                throw new ClassUtilsException(exception);
            }
        }
        return primitiveClass;
    }

    /**
     * Returns a collection of all static fields values for the given
     * <code>clazz</code> and <code>type</code> of field.
     *
     * @param clazz  the Class from which to retrieve the static fields
     * @param type the type of static fields to retrieve, if null all are retrieved
     * @return Collection the collection of static field values.
     * @throws IllegalAccessException - if some aspect of this static field prevents it from being added to this collection.
     */
    public static Collection<Object> getStaticFieldValues(
        final Class clazz,
        final Class type)
        throws IllegalAccessException
    {
        ExceptionUtils.checkNull(
            "clazz",
            clazz);
        final Field[] fields = clazz.getFields();
        int fieldsNum = fields.length;

        final List<Object> values = new ArrayList<Object>();
        Field field;
        int modifiers;
        for (int ctr = 0; ctr < fieldsNum; ctr++)
        {
            field = fields[ctr];
            modifiers = field.getModifiers();
            if (Modifier.isStatic(modifiers))
            {
                if (type != null)
                {
                    if (type == field.getType())
                    {
                        values.add(fields[ctr].get(null));
                    }
                }
                else
                {
                    values.add(fields[ctr].get(null));
                }
            }
        }
        return values;
    }

    /**
     * Retrieves all interfaces for the given <code>className</code> (including <code>className</code>
     * itself, assuming it's an interface as well).
     *
     * @param className the root interface className
     * @return a list containing all interfaces ordered from the root down.
     */
    public static List<Class> getInterfaces(final String className)
    {
        final List<Class> interfaces = new ArrayList<Class>();
        if (StringUtils.isNotBlank(className))
        {
            interfaces.addAll(getInterfaces(ClassUtils.loadClass(className.trim())));
        }
        return interfaces;
    }

    /**
     * Retrieves all interfaces for the given <code>clazz</code> (including <code>clazz</code>
     * itself, assuming it's an interface as well).
     *
     * @param clazz the root interface class
     * @return a list containing all interfaces ordered from the root down.
     */
    public static List<Class> getInterfaces(final Class clazz)
    {
        final List<Class> interfaces = new ArrayList<Class>();
        if (clazz != null)
        {
            interfaces.addAll(ClassUtils.getAllInterfaces(clazz));
            if (clazz.isInterface())
            {
                interfaces.add(
                    0,
                    clazz);
            }
        }
        return interfaces;
    }

    /**
     * Gets the interfaces for the given <code>className</code> in reverse order.
     *
     * @param className the name of the class for which to retrieve the interfaces
     * @return the array containing the reversed interfaces.
     */
    public static Class[] getInterfacesReversed(final String className)
    {
        final List<Class> interfacesList = getInterfaces(className);
        Class[] interfaces = interfacesList.toArray(new Class[interfacesList.size()]);
        if (interfaces.length > 0)
        {
            CollectionUtils.reverseArray(interfaces);
        }
        return interfaces;
    }

    /**
     * <p>
     * Checks if a given type name is a Java primitive type. </p>
     *
     * @param name a <code>String</code> with the name of the type
     * @return <code>true</code> if <code>name</code> is a Java primitive type; <code>false</code> if not
     */
    protected static boolean isPrimitiveType(final String name)
    {
        return "void".equals(name) || "char".equals(name) || "byte".equals(name) || "short".equals(name) ||
        "int".equals(name) || "long".equals(name) || "float".equals(name) || "double".equals(name) ||
        "boolean".equals(name);
    }

    /**
     * The suffix for class files.
     */
    public static final String CLASS_EXTENSION = ".class";

    /**
     * Searches the contents of the <code>directoryUri</code> and returns the first
     * Class found that is of the given <code>type</code>.
     *
     * @param directoryUris the URIs to search, ie. directories or archives.
     * @param type the type to find.
     * @return the class or null if not found.
     */
    public static Class findClassOfType(
        final URL[] directoryUris,
        final Class type)
    {
        Class found = null;
        if (directoryUris != null && directoryUris.length > 0)
        {
            for (URL directoryUri : directoryUris)
            {
                final List<String> contents = ResourceUtils.getDirectoryContents(
                        directoryUri,
                        false,
                        null);
                for (final String path : contents)
                {
                    if (path.endsWith(CLASS_EXTENSION))
                    {
                        final String typeName =
                                StringUtils.replace(
                                        ResourceUtils.normalizePath(path).replace('/', '.'),
                                        CLASS_EXTENSION,
                                        "");
                        try
                        {
                            final Class loadedClass = getClassLoader().loadClass(typeName);
                            if (type.isAssignableFrom(loadedClass))
                            {
                                found = loadedClass;
                                break;
                            }
                        }
                        catch (final ClassNotFoundException exception)
                        {
                            // - ignore, means the file wasn't a class
                        }
                    }
                }
            }
        }
        return found;
    }

    /**
     * Loads all methods from the given <code>clazz</code> (this includes
     * all super class methods, public, private and protected).
     *
     * @param clazz the class to retrieve the methods.
     * @return the loaded methods.
     */
    public static List<Method> getAllMethods(final Class clazz)
    {
        final Set<Method> methods = new LinkedHashSet<Method>();
        loadMethods(clazz, methods);
        return new ArrayList<Method>(methods);
    }

    /**
     * Loads all methods from the given <code>clazz</code> (this includes
     * all super class methods).
     *
     * @param methods the list to load full of methods.
     * @param clazz the class to retrieve the methods.
     */
    private static void loadMethods(
        final Class clazz,
        final Set<Method> methods)
    {
        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
        if (clazz.getSuperclass() != null)
        {
            loadMethods(
                    clazz.getSuperclass(),
                    methods);
        }
    }

    /**
     * Indicates whether or not a class of the given <code>type</code>
     * is present in one of the given <code>directoryUris</code>.
     *
     * @param directoryUris the URIs to search, ie. directories or archives.
     * @param type the type to check.
     * @return true/false.
     */
    public static boolean isClassOfTypePresent(
        final URL[] directoryUris,
        final Class type)
    {
        return ClassUtils.findClassOfType(directoryUris, type) != null;
    }
}