| /* |
| * reserved comment block |
| * DO NOT REMOVE OR ALTER! |
| */ |
| /* |
| * 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. |
| */ |
| |
| package com.sun.org.apache.xpath.internal.axes; |
| |
| import java.util.Vector; |
| |
| import com.sun.org.apache.xalan.internal.res.XSLMessages; |
| import com.sun.org.apache.xml.internal.dtm.DTM; |
| import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser; |
| import com.sun.org.apache.xml.internal.dtm.DTMIterator; |
| import com.sun.org.apache.xpath.internal.Expression; |
| import com.sun.org.apache.xpath.internal.ExpressionOwner; |
| import com.sun.org.apache.xpath.internal.XPathContext; |
| import com.sun.org.apache.xpath.internal.XPathVisitor; |
| import com.sun.org.apache.xpath.internal.compiler.Compiler; |
| import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; |
| |
| /** |
| * Serves as common interface for axes Walkers, and stores common |
| * state variables. |
| */ |
| public class AxesWalker extends PredicatedNodeTest |
| implements Cloneable, PathComponent, ExpressionOwner |
| { |
| static final long serialVersionUID = -2966031951306601247L; |
| |
| /** |
| * Construct an AxesWalker using a LocPathIterator. |
| * |
| * @param locPathIterator non-null reference to the parent iterator. |
| */ |
| public AxesWalker(LocPathIterator locPathIterator, int axis) |
| { |
| super( locPathIterator ); |
| m_axis = axis; |
| } |
| |
| public final WalkingIterator wi() |
| { |
| return (WalkingIterator)m_lpi; |
| } |
| |
| /** |
| * Initialize an AxesWalker during the parse of the XPath expression. |
| * |
| * @param compiler The Compiler object that has information about this |
| * walker in the op map. |
| * @param opPos The op code position of this location step. |
| * @param stepType The type of location step. |
| * |
| * @throws javax.xml.transform.TransformerException |
| */ |
| public void init(Compiler compiler, int opPos, int stepType) |
| throws javax.xml.transform.TransformerException |
| { |
| |
| initPredicateInfo(compiler, opPos); |
| |
| // int testType = compiler.getOp(nodeTestOpPos); |
| } |
| |
| /** |
| * Get a cloned AxesWalker. |
| * |
| * @return A new AxesWalker 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! |
| |
| AxesWalker clone = (AxesWalker) super.clone(); |
| |
| //clone.setCurrentNode(clone.m_root); |
| |
| // clone.m_isFresh = true; |
| |
| return clone; |
| } |
| |
| /** |
| * Do a deep clone of this walker, including next and previous walkers. |
| * If the this AxesWalker is on the clone list, don't clone but |
| * return the already cloned version. |
| * |
| * @param cloneOwner non-null reference to the cloned location path |
| * iterator to which this clone will be added. |
| * @param cloneList non-null vector of sources in odd elements, and the |
| * corresponding clones in even vectors. |
| * |
| * @return non-null clone, which may be a new clone, or may be a clone |
| * contained on the cloneList. |
| */ |
| AxesWalker cloneDeep(WalkingIterator cloneOwner, Vector cloneList) |
| throws CloneNotSupportedException |
| { |
| AxesWalker clone = findClone(this, cloneList); |
| if(null != clone) |
| return clone; |
| clone = (AxesWalker)this.clone(); |
| clone.setLocPathIterator(cloneOwner); |
| if(null != cloneList) |
| { |
| cloneList.addElement(this); |
| cloneList.addElement(clone); |
| } |
| |
| if(wi().m_lastUsedWalker == this) |
| cloneOwner.m_lastUsedWalker = clone; |
| |
| if(null != m_nextWalker) |
| clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner, cloneList); |
| |
| // If you don't check for the cloneList here, you'll go into an |
| // recursive infinate loop. |
| if(null != cloneList) |
| { |
| if(null != m_prevWalker) |
| clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner, cloneList); |
| } |
| else |
| { |
| if(null != m_nextWalker) |
| clone.m_nextWalker.m_prevWalker = clone; |
| } |
| return clone; |
| } |
| |
| /** |
| * Find a clone that corresponds to the key argument. |
| * |
| * @param key The original AxesWalker for which there may be a clone. |
| * @param cloneList vector of sources in odd elements, and the |
| * corresponding clones in even vectors, may be null. |
| * |
| * @return A clone that corresponds to the key, or null if key not found. |
| */ |
| static AxesWalker findClone(AxesWalker key, Vector cloneList) |
| { |
| if(null != cloneList) |
| { |
| // First, look for clone on list. |
| int n = cloneList.size(); |
| for (int i = 0; i < n; i+=2) |
| { |
| if(key == cloneList.elementAt(i)) |
| return (AxesWalker)cloneList.elementAt(i+1); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Detaches the walker from the set which it iterated over, releasing |
| * any computational resources and placing the iterator in the INVALID |
| * state. |
| */ |
| public void detach() |
| { |
| m_currentNode = DTM.NULL; |
| m_dtm = null; |
| m_traverser = null; |
| m_isFresh = true; |
| m_root = DTM.NULL; |
| } |
| |
| //=============== TreeWalker Implementation =============== |
| |
| /** |
| * The root node of the TreeWalker, as specified in setRoot(int root). |
| * Note that this may actually be below the current node. |
| * |
| * @return The context node of the step. |
| */ |
| public int getRoot() |
| { |
| return m_root; |
| } |
| |
| /** |
| * Get the analysis bits for this walker, as defined in the WalkerFactory. |
| * @return One of WalkerFactory#BIT_DESCENDANT, etc. |
| */ |
| public int getAnalysisBits() |
| { |
| int axis = getAxis(); |
| int bit = WalkerFactory.getAnalysisBitFromAxes(axis); |
| return bit; |
| } |
| |
| /** |
| * Set the root node of the TreeWalker. |
| * (Not part of the DOM2 TreeWalker interface). |
| * |
| * @param root The context node of this step. |
| */ |
| public void setRoot(int root) |
| { |
| // %OPT% Get this directly from the lpi. |
| XPathContext xctxt = wi().getXPathContext(); |
| m_dtm = xctxt.getDTM(root); |
| m_traverser = m_dtm.getAxisTraverser(m_axis); |
| m_isFresh = true; |
| m_foundLast = false; |
| m_root = root; |
| m_currentNode = root; |
| |
| if (DTM.NULL == root) |
| { |
| throw new RuntimeException( |
| XSLMessages.createXPATHMessage(XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL, null)); //"\n !!!! Error! Setting the root of a walker to null!!!"); |
| } |
| |
| resetProximityPositions(); |
| } |
| |
| /** |
| * The node at which the TreeWalker is currently positioned. |
| * <br> The value must not be null. Alterations to the DOM tree may cause |
| * the current node to no longer be accepted by the TreeWalker's |
| * associated filter. currentNode may also be explicitly set to any node, |
| * whether or not it is within the subtree specified by the root node or |
| * would be accepted by the filter and whatToShow flags. Further |
| * traversal occurs relative to currentNode even if it is not part of the |
| * current view by applying the filters in the requested direction (not |
| * changing currentNode where no traversal is possible). |
| * |
| * @return The node at which the TreeWalker is currently positioned, only null |
| * if setRoot has not yet been called. |
| */ |
| public final int getCurrentNode() |
| { |
| return m_currentNode; |
| } |
| |
| /** |
| * Set the next walker in the location step chain. |
| * |
| * |
| * @param walker Reference to AxesWalker derivative, or may be null. |
| */ |
| public void setNextWalker(AxesWalker walker) |
| { |
| m_nextWalker = walker; |
| } |
| |
| /** |
| * Get the next walker in the location step chain. |
| * |
| * |
| * @return Reference to AxesWalker derivative, or null. |
| */ |
| public AxesWalker getNextWalker() |
| { |
| return m_nextWalker; |
| } |
| |
| /** |
| * Set or clear the previous walker reference in the location step chain. |
| * |
| * |
| * @param walker Reference to previous walker reference in the location |
| * step chain, or null. |
| */ |
| public void setPrevWalker(AxesWalker walker) |
| { |
| m_prevWalker = walker; |
| } |
| |
| /** |
| * Get the previous walker reference in the location step chain. |
| * |
| * |
| * @return Reference to previous walker reference in the location |
| * step chain, or null. |
| */ |
| public AxesWalker getPrevWalker() |
| { |
| return m_prevWalker; |
| } |
| |
| /** |
| * This is simply a way to bottle-neck the return of the next node, for |
| * diagnostic purposes. |
| * |
| * @param n Node to return, or null. |
| * |
| * @return The argument. |
| */ |
| private int returnNextNode(int n) |
| { |
| |
| return n; |
| } |
| |
| /** |
| * Get the next node in document order on the axes. |
| * |
| * @return the next node in document order on the axes, or null. |
| */ |
| protected int getNextNode() |
| { |
| if (m_foundLast) |
| return DTM.NULL; |
| |
| if (m_isFresh) |
| { |
| m_currentNode = m_traverser.first(m_root); |
| m_isFresh = false; |
| } |
| // I shouldn't have to do this the check for current node, I think. |
| // numbering\numbering24.xsl fails if I don't do this. I think |
| // it occurs as the walkers are backing up. -sb |
| else if(DTM.NULL != m_currentNode) |
| { |
| m_currentNode = m_traverser.next(m_root, m_currentNode); |
| } |
| |
| if (DTM.NULL == m_currentNode) |
| this.m_foundLast = true; |
| |
| return m_currentNode; |
| } |
| |
| /** |
| * Moves the <code>TreeWalker</code> to the next visible node in document |
| * order relative to the current node, and returns the new node. If the |
| * current node has no next node, or if the search for nextNode attempts |
| * to step upward from the TreeWalker's root node, returns |
| * <code>null</code> , and retains the current node. |
| * @return The new node, or <code>null</code> if the current node has no |
| * next node in the TreeWalker's logical view. |
| */ |
| public int nextNode() |
| { |
| int nextNode = DTM.NULL; |
| AxesWalker walker = wi().getLastUsedWalker(); |
| |
| while (true) |
| { |
| if (null == walker) |
| break; |
| |
| nextNode = walker.getNextNode(); |
| |
| if (DTM.NULL == nextNode) |
| { |
| |
| walker = walker.m_prevWalker; |
| } |
| else |
| { |
| if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT) |
| { |
| continue; |
| } |
| |
| if (null == walker.m_nextWalker) |
| { |
| wi().setLastUsedWalker(walker); |
| |
| // return walker.returnNextNode(nextNode); |
| break; |
| } |
| else |
| { |
| AxesWalker prev = walker; |
| |
| walker = walker.m_nextWalker; |
| |
| walker.setRoot(nextNode); |
| |
| walker.m_prevWalker = prev; |
| |
| continue; |
| } |
| } // if(null != nextNode) |
| } // while(null != walker) |
| |
| return nextNode; |
| } |
| |
| //============= End TreeWalker Implementation ============= |
| |
| /** |
| * Get the index of the last node that can be itterated to. |
| * |
| * |
| * @param xctxt XPath runtime context. |
| * |
| * @return the index of the last node that can be itterated to. |
| */ |
| public int getLastPos(XPathContext xctxt) |
| { |
| |
| int pos = getProximityPosition(); |
| |
| AxesWalker walker; |
| |
| try |
| { |
| walker = (AxesWalker) clone(); |
| } |
| catch (CloneNotSupportedException cnse) |
| { |
| return -1; |
| } |
| |
| walker.setPredicateCount(m_predicateIndex); |
| walker.setNextWalker(null); |
| walker.setPrevWalker(null); |
| |
| WalkingIterator lpi = wi(); |
| AxesWalker savedWalker = lpi.getLastUsedWalker(); |
| |
| try |
| { |
| lpi.setLastUsedWalker(walker); |
| |
| int next; |
| |
| while (DTM.NULL != (next = walker.nextNode())) |
| { |
| pos++; |
| } |
| |
| // TODO: Should probably save this in the iterator. |
| } |
| finally |
| { |
| lpi.setLastUsedWalker(savedWalker); |
| } |
| |
| // System.out.println("pos: "+pos); |
| return pos; |
| } |
| |
| //============= State Data ============= |
| |
| /** |
| * The DTM for the root. This can not be used, or must be changed, |
| * for the filter walker, or any walker that can have nodes |
| * from multiple documents. |
| * Never, ever, access this value without going through getDTM(int node). |
| */ |
| private DTM m_dtm; |
| |
| /** |
| * Set the DTM for this walker. |
| * |
| * @param dtm Non-null reference to a DTM. |
| */ |
| public void setDefaultDTM(DTM dtm) |
| { |
| m_dtm = dtm; |
| } |
| |
| /** |
| * Get the DTM for this walker. |
| * |
| * @return Non-null reference to a DTM. |
| */ |
| public DTM getDTM(int node) |
| { |
| // |
| return wi().getXPathContext().getDTM(node); |
| } |
| |
| /** |
| * Returns true if all the nodes in the iteration well be returned in document |
| * order. |
| * Warning: This can only be called after setRoot has been called! |
| * |
| * @return true as a default. |
| */ |
| public boolean isDocOrdered() |
| { |
| return true; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * This will traverse the heararchy, calling the visitor for |
| * each member. If the called visitor method returns |
| * false, the subtree should not be called. |
| * |
| * @param owner The owner of the visitor, where that path may be |
| * rewritten if needed. |
| * @param visitor The visitor whose appropriate method will be called. |
| */ |
| public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) |
| { |
| if(visitor.visitStep(owner, this)) |
| { |
| callPredicateVisitors(visitor); |
| if(null != m_nextWalker) |
| { |
| m_nextWalker.callVisitors(this, visitor); |
| } |
| } |
| } |
| |
| /** |
| * @see ExpressionOwner#getExpression() |
| */ |
| public Expression getExpression() |
| { |
| return m_nextWalker; |
| } |
| |
| /** |
| * @see ExpressionOwner#setExpression(Expression) |
| */ |
| public void setExpression(Expression exp) |
| { |
| exp.exprSetParent(this); |
| m_nextWalker = (AxesWalker)exp; |
| } |
| |
| /** |
| * @see Expression#deepEquals(Expression) |
| */ |
| public boolean deepEquals(Expression expr) |
| { |
| if (!super.deepEquals(expr)) |
| return false; |
| |
| AxesWalker walker = (AxesWalker)expr; |
| if(this.m_axis != walker.m_axis) |
| return false; |
| |
| return true; |
| } |
| |
| /** |
| * The root node of the TreeWalker, as specified when it was created. |
| */ |
| transient int m_root = DTM.NULL; |
| |
| /** |
| * The node at which the TreeWalker is currently positioned. |
| */ |
| private transient int m_currentNode = DTM.NULL; |
| |
| /** True if an itteration has not begun. */ |
| transient boolean m_isFresh; |
| |
| /** The next walker in the location step chain. |
| * @serial */ |
| protected AxesWalker m_nextWalker; |
| |
| /** The previous walker in the location step chain, or null. |
| * @serial */ |
| AxesWalker m_prevWalker; |
| |
| /** 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 DTMAxisTraverser m_traverser; |
| } |