/**
*
*/
package org.minnal.autopojo.resolver;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.minnal.autopojo.AttributeMetaData;
import org.minnal.autopojo.util.PropertyUtil;
/**
* @author ganeshs
*
*/
public class ObjectResolver extends AbstractAttributeResolver {
public Object resolve(Class<?> clazz, int maxDepth, Type... genericTypes) {
if (clazz.equals(Object.class)) {
return new Object();
}
maxDepth -= 1;
Map<TypeVariable<?>, Type> genericParameters = getGenericParameterMap(clazz, genericTypes);
Object pojo = constructPojo(clazz, genericParameters, maxDepth);
if (! PropertyUtil.isSimpleProperty(clazz)) {
for (PropertyDescriptor descriptor : PropertyUtils.getPropertyDescriptors(clazz)) {
if (shouldExclude(descriptor)) {
continue;
}
strategy.resolve(pojo, new AttributeMetaData(descriptor, genericParameters), maxDepth);
}
}
return pojo;
}
protected boolean shouldExclude(PropertyDescriptor descriptor) {
if (descriptor.getWriteMethod() == null) {
return true;
}
for (Class<? extends Annotation> annotation : configuration.getExcludeAnnotations()) {
if (PropertyUtil.hasAnnotation(descriptor, annotation)) {
return true;
}
}
if (descriptor.getReadMethod().getDeclaringClass().equals(Object.class)) {
return true;
}
return configuration.getExcludeFields().contains(descriptor.getName());
}
protected Object constructPojo(Class<?> clazz, Map<TypeVariable<?>, Type> genericParameters, int maxDepth) {
Object pojo = constructPojoUsingDefaultConstructor(clazz);
if (pojo != null) {
return pojo;
}
maxDepth -= 1;
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
pojo = constructPojoUsingConstructorWithArgs(constructor, genericParameters, maxDepth);
if (pojo != null) {
return pojo;
}
}
// TODO Should we throw an exception here??
return null;
}
private Object constructPojoUsingDefaultConstructor(Class<?> clazz) {
try {
Constructor<?> constructor = clazz.getConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (Exception e) {
// log error
return null;
}
}
private Object constructPojoUsingConstructorWithArgs(Constructor<?> constructor, Map<TypeVariable<?>, Type> genericParameters, int maxDepth) {
Type[] parameterTypes = constructor.getGenericParameterTypes();
if (parameterTypes.length == 0) {
return null;
}
Object[] parameters = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i] instanceof TypeVariable) {
parameters[i] = strategy.resolve(PropertyUtil.getRawType(genericParameters.get(parameterTypes[i])), maxDepth,
PropertyUtil.getTypeArguments(genericParameters.get(parameterTypes[i])));
} else if (parameterTypes[i] instanceof ParameterizedType) {
Type[] arguments = ((ParameterizedType) parameterTypes[i]).getActualTypeArguments();
parameters[i] = strategy.resolve(PropertyUtil.getRawType(parameterTypes[i]), maxDepth, getGenericTypes(arguments, genericParameters));
} else {
parameters[i] = strategy.resolve(PropertyUtil.getRawType(parameterTypes[i]), maxDepth, PropertyUtil.getTypeArguments(parameterTypes[i]));
}
}
try {
constructor.setAccessible(true);
return constructor.newInstance(parameters);
} catch (Exception e) {
// log error
return null;
}
}
private Type[] getGenericTypes(Type[] parameters, Map<TypeVariable<?>, Type> genericParameters) {
Type[] types = new Type[parameters.length];
for (int i = 0 ; i < parameters.length; i++) {
types[i] = genericParameters.containsKey(parameters[i]) ? genericParameters.get(parameters[i]) : Object.class;
}
return types;
}
private Map<TypeVariable<?>, Type> getGenericParameterMap(Class<?> clazz, Type[] genericTypes) {
Map<TypeVariable<?>, Type> genericParameters = new HashMap<TypeVariable<?>, Type>();
TypeVariable<?>[] params = clazz.getTypeParameters();
for (int i = 0; i < params.length; i++) {
if (genericTypes != null && genericTypes.length > i) {
genericParameters.put(params[i], genericTypes[i]);
} else {
genericParameters.put(params[i], Object.class);
}
}
return genericParameters;
}
}