/* * 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.query.sql.visitor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.teiid.query.sql.lang.Delete; import org.teiid.query.sql.lang.Insert; import org.teiid.query.sql.lang.SetClause; import org.teiid.query.sql.lang.StoredProcedure; import org.teiid.query.sql.lang.UnaryFromClause; import org.teiid.query.sql.lang.Update; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.sql.symbol.MultipleElementSymbol; import org.teiid.query.sql.symbol.Symbol; /** * <p> This class is used to update LanguageObjects by replacing one set of symbols with * another. There is one abstract method which must be overridden to define how the * mapping lookup occurs.</p> */ public abstract class AbstractSymbolMappingVisitor extends ExpressionMappingVisitor { private List unmappedSymbols; protected AbstractSymbolMappingVisitor() { super(null); } /** * Get the mapped symbol from the specified symbol. Subclasses should implement * this method to look up the target symbol from the specified symbol. * @param symbol Source symbol * @return Target symbol */ protected abstract Symbol getMappedSymbol(Symbol symbol); // ############### Visitor methods for language objects ################## /** * <p> This method updates the <code>Insert</code> object it receives as an * argument by replacing the virtual groups/elements with their physical * counterparts.</p> * @param obj The Insert object to be updated with physical groups/elements */ public void visit(Insert obj) { List physicalElements = new ArrayList(); // get the GroupSymbol on the insert GroupSymbol virtualGroup = obj.getGroup(); obj.setGroup(getMappedGroup(virtualGroup)); // get all virtual columns present on the Insert and replace them with // physical elements if(obj.getVariables() != null) { Iterator elementIter = obj.getVariables().iterator(); while(elementIter.hasNext()) { ElementSymbol virtualElement = (ElementSymbol) elementIter.next(); physicalElements.add(getMappedElement(virtualElement)); } obj.setVariables(physicalElements); } } /** * <p> This method updates the <code>Delete</code> object it receives as an * argument by replacing the virtual groups/elements with their physical * counterparts.</p> * @param obj The Delete object to be updated with physical groups */ public void visit(Delete obj) { // get the GroupSymbol on the delete GroupSymbol virtualGroup = obj.getGroup(); obj.setGroup(getMappedGroup(virtualGroup)); } /** * <p> This method updates the <code>Update</code> object it receives as an * argument by replacing the virtual groups/elements with their physical * counterparts.</p> * @param obj The Update object to be updated with physical groups */ public void visit(Update obj) { // get the GroupSymbol on the update GroupSymbol virtualGroup = obj.getGroup(); obj.setGroup(getMappedGroup(virtualGroup)); } public void visit(SetClause obj) { obj.setSymbol(getMappedElement(obj.getSymbol())); } /** * Swap each ElementSymbol referenced by AllInGroupSymbol * @param obj Object to remap */ public void visit(MultipleElementSymbol obj) { List<ElementSymbol> oldSymbols = obj.getElementSymbols(); if(oldSymbols != null && oldSymbols.size() > 0) { List<ElementSymbol> newSymbols = new ArrayList<ElementSymbol>(oldSymbols.size()); Iterator<ElementSymbol> iter = oldSymbols.iterator(); while(iter.hasNext()) { ElementSymbol es = iter.next(); ElementSymbol mappedSymbol = getMappedElement(es); newSymbols.add( mappedSymbol ); } obj.setElementSymbols(newSymbols); } if (obj.getGroup() == null) { return; } obj.setGroup(getMappedGroup(obj.getGroup())); } /** * Swap group in unary from clause. * @param obj Object to remap */ public void visit(UnaryFromClause obj) { GroupSymbol srcGroup = obj.getGroup(); obj.setGroup(getMappedGroup(srcGroup)); } /** * Swap name of stored proc and elements in stored procedure parameter expressions * @param obj Object to remap */ public void visit(StoredProcedure obj) { // Swap procedure name String execName = obj.getProcedureName(); GroupSymbol fakeGroup = new GroupSymbol(execName); Object procedureID = obj.getProcedureID(); if(procedureID != null) { fakeGroup.setMetadataID(procedureID); } GroupSymbol mappedGroup = getMappedGroup(fakeGroup); obj.setProcedureName(mappedGroup.getName()); super.visit(obj); } /* ############### Helper Methods ################## */ /** * @see org.teiid.query.sql.visitor.ExpressionMappingVisitor#replaceExpression(org.teiid.query.sql.symbol.Expression) */ @Override public Expression replaceExpression(Expression element) { if (element instanceof ElementSymbol) { return getMappedElement((ElementSymbol)element); } return element; } /** * <p> This method looks up the symbol map for a physical <code>ElementSymbol</code> * given a virtual <code>ElementSymbol</code> object.</p> * @param obj The virtual <code>ElementSymbol</code> object whose physical counterpart is returned * @return The physical <code>ElementSymbol</code> object or null if the object could not be mapped */ private ElementSymbol getMappedElement(ElementSymbol obj) { ElementSymbol element = (ElementSymbol) getMappedSymbol(obj); if(element != null) { return element; } markUnmapped(obj); return obj; } /** * <p> This method looks up the symbol map for a physical <code>GroupSymbol</code> * given a virtual <code>GroupSymbol</code> object.</p> * @param obj The virtual <code>GroupSymbol</code> object whose physical counterpart is returned * @return The physical <code>GroupSymbol</code> object or null if the object could not be mapped */ private GroupSymbol getMappedGroup(GroupSymbol obj) { GroupSymbol group = (GroupSymbol) getMappedSymbol(obj); if(group != null) { return group; } markUnmapped(obj); return obj; } /** * Mark an element as unmapped as no mapping could be found. * @param symbol Unmapped symbol */ private void markUnmapped(Symbol symbol) { if(unmappedSymbols == null) { unmappedSymbols = new ArrayList(); } unmappedSymbols.add(symbol); } /** * Get all symbols that were not mapped during life of visitor. If all symbols * were mapped, this will return null. * @return List of ElementSymbol and GroupSymbol that were unmapped OR null if * all symbols mapped successfully */ public List getUnmappedSymbols() { return unmappedSymbols; } }