package com.odoo.orm.sql;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.content.Context;
import com.odoo.orm.OColumn;
import com.odoo.orm.OColumn.RelationType;
import com.odoo.orm.ODataRow;
import com.odoo.orm.OModel;
/**
* The Class OQuery. SQL Statement generator
*/
public class OQuery {
/** The Constant TAG. */
public static final String TAG = OQuery.class.getSimpleName();
/** The model. */
private OModel model = null;
/** The type. */
private QueryType type = null;
/** The context. */
private Context mContext = null;
/** The sql dump. */
private StringBuffer sqlDump = null;
/** The wheres. */
private List<OWhere> wheres = new ArrayList<OWhere>();
/** The columns. */
private List<String> columns = new ArrayList<String>();
/** The relation columns. */
private HashMap<String, RelationColumnAlias> relationColumns = new HashMap<String, RelationColumnAlias>();
/** The m offset. */
private Integer mLimit = 0, mOffset = 0;
/** The order. */
private String orderBy = null, order = "";
/**
* The Enum QueryType.
*/
public enum QueryType {
/** The Insert. */
Insert,
/** The Select. */
Select,
/** The Update. */
Update,
/** The Delete. */
Delete
}
/**
* Instantiates a new o query.
*
* @param context
* the context
* @param model
* the model
* @param type
* the type
*/
public OQuery(Context context, OModel model, QueryType type) {
mContext = context;
this.model = model;
this.type = type;
}
/**
* Columns.
*
* @param columns
* the columns
* @return the o query
*/
public OQuery columns(String... columns) {
this.columns.clear();
if (model.getColumn(OColumn.ROW_ID) != null)
this.columns.add(OColumn.ROW_ID);
if (model.getColumn("id") != null)
this.columns.add("id");
for (String col : columns) {
if (col.contains(".")) {
createRelColumn(col);
}
this.columns.add(col);
}
return this;
}
/**
* Creates the rel column.
*
* @param column
* the column
* @param fromColumns
* the from columns
*/
private void createRelColumn(String column) {
String[] parts = column.split("\\.");
OColumn col = model.getColumn(parts[0]);
OModel rel_model = model.createInstance(col.getType());
String table_alias = createAlias(rel_model);
String table_name = rel_model.getTableName();
if (col.getRelationType() == RelationType.ManyToMany) {
table_name = model.getTableName() + "_" + rel_model.getTableName()
+ "_rel";
table_alias = table_name + "_alias";
OModel rel_base_model = model.createInstance(col.getType());
String rel_base_alias = createAlias(rel_base_model);
relationColumns.put(column + ".base", new RelationColumnAlias(
rel_base_model.getTableName(), rel_base_alias,
rel_base_model.getTableName() + "_id", parts[1],
rel_base_model, null));
} else {
if (rel_model.getTableName().equals(model.getTableName()))
table_alias += "_" + parts[1];
}
relationColumns.put(column,
new RelationColumnAlias(table_name, table_alias, parts[1],
parts[0], rel_model, col.getRelationType()));
}
/**
* Adds the where.
*
* @param column
* the column
* @param operator
* the operator
* @param value
* the value
* @return the o query
*/
public OQuery addWhere(String column, String operator, Object value) {
addWhere(column, operator, value, null);
return this;
}
/**
* Adds the where.
*
* @param column
* the column
* @param operator
* the operator
* @param value
* the value
* @param conditional_operator
* the conditional_operator
* @return the o query
*/
public OQuery addWhere(String column, String operator, Object value,
String conditional_operator) {
if (column.contains(".")) {
createRelColumn(column);
}
wheres.add(new OWhere(column, operator, value, conditional_operator));
return this;
}
/**
* Creates the statement.
*/
private void createStatement() {
sqlDump = new StringBuffer();
sqlDump.append(queryType());
sqlDump.append(" ");
if (type == QueryType.Select) {
sqlDump.append(queryColumns());
sqlDump.append(" ");
}
sqlDump.append(tableNames());
sqlDump.append(getWhereClauses());
if (orderBy != null) {
sqlDump.append(" ");
sqlDump.append("ORDER BY ");
if (isJoin())
sqlDump.append(createAlias(model) + "." + orderBy);
else
sqlDump.append(orderBy);
sqlDump.append(" ");
sqlDump.append(order);
}
if (mLimit > 0) {
sqlDump.append(" ");
sqlDump.append("LIMIT ");
sqlDump.append(mOffset);
sqlDump.append(", ");
sqlDump.append(mLimit);
}
}
/**
* Query type.
*
* @return the string
*/
private String queryType() {
switch (type) {
case Insert:
return "INSERT INTO";
case Select:
return "SELECT";
case Update:
return "UPDATE";
case Delete:
return "DELETE FROM";
}
return null;
}
/**
* Query columns.
*
* @return the string
*/
private String queryColumns() {
StringBuffer cols = new StringBuffer();
String base_alias = createAlias(model);
if (columns.size() == 0) {
if (isJoin()) {
for (OColumn c : model.getColumns()) {
cols.append(base_alias);
cols.append(".");
cols.append(c.getName());
cols.append(", ");
}
cols.deleteCharAt(cols.lastIndexOf(", "));
} else {
cols.append("*");
}
} else {
for (String column : columns) {
if (isJoin()) {
if (column.contains(".")) {
RelationColumnAlias col = relationColumns.get(column);
String rel_alias = null;
String column_name = col.getColumnName();
if (col.getRelationType() == RelationType.ManyToMany) {
col = relationColumns.get(column + ".base");
column_name = col.getRelationColumnName();
}
rel_alias = col.getTable_alias();
if (base_alias.equals(col.getTable_alias())) {
rel_alias += "_" + col.getColumnName();
}
cols.append(rel_alias);
cols.append(".");
cols.append(column_name);
cols.append(" AS ");
cols.append(column.replaceAll("\\.", "_"));
} else {
if (column.equals("*")) {
for (OColumn c : model.getColumns()) {
if (c.getRelationType() == null) {
cols.append(base_alias);
cols.append(".");
cols.append(c.getName());
cols.append(", ");
}
}
cols.deleteCharAt(cols.lastIndexOf(", "));
} else {
cols.append(base_alias);
cols.append(".");
cols.append(column);
cols.append(" AS ");
cols.append(column);
}
}
cols.append(", ");
} else {
cols.append(column);
cols.append(", ");
}
}
}
if (cols.lastIndexOf(", ") > 0)
cols.deleteCharAt(cols.lastIndexOf(", "));
return cols.toString();
}
/**
* Checks if query required join with multiple tables.
*
* @return true, if there is any relation column
*/
private boolean isJoin() {
return (relationColumns.size() > 0);
}
/**
* Table names.
*
* @return the string
*/
private String tableNames() {
List<String> mAsLists = new ArrayList<String>();
switch (type) {
case Insert:
case Select:
if (!isJoin())
return "FROM " + model.getTableName();
else {
String alias = createAlias(model);
StringBuffer tables = new StringBuffer();
tables.append("FROM ");
tables.append(model.getTableName());
tables.append(" AS ");
tables.append(alias);
tables.append(", ");
for (String col_name : relationColumns.keySet()) {
RelationColumnAlias col = relationColumns.get(col_name);
if (!mAsLists.contains(col.getTable_alias())) {
tables.append(col.getTable());
tables.append(" AS ");
tables.append(col.getTable_alias());
tables.append(", ");
mAsLists.add(col.getTable_alias());
}
}
tables.deleteCharAt(tables.lastIndexOf(", "));
return tables.toString();
}
case Update:
case Delete:
}
return null;
}
/**
* Creates the alias.
*
* @param model
* the model
* @return the string
*/
private String createAlias(OModel model) {
return model.getTableName() + "_alias";
}
/**
* Gets the where clauses.
*
* @return the where clauses
*/
private String getWhereClauses() {
StringBuffer clause = new StringBuffer();
Boolean join = isJoin();
String base_alias = createAlias(model);
if (wheres.size() > 0 || join) {
clause.append(" WHERE ");
// Creating referenced table clauses
for (String col : relationColumns.keySet()) {
RelationColumnAlias alias = relationColumns.get(col);
if (alias.getRelationType() != null) {
String alias_name = base_alias;
if (alias.getRelationType() == RelationType.ManyToMany) {
alias = relationColumns.get(col + ".base");
alias_name = model.getTableName() + "_"
+ alias.getRelationModel().getTableName()
+ "_rel_alias";
// Base model key check with many2many
clause.append(base_alias);
clause.append(".");
clause.append(OColumn.ROW_ID);
clause.append(" = ");
clause.append(alias_name);
clause.append(".");
clause.append(model.getTableName() + "_id");
clause.append(" AND ");
// related model key check wity many2many
clause.append(alias_name);
clause.append(".");
clause.append(alias.getColumnName());
clause.append(" = ");
clause.append(alias.getTable_alias());
clause.append(".");
clause.append(OColumn.ROW_ID);
} else {
clause.append(alias_name);
clause.append(".");
clause.append(alias.getRelationColumnName());
clause.append(" = ");
clause.append(alias.getTable_alias());
clause.append(".");
clause.append(OColumn.ROW_ID);
}
clause.append(" AND ");
}
}
for (OWhere where : wheres) {
if (join) {
String table_alias = base_alias;
String column = null;
if (where.getColumn().contains(".")) {
RelationColumnAlias alias = relationColumns.get(where
.getColumn());
table_alias = alias.getTable_alias();
column = alias.getColumnName();
} else {
column = where.getColumn();
}
clause.append(table_alias);
clause.append(".");
clause.append(column);
} else
clause.append(where.getColumn());
clause.append(" ");
clause.append(where.getOperator());
clause.append(" ");
if (where.getValue() instanceof String
|| where.getValue() instanceof Boolean)
clause.append("'" + where.getValue() + "'");
else
clause.append(where.getValue());
clause.append(" AND ");
}
clause.delete(clause.length() - 4, clause.length());
}
return clause.toString();
}
/**
* Gets the query.
*
* @return the query
*/
public String getQuery() {
createStatement();
return sqlDump.toString();
}
/**
* Fetch.
*
* @return the list
*/
public List<ODataRow> fetch() {
return model.query(getQuery(), null);
}
public List<ODataRow> fetch(boolean closeConnection) {
return model.query(getQuery(), null, closeConnection);
}
/**
* Sets the offset.
*
* @param offset
* the offset
* @return the o query
*/
public OQuery setOffset(Integer offset) {
mOffset = offset;
return this;
}
/**
* Sets the limit.
*
* @param limit
* the limit
* @return the o query
*/
public OQuery setLimit(Integer limit) {
mLimit = limit;
return this;
}
/**
* Sets the order.
*
* @param column
* the column
* @return the o query
*/
public OQuery setOrder(String column) {
orderBy = column;
return this;
}
/**
* Sets the order.
*
* @param column
* the column
* @param order
* the order
* @return the o query
*/
public OQuery setOrder(String column, String order) {
orderBy = column;
this.order = order;
return this;
}
/**
* Gets the next offset.
*
* @return the next offset
*/
public Integer getNextOffset() {
return mOffset + mLimit;
}
/**
* With functional columns.
*
* @param withFunctionalColumns
* the with functional columns
* @return the o query
*/
public OQuery withFunctionalColumns(Boolean withFunctionalColumns) {
model.withFunctionalColumns(withFunctionalColumns);
return this;
}
}