package org.test4j.datafilling.filler.pojo;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.test4j.datafilling.Filler;
import org.test4j.datafilling.annotations.FillWith;
import org.test4j.datafilling.common.AttributeInfo;
import org.test4j.datafilling.common.ClassFieldInfo;
import org.test4j.datafilling.common.FillingConstants;
import org.test4j.datafilling.exceptions.PoJoFillException;
import org.test4j.datafilling.filler.PoJoFiller;
import org.test4j.datafilling.strategy.AttributeStrategy;
import org.test4j.datafilling.strategy.DataFactory;
import org.test4j.tools.reflector.MethodAccessor;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SetterMethodFiller extends PoJoFiller {
public SetterMethodFiller(DataFactory strategy, Map<String, Type> argsTypeMap) {
super(strategy, argsTypeMap);
}
public <T> T fillingWithSetter(AttributeInfo pojoAttr, int depth) throws PoJoFillException {
try {
ClassFieldInfo classInfo = pojoAttr.getClassInfo();
Object instance = pojoAttr.newInstance();
for (Method setter : classInfo.getClassSetters()) {
Object o = fillPoJoWithSetter(pojoAttr, depth, argsTypeMap, setter);
new MethodAccessor(setter).invoke(instance, new Object[] { o });
}
return (T) instance;
} catch (Exception e) {
throw new PoJoFillException("An filling exception occurred:" + e.getMessage(), e);
}
}
private FillWith getFilling(AttributeInfo fieldAttr) {
for (Annotation annotation : fieldAttr.getAttrAnnotations()) {
if (FillWith.class.isAssignableFrom(annotation.getClass())) {
FillWith filling = (FillWith) annotation;
return filling;
}
}
return null;
}
/**
* 通过setter方法来填充PoJo实例
*
* @param pojoInstance pojo实例
* @param depth 当前填充深度
* @param typeArgsMap 泛型名称的类型对
* @param setter set方法
* @throws Exception
*/
private Object fillPoJoWithSetter(AttributeInfo pojoAttr, int depth, final Map<String, Type> typeArgsMap,
Method setter) throws Exception {
Class[] parameterTypes = setter.getParameterTypes();
if (parameterTypes.length != 1) {
throw new IllegalStateException("A JavaBean setter should have only one argument");
}
AttributeInfo fieldAttr = getFieldAttributeInfo(pojoAttr, typeArgsMap, setter);
FillWith attributeStrategyAnnotation = getFilling(fieldAttr);
if (null != attributeStrategyAnnotation) {
AttributeStrategy attributeStrategy = attributeStrategyAnnotation.value().newInstance();
return returnAttributeDataStrategyValue(fieldAttr.getAttrClaz(), attributeStrategy);
} else if (fieldAttr.isNestedAttribute()) {
return getNestedObjectInstance(pojoAttr, depth, typeArgsMap);
} else {
Object setterArg = new Filler(strategy, typeArgsMap).fillingAttribute(fieldAttr);
return setterArg;
}
}
private AttributeInfo getFieldAttributeInfo(AttributeInfo pojoAttr, final Map<String, Type> typeArgsMap,
Method setter) {
Type attributeType = setter.getGenericParameterTypes()[0];
String attributeName = ClassFieldInfo.extractFieldNameFromSetterMethod(setter);
List<Annotation> pojoAttributeAnnotations = retrieveFieldAnnotations(pojoAttr, setter);
AttributeInfo fieldAttr = AttributeInfo.exactArgAttributeInfo(attributeType, typeArgsMap);
fieldAttr.setPojoClaz(pojoAttr.getAttrClaz()).setAttrName(attributeName);
fieldAttr.setAttrAnnotations(pojoAttributeAnnotations);
return fieldAttr;
}
/**
* Given the original class and the setter method, it returns all
* annotations for the field or an empty collection if no custom annotations
* were found on the field
*
* @param clazz The class containing the annotated attribute
* @param setter The setter method
* @return all annotations for the field
*/
private List<Annotation> retrieveFieldAnnotations(AttributeInfo pojoAttr, Method setter) {
List<Annotation> retValue = new ArrayList<Annotation>();
String attributeName = ClassFieldInfo.extractFieldNameFromSetterMethod(setter);
Field setterField = null;
Class clazz = pojoAttr.getAttrClaz();
while (clazz != null) {
try {
setterField = clazz.getDeclaredField(attributeName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
} catch (SecurityException e) {
throw e;
}
}
if (setterField != null) {
Annotation[] annotations = setterField.getAnnotations();
if (annotations != null && annotations.length != 0) {
retValue = Arrays.asList(annotations);
}
}
return retValue;
}
/**
* 创建循环引用对象
*
* @param depth
* @param typeArgsMap
* @param attributeType
* @return
* @throws Exception
*/
private Object getNestedObjectInstance(AttributeInfo pojoAttr, int depth, final Map<String, Type> typeArgsMap)
throws Exception {
if (depth < FillingConstants.MAX_DEPTH) {
Object o = new PoJoFiller(this.strategy, typeArgsMap).fillingPoJo(pojoAttr, depth + 1);
return o;
} else {
Object o = new PoJoFiller(strategy, typeArgsMap).fillingWithConstructorsOrFactory(pojoAttr);
return o;
}
}
}