| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| /* |
| * $Id: WalkerFactory.java 469314 2006-10-30 23:31:59Z minchau $ |
| */ |
| package org.apache.xpath.axes; |
| |
| import org.apache.xalan.res.XSLMessages; |
| import org.apache.xml.dtm.Axis; |
| import org.apache.xml.dtm.DTMFilter; |
| import org.apache.xml.dtm.DTMIterator; |
| import org.apache.xpath.Expression; |
| import org.apache.xpath.compiler.Compiler; |
| import org.apache.xpath.compiler.FunctionTable; |
| import org.apache.xpath.compiler.OpCodes; |
| import org.apache.xpath.compiler.OpMap; |
| import org.apache.xpath.objects.XNumber; |
| import org.apache.xpath.patterns.ContextMatchStepPattern; |
| import org.apache.xpath.patterns.FunctionPattern; |
| import org.apache.xpath.patterns.NodeTest; |
| import org.apache.xpath.patterns.StepPattern; |
| import org.apache.xpath.res.XPATHErrorResources; |
| |
| /** |
| * This class is both a factory for XPath location path expressions, |
| * which are built from the opcode map output, and an analysis engine |
| * for the location path expressions in order to provide optimization hints. |
| */ |
| public class WalkerFactory |
| { |
| |
| /** |
| * This method is for building an array of possible levels |
| * where the target element(s) could be found for a match. |
| * @param lpi The owning location path iterator. |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param stepOpCodePos The opcode position for the step. |
| * |
| * @return non-null AxesWalker derivative. |
| * |
| * @throws javax.xml.transform.TransformerException |
| * @xsl.usage advanced |
| */ |
| static AxesWalker loadOneWalker( |
| WalkingIterator lpi, Compiler compiler, int stepOpCodePos) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| AxesWalker firstWalker = null; |
| int stepType = compiler.getOp(stepOpCodePos); |
| |
| if (stepType != OpCodes.ENDOP) |
| { |
| |
| // m_axesWalkers = new AxesWalker[1]; |
| // As we unwind from the recursion, create the iterators. |
| firstWalker = createDefaultWalker(compiler, stepType, lpi, 0); |
| |
| firstWalker.init(compiler, stepOpCodePos, stepType); |
| } |
| |
| return firstWalker; |
| } |
| |
| /** |
| * This method is for building an array of possible levels |
| * where the target element(s) could be found for a match. |
| * @param lpi The owning location path iterator object. |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param stepOpCodePos The opcode position for the step. |
| * @param stepIndex The top-level step index withing the iterator. |
| * |
| * @return non-null AxesWalker derivative. |
| * |
| * @throws javax.xml.transform.TransformerException |
| * @xsl.usage advanced |
| */ |
| static AxesWalker loadWalkers( |
| WalkingIterator lpi, Compiler compiler, int stepOpCodePos, int stepIndex) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int stepType; |
| AxesWalker firstWalker = null; |
| AxesWalker walker, prevWalker = null; |
| |
| int analysis = analyze(compiler, stepOpCodePos, stepIndex); |
| |
| while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos))) |
| { |
| walker = createDefaultWalker(compiler, stepOpCodePos, lpi, analysis); |
| |
| walker.init(compiler, stepOpCodePos, stepType); |
| walker.exprSetParent(lpi); |
| |
| // walker.setAnalysis(analysis); |
| if (null == firstWalker) |
| { |
| firstWalker = walker; |
| } |
| else |
| { |
| prevWalker.setNextWalker(walker); |
| walker.setPrevWalker(prevWalker); |
| } |
| |
| prevWalker = walker; |
| stepOpCodePos = compiler.getNextStepPos(stepOpCodePos); |
| |
| if (stepOpCodePos < 0) |
| break; |
| } |
| |
| return firstWalker; |
| } |
| |
| public static boolean isSet(int analysis, int bits) |
| { |
| return (0 != (analysis & bits)); |
| } |
| |
| public static void diagnoseIterator(String name, int analysis, Compiler compiler) |
| { |
| System.out.println(compiler.toString()+", "+name+", " |
| + Integer.toBinaryString(analysis) + ", " |
| + getAnalysisString(analysis)); |
| } |
| |
| /** |
| * Create a new LocPathIterator iterator. The exact type of iterator |
| * returned is based on an analysis of the XPath operations. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param opPos The position of the operation code for this itterator. |
| * |
| * @return non-null reference to a LocPathIterator or derivative. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| public static DTMIterator newDTMIterator( |
| Compiler compiler, int opPos, |
| boolean isTopLevel) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int firstStepPos = OpMap.getFirstChildPos(opPos); |
| int analysis = analyze(compiler, firstStepPos, 0); |
| boolean isOneStep = isOneStep(analysis); |
| DTMIterator iter; |
| |
| // Is the iteration a one-step attribute pattern (i.e. select="@foo")? |
| if (isOneStep && walksSelfOnly(analysis) && |
| isWild(analysis) && !hasPredicate(analysis)) |
| { |
| if (DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("SelfIteratorNoPredicate", analysis, compiler); |
| |
| // Then use a simple iteration of the attributes, with node test |
| // and predicate testing. |
| iter = new SelfIteratorNoPredicate(compiler, opPos, analysis); |
| } |
| // Is the iteration exactly one child step? |
| else if (walksChildrenOnly(analysis) && isOneStep) |
| { |
| |
| // Does the pattern specify *any* child with no predicate? (i.e. select="child::node()". |
| if (isWild(analysis) && !hasPredicate(analysis)) |
| { |
| if (DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("ChildIterator", analysis, compiler); |
| |
| // Use simple child iteration without any test. |
| iter = new ChildIterator(compiler, opPos, analysis); |
| } |
| else |
| { |
| if (DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("ChildTestIterator", analysis, compiler); |
| |
| // Else use simple node test iteration with predicate test. |
| iter = new ChildTestIterator(compiler, opPos, analysis); |
| } |
| } |
| // Is the iteration a one-step attribute pattern (i.e. select="@foo")? |
| else if (isOneStep && walksAttributes(analysis)) |
| { |
| if (DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("AttributeIterator", analysis, compiler); |
| |
| // Then use a simple iteration of the attributes, with node test |
| // and predicate testing. |
| iter = new AttributeIterator(compiler, opPos, analysis); |
| } |
| else if(isOneStep && !walksFilteredList(analysis)) |
| { |
| if( !walksNamespaces(analysis) |
| && (walksInDocOrder(analysis) || isSet(analysis, BIT_PARENT))) |
| { |
| if (false || DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("OneStepIteratorForward", analysis, compiler); |
| |
| // Then use a simple iteration of the attributes, with node test |
| // and predicate testing. |
| iter = new OneStepIteratorForward(compiler, opPos, analysis); |
| } |
| else |
| { |
| if (false || DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("OneStepIterator", analysis, compiler); |
| |
| // Then use a simple iteration of the attributes, with node test |
| // and predicate testing. |
| iter = new OneStepIterator(compiler, opPos, analysis); |
| } |
| } |
| |
| // Analysis of "//center": |
| // bits: 1001000000001010000000000000011 |
| // count: 3 |
| // root |
| // child:node() |
| // BIT_DESCENDANT_OR_SELF |
| // It's highly possible that we should have a seperate bit set for |
| // "//foo" patterns. |
| // For at least the time being, we can't optimize patterns like |
| // "//table[3]", because this has to be analyzed as |
| // "/descendant-or-self::node()/table[3]" in order for the indexes |
| // to work right. |
| else if (isOptimizableForDescendantIterator(compiler, firstStepPos, 0) |
| // && getStepCount(analysis) <= 3 |
| // && walksDescendants(analysis) |
| // && walksSubtreeOnlyFromRootOrContext(analysis) |
| ) |
| { |
| if (DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("DescendantIterator", analysis, compiler); |
| |
| iter = new DescendantIterator(compiler, opPos, analysis); |
| } |
| else |
| { |
| if(isNaturalDocOrder(compiler, firstStepPos, 0, analysis)) |
| { |
| if (false || DEBUG_ITERATOR_CREATION) |
| { |
| diagnoseIterator("WalkingIterator", analysis, compiler); |
| } |
| |
| iter = new WalkingIterator(compiler, opPos, analysis, true); |
| } |
| else |
| { |
| // if (DEBUG_ITERATOR_CREATION) |
| // diagnoseIterator("MatchPatternIterator", analysis, compiler); |
| // |
| // return new MatchPatternIterator(compiler, opPos, analysis); |
| if (DEBUG_ITERATOR_CREATION) |
| diagnoseIterator("WalkingIteratorSorted", analysis, compiler); |
| |
| iter = new WalkingIteratorSorted(compiler, opPos, analysis, true); |
| } |
| } |
| if(iter instanceof LocPathIterator) |
| ((LocPathIterator)iter).setIsTopLevel(isTopLevel); |
| |
| return iter; |
| } |
| |
| /** |
| * Special purpose function to see if we can optimize the pattern for |
| * a DescendantIterator. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param stepOpCodePos The opcode position for the step. |
| * |
| * @return 32 bits as an integer that give information about the location |
| * path as a whole. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| public static int getAxisFromStep( |
| Compiler compiler, int stepOpCodePos) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int stepType = compiler.getOp(stepOpCodePos); |
| |
| switch (stepType) |
| { |
| case OpCodes.FROM_FOLLOWING : |
| return Axis.FOLLOWING; |
| case OpCodes.FROM_FOLLOWING_SIBLINGS : |
| return Axis.FOLLOWINGSIBLING; |
| case OpCodes.FROM_PRECEDING : |
| return Axis.PRECEDING; |
| case OpCodes.FROM_PRECEDING_SIBLINGS : |
| return Axis.PRECEDINGSIBLING; |
| case OpCodes.FROM_PARENT : |
| return Axis.PARENT; |
| case OpCodes.FROM_NAMESPACE : |
| return Axis.NAMESPACE; |
| case OpCodes.FROM_ANCESTORS : |
| return Axis.ANCESTOR; |
| case OpCodes.FROM_ANCESTORS_OR_SELF : |
| return Axis.ANCESTORORSELF; |
| case OpCodes.FROM_ATTRIBUTES : |
| return Axis.ATTRIBUTE; |
| case OpCodes.FROM_ROOT : |
| return Axis.ROOT; |
| case OpCodes.FROM_CHILDREN : |
| return Axis.CHILD; |
| case OpCodes.FROM_DESCENDANTS_OR_SELF : |
| return Axis.DESCENDANTORSELF; |
| case OpCodes.FROM_DESCENDANTS : |
| return Axis.DESCENDANT; |
| case OpCodes.FROM_SELF : |
| return Axis.SELF; |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| case OpCodes.OP_VARIABLE : |
| return Axis.FILTEREDLIST; |
| } |
| |
| throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: " |
| //+ stepType); |
| } |
| |
| /** |
| * Get a corresponding BIT_XXX from an axis. |
| * @param axis One of Axis.ANCESTOR, etc. |
| * @return One of BIT_ANCESTOR, etc. |
| */ |
| static public int getAnalysisBitFromAxes(int axis) |
| { |
| switch (axis) // Generate new traverser |
| { |
| case Axis.ANCESTOR : |
| return BIT_ANCESTOR; |
| case Axis.ANCESTORORSELF : |
| return BIT_ANCESTOR_OR_SELF; |
| case Axis.ATTRIBUTE : |
| return BIT_ATTRIBUTE; |
| case Axis.CHILD : |
| return BIT_CHILD; |
| case Axis.DESCENDANT : |
| return BIT_DESCENDANT; |
| case Axis.DESCENDANTORSELF : |
| return BIT_DESCENDANT_OR_SELF; |
| case Axis.FOLLOWING : |
| return BIT_FOLLOWING; |
| case Axis.FOLLOWINGSIBLING : |
| return BIT_FOLLOWING_SIBLING; |
| case Axis.NAMESPACE : |
| case Axis.NAMESPACEDECLS : |
| return BIT_NAMESPACE; |
| case Axis.PARENT : |
| return BIT_PARENT; |
| case Axis.PRECEDING : |
| return BIT_PRECEDING; |
| case Axis.PRECEDINGSIBLING : |
| return BIT_PRECEDING_SIBLING; |
| case Axis.SELF : |
| return BIT_SELF; |
| case Axis.ALLFROMNODE : |
| return BIT_DESCENDANT_OR_SELF; |
| // case Axis.PRECEDINGANDANCESTOR : |
| case Axis.DESCENDANTSFROMROOT : |
| case Axis.ALL : |
| case Axis.DESCENDANTSORSELFFROMROOT : |
| return BIT_ANY_DESCENDANT_FROM_ROOT; |
| case Axis.ROOT : |
| return BIT_ROOT; |
| case Axis.FILTEREDLIST : |
| return BIT_FILTER; |
| default : |
| return BIT_FILTER; |
| } |
| } |
| |
| static boolean functionProximateOrContainsProximate(Compiler compiler, |
| int opPos) |
| { |
| int endFunc = opPos + compiler.getOp(opPos + 1) - 1; |
| opPos = OpMap.getFirstChildPos(opPos); |
| int funcID = compiler.getOp(opPos); |
| // System.out.println("funcID: "+funcID); |
| // System.out.println("opPos: "+opPos); |
| // System.out.println("endFunc: "+endFunc); |
| switch(funcID) |
| { |
| case FunctionTable.FUNC_LAST: |
| case FunctionTable.FUNC_POSITION: |
| return true; |
| default: |
| opPos++; |
| int i = 0; |
| for (int p = opPos; p < endFunc; p = compiler.getNextOpPos(p), i++) |
| { |
| int innerExprOpPos = p+2; |
| int argOp = compiler.getOp(innerExprOpPos); |
| boolean prox = isProximateInnerExpr(compiler, innerExprOpPos); |
| if(prox) |
| return true; |
| } |
| |
| } |
| return false; |
| } |
| |
| static boolean isProximateInnerExpr(Compiler compiler, int opPos) |
| { |
| int op = compiler.getOp(opPos); |
| int innerExprOpPos = opPos+2; |
| switch(op) |
| { |
| case OpCodes.OP_ARGUMENT: |
| if(isProximateInnerExpr(compiler, innerExprOpPos)) |
| return true; |
| break; |
| case OpCodes.OP_VARIABLE: |
| case OpCodes.OP_NUMBERLIT: |
| case OpCodes.OP_LITERAL: |
| case OpCodes.OP_LOCATIONPATH: |
| break; // OK |
| case OpCodes.OP_FUNCTION: |
| boolean isProx = functionProximateOrContainsProximate(compiler, opPos); |
| if(isProx) |
| return true; |
| break; |
| case OpCodes.OP_GT: |
| case OpCodes.OP_GTE: |
| case OpCodes.OP_LT: |
| case OpCodes.OP_LTE: |
| case OpCodes.OP_EQUALS: |
| int leftPos = OpMap.getFirstChildPos(op); |
| int rightPos = compiler.getNextOpPos(leftPos); |
| isProx = isProximateInnerExpr(compiler, leftPos); |
| if(isProx) |
| return true; |
| isProx = isProximateInnerExpr(compiler, rightPos); |
| if(isProx) |
| return true; |
| break; |
| default: |
| return true; // be conservative... |
| } |
| return false; |
| } |
| |
| /** |
| * Tell if the predicates need to have proximity knowledge. |
| */ |
| public static boolean mightBeProximate(Compiler compiler, int opPos, int stepType) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| boolean mightBeProximate = false; |
| int argLen; |
| |
| switch (stepType) |
| { |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| argLen = compiler.getArgLength(opPos); |
| break; |
| default : |
| argLen = compiler.getArgLengthOfStep(opPos); |
| } |
| |
| int predPos = compiler.getFirstPredicateOpPos(opPos); |
| int count = 0; |
| |
| while (OpCodes.OP_PREDICATE == compiler.getOp(predPos)) |
| { |
| count++; |
| |
| int innerExprOpPos = predPos+2; |
| int predOp = compiler.getOp(innerExprOpPos); |
| |
| switch(predOp) |
| { |
| case OpCodes.OP_VARIABLE: |
| return true; // Would need more smarts to tell if this could be a number or not! |
| case OpCodes.OP_LOCATIONPATH: |
| // OK. |
| break; |
| case OpCodes.OP_NUMBER: |
| case OpCodes.OP_NUMBERLIT: |
| return true; // that's all she wrote! |
| case OpCodes.OP_FUNCTION: |
| boolean isProx |
| = functionProximateOrContainsProximate(compiler, innerExprOpPos); |
| if(isProx) |
| return true; |
| break; |
| case OpCodes.OP_GT: |
| case OpCodes.OP_GTE: |
| case OpCodes.OP_LT: |
| case OpCodes.OP_LTE: |
| case OpCodes.OP_EQUALS: |
| int leftPos = OpMap.getFirstChildPos(innerExprOpPos); |
| int rightPos = compiler.getNextOpPos(leftPos); |
| isProx = isProximateInnerExpr(compiler, leftPos); |
| if(isProx) |
| return true; |
| isProx = isProximateInnerExpr(compiler, rightPos); |
| if(isProx) |
| return true; |
| break; |
| default: |
| return true; // be conservative... |
| } |
| |
| predPos = compiler.getNextOpPos(predPos); |
| } |
| |
| return mightBeProximate; |
| } |
| |
| /** |
| * Special purpose function to see if we can optimize the pattern for |
| * a DescendantIterator. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param stepOpCodePos The opcode position for the step. |
| * @param stepIndex The top-level step index withing the iterator. |
| * |
| * @return 32 bits as an integer that give information about the location |
| * path as a whole. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| private static boolean isOptimizableForDescendantIterator( |
| Compiler compiler, int stepOpCodePos, int stepIndex) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int stepType; |
| int stepCount = 0; |
| boolean foundDorDS = false; |
| boolean foundSelf = false; |
| boolean foundDS = false; |
| |
| int nodeTestType = OpCodes.NODETYPE_NODE; |
| |
| while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos))) |
| { |
| // The DescendantIterator can only do one node test. If there's more |
| // than one, use another iterator. |
| if(nodeTestType != OpCodes.NODETYPE_NODE && nodeTestType != OpCodes.NODETYPE_ROOT) |
| return false; |
| |
| stepCount++; |
| if(stepCount > 3) |
| return false; |
| |
| boolean mightBeProximate = mightBeProximate(compiler, stepOpCodePos, stepType); |
| if(mightBeProximate) |
| return false; |
| |
| switch (stepType) |
| { |
| case OpCodes.FROM_FOLLOWING : |
| case OpCodes.FROM_FOLLOWING_SIBLINGS : |
| case OpCodes.FROM_PRECEDING : |
| case OpCodes.FROM_PRECEDING_SIBLINGS : |
| case OpCodes.FROM_PARENT : |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| case OpCodes.FROM_NAMESPACE : |
| case OpCodes.FROM_ANCESTORS : |
| case OpCodes.FROM_ANCESTORS_OR_SELF : |
| case OpCodes.FROM_ATTRIBUTES : |
| case OpCodes.MATCH_ATTRIBUTE : |
| case OpCodes.MATCH_ANY_ANCESTOR : |
| case OpCodes.MATCH_IMMEDIATE_ANCESTOR : |
| return false; |
| case OpCodes.FROM_ROOT : |
| if(1 != stepCount) |
| return false; |
| break; |
| case OpCodes.FROM_CHILDREN : |
| if(!foundDS && !(foundDorDS && foundSelf)) |
| return false; |
| break; |
| case OpCodes.FROM_DESCENDANTS_OR_SELF : |
| foundDS = true; |
| case OpCodes.FROM_DESCENDANTS : |
| if(3 == stepCount) |
| return false; |
| foundDorDS = true; |
| break; |
| case OpCodes.FROM_SELF : |
| if(1 != stepCount) |
| return false; |
| foundSelf = true; |
| break; |
| default : |
| throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: " |
| // + stepType); |
| } |
| |
| nodeTestType = compiler.getStepTestType(stepOpCodePos); |
| |
| int nextStepOpCodePos = compiler.getNextStepPos(stepOpCodePos); |
| |
| if (nextStepOpCodePos < 0) |
| break; |
| |
| if(OpCodes.ENDOP != compiler.getOp(nextStepOpCodePos)) |
| { |
| if(compiler.countPredicates(stepOpCodePos) > 0) |
| { |
| return false; |
| } |
| } |
| |
| stepOpCodePos = nextStepOpCodePos; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Analyze the location path and return 32 bits that give information about |
| * the location path as a whole. See the BIT_XXX constants for meaning about |
| * each of the bits. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param stepOpCodePos The opcode position for the step. |
| * @param stepIndex The top-level step index withing the iterator. |
| * |
| * @return 32 bits as an integer that give information about the location |
| * path as a whole. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| private static int analyze( |
| Compiler compiler, int stepOpCodePos, int stepIndex) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int stepType; |
| int stepCount = 0; |
| int analysisResult = 0x00000000; // 32 bits of analysis |
| |
| while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos))) |
| { |
| stepCount++; |
| |
| // String namespace = compiler.getStepNS(stepOpCodePos); |
| // boolean isNSWild = (null != namespace) |
| // ? namespace.equals(NodeTest.WILD) : false; |
| // String localname = compiler.getStepLocalName(stepOpCodePos); |
| // boolean isWild = (null != localname) ? localname.equals(NodeTest.WILD) : false; |
| boolean predAnalysis = analyzePredicate(compiler, stepOpCodePos, |
| stepType); |
| |
| if (predAnalysis) |
| analysisResult |= BIT_PREDICATE; |
| |
| switch (stepType) |
| { |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| analysisResult |= BIT_FILTER; |
| break; |
| case OpCodes.FROM_ROOT : |
| analysisResult |= BIT_ROOT; |
| break; |
| case OpCodes.FROM_ANCESTORS : |
| analysisResult |= BIT_ANCESTOR; |
| break; |
| case OpCodes.FROM_ANCESTORS_OR_SELF : |
| analysisResult |= BIT_ANCESTOR_OR_SELF; |
| break; |
| case OpCodes.FROM_ATTRIBUTES : |
| analysisResult |= BIT_ATTRIBUTE; |
| break; |
| case OpCodes.FROM_NAMESPACE : |
| analysisResult |= BIT_NAMESPACE; |
| break; |
| case OpCodes.FROM_CHILDREN : |
| analysisResult |= BIT_CHILD; |
| break; |
| case OpCodes.FROM_DESCENDANTS : |
| analysisResult |= BIT_DESCENDANT; |
| break; |
| case OpCodes.FROM_DESCENDANTS_OR_SELF : |
| |
| // Use a special bit to to make sure we get the right analysis of "//foo". |
| if (2 == stepCount && BIT_ROOT == analysisResult) |
| { |
| analysisResult |= BIT_ANY_DESCENDANT_FROM_ROOT; |
| } |
| |
| analysisResult |= BIT_DESCENDANT_OR_SELF; |
| break; |
| case OpCodes.FROM_FOLLOWING : |
| analysisResult |= BIT_FOLLOWING; |
| break; |
| case OpCodes.FROM_FOLLOWING_SIBLINGS : |
| analysisResult |= BIT_FOLLOWING_SIBLING; |
| break; |
| case OpCodes.FROM_PRECEDING : |
| analysisResult |= BIT_PRECEDING; |
| break; |
| case OpCodes.FROM_PRECEDING_SIBLINGS : |
| analysisResult |= BIT_PRECEDING_SIBLING; |
| break; |
| case OpCodes.FROM_PARENT : |
| analysisResult |= BIT_PARENT; |
| break; |
| case OpCodes.FROM_SELF : |
| analysisResult |= BIT_SELF; |
| break; |
| case OpCodes.MATCH_ATTRIBUTE : |
| analysisResult |= (BIT_MATCH_PATTERN | BIT_ATTRIBUTE); |
| break; |
| case OpCodes.MATCH_ANY_ANCESTOR : |
| analysisResult |= (BIT_MATCH_PATTERN | BIT_ANCESTOR); |
| break; |
| case OpCodes.MATCH_IMMEDIATE_ANCESTOR : |
| analysisResult |= (BIT_MATCH_PATTERN | BIT_PARENT); |
| break; |
| default : |
| throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: " |
| //+ stepType); |
| } |
| |
| if (OpCodes.NODETYPE_NODE == compiler.getOp(stepOpCodePos + 3)) // child::node() |
| { |
| analysisResult |= BIT_NODETEST_ANY; |
| } |
| |
| stepOpCodePos = compiler.getNextStepPos(stepOpCodePos); |
| |
| if (stepOpCodePos < 0) |
| break; |
| } |
| |
| analysisResult |= (stepCount & BITS_COUNT); |
| |
| return analysisResult; |
| } |
| |
| /** |
| * Tell if the given axis goes downword. Bogus name, if you can think of |
| * a better one, please do tell. This really has to do with inverting |
| * attribute axis. |
| * @param axis One of Axis.XXX. |
| * @return true if the axis is not a child axis and does not go up from |
| * the axis root. |
| */ |
| public static boolean isDownwardAxisOfMany(int axis) |
| { |
| return ((Axis.DESCENDANTORSELF == axis) || |
| (Axis.DESCENDANT == axis) |
| || (Axis.FOLLOWING == axis) |
| // || (Axis.FOLLOWINGSIBLING == axis) |
| || (Axis.PRECEDING == axis) |
| // || (Axis.PRECEDINGSIBLING == axis) |
| ); |
| } |
| |
| /** |
| * Read a <a href="http://www.w3.org/TR/xpath#location-paths">LocationPath</a> |
| * as a generalized match pattern. What this means is that the LocationPath |
| * is read backwards, as a test on a given node, to see if it matches the |
| * criteria of the selection, and ends up at the context node. Essentially, |
| * this is a backwards query from a given node, to find the context node. |
| * <p>So, the selection "foo/daz[2]" is, in non-abreviated expanded syntax, |
| * "self::node()/following-sibling::foo/child::daz[position()=2]". |
| * Taking this as a match pattern for a probable node, it works out to |
| * "self::daz/parent::foo[child::daz[position()=2 and isPrevStepNode()] |
| * precedingSibling::node()[isContextNodeOfLocationPath()]", adding magic |
| * isPrevStepNode and isContextNodeOfLocationPath operations. Predicates in |
| * the location path have to be executed by the following step, |
| * because they have to know the context of their execution. |
| * |
| * @param mpi The MatchPatternIterator to which the steps will be attached. |
| * @param compiler The compiler that holds the syntax tree/op map to |
| * construct from. |
| * @param stepOpCodePos The current op code position within the opmap. |
| * @param stepIndex The top-level step index withing the iterator. |
| * |
| * @return A StepPattern object, which may contain relative StepPatterns. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| static StepPattern loadSteps( |
| MatchPatternIterator mpi, Compiler compiler, int stepOpCodePos, |
| int stepIndex) |
| throws javax.xml.transform.TransformerException |
| { |
| if (DEBUG_PATTERN_CREATION) |
| { |
| System.out.println("================"); |
| System.out.println("loadSteps for: "+compiler.getPatternString()); |
| } |
| int stepType; |
| StepPattern step = null; |
| StepPattern firstStep = null, prevStep = null; |
| int analysis = analyze(compiler, stepOpCodePos, stepIndex); |
| |
| while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos))) |
| { |
| step = createDefaultStepPattern(compiler, stepOpCodePos, mpi, analysis, |
| firstStep, prevStep); |
| |
| if (null == firstStep) |
| { |
| firstStep = step; |
| } |
| else |
| { |
| |
| //prevStep.setNextWalker(step); |
| step.setRelativePathPattern(prevStep); |
| } |
| |
| prevStep = step; |
| stepOpCodePos = compiler.getNextStepPos(stepOpCodePos); |
| |
| if (stepOpCodePos < 0) |
| break; |
| } |
| |
| int axis = Axis.SELF; |
| int paxis = Axis.SELF; |
| StepPattern tail = step; |
| for (StepPattern pat = step; null != pat; |
| pat = pat.getRelativePathPattern()) |
| { |
| int nextAxis = pat.getAxis(); |
| //int nextPaxis = pat.getPredicateAxis(); |
| pat.setAxis(axis); |
| |
| // The predicate axis can't be moved!!! Test Axes103 |
| // pat.setPredicateAxis(paxis); |
| |
| // If we have an attribute or namespace axis that went up, then |
| // it won't find the attribute in the inverse, since the select-to-match |
| // axes are not invertable (an element is a parent of an attribute, but |
| // and attribute is not a child of an element). |
| // If we don't do the magic below, then "@*/ancestor-or-self::*" gets |
| // inverted for match to "self::*/descendant-or-self::@*/parent::node()", |
| // which obviously won't work. |
| // So we will rewrite this as: |
| // "self::*/descendant-or-self::*/attribute::*/parent::node()" |
| // Child has to be rewritten a little differently: |
| // select: "@*/parent::*" |
| // inverted match: "self::*/child::@*/parent::node()" |
| // rewrite: "self::*/attribute::*/parent::node()" |
| // Axes that go down in the select, do not have to have special treatment |
| // in the rewrite. The following inverted match will still not select |
| // anything. |
| // select: "@*/child::*" |
| // inverted match: "self::*/parent::@*/parent::node()" |
| // Lovely business, this. |
| // -sb |
| int whatToShow = pat.getWhatToShow(); |
| if(whatToShow == DTMFilter.SHOW_ATTRIBUTE || |
| whatToShow == DTMFilter.SHOW_NAMESPACE) |
| { |
| int newAxis = (whatToShow == DTMFilter.SHOW_ATTRIBUTE) ? |
| Axis.ATTRIBUTE : Axis.NAMESPACE; |
| if(isDownwardAxisOfMany(axis)) |
| { |
| StepPattern attrPat = new StepPattern(whatToShow, |
| pat.getNamespace(), |
| pat.getLocalName(), |
| //newAxis, pat.getPredicateAxis); |
| newAxis, 0); // don't care about the predicate axis |
| XNumber score = pat.getStaticScore(); |
| pat.setNamespace(null); |
| pat.setLocalName(NodeTest.WILD); |
| attrPat.setPredicates(pat.getPredicates()); |
| pat.setPredicates(null); |
| pat.setWhatToShow(DTMFilter.SHOW_ELEMENT); |
| StepPattern rel = pat.getRelativePathPattern(); |
| pat.setRelativePathPattern(attrPat); |
| attrPat.setRelativePathPattern(rel); |
| attrPat.setStaticScore(score); |
| |
| // This is needed to inverse a following pattern, because of the |
| // wacky Xalan rules for following from an attribute. See axes108. |
| // By these rules, following from an attribute is not strictly |
| // inverseable. |
| if(Axis.PRECEDING == pat.getAxis()) |
| pat.setAxis(Axis.PRECEDINGANDANCESTOR); |
| |
| else if(Axis.DESCENDANT == pat.getAxis()) |
| pat.setAxis(Axis.DESCENDANTORSELF); |
| |
| pat = attrPat; |
| } |
| else if(Axis.CHILD == pat.getAxis()) |
| { |
| // In this case just change the axis. |
| // pat.setWhatToShow(whatToShow); |
| pat.setAxis(Axis.ATTRIBUTE); |
| } |
| } |
| axis = nextAxis; |
| //paxis = nextPaxis; |
| tail = pat; |
| } |
| |
| if(axis < Axis.ALL) |
| { |
| StepPattern selfPattern = new ContextMatchStepPattern(axis, paxis); |
| // We need to keep the new nodetest from affecting the score... |
| XNumber score = tail.getStaticScore(); |
| tail.setRelativePathPattern(selfPattern); |
| tail.setStaticScore(score); |
| selfPattern.setStaticScore(score); |
| } |
| |
| if (DEBUG_PATTERN_CREATION) |
| { |
| System.out.println("Done loading steps: "+step.toString()); |
| |
| System.out.println(""); |
| } |
| return step; // start from last pattern?? //firstStep; |
| } |
| |
| /** |
| * Create a StepPattern that is contained within a LocationPath. |
| * |
| * |
| * @param compiler The compiler that holds the syntax tree/op map to |
| * construct from. |
| * @param stepOpCodePos The current op code position within the opmap. |
| * @param mpi The MatchPatternIterator to which the steps will be attached. |
| * @param analysis 32 bits of analysis, from which the type of AxesWalker |
| * may be influenced. |
| * @param tail The step that is the first step analyzed, but the last |
| * step in the relative match linked list, i.e. the tail. |
| * May be null. |
| * @param head The step that is the current head of the relative |
| * match step linked list. |
| * May be null. |
| * |
| * @return the head of the list. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| private static StepPattern createDefaultStepPattern( |
| Compiler compiler, int opPos, MatchPatternIterator mpi, |
| int analysis, StepPattern tail, StepPattern head) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int stepType = compiler.getOp(opPos); |
| boolean simpleInit = false; |
| boolean prevIsOneStepDown = true; |
| |
| int whatToShow = compiler.getWhatToShow(opPos); |
| StepPattern ai = null; |
| int axis, predicateAxis; |
| |
| switch (stepType) |
| { |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| prevIsOneStepDown = false; |
| |
| Expression expr; |
| |
| switch (stepType) |
| { |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| expr = compiler.compile(opPos); |
| break; |
| default : |
| expr = compiler.compile(opPos + 2); |
| } |
| |
| axis = Axis.FILTEREDLIST; |
| predicateAxis = Axis.FILTEREDLIST; |
| ai = new FunctionPattern(expr, axis, predicateAxis); |
| simpleInit = true; |
| break; |
| case OpCodes.FROM_ROOT : |
| whatToShow = DTMFilter.SHOW_DOCUMENT |
| | DTMFilter.SHOW_DOCUMENT_FRAGMENT; |
| |
| axis = Axis.ROOT; |
| predicateAxis = Axis.ROOT; |
| ai = new StepPattern(DTMFilter.SHOW_DOCUMENT | |
| DTMFilter.SHOW_DOCUMENT_FRAGMENT, |
| axis, predicateAxis); |
| break; |
| case OpCodes.FROM_ATTRIBUTES : |
| whatToShow = DTMFilter.SHOW_ATTRIBUTE; |
| axis = Axis.PARENT; |
| predicateAxis = Axis.ATTRIBUTE; |
| // ai = new StepPattern(whatToShow, Axis.SELF, Axis.SELF); |
| break; |
| case OpCodes.FROM_NAMESPACE : |
| whatToShow = DTMFilter.SHOW_NAMESPACE; |
| axis = Axis.PARENT; |
| predicateAxis = Axis.NAMESPACE; |
| // ai = new StepPattern(whatToShow, axis, predicateAxis); |
| break; |
| case OpCodes.FROM_ANCESTORS : |
| axis = Axis.DESCENDANT; |
| predicateAxis = Axis.ANCESTOR; |
| break; |
| case OpCodes.FROM_CHILDREN : |
| axis = Axis.PARENT; |
| predicateAxis = Axis.CHILD; |
| break; |
| case OpCodes.FROM_ANCESTORS_OR_SELF : |
| axis = Axis.DESCENDANTORSELF; |
| predicateAxis = Axis.ANCESTORORSELF; |
| break; |
| case OpCodes.FROM_SELF : |
| axis = Axis.SELF; |
| predicateAxis = Axis.SELF; |
| break; |
| case OpCodes.FROM_PARENT : |
| axis = Axis.CHILD; |
| predicateAxis = Axis.PARENT; |
| break; |
| case OpCodes.FROM_PRECEDING_SIBLINGS : |
| axis = Axis.FOLLOWINGSIBLING; |
| predicateAxis = Axis.PRECEDINGSIBLING; |
| break; |
| case OpCodes.FROM_PRECEDING : |
| axis = Axis.FOLLOWING; |
| predicateAxis = Axis.PRECEDING; |
| break; |
| case OpCodes.FROM_FOLLOWING_SIBLINGS : |
| axis = Axis.PRECEDINGSIBLING; |
| predicateAxis = Axis.FOLLOWINGSIBLING; |
| break; |
| case OpCodes.FROM_FOLLOWING : |
| axis = Axis.PRECEDING; |
| predicateAxis = Axis.FOLLOWING; |
| break; |
| case OpCodes.FROM_DESCENDANTS_OR_SELF : |
| axis = Axis.ANCESTORORSELF; |
| predicateAxis = Axis.DESCENDANTORSELF; |
| break; |
| case OpCodes.FROM_DESCENDANTS : |
| axis = Axis.ANCESTOR; |
| predicateAxis = Axis.DESCENDANT; |
| break; |
| default : |
| throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: " |
| //+ stepType); |
| } |
| if(null == ai) |
| { |
| whatToShow = compiler.getWhatToShow(opPos); // %REVIEW% |
| ai = new StepPattern(whatToShow, compiler.getStepNS(opPos), |
| compiler.getStepLocalName(opPos), |
| axis, predicateAxis); |
| } |
| |
| if (false || DEBUG_PATTERN_CREATION) |
| { |
| System.out.print("new step: "+ ai); |
| System.out.print(", axis: " + Axis.getNames(ai.getAxis())); |
| System.out.print(", predAxis: " + Axis.getNames(ai.getAxis())); |
| System.out.print(", what: "); |
| System.out.print(" "); |
| ai.debugWhatToShow(ai.getWhatToShow()); |
| } |
| |
| int argLen = compiler.getFirstPredicateOpPos(opPos); |
| |
| ai.setPredicates(compiler.getCompiledPredicates(argLen)); |
| |
| return ai; |
| } |
| |
| /** |
| * Analyze a step and give information about it's predicates. Right now this |
| * just returns true or false if the step has a predicate. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param opPos The opcode position for the step. |
| * @param stepType The type of step, one of OP_GROUP, etc. |
| * |
| * @return true if step has a predicate. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| static boolean analyzePredicate(Compiler compiler, int opPos, int stepType) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| int argLen; |
| |
| switch (stepType) |
| { |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| argLen = compiler.getArgLength(opPos); |
| break; |
| default : |
| argLen = compiler.getArgLengthOfStep(opPos); |
| } |
| |
| int pos = compiler.getFirstPredicateOpPos(opPos); |
| int nPredicates = compiler.countPredicates(pos); |
| |
| return (nPredicates > 0) ? true : false; |
| } |
| |
| /** |
| * Create the proper Walker from the axes type. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param opPos The opcode position for the step. |
| * @param lpi The owning location path iterator. |
| * @param analysis 32 bits of analysis, from which the type of AxesWalker |
| * may be influenced. |
| * |
| * @return non-null reference to AxesWalker derivative. |
| * @throws RuntimeException if the input is bad. |
| */ |
| private static AxesWalker createDefaultWalker(Compiler compiler, int opPos, |
| WalkingIterator lpi, int analysis) |
| { |
| |
| AxesWalker ai = null; |
| int stepType = compiler.getOp(opPos); |
| |
| /* |
| System.out.println("0: "+compiler.getOp(opPos)); |
| System.out.println("1: "+compiler.getOp(opPos+1)); |
| System.out.println("2: "+compiler.getOp(opPos+2)); |
| System.out.println("3: "+compiler.getOp(opPos+3)); |
| System.out.println("4: "+compiler.getOp(opPos+4)); |
| System.out.println("5: "+compiler.getOp(opPos+5)); |
| */ |
| boolean simpleInit = false; |
| int totalNumberWalkers = (analysis & BITS_COUNT); |
| boolean prevIsOneStepDown = true; |
| |
| switch (stepType) |
| { |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| prevIsOneStepDown = false; |
| |
| if (DEBUG_WALKER_CREATION) |
| System.out.println("new walker: FilterExprWalker: " + analysis |
| + ", " + compiler.toString()); |
| |
| ai = new FilterExprWalker(lpi); |
| simpleInit = true; |
| break; |
| case OpCodes.FROM_ROOT : |
| ai = new AxesWalker(lpi, Axis.ROOT); |
| break; |
| case OpCodes.FROM_ANCESTORS : |
| prevIsOneStepDown = false; |
| ai = new ReverseAxesWalker(lpi, Axis.ANCESTOR); |
| break; |
| case OpCodes.FROM_ANCESTORS_OR_SELF : |
| prevIsOneStepDown = false; |
| ai = new ReverseAxesWalker(lpi, Axis.ANCESTORORSELF); |
| break; |
| case OpCodes.FROM_ATTRIBUTES : |
| ai = new AxesWalker(lpi, Axis.ATTRIBUTE); |
| break; |
| case OpCodes.FROM_NAMESPACE : |
| ai = new AxesWalker(lpi, Axis.NAMESPACE); |
| break; |
| case OpCodes.FROM_CHILDREN : |
| ai = new AxesWalker(lpi, Axis.CHILD); |
| break; |
| case OpCodes.FROM_DESCENDANTS : |
| prevIsOneStepDown = false; |
| ai = new AxesWalker(lpi, Axis.DESCENDANT); |
| break; |
| case OpCodes.FROM_DESCENDANTS_OR_SELF : |
| prevIsOneStepDown = false; |
| ai = new AxesWalker(lpi, Axis.DESCENDANTORSELF); |
| break; |
| case OpCodes.FROM_FOLLOWING : |
| prevIsOneStepDown = false; |
| ai = new AxesWalker(lpi, Axis.FOLLOWING); |
| break; |
| case OpCodes.FROM_FOLLOWING_SIBLINGS : |
| prevIsOneStepDown = false; |
| ai = new AxesWalker(lpi, Axis.FOLLOWINGSIBLING); |
| break; |
| case OpCodes.FROM_PRECEDING : |
| prevIsOneStepDown = false; |
| ai = new ReverseAxesWalker(lpi, Axis.PRECEDING); |
| break; |
| case OpCodes.FROM_PRECEDING_SIBLINGS : |
| prevIsOneStepDown = false; |
| ai = new ReverseAxesWalker(lpi, Axis.PRECEDINGSIBLING); |
| break; |
| case OpCodes.FROM_PARENT : |
| prevIsOneStepDown = false; |
| ai = new ReverseAxesWalker(lpi, Axis.PARENT); |
| break; |
| case OpCodes.FROM_SELF : |
| ai = new AxesWalker(lpi, Axis.SELF); |
| break; |
| default : |
| throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: " |
| //+ stepType); |
| } |
| |
| if (simpleInit) |
| { |
| ai.initNodeTest(DTMFilter.SHOW_ALL); |
| } |
| else |
| { |
| int whatToShow = compiler.getWhatToShow(opPos); |
| |
| /* |
| System.out.print("construct: "); |
| NodeTest.debugWhatToShow(whatToShow); |
| System.out.println("or stuff: "+(whatToShow & (DTMFilter.SHOW_ATTRIBUTE |
| | DTMFilter.SHOW_ELEMENT |
| | DTMFilter.SHOW_PROCESSING_INSTRUCTION))); |
| */ |
| if ((0 == (whatToShow |
| & (DTMFilter.SHOW_ATTRIBUTE | DTMFilter.SHOW_NAMESPACE | DTMFilter.SHOW_ELEMENT |
| | DTMFilter.SHOW_PROCESSING_INSTRUCTION))) || (whatToShow == DTMFilter.SHOW_ALL)) |
| ai.initNodeTest(whatToShow); |
| else |
| { |
| ai.initNodeTest(whatToShow, compiler.getStepNS(opPos), |
| compiler.getStepLocalName(opPos)); |
| } |
| } |
| |
| return ai; |
| } |
| |
| public static String getAnalysisString(int analysis) |
| { |
| StringBuffer buf = new StringBuffer(); |
| buf.append("count: "+getStepCount(analysis)+" "); |
| if((analysis & BIT_NODETEST_ANY) != 0) |
| { |
| buf.append("NTANY|"); |
| } |
| if((analysis & BIT_PREDICATE) != 0) |
| { |
| buf.append("PRED|"); |
| } |
| if((analysis & BIT_ANCESTOR) != 0) |
| { |
| buf.append("ANC|"); |
| } |
| if((analysis & BIT_ANCESTOR_OR_SELF) != 0) |
| { |
| buf.append("ANCOS|"); |
| } |
| if((analysis & BIT_ATTRIBUTE) != 0) |
| { |
| buf.append("ATTR|"); |
| } |
| if((analysis & BIT_CHILD) != 0) |
| { |
| buf.append("CH|"); |
| } |
| if((analysis & BIT_DESCENDANT) != 0) |
| { |
| buf.append("DESC|"); |
| } |
| if((analysis & BIT_DESCENDANT_OR_SELF) != 0) |
| { |
| buf.append("DESCOS|"); |
| } |
| if((analysis & BIT_FOLLOWING) != 0) |
| { |
| buf.append("FOL|"); |
| } |
| if((analysis & BIT_FOLLOWING_SIBLING) != 0) |
| { |
| buf.append("FOLS|"); |
| } |
| if((analysis & BIT_NAMESPACE) != 0) |
| { |
| buf.append("NS|"); |
| } |
| if((analysis & BIT_PARENT) != 0) |
| { |
| buf.append("P|"); |
| } |
| if((analysis & BIT_PRECEDING) != 0) |
| { |
| buf.append("PREC|"); |
| } |
| if((analysis & BIT_PRECEDING_SIBLING) != 0) |
| { |
| buf.append("PRECS|"); |
| } |
| if((analysis & BIT_SELF) != 0) |
| { |
| buf.append(".|"); |
| } |
| if((analysis & BIT_FILTER) != 0) |
| { |
| buf.append("FLT|"); |
| } |
| if((analysis & BIT_ROOT) != 0) |
| { |
| buf.append("R|"); |
| } |
| return buf.toString(); |
| } |
| |
| /** Set to true for diagnostics about walker creation */ |
| static final boolean DEBUG_PATTERN_CREATION = false; |
| |
| /** Set to true for diagnostics about walker creation */ |
| static final boolean DEBUG_WALKER_CREATION = false; |
| |
| /** Set to true for diagnostics about iterator creation */ |
| static final boolean DEBUG_ITERATOR_CREATION = false; |
| |
| public static boolean hasPredicate(int analysis) |
| { |
| return (0 != (analysis & BIT_PREDICATE)); |
| } |
| |
| public static boolean isWild(int analysis) |
| { |
| return (0 != (analysis & BIT_NODETEST_ANY)); |
| } |
| |
| public static boolean walksAncestors(int analysis) |
| { |
| return isSet(analysis, BIT_ANCESTOR | BIT_ANCESTOR_OR_SELF); |
| } |
| |
| public static boolean walksAttributes(int analysis) |
| { |
| return (0 != (analysis & BIT_ATTRIBUTE)); |
| } |
| |
| public static boolean walksNamespaces(int analysis) |
| { |
| return (0 != (analysis & BIT_NAMESPACE)); |
| } |
| |
| public static boolean walksChildren(int analysis) |
| { |
| return (0 != (analysis & BIT_CHILD)); |
| } |
| |
| public static boolean walksDescendants(int analysis) |
| { |
| return isSet(analysis, BIT_DESCENDANT | BIT_DESCENDANT_OR_SELF); |
| } |
| |
| public static boolean walksSubtree(int analysis) |
| { |
| return isSet(analysis, BIT_DESCENDANT | BIT_DESCENDANT_OR_SELF | BIT_CHILD); |
| } |
| |
| public static boolean walksSubtreeOnlyMaybeAbsolute(int analysis) |
| { |
| return walksSubtree(analysis) |
| && !walksExtraNodes(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| ; |
| } |
| |
| public static boolean walksSubtreeOnly(int analysis) |
| { |
| return walksSubtreeOnlyMaybeAbsolute(analysis) |
| && !isAbsolute(analysis) |
| ; |
| } |
| |
| public static boolean walksFilteredList(int analysis) |
| { |
| return isSet(analysis, BIT_FILTER); |
| } |
| |
| public static boolean walksSubtreeOnlyFromRootOrContext(int analysis) |
| { |
| return walksSubtree(analysis) |
| && !walksExtraNodes(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && !isSet(analysis, BIT_FILTER) |
| ; |
| } |
| |
| public static boolean walksInDocOrder(int analysis) |
| { |
| return (walksSubtreeOnlyMaybeAbsolute(analysis) |
| || walksExtraNodesOnly(analysis) |
| || walksFollowingOnlyMaybeAbsolute(analysis)) |
| && !isSet(analysis, BIT_FILTER) |
| ; |
| } |
| |
| public static boolean walksFollowingOnlyMaybeAbsolute(int analysis) |
| { |
| return isSet(analysis, BIT_SELF | BIT_FOLLOWING_SIBLING | BIT_FOLLOWING) |
| && !walksSubtree(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| ; |
| } |
| |
| public static boolean walksUp(int analysis) |
| { |
| return isSet(analysis, BIT_PARENT | BIT_ANCESTOR | BIT_ANCESTOR_OR_SELF); |
| } |
| |
| public static boolean walksSideways(int analysis) |
| { |
| return isSet(analysis, BIT_FOLLOWING | BIT_FOLLOWING_SIBLING | |
| BIT_PRECEDING | BIT_PRECEDING_SIBLING); |
| } |
| |
| public static boolean walksExtraNodes(int analysis) |
| { |
| return isSet(analysis, BIT_NAMESPACE | BIT_ATTRIBUTE); |
| } |
| |
| public static boolean walksExtraNodesOnly(int analysis) |
| { |
| return walksExtraNodes(analysis) |
| && !isSet(analysis, BIT_SELF) |
| && !walksSubtree(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && !isAbsolute(analysis) |
| ; |
| } |
| |
| public static boolean isAbsolute(int analysis) |
| { |
| return isSet(analysis, BIT_ROOT | BIT_FILTER); |
| } |
| |
| public static boolean walksChildrenOnly(int analysis) |
| { |
| return walksChildren(analysis) |
| && !isSet(analysis, BIT_SELF) |
| && !walksExtraNodes(analysis) |
| && !walksDescendants(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && (!isAbsolute(analysis) || isSet(analysis, BIT_ROOT)) |
| ; |
| } |
| |
| public static boolean walksChildrenAndExtraAndSelfOnly(int analysis) |
| { |
| return walksChildren(analysis) |
| && !walksDescendants(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && (!isAbsolute(analysis) || isSet(analysis, BIT_ROOT)) |
| ; |
| } |
| |
| public static boolean walksDescendantsAndExtraAndSelfOnly(int analysis) |
| { |
| return !walksChildren(analysis) |
| && walksDescendants(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && (!isAbsolute(analysis) || isSet(analysis, BIT_ROOT)) |
| ; |
| } |
| |
| public static boolean walksSelfOnly(int analysis) |
| { |
| return isSet(analysis, BIT_SELF) |
| && !walksSubtree(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && !isAbsolute(analysis) |
| ; |
| } |
| |
| |
| public static boolean walksUpOnly(int analysis) |
| { |
| return !walksSubtree(analysis) |
| && walksUp(analysis) |
| && !walksSideways(analysis) |
| && !isAbsolute(analysis) |
| ; |
| } |
| |
| public static boolean walksDownOnly(int analysis) |
| { |
| return walksSubtree(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && !isAbsolute(analysis) |
| ; |
| } |
| |
| public static boolean walksDownExtraOnly(int analysis) |
| { |
| return walksSubtree(analysis) && walksExtraNodes(analysis) |
| && !walksUp(analysis) |
| && !walksSideways(analysis) |
| && !isAbsolute(analysis) |
| ; |
| } |
| |
| public static boolean canSkipSubtrees(int analysis) |
| { |
| return isSet(analysis, BIT_CHILD) | walksSideways(analysis); |
| } |
| |
| public static boolean canCrissCross(int analysis) |
| { |
| // This could be done faster. Coded for clarity. |
| if(walksSelfOnly(analysis)) |
| return false; |
| else if(walksDownOnly(analysis) && !canSkipSubtrees(analysis)) |
| return false; |
| else if(walksChildrenAndExtraAndSelfOnly(analysis)) |
| return false; |
| else if(walksDescendantsAndExtraAndSelfOnly(analysis)) |
| return false; |
| else if(walksUpOnly(analysis)) |
| return false; |
| else if(walksExtraNodesOnly(analysis)) |
| return false; |
| else if(walksSubtree(analysis) |
| && (walksSideways(analysis) |
| || walksUp(analysis) |
| || canSkipSubtrees(analysis))) |
| return true; |
| else |
| return false; |
| } |
| |
| /** |
| * Tell if the pattern can be 'walked' with the iteration steps in natural |
| * document order, without duplicates. |
| * |
| * @param analysis The general analysis of the pattern. |
| * |
| * @return true if the walk can be done in natural order. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| static public boolean isNaturalDocOrder(int analysis) |
| { |
| if(canCrissCross(analysis) || isSet(analysis, BIT_NAMESPACE) || |
| walksFilteredList(analysis)) |
| return false; |
| |
| if(walksInDocOrder(analysis)) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * Tell if the pattern can be 'walked' with the iteration steps in natural |
| * document order, without duplicates. |
| * |
| * @param compiler non-null reference to compiler object that has processed |
| * the XPath operations into an opcode map. |
| * @param stepOpCodePos The opcode position for the step. |
| * @param stepIndex The top-level step index withing the iterator. |
| * @param analysis The general analysis of the pattern. |
| * |
| * @return true if the walk can be done in natural order. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| private static boolean isNaturalDocOrder( |
| Compiler compiler, int stepOpCodePos, int stepIndex, int analysis) |
| throws javax.xml.transform.TransformerException |
| { |
| if(canCrissCross(analysis)) |
| return false; |
| |
| // Namespaces can present some problems, so just punt if we're looking for |
| // these. |
| if(isSet(analysis, BIT_NAMESPACE)) |
| return false; |
| |
| // The following, preceding, following-sibling, and preceding sibling can |
| // be found in doc order if we get to this point, but if they occur |
| // together, they produce |
| // duplicates, so it's better for us to eliminate this case so we don't |
| // have to check for duplicates during runtime if we're using a |
| // WalkingIterator. |
| if(isSet(analysis, BIT_FOLLOWING | BIT_FOLLOWING_SIBLING) && |
| isSet(analysis, BIT_PRECEDING | BIT_PRECEDING_SIBLING)) |
| return false; |
| |
| // OK, now we have to check for select="@*/axis::*" patterns, which |
| // can also cause duplicates to happen. But select="axis*/@::*" patterns |
| // are OK, as are select="@foo/axis::*" patterns. |
| // Unfortunately, we can't do this just via the analysis bits. |
| |
| int stepType; |
| int stepCount = 0; |
| boolean foundWildAttribute = false; |
| |
| // Steps that can traverse anything other than down a |
| // subtree or that can produce duplicates when used in |
| // combonation are counted with this variable. |
| int potentialDuplicateMakingStepCount = 0; |
| |
| while (OpCodes.ENDOP != (stepType = compiler.getOp(stepOpCodePos))) |
| { |
| stepCount++; |
| |
| switch (stepType) |
| { |
| case OpCodes.FROM_ATTRIBUTES : |
| case OpCodes.MATCH_ATTRIBUTE : |
| if(foundWildAttribute) // Maybe not needed, but be safe. |
| return false; |
| |
| // This doesn't seem to work as a test for wild card. Hmph. |
| // int nodeTestType = compiler.getStepTestType(stepOpCodePos); |
| |
| String localName = compiler.getStepLocalName(stepOpCodePos); |
| // System.err.println("localName: "+localName); |
| if(localName.equals("*")) |
| { |
| foundWildAttribute = true; |
| } |
| break; |
| case OpCodes.FROM_FOLLOWING : |
| case OpCodes.FROM_FOLLOWING_SIBLINGS : |
| case OpCodes.FROM_PRECEDING : |
| case OpCodes.FROM_PRECEDING_SIBLINGS : |
| case OpCodes.FROM_PARENT : |
| case OpCodes.OP_VARIABLE : |
| case OpCodes.OP_EXTFUNCTION : |
| case OpCodes.OP_FUNCTION : |
| case OpCodes.OP_GROUP : |
| case OpCodes.FROM_NAMESPACE : |
| case OpCodes.FROM_ANCESTORS : |
| case OpCodes.FROM_ANCESTORS_OR_SELF : |
| case OpCodes.MATCH_ANY_ANCESTOR : |
| case OpCodes.MATCH_IMMEDIATE_ANCESTOR : |
| case OpCodes.FROM_DESCENDANTS_OR_SELF : |
| case OpCodes.FROM_DESCENDANTS : |
| if(potentialDuplicateMakingStepCount > 0) |
| return false; |
| potentialDuplicateMakingStepCount++; |
| case OpCodes.FROM_ROOT : |
| case OpCodes.FROM_CHILDREN : |
| case OpCodes.FROM_SELF : |
| if(foundWildAttribute) |
| return false; |
| break; |
| default : |
| throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NULL_ERROR_HANDLER, new Object[]{Integer.toString(stepType)})); //"Programmer's assertion: unknown opcode: " |
| // + stepType); |
| } |
| |
| int nextStepOpCodePos = compiler.getNextStepPos(stepOpCodePos); |
| |
| if (nextStepOpCodePos < 0) |
| break; |
| |
| stepOpCodePos = nextStepOpCodePos; |
| } |
| |
| return true; |
| } |
| |
| public static boolean isOneStep(int analysis) |
| { |
| return (analysis & BITS_COUNT) == 0x00000001; |
| } |
| |
| public static int getStepCount(int analysis) |
| { |
| return (analysis & BITS_COUNT); |
| } |
| |
| /** |
| * First 8 bits are the number of top-level location steps. Hopefully |
| * there will never be more that 255 location steps!!! |
| */ |
| public static final int BITS_COUNT = 0x000000FF; |
| |
| /** 4 bits are reserved for future use. */ |
| public static final int BITS_RESERVED = 0x00000F00; |
| |
| /** Bit is on if the expression contains a top-level predicate. */ |
| public static final int BIT_PREDICATE = (0x00001000); |
| |
| /** Bit is on if any of the walkers contain an ancestor step. */ |
| public static final int BIT_ANCESTOR = (0x00001000 << 1); |
| |
| /** Bit is on if any of the walkers contain an ancestor-or-self step. */ |
| public static final int BIT_ANCESTOR_OR_SELF = (0x00001000 << 2); |
| |
| /** Bit is on if any of the walkers contain an attribute step. */ |
| public static final int BIT_ATTRIBUTE = (0x00001000 << 3); |
| |
| /** Bit is on if any of the walkers contain a child step. */ |
| public static final int BIT_CHILD = (0x00001000 << 4); |
| |
| /** Bit is on if any of the walkers contain a descendant step. */ |
| public static final int BIT_DESCENDANT = (0x00001000 << 5); |
| |
| /** Bit is on if any of the walkers contain a descendant-or-self step. */ |
| public static final int BIT_DESCENDANT_OR_SELF = (0x00001000 << 6); |
| |
| /** Bit is on if any of the walkers contain a following step. */ |
| public static final int BIT_FOLLOWING = (0x00001000 << 7); |
| |
| /** Bit is on if any of the walkers contain a following-sibiling step. */ |
| public static final int BIT_FOLLOWING_SIBLING = (0x00001000 << 8); |
| |
| /** Bit is on if any of the walkers contain a namespace step. */ |
| public static final int BIT_NAMESPACE = (0x00001000 << 9); |
| |
| /** Bit is on if any of the walkers contain a parent step. */ |
| public static final int BIT_PARENT = (0x00001000 << 10); |
| |
| /** Bit is on if any of the walkers contain a preceding step. */ |
| public static final int BIT_PRECEDING = (0x00001000 << 11); |
| |
| /** Bit is on if any of the walkers contain a preceding-sibling step. */ |
| public static final int BIT_PRECEDING_SIBLING = (0x00001000 << 12); |
| |
| /** Bit is on if any of the walkers contain a self step. */ |
| public static final int BIT_SELF = (0x00001000 << 13); |
| |
| /** |
| * Bit is on if any of the walkers contain a filter (i.e. id(), extension |
| * function, etc.) step. |
| */ |
| public static final int BIT_FILTER = (0x00001000 << 14); |
| |
| /** Bit is on if any of the walkers contain a root step. */ |
| public static final int BIT_ROOT = (0x00001000 << 15); |
| |
| /** |
| * If any of these bits are on, the expression may likely traverse outside |
| * the given subtree. |
| */ |
| public static final int BITMASK_TRAVERSES_OUTSIDE_SUBTREE = (BIT_NAMESPACE // ?? |
| | BIT_PRECEDING_SIBLING |
| | BIT_PRECEDING |
| | BIT_FOLLOWING_SIBLING |
| | BIT_FOLLOWING |
| | BIT_PARENT // except parent of attrs. |
| | BIT_ANCESTOR_OR_SELF |
| | BIT_ANCESTOR |
| | BIT_FILTER |
| | BIT_ROOT); |
| |
| /** |
| * Bit is on if any of the walkers can go backwards in document |
| * order from the context node. |
| */ |
| public static final int BIT_BACKWARDS_SELF = (0x00001000 << 16); |
| |
| /** Found "//foo" pattern */ |
| public static final int BIT_ANY_DESCENDANT_FROM_ROOT = (0x00001000 << 17); |
| |
| /** |
| * Bit is on if any of the walkers contain an node() test. This is |
| * really only useful if the count is 1. |
| */ |
| public static final int BIT_NODETEST_ANY = (0x00001000 << 18); |
| |
| // can't go higher than 18! |
| |
| /** Bit is on if the expression is a match pattern. */ |
| public static final int BIT_MATCH_PATTERN = (0x00001000 << 19); |
| } |