/* * Copyright 1999-2004 The Apache Software Foundation. * * 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. */ /* * $Id: WalkerFactory.java,v 1.28 2004/02/17 04:32:08 minchau Exp $ */ package com.sun.org.apache.xpath.internal.axes; import com.sun.org.apache.xalan.internal.res.XSLMessages; import com.sun.org.apache.xml.internal.dtm.Axis; import com.sun.org.apache.xml.internal.dtm.DTMFilter; import com.sun.org.apache.xml.internal.dtm.DTMIterator; import com.sun.org.apache.xpath.internal.Expression; import com.sun.org.apache.xpath.internal.compiler.Compiler; import com.sun.org.apache.xpath.internal.compiler.FunctionTable; import com.sun.org.apache.xpath.internal.compiler.OpCodes; import com.sun.org.apache.xpath.internal.objects.XNumber; import com.sun.org.apache.xpath.internal.patterns.ContextMatchStepPattern; import com.sun.org.apache.xpath.internal.patterns.FunctionPattern; import com.sun.org.apache.xpath.internal.patterns.NodeTest; import com.sun.org.apache.xpath.internal.patterns.StepPattern; import com.sun.org.apache.xpath.internal.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 xpath The xpath that is executing. * @param context The current source tree context node. * * @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 xpath The xpath that is executing. * @param context The current source tree context node. * * @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 = compiler.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. * @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 */ 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 = compiler.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 = compiler.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 = compiler.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 LocationPath * 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. *
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; int totalNumberWalkers = (analysis & BITS_COUNT); boolean prevIsOneStepDown = true; int firstStepPos = compiler.getFirstChildPos(opPos); 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.names[ai.getAxis()]); System.out.print(", predAxis: " + Axis.names[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); }