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.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;


/**
 * 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);
    	}
    	
    	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) {
				TreeNode node = createTreeNodeFor(type);
				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 {
    		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