- /*
- * 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: PredicatedNodeTest.java,v 1.15 2004/02/17 04:32:08 minchau Exp $
- */
- package com.sun.org.apache.xpath.internal.axes;
-
- import com.sun.org.apache.xml.internal.dtm.DTM;
- import com.sun.org.apache.xml.internal.dtm.DTMIterator;
- import com.sun.org.apache.xml.internal.utils.PrefixResolver;
- 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.objects.XObject;
- import com.sun.org.apache.xpath.internal.patterns.NodeTest;
-
- public abstract class PredicatedNodeTest extends NodeTest implements SubContextList
- {
-
- /**
- * Construct an AxesWalker using a LocPathIterator.
- *
- * @param locPathIterator non-null reference to the parent iterator.
- */
- PredicatedNodeTest(LocPathIterator locPathIterator)
- {
- m_lpi = locPathIterator;
- }
-
- /**
- * Construct an AxesWalker. The location path iterator will have to be set
- * before use.
- */
- PredicatedNodeTest()
- {
- }
-
- /**
- * 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_predicateIndex = -1;
- resetProximityPositions();
- }
- catch (ClassNotFoundException cnfe)
- {
- throw new javax.xml.transform.TransformerException(cnfe);
- }
- }
-
- /**
- * Get a cloned PrdicatedNodeTest.
- *
- * @return A new PredicatedNodeTest 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!
-
- PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();
-
- if ((null != this.m_proximityPositions)
- && (this.m_proximityPositions == clone.m_proximityPositions))
- {
- clone.m_proximityPositions = new int[this.m_proximityPositions.length];
-
- System.arraycopy(this.m_proximityPositions, 0,
- clone.m_proximityPositions, 0,
- this.m_proximityPositions.length);
- }
-
- if(clone.m_lpi == this)
- clone.m_lpi = (LocPathIterator)clone;
-
- return clone;
- }
-
- // Only for clones for findLastPos. See bug4638.
- protected int m_predCount = -1;
-
- /**
- * Get the number of predicates that this walker has.
- *
- * @return the number of predicates that this walker has.
- */
- public int getPredicateCount()
- {
- if(-1 == m_predCount)
- return (null == m_predicates) ? 0 : m_predicates.length;
- else
- return m_predCount;
- }
-
- /**
- * Set the number of predicates that this walker has. This does more
- * that one would think, as it creates a new predicate array of the
- * size of the count argument, and copies count predicates into the new
- * one from the old, and then reassigns the predicates value. All this
- * to keep from having to have a predicate count value.
- *
- * @param count The number of predicates, which must be equal or less
- * than the existing count.
- */
- public void setPredicateCount(int count)
- {
- if(count > 0)
- {
- Expression[] newPredicates = new Expression[count];
- for (int i = 0; i < count; i++)
- {
- newPredicates[i] = m_predicates[i];
- }
- m_predicates = newPredicates;
- }
- else
- m_predicates = null;
-
- }
-
- /**
- * Init predicate info.
- *
- * @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.
- *
- * @throws javax.xml.transform.TransformerException
- */
- protected void initPredicateInfo(Compiler compiler, int opPos)
- throws javax.xml.transform.TransformerException
- {
-
- int pos = compiler.getFirstPredicateOpPos(opPos);
-
- if(pos > 0)
- {
- m_predicates = compiler.getCompiledPredicates(pos);
- if(null != m_predicates)
- {
- for(int i = 0; i < m_predicates.length; i++)
- {
- m_predicates[i].exprSetParent(this);
- }
- }
- }
- }
-
- /**
- * Get a predicate expression at the given index.
- *
- *
- * @param index Index of the predicate.
- *
- * @return A predicate expression.
- */
- public Expression getPredicate(int index)
- {
- return m_predicates[index];
- }
-
- /**
- * Get the current sub-context position.
- *
- * @return The node position of this walker in the sub-context node list.
- */
- public int getProximityPosition()
- {
-
- // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex);
- return getProximityPosition(m_predicateIndex);
- }
-
- /**
- * Get the current sub-context position.
- *
- * @param xctxt The XPath runtime context.
- *
- * @return The node position of this walker in the sub-context node list.
- */
- public int getProximityPosition(XPathContext xctxt)
- {
- return getProximityPosition();
- }
-
- /**
- * 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 abstract int getLastPos(XPathContext xctxt);
-
- /**
- * Get the current sub-context position.
- *
- * @param predicateIndex The index of the predicate where the proximity
- * should be taken from.
- *
- * @return The node position of this walker in the sub-context node list.
- */
- protected int getProximityPosition(int predicateIndex)
- {
- return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0;
- }
-
- /**
- * Reset the proximity positions counts.
- */
- public void resetProximityPositions()
- {
- int nPredicates = getPredicateCount();
- if (nPredicates > 0)
- {
- if (null == m_proximityPositions)
- m_proximityPositions = new int[nPredicates];
-
- for (int i = 0; i < nPredicates; i++)
- {
- try
- {
- initProximityPosition(i);
- }
- catch(Exception e)
- {
- // TODO: Fix this...
- throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
- }
- }
- }
- }
-
- /**
- * Init the proximity position to zero for a forward axes.
- *
- * @param i The index into the m_proximityPositions array.
- *
- * @throws javax.xml.transform.TransformerException
- */
- public void initProximityPosition(int i) throws javax.xml.transform.TransformerException
- {
- m_proximityPositions[i] = 0;
- }
-
- /**
- * Count forward one proximity position.
- *
- * @param i The index into the m_proximityPositions array, where the increment
- * will occur.
- */
- protected void countProximityPosition(int i)
- {
- // Note that in the case of a UnionChildIterator, this may be a
- // static object and so m_proximityPositions may indeed be null!
- int[] pp = m_proximityPositions;
- if ((null != pp) && (i < pp.length))
- pp[i]++;
- }
-
- /**
- * Tells if this is a reverse axes.
- *
- * @return false, unless a derived class overrides.
- */
- public boolean isReverseAxes()
- {
- return false;
- }
-
- /**
- * Get which predicate is executing.
- *
- * @return The current predicate index, or -1 if no predicate is executing.
- */
- public int getPredicateIndex()
- {
- return m_predicateIndex;
- }
-
- /**
- * Process the predicates.
- *
- * @param context The current context node.
- * @param xctxt The XPath runtime context.
- *
- * @return the result of executing the predicate expressions.
- *
- * @throws javax.xml.transform.TransformerException
- */
- boolean executePredicates(int context, XPathContext xctxt)
- throws javax.xml.transform.TransformerException
- {
-
- int nPredicates = getPredicateCount();
- // System.out.println("nPredicates: "+nPredicates);
- if (nPredicates == 0)
- return true;
-
- PrefixResolver savedResolver = xctxt.getNamespaceContext();
-
- try
- {
- m_predicateIndex = 0;
- xctxt.pushSubContextList(this);
- xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
- xctxt.pushCurrentNode(context);
-
- for (int i = 0; i < nPredicates; i++)
- {
- // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
- XObject pred = m_predicates[i].execute(xctxt);
- // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
- // System.out.println("pred.getType(): "+pred.getType());
- if (XObject.CLASS_NUMBER == pred.getType())
- {
- if (DEBUG_PREDICATECOUNTING)
- {
- System.out.flush();
- System.out.println("\n===== start predicate count ========");
- System.out.println("m_predicateIndex: " + m_predicateIndex);
- // System.out.println("getProximityPosition(m_predicateIndex): "
- // + getProximityPosition(m_predicateIndex));
- System.out.println("pred.num(): " + pred.num());
- }
-
- int proxPos = this.getProximityPosition(m_predicateIndex);
- int predIndex = (int) pred.num();
- if (proxPos != predIndex)
- {
- if (DEBUG_PREDICATECOUNTING)
- {
- System.out.println("\nnode context: "+nodeToString(context));
- System.out.println("index predicate is false: "+proxPos);
- System.out.println("\n===== end predicate count ========");
- }
- return false;
- }
- else if (DEBUG_PREDICATECOUNTING)
- {
- System.out.println("\nnode context: "+nodeToString(context));
- System.out.println("index predicate is true: "+proxPos);
- System.out.println("\n===== end predicate count ========");
- }
-
- // If there is a proximity index that will not change during the
- // course of itteration, then we know there can be no more true
- // occurances of this predicate, so flag that we're done after
- // this.
- //
- // bugzilla 14365
- // We can't set m_foundLast = true unless we're sure that -all-
- // remaining parameters are stable, or else last() fails. Fixed so
- // only sets m_foundLast if on the last predicate
- if(m_predicates[i].isStableNumber() && i == nPredicates - 1)
- {
- m_foundLast = true;
- }
- }
- else if (!pred.bool())
- return false;
-
- countProximityPosition(++m_predicateIndex);
- }
- }
- finally
- {
- xctxt.popCurrentNode();
- xctxt.popNamespaceContext();
- xctxt.popSubContextList();
- m_predicateIndex = -1;
- }
-
- return true;
- }
-
- /**
- * 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)
- {
- super.fixupVariables(vars, globalsSize);
-
- int nPredicates = getPredicateCount();
-
- for (int i = 0; i < nPredicates; i++)
- {
- m_predicates[i].fixupVariables(vars, globalsSize);
- }
- }
-
-
- /**
- * Diagnostics.
- *
- * @param n Node to give diagnostic information about, or null.
- *
- * @return Informative string about the argument.
- */
- protected String nodeToString(int n)
- {
- if(DTM.NULL != n)
- {
- DTM dtm = m_lpi.getXPathContext().getDTM(n);
- return dtm.getNodeName(n) + "{" + (n+1) + "}";
- }
- else
- {
- return "null";
- }
- }
-
- //=============== NodeFilter Implementation ===============
-
- /**
- * Test whether a specified node is visible in the logical view of a
- * TreeWalker or NodeIterator. This function will be called by the
- * implementation of TreeWalker and NodeIterator; it is not intended to
- * be called directly from user code.
- * @param n The node to check to see if it passes the filter or not.
- * @return a constant to determine whether the node is accepted,
- * rejected, or skipped, as defined above .
- */
- public short acceptNode(int n)
- {
-
- XPathContext xctxt = m_lpi.getXPathContext();
-
- try
- {
- xctxt.pushCurrentNode(n);
-
- XObject score = execute(xctxt, n);
-
- // System.out.println("\n::acceptNode - score: "+score.num()+"::");
- if (score != NodeTest.SCORE_NONE)
- {
- if (getPredicateCount() > 0)
- {
- countProximityPosition(0);
-
- if (!executePredicates(n, xctxt))
- return DTMIterator.FILTER_SKIP;
- }
-
- return DTMIterator.FILTER_ACCEPT;
- }
- }
- catch (javax.xml.transform.TransformerException se)
- {
-
- // TODO: Fix this.
- throw new RuntimeException(se.getMessage());
- }
- finally
- {
- xctxt.popCurrentNode();
- }
-
- return DTMIterator.FILTER_SKIP;
- }
-
-
- /**
- * Get the owning location path iterator.
- *
- * @return the owning location path iterator, which should not be null.
- */
- public LocPathIterator getLocPathIterator()
- {
- return m_lpi;
- }
-
- /**
- * Set the location path iterator owner for this walker. Besides
- * initialization, this function is called during cloning operations.
- *
- * @param li non-null reference to the owning location path iterator.
- */
- public void setLocPathIterator(LocPathIterator li)
- {
- m_lpi = li;
- if(this != li)
- li.exprSetParent(this);
- }
-
- /**
- * Tell if this expression or it's subexpressions can traverse outside
- * the current subtree.
- *
- * @return true if traversal outside the context node's subtree can occur.
- */
- public boolean canTraverseOutsideSubtree()
- {
- int n = getPredicateCount();
- for (int i = 0; i < n; i++)
- {
- if(getPredicate(i).canTraverseOutsideSubtree())
- return true;
- }
- return false;
- }
-
- /**
- * 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 callPredicateVisitors(XPathVisitor visitor)
- {
- if (null != m_predicates)
- {
- int n = m_predicates.length;
- for (int i = 0; i < n; i++)
- {
- ExpressionOwner predOwner = new PredOwner(i);
- if (visitor.visitPredicate(predOwner, m_predicates[i]))
- {
- m_predicates[i].callVisitors(predOwner, visitor);
- }
-
- }
- }
- }
-
- /**
- * @see Expression#deepEquals(Expression)
- */
- public boolean deepEquals(Expression expr)
- {
- if (!super.deepEquals(expr))
- return false;
-
- PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
- if (null != m_predicates)
- {
-
- int n = m_predicates.length;
- if ((null == pnt.m_predicates) || (pnt.m_predicates.length != n))
- return false;
- for (int i = 0; i < n; i++)
- {
- if (!m_predicates[i].deepEquals(pnt.m_predicates[i]))
- return false;
- }
- }
- else if (null != pnt.m_predicates)
- return false;
-
- return true;
- }
-
- /** This is true if nextNode returns null. */
- transient protected boolean m_foundLast = false;
-
- /** The owning location path iterator.
- * @serial */
- protected LocPathIterator m_lpi;
-
- /**
- * Which predicate we are executing.
- */
- transient int m_predicateIndex = -1;
-
- /** The list of predicate expressions. Is static and does not need
- * to be deep cloned.
- * @serial
- */
- private Expression[] m_predicates;
-
- /**
- * An array of counts that correspond to the number
- * of predicates the step contains.
- */
- transient protected int[] m_proximityPositions;
-
- /** If true, diagnostic messages about predicate execution will be posted. */
- static final boolean DEBUG_PREDICATECOUNTING = false;
-
- class PredOwner implements ExpressionOwner
- {
- int m_index;
-
- PredOwner(int index)
- {
- m_index = index;
- }
-
- /**
- * @see ExpressionOwner#getExpression()
- */
- public Expression getExpression()
- {
- return m_predicates[m_index];
- }
-
-
- /**
- * @see ExpressionOwner#setExpression(Expression)
- */
- public void setExpression(Expression exp)
- {
- exp.exprSetParent(PredicatedNodeTest.this);
- m_predicates[m_index] = exp;
- }
- }
-
- }