package org.test4j.datafilling.filler;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.test4j.datafilling.Filler;
import org.test4j.datafilling.annotations.FillList;
import org.test4j.datafilling.common.AttributeInfo;
import org.test4j.datafilling.common.FillingConstants;
import org.test4j.datafilling.exceptions.PoJoFillException;
import org.test4j.datafilling.strategy.AttributeStrategy;
import org.test4j.datafilling.strategy.DataFactory;
import org.test4j.datafilling.strategy.EmptyStrategy;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class MapFiller extends Filler {
public MapFiller(DataFactory strategy, Map<String, Type> argsTypeMap) {
super(strategy, argsTypeMap);
}
private FillList getFilling(AttributeInfo attribute) {
for (Annotation annotation : attribute.getAttrAnnotations()) {
if (FillList.class.isAssignableFrom(annotation.getClass())) {
FillList filling = (FillList) annotation;
return filling;
}
}
return null;
}
/**
* It manufactures and returns a Map with at least one element in it It
* fills a Map with the required number of elements of the required type.
* <p>
* This method has a so-called side-effect. It updates the Map given as
* argument.
* </p>
*
* @param attribute The type of the POJO map attribute
* @throws PoJoFillException If an error occurred while creating the Map
* object
*/
public Map fillingMap(AttributeInfo attribute) {
FillList filling = this.getFilling(attribute);
try {
AttributeInfo keyAttribute = attribute.getArgTypeAttribute(0, this.argsTypeMap).setAttrAnnotations(
attribute.getAttrAnnotations());
AttributeInfo valueAttribute = attribute.getArgTypeAttribute(1, this.argsTypeMap).setAttrAnnotations(
attribute.getAttrAnnotations());
Map mapInstance = getAttributeMapInstance(attribute);
AttributeStrategy keyStrategy = null;
int nbrElements = FillingConstants.ARRAY_DEFAULT_SIZE;
AttributeStrategy<?> elementStrategy = null;
if (null != filling) {
nbrElements = filling.size();
keyStrategy = filling.mapKeyStrategy().newInstance();
elementStrategy = filling.mapElementStrategy().newInstance();
}
for (int i = 0; i < nbrElements; i++) {
Object keyValue = getMapKeyOrElementValue(keyStrategy, keyAttribute);
Object elementValue = getMapKeyOrElementValue(elementStrategy, valueAttribute);
mapInstance.put(keyValue, elementValue);
}
return mapInstance;
} catch (Exception e) {
throw new PoJoFillException("An exception occurred while creating a Map object", e);
}
}
/**
* 根据Pojo属性attribute返回一个具体的map实例
*
* @return
*/
private Map getAttributeMapInstance(AttributeInfo attribute) {
try {
Map retValue = attribute.getAttributeValue();
if (retValue == null) {
throw new RuntimeException("create map instance use atribute error!");
} else {
return retValue;
}
} catch (Exception e) {
return createDefaultMap(attribute.getAttrClaz());
}
}
/**
* It fills a Map key or value with the appropriate value, considering
* attribute-level customisation.
*
* @param strategy The strategy to use to fill the Map key or value element
* @param keyOrValueType The Map key / element type
* @return A Map key or value
* @throws Exception
*/
private Object getMapKeyOrElementValue(AttributeStrategy strategy, AttributeInfo keyOrValueType) throws Exception {
Object retValue = null;
if (null != strategy && EmptyStrategy.class.isAssignableFrom(strategy.getClass())
&& Object.class.equals(keyOrValueType.getAttrClaz())) {
retValue = strategy.getValue();
} else if (null != strategy && !EmptyStrategy.class.isAssignableFrom(strategy.getClass())) {
retValue = returnAttributeDataStrategyValue(keyOrValueType.getAttrClaz(), strategy);
} else {
retValue = fillingAttribute(keyOrValueType);
}
return retValue;
}
/**
* It manufactures and returns a default instance for each map type<br>
* 返回一个默认的map实例
* <p>
* The default implementation for a {@link ConcurrentMap} is
* {@link ConcurrentHashMap}
* </p>
* <p>
* The default implementation for a {@link SortedMap} is a {@link TreeMap}
* </p>
* <p>
* The default Map is none of the above was recognised is a {@link HashMap}
* </p>
*
* @param attributeType The attribute type
* @return A default instance for each map type
*/
public static Map createDefaultMap(Class attributeType) {
Map mapInstance = null;
if (SortedMap.class.isAssignableFrom(attributeType)) {
mapInstance = new TreeMap();
} else if (ConcurrentMap.class.isAssignableFrom(attributeType)) {
mapInstance = new ConcurrentHashMap();
} else {
mapInstance = new HashMap();
}
return mapInstance;
}
}