/*
* 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.meta;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.OrderBy;
import jef.database.annotation.JoinDescription;
import jef.database.annotation.JoinType;
import jef.database.query.Join;
import jef.database.query.JoinElement;
import jef.database.query.Query;
import jef.tools.ArrayUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import com.google.common.base.Objects;
/**
* 描述两表间的一组关系
*
* @author Administrator
*
*/
public class JoinPath {
private static final JoinKey[] EMPTY = new JoinKey[0];
private JoinKey[] joinKeys;
private JoinKey[] joinExpression;
private JoinType type;
private JoinDescription description;
private OrderBy orderBy;
/**
* ManyToMany时三表连接
*/
private TupleMetadata relationTable;
private JoinPath relationToTarget;
public TupleMetadata getRelationTable() {
return relationTable;
}
public JoinPath getRelationToTarget() {
return relationToTarget;
}
public void setRelationTable(TupleMetadata relationTable,JoinPath toTarget) {
this.relationTable = relationTable;
this.relationToTarget=toTarget;
}
public JoinDescription getDescription() {
return description;
}
public void setDescription(JoinDescription description, OrderBy orderBy) {
this.description = description;
this.orderBy = orderBy;
}
public JoinKey[] getJoinKeys() {
if (joinKeys == null)
return EMPTY;
return joinKeys;
}
public JoinKey[] getJoinExpression() {
if (joinExpression == null)
return EMPTY;
return joinExpression;
}
public void addJoinKey(JoinKey s) {
if (joinKeys == null) {
joinKeys = new JoinKey[] { s };
} else {
joinKeys = (JoinKey[]) ArrayUtils.add(joinKeys, s);
}
this.flip = null;
check();
}
public void setJoinKeys(JoinKey[] joinKeys) {
this.joinKeys = joinKeys;
check();
this.flip = null;
}
public void setType(JoinType type) {
this.type = type;
}
public JoinType getType() {
return type;
}
private void check() {
// 整理并移动
if (joinKeys.length == 0)
return;
List<JoinKey> moved = new ArrayList<JoinKey>();
for (JoinKey s : this.joinKeys) {
if (!s.isSimple()) {
moved.add(s);
}
}
for (JoinKey s : moved) {
joinKeys = (JoinKey[]) ArrayUtils.removeElement(joinKeys, s);
joinExpression = ArrayUtils.addElement(joinExpression, s);
}
}
public JoinPath(JoinType type, JoinKey... relationships) {
this.type = type;
joinKeys = relationships;
check();
}
JoinPath() {
};
// 其实就一个join条件表达式来说,颠倒与否没有多大的实际意义。纯粹视觉效果。
private JoinPath flip;
public JoinPath flip() {
if (flip == null) {
JoinPath flipObj = new JoinPath();
flipObj.joinKeys = new JoinKey[this.joinKeys.length];
flipObj.type = JoinType.flip(type);
for (int n = 0; n < this.joinKeys.length; n++) {
flipObj.joinKeys[n] = joinKeys[n].flip();
}
flipObj.flip = this;
this.flip = flipObj;
}
return flip;
}
/**
* 检查链接路径是否正确
* @param fromType
* @param targetType
* @return
*/
public JoinPath accept(ITableMetadata left, ITableMetadata obj) {
if (relationTable != null) {
return pathAccept(obj);
}
if (joinKeys != null) {
for (int i = 0; i < joinKeys.length; i++) {
JoinKey jk = joinKeys[i];
int flag = jk.validate(left, obj);
if (flag == 0) {
return null;
} else if (flag == -1) {
joinKeys[i] = jk.flip();
}
}
}
if (joinExpression != null) {
for (int i = 0; i < joinExpression.length; i++) {
JoinKey jk = joinExpression[i];
int flag = jk.validate(left, obj);
if (flag == 0) {
return null;
} else if (flag == -1) {
joinExpression[i] = jk.flip();
}
}
}
return this;
}
/**
* 这个方法用来检测当前连接路径是否满足left,right的连接需要
*
* 要注意 1、这个方法只需对用户输入的条件进行检查,不能对系统自身Reference中存在的对象进行绑定操作
*
* @param left
* @param obj
* @return
*/
public JoinPath accept(JoinElement left, Query<?> obj) {
if (relationTable != null) {
return pathAccept(obj.getMeta());
}
if (joinKeys != null) {
for (int i = 0; i < joinKeys.length; i++) {
JoinKey jk = joinKeys[i];
int flag = jk.validate(left, obj);
if (flag == 0) {
return null;
} else if (flag == -1) {
joinKeys[i] = jk.flip();
}
}
}
if (joinExpression != null) {
for (int i = 0; i < joinExpression.length; i++) {
JoinKey jk = joinExpression[i];
int flag = jk.validate(left, obj);
if (flag == 0) {
return null;
} else if (flag == -1) {
joinExpression[i] = jk.flip();
}
}
}
if (left instanceof Join) {
Join j = (Join) left;
if (joinKeys != null) {
for (int i = 0; i < joinKeys.length; i++) {
JoinKey jk = joinKeys[i];
jk.findAndLockLeft(j);
}
}
if (joinExpression != null) {
for (int i = 0; i < joinExpression.length; i++) {
JoinKey jk = joinExpression[i];
jk.findAndLockLeft(j);
}
}
}
return this;
}
private JoinPath pathAccept(ITableMetadata obj) {
for (Reference ref : relationTable.getRefFieldsByRef().keySet()) {
if (ref.getThisType() == relationTable && ref.getTargetType() == obj && ref.getHint() != null) {
return this;
}
}
return null;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(joinKeys).append(joinExpression).toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof JoinPath))
return false;
JoinPath o = (JoinPath) obj;
if (!ArrayUtils.equals(this.joinKeys, o.joinKeys))
return false;
if (type != o.type)
return false;
// Annotation自身已经很好的实现的equals方法,不会受代理类等因素影响
if(relationTable!=o.relationTable) {
return false;
}
if (!Objects.equal(this.orderBy, o.orderBy)) {
return false;
}
return Objects.equal(this.description, o.description);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (joinKeys != null) {
for (JoinKey jk : joinKeys) {
if (sb.length() > 0)
sb.append(" and ");
sb.append(jk.toString());
}
}
if (joinExpression != null) {
for (JoinKey jk : joinExpression) {
if (sb.length() > 0)
sb.append(" and ");
if (jk.getField() == null) {
sb.append(jk.getValue());
} else {
sb.append(jk.toString());
}
}
}
return sb.toString();
}
// Assert all left meta are assiagn to one table
public ITableMetadata getLeftMeta() {
ITableMetadata left = null;
if (joinKeys != null) {
for (JoinKey j : joinKeys) {
ITableMetadata m = j.getLeftTableMeta();
if (left == null) {
left = m;
} else if (m == null) {
} else {
if (left != m) {
throw new IllegalArgumentException();
}
}
}
}
if (joinExpression != null) {
for (JoinKey j : joinExpression) {
ITableMetadata m = j.getLeftTableMeta();
if (left == null) {
left = m;
} else if (m == null) {
} else {
if (left != m) {
throw new IllegalArgumentException();
}
}
}
}
return left;
}
public ITableMetadata getRightMeta() {
ITableMetadata right = null;
if (joinKeys != null) {
for (JoinKey j : joinKeys) {
ITableMetadata m = j.getRightTableMeta();
if (right == null) {
right = m;
} else if (m == null) {
} else {
if (right != m) {
throw new IllegalArgumentException();
}
}
}
}
if (joinExpression != null) {
for (JoinKey j : joinExpression) {
ITableMetadata m = j.getRightTableMeta();
if (right == null) {
right = m;
} else if (m == null) {
} else {
if (right != m) {
throw new IllegalArgumentException();
}
}
}
}
return right;
}
public String getOrderBy() {
return orderBy == null ? null : orderBy.value();
}
}