/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.salesforce.execution.visitors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.teiid.language.*;
import org.teiid.language.AndOr.Operator;
import org.teiid.metadata.Column;
import org.teiid.metadata.RuntimeMetadata;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.salesforce.Constants;
import org.teiid.translator.salesforce.SalesForcePlugin;
public class SelectVisitor extends CriteriaVisitor implements IQueryProvidingVisitor {
public static final String AGG_PREFIX = "expr"; //$NON-NLS-1$
private Map<Integer, Expression> selectSymbolIndexToElement = new HashMap<Integer, Expression>();
private int selectSymbolCount;
private int idIndex = -1; // index of the ID select symbol.
protected List<DerivedColumn> selectSymbols;
protected StringBuilder limitClause = new StringBuilder();
protected StringBuilder groupByClause = new StringBuilder();
protected StringBuilder havingClause = new StringBuilder();
private Boolean objectSupportsRetrieve;
private Condition implicitCondition;
public SelectVisitor(RuntimeMetadata metadata) {
super(metadata);
}
@Override
public void visit(Select query) {
super.visitNodes(query.getFrom());
Condition condition = query.getWhere();
if (this.implicitCondition != null) {
if (condition != null) {
condition = LanguageFactory.INSTANCE.createAndOr(Operator.AND, condition, this.implicitCondition);
}
else {
condition = implicitCondition;
}
}
super.visitNode(condition);
super.visitNode(query.getGroupBy());
if (query.getHaving() != null) {
//since the base is a criteria hierarchy visitor,
//we must separately visit the having clause
//TODO: if further uses of criteria come up, we should not use hierarchy visitor as the base
Condition c = query.getHaving();
CriteriaVisitor cv = new CriteriaVisitor(this.metadata);
cv.visitNode(c);
cv.addCriteriaString(SQLConstants.Reserved.HAVING, this.havingClause);
if (this.havingClause.length() > 0) {
this.havingClause.append(SPACE);
}
}
super.visitNode(query.getLimit());
if (query.isDistinct()) {
exceptions.add(new TranslatorException(SalesForcePlugin.Util.getString("SelectVisitor.distinct.not.supported"))); //$NON-NLS-1$
}
selectSymbols = query.getDerivedColumns();
selectSymbolCount = selectSymbols.size();
for (int index = 0; index < selectSymbols.size(); index++) {
DerivedColumn symbol = selectSymbols.get(index);
// get the name in source
Expression expression = symbol.getExpression();
selectSymbolIndexToElement.put(index, expression);
if (expression instanceof ColumnReference) {
Column element = ((ColumnReference) expression).getMetadataObject();
String nameInSource = element.getSourceName();
if (nameInSource.equalsIgnoreCase("id")) { //$NON-NLS-1$
idIndex = index;
}
}
}
}
protected void addCriteria(Condition condition) {
this.implicitCondition = condition;
}
@Override
public void visit(GroupBy obj) {
this.groupByClause.append("GROUP BY "); //$NON-NLS-1$
for (Iterator<Expression> iter = obj.getElements().iterator(); iter.hasNext();) {
Expression expr = iter.next();
this.groupByClause.append(getValue(expr, false));
if (iter.hasNext()) {
this.groupByClause.append(", "); //$NON-NLS-1$
}
}
this.groupByClause.append(SPACE);
}
@Override
public void visit(NamedTable obj) {
try {
table = obj.getMetadataObject();
String supportsQuery = table.getProperty(Constants.SUPPORTS_QUERY, true);
objectSupportsRetrieve = Boolean.valueOf(table.getProperty(Constants.SUPPORTS_RETRIEVE, true));
if (supportsQuery != null && !Boolean.valueOf(supportsQuery)) {
throw new TranslatorException(table.getSourceName() + " " + SalesForcePlugin.Util.getString("CriteriaVisitor.query.not.supported")); //$NON-NLS-1$ //$NON-NLS-2$
}
loadColumnMetadata(obj);
} catch (TranslatorException ce) {
exceptions.add(ce);
}
}
@Override
public void visit(Limit obj) {
super.visit(obj);
limitClause.append(LIMIT).append(SPACE).append(obj.getRowLimit());
}
/*
* The SOQL SELECT command uses the following syntax: SELECT fieldList FROM
* objectType [WHERE The Condition Expression (WHERE Clause)] [ORDER BY]
* LIMIT ?
*/
public String getQuery() throws TranslatorException {
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
StringBuilder result = new StringBuilder();
result.append(SELECT).append(SPACE);
addSelectSymbols(result);
result.append(SPACE);
result.append(FROM).append(SPACE);
result.append(table.getSourceName()).append(SPACE);
addCriteriaString(result);
appendGroupByHaving(result);
//result.append(orderByClause).append(SPACE);
result.append(limitClause);
return result.toString();
}
protected void appendGroupByHaving(StringBuilder result) {
result.append(this.groupByClause);
result.append(this.havingClause);
}
private void addSelectSymbols(StringBuilder result) {
for (int i = 0; i < selectSymbols.size(); i++) {
DerivedColumn symbol = selectSymbols.get(i);
if (i > 0) {
result.append(", "); //$NON-NLS-1$
}
Expression expression = symbol.getExpression();
if (expression instanceof ColumnReference) {
appendColumnReference(result, (ColumnReference) expression);
} else if (expression instanceof AggregateFunction) {
AggregateFunction af = (AggregateFunction)expression;
appendAggregateFunction(result, af);
} else {
throw new AssertionError("Unknown select symbol type" + symbol); //$NON-NLS-1$
}
}
}
public int getSelectSymbolCount() {
return selectSymbolCount;
}
public Expression getSelectSymbolMetadata(int index) {
return selectSymbolIndexToElement.get(index);
}
/**
* Returns the index of the ID column.
* @return the index of the ID column, -1 if there is no ID column.
*/
public int getIdIndex() {
return idIndex;
}
public boolean getQueryAll() {
return queryAll;
}
public String getRetrieveFieldList() {
assertRetrieveValidated();
StringBuilder result = new StringBuilder();
addSelectSymbols(result);
return result.toString();
}
public List<String> getIdInCriteria() {
assertRetrieveValidated();
List<Expression> expressions = this.idInCriteria.getRightExpressions();
List<String> result = new ArrayList<String>(expressions.size());
for(int i = 0; i < expressions.size(); i++) {
result.add(getValue(expressions.get(i), true));
}
return result;
}
private void assertRetrieveValidated() throws AssertionError {
if(!hasOnlyIDCriteria()) {
throw new AssertionError("Must call hasOnlyIdInCriteria() before this method"); //$NON-NLS-1$
}
}
public boolean hasOnlyIdInCriteria() {
return hasOnlyIDCriteria() && idInCriteria != null;
}
public boolean canRetrieve() {
return objectSupportsRetrieve && hasOnlyIDCriteria() && this.limitClause.length() == 0 && groupByClause.length() == 0;
}
public boolean hasGroupBy() {
return groupByClause.length() > 0;
}
}