| /* |
| * 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. |
| */ |
| /* |
| * $Id: PredicatedNodeTest.java 468655 2006-10-28 07:12:06Z minchau $ |
| */ |
| package org.apache.xpath.axes; |
| |
| import org.apache.xml.dtm.DTM; |
| import org.apache.xml.dtm.DTMIterator; |
| import org.apache.xml.utils.PrefixResolver; |
| import org.apache.xpath.Expression; |
| import org.apache.xpath.ExpressionOwner; |
| import org.apache.xpath.XPathContext; |
| import org.apache.xpath.XPathVisitor; |
| import org.apache.xpath.compiler.Compiler; |
| import org.apache.xpath.objects.XObject; |
| import org.apache.xpath.patterns.NodeTest; |
| |
| public abstract class PredicatedNodeTest extends NodeTest implements SubContextList |
| { |
| static final long serialVersionUID = -6193530757296377351L; |
| |
| /** |
| * 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 org.apache.xml.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 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; |
| } |
| } |
| |
| } |