/* * 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.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidRuntimeException; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.resolver.util.ResolverUtil; import org.teiid.query.sql.LanguageObject; import org.teiid.query.sql.LanguageVisitor; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.Query; import org.teiid.query.sql.navigator.PreOrPostOrderNavigator; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.sql.symbol.Reference; /** * <p>This visitor class will traverse a language object tree and collect references that * correspond to correlated subquery references.</p> * * <p>The easiest way to use this visitor is to call the static method which creates the * the visitor by passing it the Language Object and the variable context to be looked up. * The public visit() methods should NOT be called directly.</p> */ public class CorrelatedReferenceCollectorVisitor extends LanguageVisitor { // index of the reference on the language object private List<Reference> references; private ArrayList<Map<GroupSymbol,GroupSymbol>> outerGroups = new ArrayList<Map<GroupSymbol,GroupSymbol>>(2); private QueryMetadataInterface metadata; private boolean queryRoot; CorrelatedReferenceCollectorVisitor(Collection<GroupSymbol> groupSymbols, List<Reference> correlatedReferences) { HashMap<GroupSymbol, GroupSymbol> groupMap = new HashMap<GroupSymbol, GroupSymbol>(); for (GroupSymbol g : groupSymbols) { groupMap.put(g, g); } outerGroups.add(groupMap); this.references = correlatedReferences; } public List<Reference> getReferences(){ return this.references; } // ############### Visitor methods for language objects ################## /** * Visit a language object and collect symbols. This method should <b>NOT</b> be * called directly. * @param obj Language object */ public void visit(Reference obj) { ElementSymbol e = obj.getExpression(); if (e == null|| !e.isExternalReference()) { return; } GroupSymbol g = e.getGroupSymbol(); for (int i = this.outerGroups.size() - (queryRoot?2:1); i >= 0; i--) { GroupSymbol outer = this.outerGroups.get(i).get(g); if (outer == null) { continue; } try { if (ResolverUtil.resolveElementsInGroup(outer, metadata).contains(e)) { //add if correlated to the root groups if (i == 0) { this.references.add(obj); } return; } } catch (TeiidComponentException e1) { throw new TeiidRuntimeException(e1); } } } /** * <p>Helper to use this visitor.</p> * @param obj The Language object that is to be visited * @param groupSymbols Collection of GroupSymbols to restrict collection to - these are the groups * that the client (outer query) is interested in references to from the correlated subquery * @param correlatedReferences List of References collected */ public static final void collectReferences(final LanguageObject obj, final Collection<GroupSymbol> groupSymbols, List<Reference> correlatedReferences, QueryMetadataInterface metadata) { final Set<GroupSymbol> groups = new HashSet<GroupSymbol>(groupSymbols); final CorrelatedReferenceCollectorVisitor visitor = new CorrelatedReferenceCollectorVisitor(groups, correlatedReferences); visitor.metadata = metadata; visitor.queryRoot = obj instanceof Command; obj.acceptVisitor(new PreOrPostOrderNavigator(visitor, PreOrPostOrderNavigator.PRE_ORDER, true) { @Override public void visit(Query query) { //don't allow confusion with deep nesting by removing intermediate groups List<GroupSymbol> fromGroups = null; if (query.getFrom() != null) { fromGroups = query.getFrom().getGroups(); HashMap<GroupSymbol, GroupSymbol> groupMap = new HashMap<GroupSymbol, GroupSymbol>(); for (GroupSymbol g : fromGroups) { groupMap.put(g, g); } visitor.outerGroups.add(groupMap); } super.visit(query); if (fromGroups != null) { visitor.outerGroups.remove(visitor.outerGroups.size() - 1); } } }); } }