/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jef.database;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import jef.common.log.LogUtil;
import jef.database.dialect.DatabaseDialect;
import jef.database.dialect.type.ColumnMapping;
import jef.database.dialect.type.ColumnMappings;
import jef.database.jsqlparser.visitor.Expression;
import jef.database.meta.FBIField;
import jef.database.meta.Feature;
import jef.database.meta.ITableMetadata;
import jef.database.meta.TupleField;
import jef.database.query.JpqlExpression;
import jef.database.query.LazyQueryBindField;
import jef.database.query.RefField;
import jef.database.query.SqlContext;
import jef.database.query.SqlExpression;
import jef.database.wrapper.clause.SqlBuilder;
import jef.database.wrapper.variable.ConstantVariable;
import jef.database.wrapper.variable.QueryLookupVariable;
import jef.tools.ArrayUtils;
import jef.tools.Assert;
import jef.tools.StringUtils;
import jef.tools.collection.CollectionUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* 描述一个条件对象,一个条件一般是形如 {@code name='John Smith'}这样的表达式。 一个典型的条件由
* 左表达式、运算符、右表达式三部分组成
*
* @author Administrator
*
*/
public class Condition implements Serializable {
private static final long serialVersionUID = -864876729368518762L;
// public static final Condition AllRecordsCondition=new Condition();
private static final Class<?>[] VALID_FIELD_TYPE_FOR_CONDITION = { FBIField.class, RefField.class };
private static final Operator[] NULL_ABLE_VALUE = new Operator[] { Operator.IS_NULL, Operator.IS_NOT_NULL };
protected Condition() {
}
public Condition(Field field2, Operator equals, Object value2) {
this.field = field2;
this.operator = equals;
this.value = value2;
}
/**
* 字段(也可用于描述一个条件组)
*/
protected Field field;
/**
* 运算符
*/
protected Operator operator;
/**
* 表达式
*/
protected Object value;
/**
* 根据运算构造一个条件
*
* @param field
* @param oper
* @param value
* @return
*/
public static Condition get(Field field, Operator oper, Object value) {
Assert.notNull(field);
boolean valid = (field instanceof IConditionField) || (field instanceof Enum) || (field instanceof TupleField);
if (!valid) {
valid = ArrayUtils.fastContains(VALID_FIELD_TYPE_FOR_CONDITION, field.getClass());
}
Assert.isTrue(valid, "The field type " + field.getClass() + " is not a valid field for condition!");
Condition c = new Condition();
c.field = field;
c.operator = oper;
c.value = value;
return c;
}
/**
* 条件分为两类 (基本条件/容器条件) 根据Field的类型来判断 元模型字段,FBIField.class,RefField.class
* 三种为基本条件 IConditionField为容器条件
*
* @return
*/
public boolean isContainer() {
return field instanceof IConditionField;
}
/**
* 条件运算符 <li>=等于</li> <li>>大于</li> <li>< 小于</li> <li>>=大于等于</li> <li><= 小于等于
* </li> <li>!= 不等于</li> <li>^= 匹配字符串头</li> <li>$= 匹配字符串尾</li> <li>*=
* 匹配字符串任意位置</li> <li>in in</li> <li>[] BETWEEN</li>
*/
public enum Operator {
EQUALS("="), GREAT(">"), LESS("<"), GREAT_EQUALS(">="), LESS_EQUALS("<="), MATCH_ANY("*=", " like "), MATCH_START("^=", " like "), MATCH_END("$=", " like "), IN("in", " in "), NOT_IN("not in"), NOT_EQUALS("!="), BETWEEN_L_L("[]"), IS_NULL("=NULL"), IS_NOT_NULL("!=NULL");
Operator(String key, String oper) {
this.key = key;
this.oper = oper;
}
Operator(String key) {
this.key = key;
this.oper = key;
}
private String key;
private String oper;
public String getKey() {
return key;
}
//
public static Operator valueOfKey(String key) {
for (Operator o : Operator.values()) {
if (o.key.equals(key)) {
return o;
}
}
if ("!=".equals(key)) {
return Operator.NOT_EQUALS;
}
return null;
}
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
// 产生非绑定变量下的where条件
public String toSqlClause(ITableMetadata meta, SqlContext context, SqlProcessor processor, IQueryableEntity instance, DatabaseDialect profile, boolean batch) {
if (field == null) {
throw new NullPointerException("Condition not complete!");
}
Field field = this.field;
// 特殊Field条件
if (field instanceof IConditionField) {
return ((IConditionField) field).toSql(meta, processor, context, instance, profile, batch);
} else if (field instanceof RefField) {
RefField ref = (RefField) field;
ITableMetadata refMeta = ((RefField) field).getInstanceQuery(context).getMeta();
SqlContext refContext = context.getContextOf(ref.getInstanceQuery(context));
// 替换上下文,然后继续处理……
field = ref.getField();
meta = refMeta;
context = refContext;
instance = ref.getInstanceQuery(context).getInstance();
}
// Like条件下
if (ArrayUtils.contains(Like.LIKE_OPERATORS, operator)) {
Like like = new Like(field, operator, value);
return like.toSql(meta, profile, context, instance);
}
String columnName;
if (field instanceof JpqlExpression) {
columnName = ((JpqlExpression) field).toSqlAndBindAttribs(context, profile);
} else {
columnName = DbUtils.toColumnName(field, profile, context == null ? null : context.getCurrentAliasAndCheck(field));
}
if (operator == null) {
return columnName;
}
if (profile.has(Feature.EMPTY_CHAR_IS_NULL) && value instanceof CharSequence) {
if (((CharSequence) value).length() == 0) {
value = null;
}
}
// 常规Field条件
if (ArrayUtils.notContains(NULL_ABLE_VALUE, operator) && value == null) {
if (operator == Operator.EQUALS) {
operator = Operator.IS_NULL;
} else if (operator == Operator.NOT_EQUALS) {
operator = Operator.IS_NOT_NULL;
} else {
throw new NullPointerException("Condition value can not be null: " + field.name() + " " + operator.getKey());
}
}
ColumnMapping type = null;
if (field instanceof Enum || field instanceof TupleField) {// 计算列的数据类型
type = meta.getColumnDef(field);
if (type == null) {
throw new IllegalArgumentException("There is no proper field in " + meta.getThisType().getName() + " to populate a SQL condition expression:" + field.name());
}
}
return toSql(columnName, operator, value, profile, context, type);
}
/*
* 产生用于批处理的Update语句的Where字句中的sql语句,形如 xx=? 等等 xx is null预处理若干复杂的Field类型
*/
public void toPrepareSqlClause(SqlBuilder builder, ITableMetadata meta, SqlContext context, SqlProcessor processor, IQueryableEntity instance, DatabaseDialect profile, boolean batch) {
// 特殊Field条件
if (field instanceof IConditionField) {
((IConditionField) field).toPrepareSql(builder, meta, processor, context, instance, profile, batch);
return;
} else if (field instanceof LazyQueryBindField) {
LazyQueryBindField ref = (LazyQueryBindField) field;
ITableMetadata refMeta = ref.getInstanceQuery(context).getMeta();
SqlContext refContext = context.getContextOf(ref.getInstanceQuery(context));
if (ref instanceof RefField) {
toPrepareSqlClause(builder, refMeta, refContext, processor, ref.getInstanceQuery(context).getInstance(), ((RefField) ref).getField(), profile, batch);
return;
}
}
toPrepareSqlClause(builder, meta, context, processor, instance, field, profile, batch);
}
/**
* 实际生成条件
*
* @return
*/
private void toPrepareSqlClause(SqlBuilder builder, ITableMetadata meta, SqlContext context, SqlProcessor processor, IQueryableEntity instance, Field rawField, DatabaseDialect profile, boolean batch) {
// 当value为Field的时候……
if (value instanceof jef.database.Field && value.getClass().isEnum()) {// 当value为基本field时
value = new RefField((Field) value);
}
// 表达式对象无需绑定变量
if ((value instanceof Expression) || value instanceof RefField) {
builder.append(toSqlClause(meta, context, processor, instance, profile, batch));
return;
}
// Like条件下
if (ArrayUtils.contains(Like.LIKE_OPERATORS, operator)) {
Like like = new Like(rawField, operator, value);
like.toPrepareSql(builder, meta, profile, context, instance,batch);
return;
}
// 其他简单条件情况下
if (operator == null || rawField == null) {
throw new NullPointerException("Condition not complete!");
}
if (profile.has(Feature.EMPTY_CHAR_IS_NULL) && value instanceof CharSequence) {
if (((CharSequence) value).length() == 0) {
value = null;
}
}
// 修复is null和is not null两种特殊情况
if (value == null && ArrayUtils.notContains(NULL_ABLE_VALUE, operator)) {
if (operator == Operator.EQUALS) {
operator = Operator.IS_NULL;
} else if (operator == Operator.NOT_EQUALS) {
operator = Operator.IS_NOT_NULL;
} else {
throw new IllegalArgumentException("The value of condition [" + field + " " + operator.getKey() + " (...)] must not be null!");
}
}
String columnName;
if (rawField instanceof JpqlExpression) {
columnName = ((JpqlExpression) rawField).toSqlAndBindAttribs(context, profile);
} else {
columnName = DbUtils.toColumnName(rawField, profile, context == null ? null : context.getCurrentAliasAndCheck(rawField));
}
if (operator == Operator.IS_NULL) {
builder.append(columnName," IS NULL");
return;
} else if (operator == Operator.IS_NOT_NULL) {
builder.append(columnName," IS NOT NULL");
return;
}
if (getInBetweenOper(operator) != null) {
processBetween(builder, columnName, rawField, profile, meta, context, processor, instance, batch);
return;
} else {
builder.append(columnName, operator.oper, "?");
if (batch) {
builder.addBind(new QueryLookupVariable(rawField, operator));
} else {
builder.addBind(new ConstantVariable(rawField.name() + operator, value, meta.getColumnDef(rawField)));
}
}
}
private void processBetween(SqlBuilder builder, String columnName, Field rawField, DatabaseDialect profile, ITableMetadata meta, SqlContext context, SqlProcessor processor, IQueryableEntity instance, boolean batch) {
String[] spOpers = getInBetweenOper(operator);
String oper = spOpers[0];
String div = spOpers[1];
String tailer = spOpers[2];
// 修改,支持String
if (value instanceof CharSequence) {
String[] value1 = StringUtils.split(value.toString(), ',');
for (int i = 0; i < value1.length; i++) {
value1[i] = value1[i].trim();
}
value = value1;
}
if (CollectionUtils.isArrayOrCollection(value.getClass())) {
int len = CollectionUtils.length(value);
if (operator == Operator.BETWEEN_L_L && len != 2) {// 无效的BETWEEN操作
throw new RuntimeException("The between operator must have 2 params");
}
if (len == 0) {
if (Operator.IN == operator) {// 对于空集合的IN条件为永假
builder.append("1=2");
} else if (Operator.NOT_IN == operator) {// 对于空集合的NOT IN条件为永真
builder.append("1=1");
}
} else {
builder.append(columnName, oper);
int n = 0;
for (Iterator<Object> iter = CollectionUtils.iterator(value, Object.class); iter.hasNext();) {
Object o = iter.next();
if (n > 0)
builder.append(div);
if (o instanceof Field) {
Condition c = get((Field) o, null, null);
builder.append(c.toSqlClause(meta, context, processor, instance, profile, batch));
} else {
builder.append("?");
if (batch) {
builder.addBind(new QueryLookupVariable(rawField, operator));
} else {
builder.addBind(new ConstantVariable(rawField.name() + operator, o,meta.getColumnDef(rawField)));
}
}
n++;
}
builder.append(tailer);
}
} else {
throw new RuntimeException("Error param, the value of in operator must be a array or string :" + value.getClass().getName());
}
}
private static final String[] IN_OPER = new String[] { " in (", ", ", ")" };
private static final String[] NOT_IN_OPER = new String[] { " not in (", ", ", ")" };
private static final String[] BETWEEN_OPER = new String[] { " between ", " and ", "" };
private static String[] getInBetweenOper(Operator oper) {
if (oper == Operator.IN) {
return IN_OPER;
} else if (oper == Operator.BETWEEN_L_L) {
return BETWEEN_OPER;
} else if (oper == Operator.NOT_IN) {
return NOT_IN_OPER;
}
return null;
}
public static String toSql(String columnName, Operator operator, Object value, DatabaseDialect profile, SqlContext context, ColumnMapping type) {
if (value instanceof jef.database.Field && value.getClass().isEnum()) {// 基本field
value = new RefField((Field) value);
}
// 生成条件SQL
String[] spOpers;
StringBuilder sb = new StringBuilder();
if (operator == Operator.IS_NULL) {
sb.append(columnName).append(" is null");
} else if (value == Operator.IS_NOT_NULL) {
sb.append(columnName).append(" is not null");
} else if (value instanceof RefField) {
RefField rf = (RefField) value;
String alias = context == null ? "" : context.getAliasOf(rf.getInstanceQuery(context));
if (alias != null) {
sb.append(columnName).append(operator.oper).append(alias);
if (alias.length() > 0)
sb.append('.');
sb.append(DbUtils.toColumnName(rf.getFieldDef(), profile, null));
} else {
LogUtil.show("Not found Table Alias for ref-field:" + rf.toString());
sb.append(columnName).append(operator.oper).append(DbUtils.toColumnName(rf.getFieldDef(), profile, null));
}
} else if ((spOpers = getInBetweenOper(operator)) != null) {
String oper = spOpers[0];
String div = spOpers[1];
String tailer = spOpers[2];
if (value instanceof CharSequence) {
sb.append(columnName).append(oper).append('\'').append(value.toString()).append('\'').append(tailer);
} else {
List<String> sqlValues = new ArrayList<String>();
Iterator<Object> iter = CollectionUtils.iterator(value, Object.class);
Assert.notNull(iter, "Error param, the value of in operator must be a array or string :" + value.getClass().getName());
for (; iter.hasNext();) {
Object v = iter.next();
sqlValues.add(type.getSqlStr(v, profile));
}
if (operator == Operator.BETWEEN_L_L && sqlValues.size() != 2) {// 无效的BETWEEN操作
throw new RuntimeException("The between operator must have 2 params");
}
if (sqlValues.size() == 0) {// 无效果的in操作
sb.append("1=2");
} else {
sb.append(columnName).append(oper).append(StringUtils.join(sqlValues, div)).append(tailer);
}
}
} else if (value instanceof SqlExpression) {
sb.append(columnName).append(operator.oper).append(((SqlExpression) value).getText());
} else if (value instanceof JpqlExpression) {
JpqlExpression jpql = (JpqlExpression) value;
sb.append(columnName).append(operator.oper).append(jpql.toSqlAndBindAttribs(context, profile));
} else {
sb.append(columnName).append(operator.oper).append(type == null ? ColumnMappings.getSqlStr(value, profile) : type.getSqlStr(value, profile));
}
return sb.toString();
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (field != null) {
sb.append(field.name()).append(' ');
if (field instanceof IConditionField) {
return sb.toString();
}
}
sb.append(operator == null ? "=" : operator.getKey()).append(' ');
if (value == null) {
sb.append("null");
} else if (value.getClass().isArray()) {
sb.append(ArrayUtils.toString(value));
} else {
sb.append(value);
}
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Condition))
return false;
Condition oo = (Condition) o;
EqualsBuilder eb = new EqualsBuilder();
eb.append(this.field, oo.field);
eb.append(this.operator, oo.operator);
eb.append(this.value, oo.value);
return eb.isEquals();
}
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
// modify by mjj,增加value值得变化因子,以达到交换属性值时算出的hashcode不一样
// return b.append(field).append(operator).append(value).toHashCode();
return b.append(field).append(operator).append(value).toHashCode() * (value == null ? 3 : new HashCodeBuilder().append(value).toHashCode());
}
}