/* * 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.odata4; import static org.teiid.language.SQLConstants.Reserved.DESC; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.apache.olingo.client.core.uri.URIBuilderImpl; import org.teiid.language.AggregateFunction; import org.teiid.language.ColumnReference; import org.teiid.language.Condition; import org.teiid.language.DerivedColumn; import org.teiid.language.Join; import org.teiid.language.LanguageObject; import org.teiid.language.LanguageUtil; import org.teiid.language.Limit; import org.teiid.language.NamedTable; import org.teiid.language.OrderBy; import org.teiid.language.QueryExpression; import org.teiid.language.SQLConstants.Tokens; import org.teiid.language.Select; import org.teiid.language.SortSpecification; import org.teiid.language.SortSpecification.Ordering; import org.teiid.language.visitor.HierarchyVisitor; import org.teiid.metadata.Column; import org.teiid.metadata.RuntimeMetadata; import org.teiid.metadata.Table; import org.teiid.translator.TranslatorException; public class ODataSQLVisitor extends HierarchyVisitor { protected ArrayList<TranslatorException> exceptions = new ArrayList<TranslatorException>(); protected QueryExpression command; protected ODataExecutionFactory executionFactory; protected RuntimeMetadata metadata; protected ArrayList<Column> projectedColumns = new ArrayList<Column>(); private ODataSelectQuery odataQuery; private StringBuilder orderBy = new StringBuilder(); private boolean count = false; private ArrayList<Condition> conditionFragments = new ArrayList<Condition>(); public ODataSQLVisitor(ODataExecutionFactory executionFactory, RuntimeMetadata metadata) { this.executionFactory = executionFactory; this.metadata = metadata; this.odataQuery = new ODataSelectQuery(executionFactory, metadata); } public List<Column> getProjectedColumns(){ return this.projectedColumns; } public ODataSelectQuery getODataQuery() { return this.odataQuery; } public boolean isCount() { return this.count; } public String buildURL(String serviceRoot) throws TranslatorException { URIBuilderImpl uriBuilder = this.odataQuery.buildURL(serviceRoot, this.projectedColumns, LanguageUtil.combineCriteria(this.conditionFragments)); if (this.orderBy.length() > 0) { uriBuilder.orderBy(this.orderBy.toString()); } URI uri = uriBuilder.build(); return uri.toString(); } List<String> getColumnNames(List<Column> columns) { ArrayList<String> names = new ArrayList<String>(); for (Column c : columns) { names.add(c.getName()); } return names; } @Override public void visit(NamedTable obj) { try { this.odataQuery.addRootDocument(obj.getMetadataObject()); } catch (TranslatorException e) { this.exceptions.add(e); } } @Override public void visit(Join obj) { // joins are not used currently if (obj.getLeftItem() instanceof Join) { Condition updated = obj.getCondition(); append(obj.getLeftItem()); Table right = ((NamedTable)obj.getRightItem()).getMetadataObject(); try { updated = this.odataQuery.addNavigation(obj.getCondition(), obj.getJoinType(), right); obj.setCondition(updated); if (updated != null) { this.conditionFragments.add(obj.getCondition()); } } catch (TranslatorException e) { this.exceptions.add(e); } } else if (obj.getRightItem() instanceof Join) { Condition updated = obj.getCondition(); append(obj.getRightItem()); Table left = ((NamedTable)obj.getLeftItem()).getMetadataObject(); try { updated = this.odataQuery.addNavigation(obj.getCondition(), obj.getJoinType(), left); obj.setCondition(updated); if (updated != null) { this.conditionFragments.add(obj.getCondition()); } } catch (TranslatorException e) { this.exceptions.add(e); } } else { Condition updated = obj.getCondition(); Table left = ((NamedTable)obj.getLeftItem()).getMetadataObject(); Table right = ((NamedTable)obj.getRightItem()).getMetadataObject(); try { if (ODataMetadataProcessor.isComplexType(left) || ODataMetadataProcessor.isNavigationType(left)) { throw new TranslatorException(ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17027, left.getName())); } updated = this.odataQuery.addNavigation(obj.getCondition(), obj.getJoinType(), left, right); obj.setCondition(updated); if (updated != null) { this.conditionFragments.add(obj.getCondition()); } } catch (TranslatorException e) { this.exceptions.add(e); } } } @Override public void visit(Limit obj) { if (obj.getRowOffset() != 0) { this.odataQuery.setSkip(new Integer(obj.getRowOffset())); } if (obj.getRowLimit() != 0) { this.odataQuery.setTop(new Integer(obj.getRowLimit())); } } @Override public void visit(OrderBy obj) { append(obj.getSortSpecifications()); } @Override public void visit(SortSpecification obj) { if (this.orderBy.length() > 0) { this.orderBy.append(Tokens.COMMA); } ColumnReference column = (ColumnReference)obj.getExpression(); try { Column c = normalizePseudoColumn(column.getMetadataObject()); this.orderBy.append(c.getName()); } catch (TranslatorException e) { this.exceptions.add(e); } // default is ascending if (obj.getOrdering() == Ordering.DESC) { this.orderBy.append(Tokens.SPACE).append(DESC.toLowerCase()); } } @Override public void visit(Select obj) { visitNodes(obj.getFrom()); this.conditionFragments.add(obj.getWhere()); visitNode(obj.getOrderBy()); visitNode(obj.getLimit()); visitNodes(obj.getDerivedColumns()); } @Override public void visit(DerivedColumn obj) { if (obj.getExpression() instanceof ColumnReference) { Column column = ((ColumnReference)obj.getExpression()).getMetadataObject(); if (!column.isSelectable()) { this.exceptions.add(new TranslatorException(ODataPlugin.Util .gs(ODataPlugin.Event.TEIID17006, column.getName()))); } try { this.projectedColumns.add(normalizePseudoColumn(column)); } catch (TranslatorException e) { this.exceptions.add(e); } } else if (obj.getExpression() instanceof AggregateFunction) { AggregateFunction func = (AggregateFunction)obj.getExpression(); if (func.getName().equalsIgnoreCase("COUNT")) { //$NON-NLS-1$ this.odataQuery.setAsCount(); } else { this.exceptions.add(new TranslatorException(ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17007, func.getName()))); } } else { this.exceptions.add(new TranslatorException(ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17008))); } } private Column normalizePseudoColumn(Column column) throws TranslatorException { String pseudo = ODataMetadataProcessor.getPseudo(column); if (pseudo != null) { try { Table columnParent = (Table)column.getParent(); Table pseudoColumnParent = this.metadata.getTable( ODataMetadataProcessor.getMerge(columnParent)); return pseudoColumnParent.getColumnByName(pseudo); } catch (TranslatorException e) { this.exceptions.add(e); } } return column; } public void append(LanguageObject obj) { visitNode(obj); } protected void append(List<? extends LanguageObject> items) { if (items != null && items.size() != 0) { for (int i = 0; i < items.size(); i++) { append(items.get(i)); } } } protected void append(LanguageObject[] items) { if (items != null && items.length != 0) { for (int i = 0; i < items.length; i++) { append(items[i]); } } } }