/*
* 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: UnionPathIterator.java,v 1.33 2004/02/17 04:32:08 minchau Exp $
*/
package com.sun.org.apache.xpath.internal.axes;
import com.sun.org.apache.xml.internal.dtm.Axis;
import com.sun.org.apache.xml.internal.dtm.DTM;
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.XPathVisitor;
import com.sun.org.apache.xpath.internal.compiler.Compiler;
import com.sun.org.apache.xpath.internal.compiler.OpCodes;
/**
* This class extends NodeSetDTM, which implements DTMIterator,
* and fetches nodes one at a time in document order based on a XPath
* UnionExpr.
* As each node is iterated via nextNode(), the node is also stored
* in the NodeVector, so that previousNode() can easily be done.
* @xsl.usage advanced
*/
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 com.sun.org.apache.xml.internal.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. Afterdetach
has been invoked, calls to
* nextNode
orpreviousNode
will raise the
* exception INVALID_STATE_ERR.
*/
public void detach()
{
if(m_allowDetach && null != m_iterators){
int n = m_iterators.length;
for(int i = 0; i < n; i++)
{
m_iterators[i].detach();
}
m_iterators = null;
}
}
/**
* Create a UnionPathIterator object, including creation
* of location path iterators from the opcode list, and call back
* into the Compiler to create predicate expressions.
*
* @param compiler The Compiler which is creating
* this expression.
* @param opPos The position of this iterator in the
* opcode list from the compiler.
*
* @throws javax.xml.transform.TransformerException
*/
public UnionPathIterator(Compiler compiler, int opPos)
throws javax.xml.transform.TransformerException
{
super();
opPos = compiler.getFirstChildPos(opPos);
loadLocationPaths(compiler, opPos, 0);
}
/**
* This will return an iterator capable of handling the union of paths given.
*
* @param compiler The Compiler which is creating
* this expression.
* @param opPos The position of this iterator in the
* opcode list from the compiler.
*
* @return Object that is derived from LocPathIterator.
*
* @throws javax.xml.transform.TransformerException
*/
public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
throws javax.xml.transform.TransformerException
{
// For the moment, I'm going to first create a full UnionPathIterator, and
// then see if I can reduce it to a UnionChildIterator. It would obviously
// be more effecient to just test for the conditions for a UnionChildIterator,
// and then create that directly.
UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
int nPaths = upi.m_exprs.length;
boolean isAllChildIterators = true;
for(int i = 0; i < nPaths; i++)
{
LocPathIterator lpi = upi.m_exprs[i];
if(lpi.getAxis() != Axis.CHILD)
{
isAllChildIterators = false;
break;
}
else
{
// check for positional predicates or position function, which won't work.
if(HasPositionalPredChecker.check(lpi))
{
isAllChildIterators = false;
break;
}
}
}
if(isAllChildIterators)
{
UnionChildIterator uci = new UnionChildIterator();
for(int i = 0; i < nPaths; i++)
{
PredicatedNodeTest lpi = upi.m_exprs[i];
// I could strip the lpi down to a pure PredicatedNodeTest, but
// I don't think it's worth it. Note that the test can be used
// as a static object... so it doesn't have to be cloned.
uci.addNodeTest(lpi);
}
return uci;
}
else
return upi;
}
/**
* 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();
// if (m_iterators != null)
// {
// int n = m_iterators.length;
//
// clone.m_iterators = new LocPathIterator[n];
//
// for (int i = 0; i < n; i++)
// {
// clone.m_iterators[i] = (LocPathIterator)m_iterators[i].clone();
// }
// }
return clone;
}
/**
* Create a new location path iterator.
*
* @param compiler The Compiler which is creating
* this expression.
* @param opPos The position of this iterator in the
*
* @return New location path iterator.
*
* @throws javax.xml.transform.TransformerException
*/
protected LocPathIterator createDTMIterator(
Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
{
LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos,
(compiler.getLocationPathDepth() <= 0));
return lpi;
}
/**
* Initialize the location path iterators. Recursive.
*
* @param compiler The Compiler which is creating
* this expression.
* @param opPos The position of this iterator in the
* opcode list from the compiler.
* @param count The insert position of the iterator.
*
* @throws javax.xml.transform.TransformerException
*/
protected void loadLocationPaths(Compiler compiler, int opPos, int count)
throws javax.xml.transform.TransformerException
{
// TODO: Handle unwrapped FilterExpr
int steptype = compiler.getOp(opPos);
if (steptype == OpCodes.OP_LOCATIONPATH)
{
loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
m_exprs[count] = createDTMIterator(compiler, opPos);
m_exprs[count].exprSetParent(this);
}
else
{
// Have to check for unwrapped functions, which the LocPathIterator
// doesn't handle.
switch (steptype)
{
case OpCodes.OP_VARIABLE :
case OpCodes.OP_EXTFUNCTION :
case OpCodes.OP_FUNCTION :
case OpCodes.OP_GROUP :
loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
WalkingIterator iter =
new WalkingIterator(compiler.getNamespaceContext());
iter.exprSetParent(this);
if(compiler.getLocationPathDepth() <= 0)
iter.setIsTopLevel(true);
iter.m_firstWalker = new com.sun.org.apache.xpath.internal.axes.FilterExprWalker(iter);
iter.m_firstWalker.init(compiler, opPos, steptype);
m_exprs[count] = iter;
break;
default :
m_exprs = new LocPathIterator[count];
}
}
}
/**
* 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 Node
in the set being iterated over, or
* null
if there are no more members in that set.
*/
public int nextNode()
{
if(m_foundLast)
return DTM.NULL;
// 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;
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.
m_iterators[i].nextNode();
}
else
{
DTM dtm = getDTM(node);
if (dtm.isNodeAfter(node, earliestNode))
{
iteratorUsed = i;
earliestNode = node;
}
}
}
}
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(java.util.Vector vars, int globalsSize)
{
for (int i = 0; i < m_exprs.length; i++)
{
m_exprs[i].fixupVariables(vars, globalsSize);
}
}
/**
* The location path iterators, one for each
* location
* path contained in the union expression.
* @serial
*/
protected LocPathIterator[] m_exprs;
/**
* The location path iterators, one for each
* location
* path 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 com.sun.org.apache.xpath.internal.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;
}
}