blob: d8fa524c1bdcf050567d782ea5c17b5f5ccd7fec [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xpath.axes;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xpath.Expression;
import org.apache.xpath.ExpressionOwner;
import org.apache.xpath.VariableComposeState;
import org.apache.xpath.XPathVisitor;
import org.apache.xpath.functions.Function;
import org.apache.xpath.operations.Variable;
import org.apache.xpath.parser.Node;
/**
* <meta name="usage" content="advanced"/>
* This class extends NodeSetDTM, which implements DTMIterator,
* and fetches nodes one at a time in document order based on a XPath
* <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
* As each node is iterated via nextNode(), the node is also stored
* in the NodeVector, so that previousNode() can easily be done.
*/
public class UnionPathIterator
extends LocPathIterator
implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
{
/**
* Constructor to create an instance which you can add location paths to.
*/
public UnionPathIterator()
{
super();
// m_mutable = false;
// m_cacheNodes = false;
m_iterators = null;
m_exprs = null;
}
/**
* 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);
try
{
if (null != m_exprs)
{
int n = m_exprs.length;
DTMIterator newIters[] = new DTMIterator[n];
for (int i = 0; i < n; i++)
{
DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
newIters[i] = iter;
iter.nextNode();
}
m_iterators = newIters;
}
}
catch (Exception e)
{
throw new org.apache.xml.utils.WrappedRuntimeException(e);
}
}
/**
* Add an iterator to the union list.
*
* @param iter non-null reference to a location path iterator.
*/
public void addIterator(DTMIterator expr)
{
// Increase array size by only 1 at a time. Fix this
// if it looks to be a problem.
if (null == m_iterators)
{
m_iterators = new DTMIterator[1];
m_iterators[0] = expr;
}
else
{
DTMIterator[] exprs = m_iterators;
int len = m_iterators.length;
m_iterators = new DTMIterator[len + 1];
System.arraycopy(exprs, 0, m_iterators, 0, len);
m_iterators[len] = expr;
}
expr.nextNode();
if (expr instanceof Expression)
((Expression) expr).exprSetParent(this);
}
/**
* Detaches the iterator from the set which it iterated over, releasing
* any computational resources and placing the iterator in the INVALID
* state. After<code>detach</code> has been invoked, calls to
* <code>nextNode</code> or<code>previousNode</code> will raise the
* exception INVALID_STATE_ERR.
*/
public void detach()
{
if (null != m_iterators)
{
int n = m_iterators.length;
for (int i = 0; i < n; i++)
{
m_iterators[i].detach();
}
m_iterators = null;
}
}
/**
* @see org.apache.xpath.parser.Node#jjtAddChild(Node, int)
*/
public void jjtAddChild(Node n, int i)
{
if (n instanceof UnionPathIterator)
{
int nChildren = n.jjtGetNumChildren();
for (int j = 0; j < nChildren; j++)
{
Node child = n.jjtGetChild(j);
child.jjtSetParent(this);
if (j == 0)
jjtAddChild(child, i);
else
jjtAddChild(child, m_exprs.length); // order doesn't matter!
}
return;
}
if (null == m_exprs)
{
m_exprs = new LocPathIterator[i + 1];
}
else
if (i >= m_exprs.length)
{
// Slow but space conservative.
LocPathIterator[] exprs = new LocPathIterator[m_exprs.length + 1];
System.arraycopy(m_exprs, 0, exprs, 0, m_exprs.length);
m_exprs = exprs;
}
n = fixupPrimarys(n);
// If a function or variable has been reduced from a path
// expression, essentially
// we need to turn it back into a path expression here! There
// might be a way to do this a bit earlier. -sb
// if(!(((SimpleNode)n).isPathExpr()))
if(n instanceof Variable || n instanceof Function)
{
FilterExprIteratorSimple feis =
new FilterExprIteratorSimple((Expression) n);
feis.jjtSetParent(this);
n = feis;
}
m_exprs[i] = (LocPathIterator) n;
}
/**
* @see org.apache.xpath.parser.Node#jjtGetChild(int)
*/
public Node jjtGetChild(int i)
{
return m_exprs[i];
}
/**
* @see org.apache.xpath.parser.Node#jjtGetNumChildren()
*/
public int jjtGetNumChildren()
{
return m_exprs.length;
}
/**
* Get the analysis bits for this walker, as defined in the WalkerFactory.
* @return One of WalkerFactory#BIT_DESCENDANT, etc.
*/
public int getAnalysisBits()
{
int bits = 0;
if (m_exprs != null)
{
int n = m_exprs.length;
for (int i = 0; i < n; i++)
{
int bit = m_exprs[i].getAnalysisBits();
bits |= bit;
}
}
return bits;
}
/**
* Read the object from a serialization stream.
*
* @param stream Input stream to read from
*
* @throws java.io.IOException
* @throws javax.xml.transform.TransformerException
*/
private void readObject(java.io.ObjectInputStream stream)
throws java.io.IOException, javax.xml.transform.TransformerException
{
try
{
stream.defaultReadObject();
m_clones = new IteratorPool(this);
}
catch (ClassNotFoundException cnfe)
{
throw new javax.xml.transform.TransformerException(cnfe);
}
}
/**
* Get a cloned LocPathIterator that holds the same
* position as this iterator.
*
* @return A clone of this iterator that holds the same node position.
*
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException
{
UnionPathIterator clone = (UnionPathIterator) super.clone();
// %REVIEW% Not 100% sure this clone should be done here! -sb
if(clone.m_iterators == m_iterators && null != m_iterators)
{
DTMIterator[] clonedIters = new DTMIterator[m_iterators.length];
clone.m_iterators = clonedIters;
for (int i = 0; i < m_iterators.length; i++)
{
clonedIters[i] = (DTMIterator)((Expression)m_iterators[i]).cloneDeep();
}
}
return clone;
}
/**
* Get a cloned Iterator that is reset to the beginning
* of the query.
*
* @return A cloned NodeIterator set of the start of the query.
*
* @throws CloneNotSupportedException
*/
public DTMIterator cloneWithReset() throws CloneNotSupportedException
{
UnionPathIterator clone = (UnionPathIterator) super.cloneWithReset();
clone.resetProximityPositions();
return clone;
}
/**
* Returns the next node in the set and advances the position of the
* iterator in the set. After a DTMIterator is created, the first call
* to nextNode() returns the first node in the set.
*
* @return The next <code>Node</code> in the set being iterated over, or
* <code>null</code> if there are no more members in that set.
*/
public int nextNode()
{
if (m_foundLast)
return DTM.NULL;
if (DTM.NULL == m_lastFetched)
{
resetProximityPositions();
}
// Loop through the iterators getting the current fetched
// node, and get the earliest occuring in document order
int earliestNode = DTM.NULL;
if (null != m_iterators)
{
int n = m_iterators.length;
int iteratorUsed = -1;
int savedIteratorUsed = -1;
int savedEarliestNode = DTM.NULL;
while (true)
{
for (int i = 0; i < n; i++)
{
int node = m_iterators[i].getCurrentNode();
if (DTM.NULL == node)
continue;
else
if (DTM.NULL == earliestNode)
{
iteratorUsed = i;
earliestNode = node;
}
else
{
if (node == earliestNode)
{
// Found a duplicate, so skip past it.
// %REVIEW% Make sure this is really what we
// want to do for XPath 2.0.
m_iterators[i].nextNode();
}
else
{
DTM dtm = getDTM(node);
if (dtm.isNodeAfter(node, earliestNode))
{
iteratorUsed = i;
earliestNode = node;
}
}
}
}
if (DTM.NULL == earliestNode)
break;
else
if (getPredicateCount() == 0)
{
m_iterators[iteratorUsed].nextNode();
break;
}
else
{
m_iterators[iteratorUsed].nextNode();
int acceptence = acceptNode(earliestNode);
if (DTMIterator.FILTER_ACCEPT == acceptence)
break;
earliestNode = DTM.NULL;
}
}
if (DTM.NULL != earliestNode)
{
// m_iterators[iteratorUsed].nextNode();
incrementCurrentPos();
}
else
m_foundLast = true;
}
m_lastFetched = earliestNode;
return earliestNode;
}
/**
* This function is used to fixup variables from QNames to stack frame
* indexes at stylesheet build time.
*
* @param vars List of QNames that correspond to variables. This list
* should be searched backwards for the first qualified name that
* corresponds to the variable reference qname. The position of the
* QName in the vector from the start of the vector will be its position
* in the stack frame (but variables above the globalsTop value will need
* to be offset to the current stack frame).
*/
public void fixupVariables(VariableComposeState vcs)
{
for (int i = 0; i < m_exprs.length; i++)
{
m_exprs[i].fixupVariables(vcs);
}
}
/**
* The location path iterators, one for each
* <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
* path</a> contained in the union expression.
* @serial
*/
protected LocPathIterator[] m_exprs;
/**
* The location path iterators, one for each
* <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
* path</a> contained in the union expression.
* @serial
*/
protected DTMIterator[] m_iterators;
/**
* 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()
{
// Could be smarter.
return -1;
}
class iterOwner implements ExpressionOwner
{
int m_index;
iterOwner(int index)
{
m_index = index;
}
/**
* @see ExpressionOwner#getExpression()
*/
public Expression getExpression()
{
return m_exprs[m_index];
}
/**
* @see ExpressionOwner#setExpression(Expression)
*/
public void setExpression(Expression exp)
{
if (!(exp instanceof LocPathIterator))
{
// Yuck. Need FilterExprIter. Or make it so m_exprs can be just
// plain expressions?
WalkingIterator wi = new WalkingIterator(getPrefixResolver());
FilterExprWalker few = new FilterExprWalker(wi);
wi.setFirstWalker(few);
few.setInnerExpression(exp);
wi.exprSetParent(UnionPathIterator.this);
few.exprSetParent(wi);
exp.exprSetParent(few);
exp = wi;
}
else
exp.exprSetParent(UnionPathIterator.this);
m_exprs[m_index] = (LocPathIterator) exp;
}
}
/**
* @see XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
*/
public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
{
if (visitor.visitUnionPath(owner, this))
{
if (null != m_exprs)
{
int n = m_exprs.length;
for (int i = 0; i < n; i++)
{
m_exprs[i].callVisitors(new iterOwner(i), visitor);
}
}
}
}
/**
* @see Expression#deepEquals(Expression)
*/
public boolean deepEquals(Expression expr)
{
if (!super.deepEquals(expr))
return false;
UnionPathIterator upi = (UnionPathIterator) expr;
if (null != m_exprs)
{
int n = m_exprs.length;
if ((null == upi.m_exprs) || (upi.m_exprs.length != n))
return false;
for (int i = 0; i < n; i++)
{
if (!m_exprs[i].deepEquals(upi.m_exprs[i]))
return false;
}
}
else
if (null != upi.m_exprs)
{
return false;
}
return true;
}
}