You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
593 lines
15 KiB
593 lines
15 KiB
/*
|
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
/*
|
|
* 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: AxesWalker.java,v 1.2.4.1 2005/09/14 19:45:22 jeffsuttor Exp $
|
|
*/
|
|
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;
|
|
}
|