/*
* This software is distributed under the terms of the FSF
* Gnu Lesser General Public License (see lgpl.txt).
*
* This program is distributed WITHOUT ANY WARRANTY. See the
* GNU General Public License for more details.
*/
package com.scooterframework.orm.activerecord;
import java.util.Map;
import com.scooterframework.common.util.Converters;
/**
* IncludeData holds details about an include option element.
*
* @author (Fei) John Chen
*/
public class IncludeNode {
/**
* Constructs an IncludeNode instance.
*
*
* @param include name of the include node
* @param controlHome home instance of the main entity of the sql query
* @param order index of the include node starting from 1
* @param previous previous include node instance
* @param relation relation linking to this include node
* @param useTableAlias true if table alias is used
* @param joinType type of join
*/
public IncludeNode(String include, ActiveRecord controlHome, int order,
IncludeNode previous, Relation relation,
boolean useTableAlias, String joinType) {
if (include == null || order <= 0 || relation == null) {
throw new IllegalArgumentException("Either entity name or " +
"include order or relation instance is missing.");
}
this.include = include;
this.controlHome = controlHome;
this.order = order;
this.previousIncludeNode = previous;
this.relation = relation;
this.useTableAlias = useTableAlias;
this.joinType =joinType;
this.home = ActiveRecordUtil.getHomeInstance(relation.getTargetClass());
if (useTableAlias) {
tableAlias = "t" + order;
}
}
public String getIncludeName() {
return include;
}
/**
* Returns home intance of the control entity.
*
* @return home intance of the control entity
*/
public ActiveRecord getControlHome() {
return controlHome;
}
/**
* Returns include order.
*
* @return include order
*/
public int getOrder() {
return order;
}
/**
* Returns relation from endA entity to (this) entity.
*
* @return relation from endA entity to (this) entity
*/
public Relation getRelation() {
return relation;
}
/**
* Returns home instance.
*
* @return home instance
*/
public ActiveRecord getHomeInstance() {
return home;
}
/**
* Sets alias name of the underlying table.
*/
public void setTableAlias(String tableAlias) {
this.tableAlias = tableAlias;
useTableAlias = true;
}
/**
* Checks if there is a previous IncludeNode.
*/
public boolean hasPrevious() {
return (previousIncludeNode != null)?true:false;
}
/**
* Returns previous IncludeNode.
*/
public IncludeNode previous() {
return previousIncludeNode;
}
/**
* Checks if there is a next IncludeNode.
*/
public boolean hasNext() {
return (nextIncludeNode != null)?true:false;
}
/**
* Returns next IncludeNode.
*/
public IncludeNode next() {
return nextIncludeNode;
}
/**
* Sets next IncludeNode.
*/
public void setNext(IncludeNode next) {
nextIncludeNode = next;
}
/**
* Returns mapping name of the underlying table.
*
* @return mapping name
*/
public String getMappingName() {
return (useTableAlias)?tableAlias:home.getTableName();
}
/**
* Returns a sql select part of an ActiveRecord class in the following format:
* tableName.columnName AS tableName_columnName, ...
* for all columns of this entity.
*
* @return part of select statement
*/
public StringBuilder toSqlSelectPart() {
String tableMappingName = getMappingName();
String[] columnNames = home.getRowInfo().getColumnNames();
return getSqlSelectPart(tableMappingName, columnNames);
}
public static StringBuilder getSqlSelectPart(String tableMappingName, String[] columnNames) {
StringBuilder sqlPartSB = new StringBuilder();
int dimension = columnNames.length;
int lastIndex = dimension - 1;
for (int i=0; i<lastIndex; i++) {
sqlPartSB.append(tableMappingName).append(".").append(columnNames[i]);
sqlPartSB.append(" AS ");
sqlPartSB.append(tableMappingName).append("_").append(columnNames[i]);
sqlPartSB.append(", ");
}
sqlPartSB.append(tableMappingName).append(".").append(columnNames[lastIndex]);
sqlPartSB.append(" AS ");
sqlPartSB.append(tableMappingName).append("_").append(columnNames[lastIndex]);
return sqlPartSB;
}
public StringBuilder toSqlJoinPart() {
StringBuilder joinSqlSB = new StringBuilder();
if (relation instanceof HasManyThroughRelation) {
HasManyThroughRelation hmt = (HasManyThroughRelation)relation;
ActiveRecord middleC = ActiveRecordUtil.getHomeInstance(hmt.getMiddleC());
Relation acRel = hmt.getACRelation();
Relation cbRel = hmt.getCBRelation();
String tableAMappingName = getEndAMappingName();
String tableBName = home.getTableName();
String tableCName = middleC.getTableName();
String tableCAlias = (useTableAlias)?(tableAlias + "_"+ tableCName):tableCName;
joinSqlSB.append(constructSqlJoinPart(tableAMappingName, tableCName, tableCAlias, acRel, useTableAlias, joinType))
.append(constructSqlJoinPart(tableCAlias, tableBName, tableAlias, cbRel, useTableAlias, joinType));
String conditions = relation.getConditionsString(tableCName, tableCAlias);
if (conditions != null && !"".equals(conditions)) joinSqlSB.append(" AND ").append(conditions);
}
else {
String tableAMappingName = getEndAMappingName();
String tableBName = home.getTableName();
joinSqlSB.append(constructSqlJoinPart(tableAMappingName, tableBName, tableAlias, relation, useTableAlias, joinType));
}
return joinSqlSB;
}
public String getEndAMappingName() {
String endA = "";
if (previousIncludeNode != null) {
endA = previousIncludeNode.getMappingName();
}
else {
endA = controlHome.getTableName();
}
return endA;
}
private static StringBuilder constructSqlJoinPart(String tableAMappingName,
String tableBName, String tableBAlias, Relation abRelation,
boolean useAlias, String joinType) {
StringBuilder sqlJoinSB = new StringBuilder();
sqlJoinSB.append(" ").append(joinType).append(" ");
sqlJoinSB.append(tableBName).append(" ");
String tableBMappingName = tableBName;
if (useAlias) {
sqlJoinSB.append(tableBAlias).append(" ");
tableBMappingName = tableBAlias;
}
sqlJoinSB.append("ON ");
sqlJoinSB.append(getTableLinks(tableAMappingName, abRelation.getMapping(), tableBMappingName));
String conditions = abRelation.getConditionsString(tableBName, tableBMappingName);
if (conditions != null && !"".equals(conditions)) sqlJoinSB.append(" AND ").append(conditions);
return sqlJoinSB;
}
protected static String getTableLinks(String tableA, String mappingAB, String tableB) {
if (mappingAB == null || mappingAB.trim().equals("")) return null;
StringBuilder sb = new StringBuilder();
Map<String, String> mappingMap = Converters.convertStringToMap(mappingAB);
int total = mappingMap.size();
int count = 0;
for (Map.Entry<String, String> entry : mappingMap.entrySet()) {
count++;
String leftField = entry.getKey();
String rightField = entry.getValue();
if (leftField == null || rightField == null) continue;
if (leftField.indexOf('.') == -1) {
sb.append(tableA).append(".").append(leftField).append("=");
}
else {
sb.append(leftField).append("=");
}
if (rightField.indexOf('.') == -1) {
sb.append(tableB).append(".").append(rightField);
}
else {
sb.append(rightField);
}
if (count < total) sb.append(" AND ");
}
return sb.toString();
}
public String toString() {
String separator = "; ";
StringBuilder sb = new StringBuilder();
sb.append("entity: " + include).append(separator);
sb.append("order: " + order).append(separator);
if (previousIncludeNode != null) {
sb.append("previous entity: " + previousIncludeNode.getIncludeName()).append(separator);
}
else {
sb.append("previous entity: null").append(separator);
}
sb.append("relation: " + relation.getRelationType()).append(separator);
sb.append("useTableAlias: " + useTableAlias).append(separator);
sb.append("tableAlias: " + tableAlias).append(separator);
sb.append("mappingName: " + getMappingName()).append(separator);
sb.append("joinType: " + joinType);
return sb.toString();
}
/**
* Entity name
*/
private String include;
/**
* Home instance of the control entity
*/
private ActiveRecord controlHome;
/**
* Include order of the table
*/
private int order;
/**
* Relation from endA entity to (this) entity
*/
private Relation relation;
/**
* Home instance of the entity
*/
private ActiveRecord home;
/**
* Join type
*/
private String joinType;
/**
* Alias name of the table
*/
private String tableAlias;
/**
* Boolean indicator on if alias is used
*/
private boolean useTableAlias = false;
private IncludeNode previousIncludeNode;
private IncludeNode nextIncludeNode;
}