package com.taobao.tddl.optimizer.config.table.parse;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.google.common.collect.Lists;
import com.taobao.tddl.common.utils.XmlHelper;
import com.taobao.tddl.optimizer.config.table.ColumnMeta;
import com.taobao.tddl.optimizer.config.table.IndexMeta;
import com.taobao.tddl.optimizer.config.table.IndexType;
import com.taobao.tddl.optimizer.config.table.Relationship;
import com.taobao.tddl.optimizer.config.table.TableMeta;
import com.taobao.tddl.optimizer.core.datatype.DataType;
/**
* 基于xml定义{@linkplain TableMeta},对应的解析器
*
* @author jianghang 2013-11-12 下午6:11:17
* @since 5.0.0
*/
public class TableMetaParser {
private static final String XSD_SCHEMA = "META-INF/table.xsd";
/**
* 基于数据流创建TableMeta对象
*
* @param in
* @return
*/
public static List<TableMeta> parse(InputStream in) {
Document doc = XmlHelper.createDocument(in,
Thread.currentThread().getContextClassLoader().getResourceAsStream(XSD_SCHEMA));
Element root = doc.getDocumentElement();
NodeList tableNodeList = root.getElementsByTagName("table");
List<TableMeta> tables = Lists.newArrayList();
for (int i = 0; i < tableNodeList.getLength(); i++) {
tables.add(parseTable(tableNodeList.item(i)));
}
return tables;
}
/**
* 基于string的data文本创建TableMeta对象
*
* @param data
* @return
*/
public static List<TableMeta> parse(String data) {
InputStream is = new ByteArrayInputStream(data.getBytes());
return parse(is);
}
// ================== helper method ================
private static TableMeta parseTable(Node node) {
Node nameNode = node.getAttributes().getNamedItem("name");
String tableName = null;
if (nameNode != null) {
tableName = nameNode.getNodeValue().toUpperCase(); // 转化为大写
}
String[] primaryKeys = new String[0];
String[] partitionColumns = new String[0];
List<ColumnMeta> columns = Lists.newArrayList();
List<IndexMeta> indexs = Lists.newArrayList();
boolean strongConsistent = true;
Map<String, ColumnMeta> columnMetas = new HashMap<String, ColumnMeta>();
NodeList childs = node.getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
Node cnode = childs.item(i);
if ("columns".equals(cnode.getNodeName())) {
NodeList columnsChild = cnode.getChildNodes();
for (int j = 0; j < columnsChild.getLength(); j++) {
Node ccnode = columnsChild.item(j);
if ("column".equals(ccnode.getNodeName())) {
columns.add(parseColumn(tableName, columnsChild.item(j)));
}
}
for (ColumnMeta column : columns) {
columnMetas.put(column.getName(), column);
}
} else if ("primaryKey".equals(cnode.getNodeName())) {
primaryKeys = StringUtils.split(cnode.getFirstChild().getNodeValue(), ',');
} else if ("partitionColumns".equals(cnode.getNodeName())) {
partitionColumns = StringUtils.split(cnode.getFirstChild().getNodeValue(), ',');
} else if ("strongConsistent".equals(cnode.getNodeName())) {
strongConsistent = BooleanUtils.toBoolean(cnode.getFirstChild().getNodeValue());
} else if ("secondaryIndexes".equals(cnode.getNodeName())) {
NodeList indexsChild = cnode.getChildNodes();
for (int j = 0; j < indexsChild.getLength(); j++) {
Node ccnode = indexsChild.item(j);
if ("indexMeta".equals(ccnode.getNodeName())) {
indexs.add(parseIndex(tableName, columnMetas, indexsChild.item(j)));
}
}
}
}
int i = 0;
String[] primaryValues = new String[columns.size() - primaryKeys.length];
for (ColumnMeta column : columns) {
boolean c = false;
for (String s : primaryKeys) {
if (column.getName().equals(s.toUpperCase())) {
c = true;
break;
}
}
if (!c) {
primaryValues[i++] = column.getName();
}
}
IndexMeta primaryIndex = new IndexMeta(tableName,
toColumnMeta(primaryKeys, columnMetas, tableName),
toColumnMeta(primaryValues, columnMetas, tableName),
IndexType.BTREE,
Relationship.NONE,
strongConsistent,
true,
toColumnMeta(partitionColumns, columnMetas, tableName));
return new TableMeta(tableName, columns, primaryIndex, indexs);
}
private static ColumnMeta parseColumn(String tableName, Node node) {
Node nameNode = node.getAttributes().getNamedItem("name");
Node typeNode = node.getAttributes().getNamedItem("type");
Node aliasNode = node.getAttributes().getNamedItem("alias");
Node nullableNode = node.getAttributes().getNamedItem("nullable");
String name = null;
String type = null;
String alias = null;
boolean nullable = true;
if (nameNode != null) {
name = nameNode.getNodeValue();
}
if (typeNode != null) {
type = typeNode.getNodeValue();
}
if (aliasNode != null) {
alias = aliasNode.getNodeValue();
}
if (nullableNode != null) {
nullable = BooleanUtils.toBoolean(nullableNode.getNodeValue());
}
return new ColumnMeta(tableName, name, getDataType(type), alias, nullable);
}
private static IndexMeta parseIndex(String tableName, Map<String, ColumnMeta> columnMetas, Node node) {
// Node nameNode = node.getAttributes().getNamedItem("name");
Node typeNode = node.getAttributes().getNamedItem("type");
Node relNode = node.getAttributes().getNamedItem("rel");
Node strongConsistentNode = node.getAttributes().getNamedItem("strongConsistent");
// String name = null;
String type = null;
String rel = null;
boolean strongConsistent = true;
// if (nameNode != null) {
// name = nameNode.getNodeValue();
// }
if (typeNode != null) {
type = typeNode.getNodeValue();
}
if (relNode != null) {
rel = relNode.getNodeValue();
}
if (strongConsistentNode != null) {
strongConsistent = BooleanUtils.toBoolean(strongConsistentNode.getNodeValue());
}
String[] keys = new String[0];
String[] values = new String[0];
String[] partitionColumns = new String[0];
NodeList childs = node.getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
Node cnode = childs.item(i);
if ("keys".equals(cnode.getNodeName())) {
keys = StringUtils.split(cnode.getFirstChild().getNodeValue(), ',');
} else if ("values".equals(cnode.getNodeName())) {
values = StringUtils.split(cnode.getFirstChild().getNodeValue(), ',');
} else if ("partitionColumns".equals(cnode.getNodeName())) {
partitionColumns = StringUtils.split(cnode.getFirstChild().getNodeValue(), ',');
}
}
return new IndexMeta(tableName,
toColumnMeta(keys, columnMetas, tableName),
toColumnMeta(values, columnMetas, tableName),
getIndexType(type),
getRelationship(rel),
strongConsistent,
false,
toColumnMeta(partitionColumns, columnMetas, tableName));
}
private static List<ColumnMeta> toColumnMeta(String[] columns, Map<String, ColumnMeta> columnMetas, String tableName) {
List<ColumnMeta> metas = Lists.newArrayList();
for (int i = 0; i < columns.length; i++) {
String cname = columns[i].toUpperCase();
if (!columnMetas.containsKey(cname)) {
throw new RuntimeException("column: " + cname + ", is not a column of table " + tableName);
}
metas.add(columnMetas.get(cname));
}
return metas;
}
private static IndexType getIndexType(String type) {
if ("BTREE".equalsIgnoreCase(type)) {
return IndexType.BTREE;
} else if ("HASH".equalsIgnoreCase(type)) {
return IndexType.HASH;
} else if ("INVERSE".equalsIgnoreCase(type)) {
return IndexType.INVERSE;
}
return IndexType.NONE;
}
private static Relationship getRelationship(String rel) {
if ("MANY_TO_MANY".equalsIgnoreCase(rel)) {
return Relationship.MANY_TO_MANY;
} else if ("ONE_TO_ONE".equalsIgnoreCase(rel)) {
return Relationship.ONE_TO_ONE;
} else if ("MANY_TO_ONE".equalsIgnoreCase(rel)) {
return Relationship.MANY_TO_ONE;
} else if ("ONE_TO_MANY".equalsIgnoreCase(rel)) {
return Relationship.ONE_TO_MANY;
}
return Relationship.NONE;
}
public static DataType jdbcTypeToDataType(int jdbcType) {
return getDataType(jdbcTypeToDataTypeString(jdbcType));
}
private static DataType getDataType(String type) {
if ("INT".equalsIgnoreCase(type) || "INTEGER".equalsIgnoreCase(type)) {
return DataType.IntegerType;
} else if ("LONG".equalsIgnoreCase(type)) {
return DataType.LongType;
} else if ("SHORT".equalsIgnoreCase(type)) {
return DataType.ShortType;
} else if ("BIGINTEGER".equalsIgnoreCase(type)) {
return DataType.BigIntegerType;
} else if ("BIGDECIMAL".equalsIgnoreCase(type)) {
return DataType.BigDecimalType;
} else if ("FLOAT".equalsIgnoreCase(type)) {
return DataType.FloatType;
} else if ("DOUBLE".equalsIgnoreCase(type)) {
return DataType.DoubleType;
} else if ("STRING".equalsIgnoreCase(type)) {
return DataType.StringType;
} else if ("BYTES".equalsIgnoreCase(type)) {
return DataType.BytesType;
} else if ("BOOLEAN".equalsIgnoreCase(type)) {
return DataType.BooleanType;
} else if ("DATE".equalsIgnoreCase(type)) {
return DataType.DateType;
} else if ("TIMESTAMP".equalsIgnoreCase(type)) {
return DataType.TimestampType;
} else if ("DATETIME".equalsIgnoreCase(type)) {
return DataType.DatetimeType;
} else if ("TIME".equalsIgnoreCase(type)) {
return DataType.TimeType;
} else if ("BLOB".equalsIgnoreCase(type)) {
return DataType.BlobType;
} else if ("BIT".equalsIgnoreCase(type)) {
return DataType.BitType;
} else {
throw new IllegalArgumentException("不支持的类型:" + type);
}
}
public static String jdbcTypeToDataTypeString(int jdbcType) {
String type = null;
switch (jdbcType) {
case Types.BIGINT:
// 考虑unsigned
type = "BIGINTEGER";
break;
case Types.NUMERIC:
case Types.DECIMAL:
type = "BIGDECIMAL";
break;
case Types.INTEGER:
// 考虑unsigned
type = "LONG";
break;
case Types.TINYINT:
case Types.SMALLINT:
// 考虑unsigned
type = "INT";
break;
case Types.DATE:
type = "DATE";
break;
case Types.TIMESTAMP:
type = "TIMESTAMP";
break;
case Types.TIME:
type = "TIME";
break;
case Types.FLOAT:
type = "FLOAT";
break;
case Types.REAL:
case Types.DOUBLE:
type = "DOUBLE";
break;
case Types.CHAR:
case Types.VARCHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
case Types.LONGVARCHAR:
case Types.CLOB:
type = "STRING";
break;
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
type = "BYTES";
break;
case Types.BLOB:
type = "BLOB";
break;
case Types.BIT:
type = "BIT";
break;
default:
throw new IllegalArgumentException("不支持的类型:" + jdbcType);
}
return type;
}
}