package com.alibaba.tamper.core.config.parse;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.alibaba.tamper.core.BeanMappingException;
import com.alibaba.tamper.core.config.BeanMappingBehavior;
import com.alibaba.tamper.core.config.BeanMappingConfigHelper;
import com.alibaba.tamper.core.config.BeanMappingField;
import com.alibaba.tamper.core.config.BeanMappingFieldAttributes;
import com.alibaba.tamper.core.config.BeanMappingObject;
import com.alibaba.tamper.core.helper.ReflectionHelper;
import com.alibaba.tamper.core.helper.XmlHelper;
/**
* 解析对应的mapping配置
*
* @author jianghang 2011-5-26 下午07:13:45
*/
public class BeanMappingParser {
private static final String MAPPING_SCHEMA = "META-INF/mapping.xsd";
public static List<BeanMappingObject> parseMapping(InputStream in) throws BeanMappingException {
List<BeanMappingObject> result = parseMappingObject(in);
List<BeanMappingObject> reseverResult = new ArrayList<BeanMappingObject>(result.size());
for (BeanMappingObject object : result) {
if (object.isReversable()) {
BeanMappingObject reverseObject = reverse(object);
if (reverseObject != null) {
reseverResult.add(reverseObject);
}
}
}
result.addAll(reseverResult);
return result;
}
// 自动解析下src/target匹配的属性
public static List<BeanMappingObject> parseMapping(Class src, Class target) throws BeanMappingException {
List<BeanMappingObject> result = new ArrayList<BeanMappingObject>(2);
PropertyDescriptor[] targetPds = ReflectionHelper.getPropertyDescriptors(target);
PropertyDescriptor[] srcPds = ReflectionHelper.getPropertyDescriptors(src);
BeanMappingObject object = new BeanMappingObject();
object.setSrcClass(src);
object.setTargetClass(target);
object.setBatch(true);
BeanMappingBehavior globalBehavior = BeanMappingConfigHelper.getInstance().getGlobalBehavior();
List<BeanMappingField> fields = new ArrayList<BeanMappingField>();
for (PropertyDescriptor targetPd : targetPds) {
String property = targetPd.getName();
PropertyDescriptor srcPd = getMatchPropertyDescriptor(srcPds, property);
if (srcPd == null) {// 没有匹配属性
continue;
}
if (targetPd.getWriteMethod() != null && srcPd.getReadMethod() != null) {
BeanMappingField field = new BeanMappingField();
BeanMappingFieldAttributes srcFieldAttribute = new BeanMappingFieldAttributes();
srcFieldAttribute.setName(property);
srcFieldAttribute.setClazz(srcPd.getPropertyType());
BeanMappingFieldAttributes targetFieldAttribute = new BeanMappingFieldAttributes();
targetFieldAttribute.setName(property);
targetFieldAttribute.setClazz(targetPd.getPropertyType());
// 添加记录
field.setSrcField(srcFieldAttribute);
field.setTargetField(targetFieldAttribute);
field.setBehavior(globalBehavior);
fields.add(field);
}
}
object.setBeanFields(fields);
object.setBehavior(globalBehavior);// 设置为global
result.add(object);
if (object.isReversable()) {
BeanMappingObject reverseObject = reverse(object);
if (reverseObject != null) {
result.add(reverseObject);
}
}
return result;
}
// 解析class的属性为BeanMapping对象
public static List<BeanMappingObject> parseMapMapping(Class src) throws BeanMappingException {
List<BeanMappingObject> result = new ArrayList<BeanMappingObject>(2);
PropertyDescriptor[] targetPds = ReflectionHelper.getPropertyDescriptors(src);
BeanMappingObject object = new BeanMappingObject();
object.setSrcClass(src);
object.setTargetClass(Map.class); // 设置为map接口
object.setBatch(true);
List<BeanMappingField> fields = new ArrayList<BeanMappingField>();
BeanMappingBehavior globalBehavior = BeanMappingConfigHelper.getInstance().getGlobalBehavior();
for (PropertyDescriptor targetPd : targetPds) {
if (targetPd.getWriteMethod() != null && targetPd.getReadMethod() != null) {
BeanMappingField field = new BeanMappingField();
BeanMappingFieldAttributes srcFieldAttribute = new BeanMappingFieldAttributes();
srcFieldAttribute.setName(targetPd.getName());
srcFieldAttribute.setClazz(targetPd.getPropertyType());
BeanMappingFieldAttributes targetFieldAttribute = new BeanMappingFieldAttributes();
targetFieldAttribute.setName(targetPd.getName());
targetFieldAttribute.setClazz(targetPd.getPropertyType());
field.setSrcField(srcFieldAttribute);
field.setTargetField(targetFieldAttribute);
field.setBehavior(globalBehavior);// 设置为global
fields.add(field);
}
}
object.setBeanFields(fields);
object.setBehavior(globalBehavior);// 设置为global
result.add(object);
if (object.isReversable()) {
BeanMappingObject reverseObject = reverse(object);
if (reverseObject != null) {
result.add(reverseObject);
}
}
return result;
}
// 反转处理BeanMappingObject对象
private static BeanMappingObject reverse(BeanMappingObject object) {
BeanMappingObject newObject = new BeanMappingObject();
// 反转一下属性,主要是一些srcName,srcClass等
newObject.setSrcClass(object.getTargetClass());
newObject.setTargetClass(object.getSrcClass());
newObject.setReversable(object.isReversable());
newObject.setBatch(object.isBatch());
newObject.setSrcKey(object.getSrcKey());
newObject.setTargetKey(object.getTargetKey());
newObject.setBehavior(object.getBehavior());
List<BeanMappingField> fields = newObject.getBeanFields();
for (BeanMappingField field : object.getBeanFields()) {
// 倒转一下
BeanMappingField newField = new BeanMappingField();
BeanMappingFieldAttributes srcFieldAttribute = new BeanMappingFieldAttributes();
srcFieldAttribute.setName(field.getTargetField().getName());
srcFieldAttribute.setClazz(field.getTargetField().getClazz());
BeanMappingFieldAttributes targetFieldAttribute = new BeanMappingFieldAttributes();
targetFieldAttribute.setName(field.getSrcField().getName());
targetFieldAttribute.setClazz(field.getSrcField().getClazz());
newField.setSrcField(srcFieldAttribute);
newField.setTargetField(targetFieldAttribute);
newField.setDefaultValue(field.getDefaultValue());
newField.setMapping(field.isMapping());
newField.setBehavior(field.getBehavior());
if (StringUtils.isNotEmpty(field.getConvertor()) || StringUtils.isNotEmpty(field.getScript())) {
object.setReversable(false);// 强制设置为false
return null;
}
fields.add(newField);
}
return newObject;
}
// 根据属性名获取一下匹配的PropertyDescriptor
private static PropertyDescriptor getMatchPropertyDescriptor(PropertyDescriptor[] srcPds, String property) {
for (PropertyDescriptor srcPd : srcPds) {
if (srcPd.getName().equals(property)) {
return srcPd;
}
}
return null;
}
// 解析一下bean-mapping
private static List<BeanMappingObject> parseMappingObject(InputStream in) {
Document doc = XmlHelper.createDocument(
in,
Thread.currentThread().getContextClassLoader().getResourceAsStream(
MAPPING_SCHEMA));
Element root = doc.getDocumentElement();
NodeList globalNodeList = root.getElementsByTagName("global-configurations");
if (globalNodeList.getLength() > 1) {
throw new BeanMappingException("global-configurations is exceed one node!");
}
BeanMappingBehavior globalBehavior = BeanMappingConfigHelper.getInstance().getGlobalBehavior();
if (globalNodeList.getLength() == 1) {
globalBehavior = BeanMappingBehaviorParse.parse(globalNodeList.item(0), globalBehavior);
BeanMappingConfigHelper.getInstance().setGlobalBehavior(globalBehavior);
}
NodeList classAliasNodeList = root.getElementsByTagName("class-alias-configurations");
for (int i = 0; i < classAliasNodeList.getLength(); i++) {
ClassAliasParse.parseAndRegister(classAliasNodeList.item(i));
}
NodeList convertorNodeList = root.getElementsByTagName("convertors-configurations");
for (int i = 0; i < convertorNodeList.getLength(); i++) {
ConvertorParse.parseAndRegister(convertorNodeList.item(i));
}
NodeList functionClassNodeList = root.getElementsByTagName("function-class-configurations");
for (int i = 0; i < functionClassNodeList.getLength(); i++) {
FunctionClassParse.parseAndRegister(functionClassNodeList.item(i));
}
NodeList nodeList = root.getElementsByTagName("bean-mapping");
List<BeanMappingObject> mappings = new ArrayList<BeanMappingObject>();
// 解析BeanMappingObject属性
for (int i = 0; i < nodeList.getLength(); i++) {
BeanMappingObject config = BeanMappingParse.parse(nodeList.item(i), globalBehavior);
// 添加到返回结果
mappings.add(config);
}
return mappings;
}
}