// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.
package jodd.db.oom.sqlgen.chunks;
import jodd.db.oom.ColumnAliasType;
import jodd.db.oom.DbEntityDescriptor;
import jodd.db.oom.DbEntityColumnDescriptor;
import jodd.db.oom.sqlgen.DbSqlBuilderException;
import jodd.db.oom.sqlgen.TemplateData;
import jodd.util.ArraysUtil;
import jodd.util.StringPool;
import jodd.util.StringUtil;
/**
* Columns select chunk resolves entity column(s) from column references. Should be used for SELECT queries.
* <p>
* Column reference is specified as: <code>{@link TableChunk tableReference}.propertyName</code> where property name is
* a property of the entity references by table reference. Result is rendered as:
* <code>tableName.column</code> or <code>alias.column</code> (if table has an alias).
* <p>
* There are some special values for propertyName
* <ul>
* <li>wildcard (*), all table columns will be listed</li>
* <li>id sign (+), all table id columns will be listed</li>
* </ul>
* <p>
* If previous chunk is also a column chunk, comma separator will be added in between.
* <p>
* Note that column alias are appended to the column name (using 'as' construct).
* <p>
* Macro rules:
* <ul>
* <li><code>$C{tableRef}</code> is rendered as FOO.col1, FOO.col2,...</li>
* <li><code>$C{tableRef.*}</code> is equal to above, renders all entity columns</li>
* <li><code>$C{tableRef.+}</code> renders to only identity columns</li>
* <li><code>$C{tableRef.%}</code> renders all but identity columns</li>
* <li><code>$C{tableRef.colRef}</code> is rendered as FOO.column</li>
* <li><code>$C{tableRef.[colRef1,colRef2|...]}</code> is rendered as FOO.column1, FOO.column2,..., support id sign (+)</li>
* <li><code>$C{entityRef.colRef}</code> renders to FOO$column</li>
* <li><code>$C{hint.entityRef...}</code> defines a hint</li>
* <li><code>$C{hint:entityRef...}</code> defines a hint with custom name</li>
* <li><code>$C{.columName}</code> renders as column name</li>
* <li><code>$C{hint:.columName}</code> renders as column name and defines its hint</li>
* </ul>
*/
public class ColumnsSelectChunk extends SqlChunk {
private static final String AS = " as ";
private static final char SPLIT = ',';
protected final String tableRef;
protected final String columnRef;
protected final String[] columnRefArr;
protected final int includeColumns;
protected final String hint;
protected ColumnsSelectChunk(String tableRef, String columnRef,String[] columnRefArr, int includeColumns, String hint) {
super(CHUNK_SELECT_COLUMNS);
this.tableRef = tableRef;
this.columnRef = columnRef;
this.columnRefArr = columnRefArr;
this.includeColumns = includeColumns;
this.hint = hint;
}
public ColumnsSelectChunk(String tableRef, String columnRef) {
this(tableRef, columnRef, null, COLS_NA, null);
}
public ColumnsSelectChunk(String tableRef, String... columnRefArr) {
this(tableRef, null, columnRefArr, COLS_NA_MULTI, null);
}
public ColumnsSelectChunk(String tableRef, boolean includeAll) {
this(tableRef, null, null, includeAll == true ? COLS_ALL : COLS_ONLY_IDS, null);
}
public ColumnsSelectChunk(String reference) {
super(CHUNK_SELECT_COLUMNS);
reference = reference.trim();
int dotNdx = reference.lastIndexOf('.');
if (dotNdx == -1) {
this.tableRef = reference;
this.columnRef = null;
this.columnRefArr = null;
this.includeColumns = COLS_ALL;
this.hint = null;
} else {
String tref = reference.substring(0, dotNdx);
reference = reference.substring(dotNdx + 1);
// table
dotNdx = tref.lastIndexOf('.');
if (dotNdx == -1) {
this.tableRef = tref;
this.hint = null;
} else {
int doubleColumnNdx = tref.indexOf(':');
if (doubleColumnNdx == -1) {
// no special hint
this.tableRef = tref.substring(dotNdx + 1);
this.hint = tref;
} else {
// hint is different
this.tableRef = tref.substring(doubleColumnNdx + 1);
this.hint = tref.substring(0, doubleColumnNdx);
}
}
// column
if (reference.equals(StringPool.STAR)) {
this.columnRef = null;
this.columnRefArr = null;
this.includeColumns = COLS_ALL;
} else if (reference.equals(StringPool.PLUS)) {
this.columnRef = null;
this.columnRefArr = null;
this.includeColumns = COLS_ONLY_IDS;
} else if (reference.equals(StringPool.PERCENT)) {
this.columnRef = null;
this.columnRefArr = null;
this.includeColumns = COLS_ALL_BUT_ID;
} else if (
reference.length() != 0
&& reference.charAt(0) == '['
&& reference.charAt(reference.length() - 1) == ']') {
this.columnRef = null;
this.columnRefArr = StringUtil.splitc(reference.substring(1, reference.length() - 1), SPLIT);
StringUtil.trimAll(this.columnRefArr);
this.includeColumns = COLS_NA_MULTI;
} else {
this.columnRef = reference;
this.columnRefArr = null;
this.includeColumns = COLS_NA;
}
}
}
// ---------------------------------------------------------------- process
/**
* Counts actual real hints.
*/
@Override
public void init(TemplateData templateData) {
super.init(templateData);
if (hint != null) {
templateData.incrementHintsCount();
}
}
@Override
public void process(StringBuilder out) {
// hints
if (templateData.hasHints()) {
templateData.registerHint(hint == null ? tableRef : hint);
}
// columns
separateByCommaOrSpace(out);
// special case, only column name, no table ref/name
if (tableRef.length() == 0) {
out.append(columnRef);
return;
}
boolean useTableReference = true;
DbEntityDescriptor ded = lookupTableRef(tableRef, false);
if (ded == null) {
useTableReference = false;
ded = lookupName(tableRef);
}
if (columnRef == null) {
DbEntityColumnDescriptor[] decList = ded.getColumnDescriptors();
int count = 0;
boolean withIds = (columnRefArr != null) && ArraysUtil.contains(columnRefArr, StringPool.PLUS);
for (DbEntityColumnDescriptor dec : decList) {
if ((includeColumns == COLS_ONLY_IDS) && (dec.isId() == false)) {
continue;
}
if ((includeColumns == COLS_ALL_BUT_ID) && (dec.isId() == true)) {
continue;
}
if ((includeColumns == COLS_NA_MULTI)
&& (!withIds || (dec.isId() == false))
&& (!ArraysUtil.contains(columnRefArr, dec.getPropertyName()))) {
continue;
}
if (count > 0) {
out.append(',').append(' ');
}
templateData.lastColumnDec = dec;
if (useTableReference) {
appendColumnName(out, ded, dec.getColumnName());
} else {
appendAlias(out, ded, dec.getColumnName());
}
count++;
}
} else {
DbEntityColumnDescriptor dec = ded.findByPropertyName(columnRef);
templateData.lastColumnDec = dec;
String columnName = dec == null ? null : dec.getColumnName();
//String columnName = ded.getColumnName(columnRef);
if (columnName == null) {
throw new DbSqlBuilderException("Invalid column reference: " + tableRef + '.' + columnRef);
}
if (useTableReference) {
appendColumnName(out, ded, columnName);
} else {
appendAlias(out, ded, columnName);
}
}
}
/**
* Appends alias.
*/
protected void appendAlias(StringBuilder query, DbEntityDescriptor ded, String column) {
String tableName = ded.getTableName();
ColumnAliasType columnAliasType = templateData.getColumnAliasType();
String columnAliasSeparator = templateData.getDbOomManager().getColumnAliasSeparator();
if (columnAliasType == null || columnAliasType == ColumnAliasType.TABLE_REFERENCE) {
templateData.registerColumnDataForTableRef(tableRef, tableName);
query.append(tableRef).append(columnAliasSeparator).append(column);
} else
if (columnAliasType == ColumnAliasType.COLUMN_CODE) {
String code = templateData.registerColumnDataForColumnCode(tableName, column);
query.append(code);
} else
if (columnAliasType == ColumnAliasType.TABLE_NAME) {
query.append(tableName).append(columnAliasSeparator).append(column);
}
}
/**
* Simply appends column name with optional table reference and alias.
*/
protected void appendColumnName(StringBuilder query, DbEntityDescriptor ded, String column) {
query.append(resolveTable(tableRef, ded)).append('.').append(column);
if (templateData.getColumnAliasType() != null) { // create column aliases
String tableName = ded.getTableName();
query.append(AS);
switch (templateData.getColumnAliasType()) {
case TABLE_NAME:
query.append(tableName).append(templateData.getDbOomManager().getColumnAliasSeparator()).append(column);
break;
case TABLE_REFERENCE:
templateData.registerColumnDataForTableRef(tableRef, tableName);
query.append(tableRef).append(templateData.getDbOomManager().getColumnAliasSeparator()).append(column);
break;
case COLUMN_CODE:
String code = templateData.registerColumnDataForColumnCode(tableName, column);
query.append(code);
break;
}
}
}
}