/* * 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; } } }