| package org.apache.xpath.axes; |
| |
| import javax.xml.transform.TransformerException; |
| |
| import org.apache.xpath.XPathContext; |
| import org.apache.xpath.compiler.Compiler; |
| import org.apache.xpath.patterns.NodeTest; |
| import org.apache.xpath.objects.XObject; |
| |
| import org.apache.xml.dtm.DTM; |
| import org.apache.xml.dtm.DTMIterator; |
| import org.apache.xml.dtm.DTMFilter; |
| import org.apache.xml.dtm.Axis; |
| import org.apache.xml.dtm.DTMAxisIterator; |
| |
| /** |
| * <meta name="usage" content="advanced"/> |
| * This class implements a general iterator for |
| * those LocationSteps with only one step, and perhaps a predicate. |
| * @see org.apache.xpath.axes.WalkerFactory#newLocPathIterator |
| */ |
| public class OneStepIterator extends ChildTestIterator |
| { |
| /** The traversal axis from where the nodes will be filtered. */ |
| protected int m_axis = -1; |
| |
| /** The DTM inner traversal class, that corresponds to the super axis. */ |
| protected DTMAxisIterator m_iterator; |
| |
| /** |
| * Create a OneStepIterator object. |
| * |
| * @param compiler A reference to the Compiler that contains the op map. |
| * @param opPos The position within the op map, which contains the |
| * location path expression for this itterator. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| OneStepIterator(Compiler compiler, int opPos, int analysis) |
| throws javax.xml.transform.TransformerException |
| { |
| super(compiler, opPos, analysis); |
| int firstStepPos = compiler.getFirstChildPos(opPos); |
| |
| m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos); |
| |
| } |
| |
| /** |
| * Create a OneStepIterator object. |
| * |
| * @param iterator The DTM iterator which this iterator will use. |
| * @param axis One of Axis.Child, etc., or -1 if the axis is unknown. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| public OneStepIterator(DTMAxisIterator iterator, int axis) |
| throws javax.xml.transform.TransformerException |
| { |
| super(null); |
| |
| m_iterator = iterator; |
| m_axis = axis; |
| int whatToShow = DTMFilter.SHOW_ALL; |
| initNodeTest(whatToShow); |
| } |
| |
| /** |
| * Initialize the context values for this expression |
| * after it is cloned. |
| * |
| * @param execContext The XPath runtime context for this |
| * transformation. |
| */ |
| public void setRoot(int context, Object environment) |
| { |
| super.setRoot(context, environment); |
| if(m_axis > -1) |
| m_iterator = m_cdtm.getAxisIterator(m_axis); |
| m_iterator.setStartNode(m_context); |
| } |
| |
| /** |
| * Get the next node via getFirstAttribute && getNextAttribute. |
| */ |
| protected int getNextNode() |
| { |
| return m_lastFetched = m_iterator.next(); |
| } |
| |
| /** |
| * Get a cloned iterator. |
| * |
| * @return A new iterator that can be used without mutating this one. |
| * |
| * @throws CloneNotSupportedException |
| */ |
| public Object clone() throws CloneNotSupportedException |
| { |
| // Do not access the location path itterator during this operation! |
| |
| OneStepIterator clone = (OneStepIterator) super.clone(); |
| |
| if(m_iterator != null) |
| { |
| clone.m_iterator = m_iterator.cloneIterator(); |
| } |
| return clone; |
| } |
| |
| |
| /** |
| * Tells if this is a reverse axes. Overrides AxesWalker#isReverseAxes. |
| * |
| * @return true for this class. |
| */ |
| public boolean isReverseAxes() |
| { |
| return m_iterator.isReverse(); |
| } |
| |
| /** |
| * Get the current sub-context position. In order to do the |
| * reverse axes count, for the moment this re-searches the axes |
| * up to the predicate. An optimization on this is to cache |
| * the nodes searched, but, for the moment, this case is probably |
| * rare enough that the added complexity isn't worth it. |
| * |
| * @param predicateIndex The predicate index of the proximity position. |
| * |
| * @return The pridicate index, or -1. |
| */ |
| protected int getProximityPosition(int predicateIndex) |
| { |
| if(!isReverseAxes()) |
| return super.getProximityPosition(predicateIndex); |
| |
| // A negative predicate index seems to occur with |
| // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()] |
| // -sb |
| if(predicateIndex < 0) |
| return -1; |
| |
| if (m_proximityPositions[predicateIndex] <= 0) |
| { |
| XPathContext xctxt = getXPathContext(); |
| try |
| { |
| OneStepIterator clone = (OneStepIterator) this.clone(); |
| |
| int root = getRoot(); |
| xctxt.pushCurrentNode(root); |
| clone.setRoot(root, xctxt); |
| |
| // clone.setPredicateCount(predicateIndex); |
| clone.m_predCount = predicateIndex; |
| |
| // Count 'em all |
| int count = 1; |
| int next; |
| |
| while (DTM.NULL != (next = clone.nextNode())) |
| { |
| count++; |
| } |
| |
| m_proximityPositions[predicateIndex] += count; |
| } |
| catch (CloneNotSupportedException cnse) |
| { |
| |
| // can't happen |
| } |
| finally |
| { |
| xctxt.popCurrentNode(); |
| } |
| } |
| |
| return m_proximityPositions[predicateIndex]; |
| } |
| |
| /** |
| * Count backwards one proximity position. |
| * |
| * @param i The predicate index. |
| */ |
| protected void countProximityPosition(int i) |
| { |
| if(!isReverseAxes()) |
| super.countProximityPosition(i); |
| else if (i < m_proximityPositions.length) |
| m_proximityPositions[i]--; |
| } |
| |
| /** |
| * Get the number of nodes in this node list. The function is probably ill |
| * named? |
| * |
| * |
| * @param xctxt The XPath runtime context. |
| * |
| * @return the number of nodes in this node list. |
| */ |
| public int findLastPos(XPathContext xctxt) |
| { |
| if(!isReverseAxes()) |
| return super.getLastPos(xctxt); |
| |
| int count = 0; |
| |
| try |
| { |
| OneStepIterator clone = (OneStepIterator) this.clone(); |
| |
| int root = getRoot(); |
| xctxt.pushCurrentNode(root); |
| clone.setRoot(root, xctxt); |
| |
| int predCount = clone.getPredicateCount(); |
| if(predCount > 0 && this == m_execContext.getSubContextList()) |
| { |
| // Don't call setPredicateCount, because it clones and is slower. |
| clone.m_predCount = predCount - 1; |
| } |
| |
| // Count 'em all |
| // count = 1; |
| int next; |
| |
| while (DTM.NULL != (next = clone.nextNode())) |
| { |
| count++; |
| } |
| } |
| catch (CloneNotSupportedException cnse) |
| { |
| |
| // can't happen |
| } |
| finally |
| { |
| xctxt.popCurrentNode(); |
| } |
| |
| // System.out.println("getLastPos - pos: "+count); |
| // System.out.println("pos (ReverseAxesWalker): "+count); |
| return count; |
| } |
| |
| /** |
| * Reset the iterator. |
| */ |
| public void reset() |
| { |
| |
| super.reset(); |
| if(null != m_iterator) |
| m_iterator.reset(); |
| } |
| |
| /** |
| * Returns the axis being iterated, if it is known. |
| * |
| * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple |
| * types. |
| */ |
| public int getAxis() |
| { |
| return m_axis; |
| } |
| |
| |
| } |