package jef.database.meta;
import java.io.File;
import java.math.BigDecimal;
import java.sql.Types;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GenerationType;
import javax.persistence.Lob;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import jef.database.DbUtils;
import jef.database.annotation.Parameter;
import jef.database.dialect.ColumnType;
import jef.database.dialect.TypeDefImpl;
import jef.database.dialect.type.ColumnMapping;
import jef.database.jsqlparser.parser.ParseException;
import jef.database.jsqlparser.statement.create.ColumnDefinition;
import jef.database.meta.AnnotationProvider.FieldAnnotationProvider;
import jef.database.meta.def.GenerateTypeDef;
import jef.database.query.SqlExpression;
import jef.tools.ArrayUtils;
import jef.tools.Assert;
import jef.tools.StringUtils;
import jef.tools.reflect.BeanUtils;
import jef.tools.reflect.BeanWrapper;
/**
* 根据字段上的注解来计算ColumnType对象
*
* @author publicxtgxrj10
*
*/
public class ColumnTypeBuilder {
/**
* java 字段
*/
private java.lang.reflect.Field field;
/**
* Java数据类型(不一定等于field.getType(),可能受注解@Type影响)
*/
private Class<?> javaType;
/**
* AnnotationProvider
*/
private FieldAnnotationProvider fieldProvider;
// ////////////////////////////初步解析后的数据////////////////////////////////////////
/**
* 注解中的类型
*
* Eg. varchar(100)中的varchar
*/
private String def;
/**
* 注解中的类型参数
*
* Eg. varchar(100)中的100
*/
private String[] typeArgs;
// ///////////完全解析后的数据/////////////
/**
* 是否为null
*/
private boolean nullable;
/**
* 是否唯一
*/
private boolean unique;
/**
* 长度
*/
private int length;
/**
* 精度
*/
private int precision;
/**
* 小数位数
*/
private int scale;
/**
* 是否为版本字段
*/
private boolean version;
/**
* 是否为LOB字段
*/
private boolean lob;
// ////////////////////////////////////
/**
* 缺省值:仅当指定了default关键字后才有值
*/
private SqlExpression defaultExpression = null;
/**
* 自增
*/
private GenerateTypeDef generatedValue;
/**
* 自定义的映射
*/
private ColumnMapping customType;
public ColumnTypeBuilder(Column col, java.lang.reflect.Field field,
Class<?> treatJavaType, FieldAnnotationProvider fieldProvider) {
this.field = field;
this.javaType = treatJavaType;
this.fieldProvider = fieldProvider;
init(col);
}
private void init(Column col) {
generatedValue = GenerateTypeDef.create(fieldProvider
.getAnnotation(javax.persistence.GeneratedValue.class));
version = fieldProvider.getAnnotation(javax.persistence.Version.class) != null;
lob = fieldProvider.getAnnotation(Lob.class) != null;
if (col != null) {
length = col.length();
precision = col.precision();
scale = col.scale();
nullable = col.nullable();
unique = col.unique();
if (col.columnDefinition().length() > 0) {
parseColumnDef(col.columnDefinition());
}
} else {
nullable = !javaType.isPrimitive();
}
}
private void parseColumnDef(String columnDef) {
ColumnDefinition c;
try {
c = DbUtils.parseColumnDef(columnDef.toUpperCase());
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
this.def = c.getColDataType().getDataType();
List<String> params = c.getColDataType().getArgumentsStringList();
this.typeArgs = params == null ? ArrayUtils.EMPTY_STRING_ARRAY : params
.toArray(new String[params.size()]);
// 在ColumnDef中定义的属性,优先级更高
if (c.getColumnSpecStrings() != null) {
for (int i = 0; i < c.getColumnSpecStrings().size(); i++) {
String spec = c.getColumnSpecStrings().get(i);
if ("not".equalsIgnoreCase(spec)) {
i++;
String nextWord = c.getColumnSpecStrings().get(i);
if ("null".equalsIgnoreCase(nextWord)) {
nullable = false;
}
} else if ("null".equalsIgnoreCase(spec)) {
nullable = true;
} else if ("unique".equalsIgnoreCase(spec)) {
unique = true;
} else {
if ("default".equalsIgnoreCase(spec)) {
String ex = c.getColumnSpecStrings().get(++i);
if (ex.length() > 0) {
defaultExpression = new SqlExpression(ex);
}
}
}
}
}
}
public ColumnType build() {
ColumnType result;
if (customType != null) {
int sqlType=customType.getSqlType();
if(lob){
if(isCharType(sqlType)){
sqlType=Types.CLOB;
}else{
sqlType=Types.BLOB;
}
}
result = new TypeDefImpl(def, sqlType).javaType(field.getType()).spec(length, precision, scale);
}else if (this.def == null) {
result = createDefault();
} else {
result = createByDef();
}
result.setUnique(unique);
return result.setNullable(nullable).defaultIs(defaultExpression);
}
private boolean isCharType(int sqlType) {
return sqlType==Types.CHAR || sqlType==Types.VARCHAR || sqlType==Types.CLOB;
}
private ColumnType createDefault() {
if (this.generatedValue != null
&& this.generatedValue.isKeyGeneration()
&& javaType == String.class) {
return new ColumnType.GUID();
} else if (generatedValue != null
&& generatedValue.isKeyGeneration()
&& Number.class.isAssignableFrom(BeanUtils
.toWrapperClass(javaType))) {
return new ColumnType.AutoIncrement(precision,
generatedValue.getGeType(), fieldProvider);
}
if (javaType == String.class) {
if (lob)
return new ColumnType.Clob();
return new ColumnType.Varchar(length > 0 ? length : 255);
} else if (javaType == Integer.class || javaType == Integer.TYPE) {
return new ColumnType.Int(precision).setVersion(version);
} else if (javaType == Double.class || javaType == Double.TYPE) {
return new ColumnType.Double(precision, scale);
} else if (javaType == Float.class || javaType == Float.TYPE) {
return new ColumnType.Double(precision, scale);
} else if (javaType == Boolean.class || javaType == Boolean.TYPE) {
return new ColumnType.Boolean();
} else if (javaType == Long.class || javaType == Long.TYPE) {
return createLong(precision, version, generatedValue);
} else if (javaType == Character.class || javaType == Character.TYPE) {
return new ColumnType.Char(1);
} else if (javaType == BigDecimal.class) {
return new ColumnType.Double(16, 6);
} else if (javaType == Date.class) {
Temporal t = fieldProvider.getAnnotation(Temporal.class);
return ColumnTypeBuilder.newDateTimeColumnDef(
t == null ? TemporalType.TIMESTAMP : t.value(),
generatedValue, version);
} else if (javaType == java.sql.Date.class) {
return ColumnTypeBuilder.newDateTimeColumnDef(TemporalType.DATE,
generatedValue, false);
} else if (javaType == java.sql.Timestamp.class) {
return ColumnTypeBuilder.newDateTimeColumnDef(
TemporalType.TIMESTAMP, generatedValue, version);
} else if (javaType == java.sql.Time.class) {
return ColumnTypeBuilder.newDateTimeColumnDef(TemporalType.TIME,
generatedValue, false);
} else if (Enum.class.isAssignableFrom(javaType)) {
Enumerated e = fieldProvider.getAnnotation(Enumerated.class);
if (e.value() == EnumType.ORDINAL) {
return new ColumnType.Int(2);
} else {
return new ColumnType.Varchar(length > 0 ? length : 32);
}
} else if (javaType.isArray()
&& javaType.getComponentType() == Byte.TYPE) {
return new ColumnType.Blob();
} else if (javaType == File.class) {
return new ColumnType.Blob();
} else {
throw new IllegalArgumentException("Java type "
+ javaType.getName()
+ " can't mapping to a Db column type by default");
}
}
@Deprecated
private static ColumnType createLong(int precision, boolean version,
GenerateTypeDef gType) {
ColumnType.Int i = new ColumnType.Int(precision > 0 ? precision : 16);
i.setGenerateType(gType == null ? null : gType.getDateGenerate());
i.setVersion(version);
return i;
}
private ColumnType createByDef() {
if ("VARCHAR".equals(def) || "VARCHAR2".equals(def)) {
if (generatedValue != null && generatedValue.isKeyGeneration()) {
GenerationType generationType = generatedValue.getGeType();
if (generationType == GenerationType.TABLE
|| generationType == GenerationType.SEQUENCE) {
return new ColumnType.AutoIncrement(length, generationType,
fieldProvider);
} else {
return new ColumnType.GUID();
}
}
length = getParamInt(0, length);
Assert.isTrue(length > 0,
"The length of a varchar column must greater than 0!");
return new ColumnType.Varchar(length);
} else if ("CHAR".equalsIgnoreCase(def)) {
if (generatedValue != null && generatedValue.isKeyGeneration()) {
GenerationType generationType = generatedValue.getGeType();
if (generationType == GenerationType.TABLE
|| generationType == GenerationType.SEQUENCE) {
return new ColumnType.AutoIncrement(length, generationType,
fieldProvider);
} else {
return new ColumnType.GUID();
}
}
length = getParamInt(0, length);
Assert.isTrue(length > 0,
"The length of a char column must greater than 0!");
return new ColumnType.Char(length);
} else if ("NUMBER".equals(def) || "NUMERIC".equals(def)) {
this.precision = getParamInt(0, precision);
this.scale = getParamInt(1, scale);
return createNumberType();
} else if ("DOUBLE".equals(def)) {
if (this.precision < 1)
precision = 16;
if (this.scale < 1)
scale = 8;
return createNumberType();
} else if ("FLOAT".equals(def)) {
if (this.precision < 1)
precision = 12;
if (this.scale < 1)
scale = 6;
return createNumberType();
} else if ("INT".equals(def) || "INTEGER".equals(def)) {
return createNumberType();
} else if ("CLOB".equalsIgnoreCase(def)) {
return new ColumnType.Clob();
} else if ("BLOB".equalsIgnoreCase(def)) {
return new ColumnType.Blob();
} else if ("Date".equalsIgnoreCase(def)) {
Temporal temporal = fieldProvider.getAnnotation(Temporal.class);
TemporalType t = temporal == null ? TemporalType.DATE : temporal
.value();
return newDateTimeColumnDef(t, generatedValue, version);
} else if ("TIMESTAMP".equalsIgnoreCase(def)) {
Temporal temporal = fieldProvider.getAnnotation(Temporal.class);
TemporalType t = temporal == null ? TemporalType.TIMESTAMP
: temporal.value();
return newDateTimeColumnDef(t, generatedValue, version);
} else if ("BOOLEAN".equalsIgnoreCase(def)) {
return new ColumnType.Boolean();
} else if ("XML".equalsIgnoreCase(def)) {
return new ColumnType.XML();
} else if ("BIT".equalsIgnoreCase(def)) {
return new ColumnType.Boolean();
} else {
throw new IllegalArgumentException("Unknow column Definition["
+ def + "] in entity " + field.getDeclaringClass());
}
}
private ColumnType createNumberType() {
// 修正precision精度
if (precision == 0 && (length > 0 && length < 100)) {
precision = length;
}
if (generatedValue != null && generatedValue.isKeyGeneration()) {
return new ColumnType.AutoIncrement(precision,
generatedValue.getGeType(), fieldProvider);
} else if (scale > 0) {
return new ColumnType.Double(precision, scale);
} else {
ColumnType.Int cType = new ColumnType.Int(precision);
cType.setVersion(version);
return cType;
}
}
private int getParamInt(int index, int defaultValue) {
if (typeArgs.length > index) {
return StringUtils.toInt(typeArgs[index], defaultValue);
}
return defaultValue;
}
static ColumnType newDateTimeColumnDef(TemporalType temporalType,
GenerateTypeDef gv, boolean version) {
ColumnType ct;
switch (temporalType) {
case DATE:
ct = new ColumnType.Date().setGenerateType(gv == null ? null : gv
.getDateGenerate());
break;
case TIME:
ct = new ColumnType.TimeStamp().setGenerateType(gv == null ? null
: gv.getDateGenerate());// FIXME
break;
case TIMESTAMP:
ColumnType.TimeStamp ctt = new ColumnType.TimeStamp();
ctt.setVersion(version);
ctt.setGenerateType(gv == null ? null : gv.getDateGenerate());
ct = ctt;
break;
default:
throw new UnsupportedOperationException();
}
return ct;
}
static void applyParams(Parameter[] parameters, ColumnMapping type) {
if (parameters == null || parameters.length == 0)
return;
BeanWrapper bw = BeanWrapper.wrap(type);
for (Parameter p : parameters) {
if (bw.isWritableProperty(p.name())) {
bw.setPropertyValueByString(p.name(), p.value());
}
}
}
public ColumnTypeBuilder withCustomType(ColumnMapping type) {
this.customType=type;
return this;
}
}