All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.unitils.objectvalidation.utils.TreeNodeCreator Maven / Gradle / Ivy

There is a newer version: 1.1.9
Show newest version
package org.unitils.objectvalidation.utils;

import static org.unitils.objectvalidation.utils.Utils.checkNotNull;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;

import org.unitils.objectvalidation.objectcreator.generator.helper.GeneratorHelper;

import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl;
import sun.reflect.generics.tree.Wildcard;

/**
 * Utility to visit a class hierarchy and return its tree node of classes.
 *
 * @author Jeroen Horemans
 * @author Matthieu Mestrez
 * @since Oct 18, 2013
 */
public final class TreeNodeCreator {

    private TreeNodeCreator() {
    }

    private static final Logger LOGGER = Logger.getLogger(TreeNodeCreator.class.getName());

    public static final TreeNode createTreeNodeFor(Type type) {

        TreeNode parentNode = new TreeNode(classForType(type));

        parentNode.setGenericSubtype(getGenericTypes(type));

        return fillInChildren(parentNode);
    }

    private static final TreeNode fillInChildren(TreeNode node) {

        checkNotNull(node, "The node cannot be null");

        Class value = node.getValue();

        if (valueConsideredAsPrimitiveOrWithoutChildNodes(value)) {
            return node;
        }

        createNodeForConstructor(node);

        return node;
    }

    private static void createNodeForConstructor(TreeNode node) {
        Constructor constructor = figureOutConstructor(node.getValue(), node);

        if (emptyOrNoConstructor(constructor)) {
            constructor.setAccessible(true);

            for (Type parameterType : constructor.getGenericParameterTypes()) {

                TreeNode child = new TreeNode(classForType(parameterType));
                child.setGenericSubtype(getGenericTypes(parameterType));
                node.addChild(child);
                fillInChildren(child);
            }
        }
    }

    private static Class classForType(Type type) {

        if (type instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) type).getRawType();
        } else if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof GenericArrayType) {
            return getGenericArrayTypeType(type);
        } else if (type instanceof TypeVariable) {
			TypeVariable temp = (TypeVariable) type;
			Type[] bounds = temp.getBounds();
			if (bounds.length != 0) {
				Object obj = GeneratorHelper.generateSubTypesObject(classForType(bounds[0]));
				return obj.getClass();
			}
		}

        LOGGER.warning("Objectvalidation does not yet handle type : " + type);
        return null;
    }

    private static Class getGenericArrayTypeType(Type type) {
        Type componentType = ((GenericArrayType) type).getGenericComponentType();
        Class componentClass = classForType(componentType);
        if (componentClass != null) {
            return Array.newInstance(componentClass, 0).getClass();
        } else {
            return null;
        }
    }

    private static List getGenericTypes(Type parameterType) {
        ArrayList treeNodes = new ArrayList();

        if (parameterType instanceof ParameterizedType) {
            Type[] arrayTypes = ((ParameterizedType) parameterType).getActualTypeArguments();

            for (Type type : arrayTypes) {
                if (!type.getClass().equals(Wildcard.class) && !type.getClass().equals(WildcardTypeImpl.class)) {
                    TreeNode node = createTreeNodeFor(type);
                    treeNodes.add(node);
                } else {
                    TreeNode node = new TreeNode(WildcardTypeImpl.class);
                    node.setType(parameterType);
                    treeNodes.add(node);
                }

            }

        } else if (parameterType instanceof Class) {

            Class parameterClass = (Class) parameterType;

            if (parameterClass.isArray()) {

                TreeNode node = createTreeNodeFor(parameterClass.getComponentType());
                treeNodes.add(node);

            }

        } else if (parameterType instanceof GenericArrayType) {

            GenericArrayType parameterClass = (GenericArrayType) parameterType;

            TreeNode node = createTreeNodeFor(parameterClass.getGenericComponentType());
            treeNodes.add(node);

        } else if (parameterType instanceof TypeVariable) {
			Class parameterClass = classForType(parameterType);
			if (parameterClass.isArray()) {

                TreeNode node = createTreeNodeFor(parameterClass.getComponentType());
                treeNodes.add(node);

            }
		} else {
            LOGGER.warning("TypeVariable " + parameterType.toString() + " is not fully managed in objectvalidation yet.");
        }
        return treeNodes;
    }

    private static boolean emptyOrNoConstructor(Constructor constructor) {
        return constructor != null && constructor.getParameterTypes().length > 0;
    }

    private static boolean valueConsideredAsPrimitiveOrWithoutChildNodes(Class value) {
        return value.isEnum()
                || value.isInterface()
                || value.isPrimitive()
                || value.isArray()
                || Number.class.isAssignableFrom(value)
                || String.class.isAssignableFrom(value)
                || Boolean.class.isAssignableFrom(value)
                || Collection.class.isAssignableFrom(value)
                || Exception.class.isAssignableFrom(value);
    }

    private static Constructor figureOutConstructor(Class value, TreeNode parentNode) {

        List> constructors = Arrays.asList(value.getDeclaredConstructors());
        Collections.sort(constructors, new ConstructorSizeComparator());

        for (Constructor constructor : constructors) {
            if (isCyclycDependencyOk(constructor, parentNode) && isNotGeneratedConstructor(constructor)) {
                return constructor;
            }
        }

        return null;
    }

    private static boolean isNotGeneratedConstructor(Constructor constructor) {
        return !constructor.isSynthetic();
    }

    private static boolean isCyclycDependencyOk(Constructor constructor, TreeNode parentNode) {

        for (Class type : constructor.getParameterTypes()) {
            if (isClassAlreadyInTheTree(type, parentNode)) {
                return false;
            }
        }

        return true;
    }

    private static boolean isClassAlreadyInTheTree(Class type, TreeNode node) {

        if (node == null) {
            return false;
        } else if (type.equals(node.getValue())) {
            return true;
        }

        return isClassAlreadyInTheTree(type, node.getParent());
    }

    /**
     * Sorts the constructor by their parameter size from the smallest to the
     * biggest.
     */
    private static class ConstructorSizeComparator implements Comparator>, Serializable {

        private static final long serialVersionUID = -7354619453997861875L;

        @Override
        public int compare(Constructor o1, Constructor o2) {
            return o2.getParameterTypes().length - o1.getParameterTypes().length;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy