/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr.query.optimize; import java.util.LinkedList; import org.modeshape.common.annotation.Immutable; import org.modeshape.jcr.query.QueryContext; import org.modeshape.jcr.query.model.And; import org.modeshape.jcr.query.model.Between; import org.modeshape.jcr.query.model.ChildNode; import org.modeshape.jcr.query.model.Comparison; import org.modeshape.jcr.query.model.Constraint; import org.modeshape.jcr.query.model.DescendantNode; import org.modeshape.jcr.query.model.DynamicOperand; import org.modeshape.jcr.query.model.FullTextSearch; import org.modeshape.jcr.query.model.NodeDepth; import org.modeshape.jcr.query.model.NodeLocalName; import org.modeshape.jcr.query.model.NodeName; import org.modeshape.jcr.query.model.NodePath; import org.modeshape.jcr.query.model.Not; import org.modeshape.jcr.query.model.Or; import org.modeshape.jcr.query.model.PropertyExistence; import org.modeshape.jcr.query.model.SetCriteria; import org.modeshape.jcr.query.model.Visitors; import org.modeshape.jcr.query.plan.PlanNode; import org.modeshape.jcr.query.plan.PlanNode.Property; import org.modeshape.jcr.query.plan.PlanNode.Type; /** * An {@link OptimizerRule optimizer rule} that rewrites Constraint trees, moving path-, name-, or depth-oriented criteria to the * left-most parts of the constraint tree. */ @Immutable public class RewritePathAndNameCriteria implements OptimizerRule { public static final RewritePathAndNameCriteria INSTANCE = new RewritePathAndNameCriteria(); @Override public PlanNode execute( QueryContext context, PlanNode plan, LinkedList<OptimizerRule> ruleStack ) { // Remove the view from the selectors ... for (PlanNode node : plan.findAllAtOrBelow(Type.SELECT)) { Constraint constraint = node.getProperty(Property.SELECT_CRITERIA, Constraint.class); Constraint newConstraint = rewriteCriteria(context, constraint); if (constraint != newConstraint) { node.getSelectors().clear(); node.addSelectors(Visitors.getSelectorsReferencedBy(newConstraint)); node.setProperty(Property.SELECT_CRITERIA, newConstraint); } } return plan; } protected Constraint rewriteCriteria( QueryContext context, Constraint constraint ) { if (constraint instanceof And) { And and = (And)constraint; Constraint oldLeft = and.left(); Constraint oldRight = and.right(); Constraint newLeft = rewriteCriteria(context, oldLeft); Constraint newRight = rewriteCriteria(context, oldRight); if (newRight != oldRight) { // The right side is path-oriented ... if (newLeft != oldLeft) { // The left is too, so create a new And constraint to signal that it's path oriented ... return new And(newLeft, newRight); } // Only the right is, so swap the order ... return new And(newRight, newLeft); } // Neither are path-oriented ... return and; } if (constraint instanceof Or) { Or or = (Or)constraint; Constraint oldLeft = or.left(); Constraint oldRight = or.right(); Constraint newLeft = rewriteCriteria(context, oldLeft); Constraint newRight = rewriteCriteria(context, oldRight); if (newRight != oldRight) { // The right side is path-oriented ... if (newLeft != oldLeft) { // The left is too, so create a new Or constraint to signal that it's path oriented ... return new Or(newLeft, newRight); } // Only the right is, so swap the order ... return new Or(newRight, newLeft); } // Neither are path-oriented ... return or; } if (constraint instanceof Not) { Not not = (Not)constraint; Constraint oldWrapped = not.getConstraint(); Constraint newWrapped = rewriteCriteria(context, oldWrapped); if (newWrapped != oldWrapped) { // The wrapped constraint is path-oriented, so create a new Not to signal it ... return new Not(newWrapped); } return not; } if (constraint instanceof ChildNode) { ChildNode childNode = (ChildNode)constraint; return new ChildNode(childNode.selectorName(), childNode.getParentPath()); } if (constraint instanceof DescendantNode) { DescendantNode descNode = (DescendantNode)constraint; return new DescendantNode(descNode.selectorName(), descNode.getAncestorPath()); } if (constraint instanceof PropertyExistence) { return constraint; } if (constraint instanceof FullTextSearch) { return constraint; } if (constraint instanceof Between) { Between between = (Between)constraint; DynamicOperand operand = between.getOperand(); if (isPathOriented(operand)) { return new Between(operand, between.getLowerBound(), between.getUpperBound(), between.isLowerBoundIncluded(), between.isUpperBoundIncluded()); } return between; } if (constraint instanceof Comparison) { Comparison comparison = (Comparison)constraint; DynamicOperand operand = comparison.getOperand1(); if (isPathOriented(operand)) { return new Comparison(operand, comparison.operator(), comparison.getOperand2()); } return comparison; } if (constraint instanceof SetCriteria) { SetCriteria criteria = (SetCriteria)constraint; DynamicOperand operand = criteria.getOperand(); if (isPathOriented(operand)) { return new SetCriteria(operand, criteria.rightOperands()); } return criteria; } return constraint; } protected boolean isPathOriented( DynamicOperand op ) { return op instanceof NodeDepth || op instanceof NodeLocalName || op instanceof NodeName || op instanceof NodePath; } @Override public String toString() { return getClass().getSimpleName(); } }