org.unitils.objectvalidation.utils.TreeNodeCreator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unitils-objectvalidation Show documentation
Show all versions of unitils-objectvalidation Show documentation
Unitils module to validate objects.
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;
}
}
}