Home | History | Annotate | Download | only in axes
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: PredicatedNodeTest.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xpath.axes;
     22 
     23 import org.apache.xml.dtm.DTM;
     24 import org.apache.xml.dtm.DTMIterator;
     25 import org.apache.xml.utils.PrefixResolver;
     26 import org.apache.xpath.Expression;
     27 import org.apache.xpath.ExpressionOwner;
     28 import org.apache.xpath.XPathContext;
     29 import org.apache.xpath.XPathVisitor;
     30 import org.apache.xpath.compiler.Compiler;
     31 import org.apache.xpath.objects.XObject;
     32 import org.apache.xpath.patterns.NodeTest;
     33 
     34 public abstract class PredicatedNodeTest extends NodeTest implements SubContextList
     35 {
     36     static final long serialVersionUID = -6193530757296377351L;
     37 
     38   /**
     39    * Construct an AxesWalker using a LocPathIterator.
     40    *
     41    * @param locPathIterator non-null reference to the parent iterator.
     42    */
     43   PredicatedNodeTest(LocPathIterator locPathIterator)
     44   {
     45     m_lpi = locPathIterator;
     46   }
     47 
     48   /**
     49    * Construct an AxesWalker.  The location path iterator will have to be set
     50    * before use.
     51    */
     52   PredicatedNodeTest()
     53   {
     54   }
     55 
     56   /**
     57    * Read the object from a serialization stream.
     58    *
     59    * @param stream Input stream to read from
     60    *
     61    * @throws java.io.IOException
     62    * @throws javax.xml.transform.TransformerException
     63    */
     64   private void readObject(java.io.ObjectInputStream stream)
     65           throws java.io.IOException, javax.xml.transform.TransformerException
     66   {
     67     try
     68     {
     69       stream.defaultReadObject();
     70       m_predicateIndex = -1;
     71       resetProximityPositions();
     72     }
     73     catch (ClassNotFoundException cnfe)
     74     {
     75       throw new javax.xml.transform.TransformerException(cnfe);
     76     }
     77   }
     78 
     79   /**
     80    * Get a cloned PrdicatedNodeTest.
     81    *
     82    * @return A new PredicatedNodeTest that can be used without mutating this one.
     83    *
     84    * @throws CloneNotSupportedException
     85    */
     86   public Object clone() throws CloneNotSupportedException
     87   {
     88     // Do not access the location path itterator during this operation!
     89 
     90     PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();
     91 
     92     if ((null != this.m_proximityPositions)
     93             && (this.m_proximityPositions == clone.m_proximityPositions))
     94     {
     95       clone.m_proximityPositions = new int[this.m_proximityPositions.length];
     96 
     97       System.arraycopy(this.m_proximityPositions, 0,
     98                        clone.m_proximityPositions, 0,
     99                        this.m_proximityPositions.length);
    100     }
    101 
    102     if(clone.m_lpi == this)
    103       clone.m_lpi = (LocPathIterator)clone;
    104 
    105     return clone;
    106   }
    107 
    108   // Only for clones for findLastPos.  See bug4638.
    109   protected int m_predCount = -1;
    110 
    111   /**
    112    * Get the number of predicates that this walker has.
    113    *
    114    * @return the number of predicates that this walker has.
    115    */
    116   public int getPredicateCount()
    117   {
    118     if(-1 == m_predCount)
    119       return (null == m_predicates) ? 0 : m_predicates.length;
    120     else
    121       return m_predCount;
    122   }
    123 
    124   /**
    125    * Set the number of predicates that this walker has.  This does more
    126    * that one would think, as it creates a new predicate array of the
    127    * size of the count argument, and copies count predicates into the new
    128    * one from the old, and then reassigns the predicates value.  All this
    129    * to keep from having to have a predicate count value.
    130    *
    131    * @param count The number of predicates, which must be equal or less
    132    *               than the existing count.
    133    */
    134   public void setPredicateCount(int count)
    135   {
    136     if(count > 0)
    137     {
    138       Expression[] newPredicates = new Expression[count];
    139       for (int i = 0; i < count; i++)
    140       {
    141         newPredicates[i] = m_predicates[i];
    142       }
    143       m_predicates = newPredicates;
    144     }
    145     else
    146       m_predicates = null;
    147 
    148   }
    149 
    150   /**
    151    * Init predicate info.
    152    *
    153    * @param compiler The Compiler object that has information about this
    154    *                 walker in the op map.
    155    * @param opPos The op code position of this location step.
    156    *
    157    * @throws javax.xml.transform.TransformerException
    158    */
    159   protected void initPredicateInfo(Compiler compiler, int opPos)
    160           throws javax.xml.transform.TransformerException
    161   {
    162 
    163     int pos = compiler.getFirstPredicateOpPos(opPos);
    164 
    165     if(pos > 0)
    166     {
    167       m_predicates = compiler.getCompiledPredicates(pos);
    168       if(null != m_predicates)
    169       {
    170       	for(int i = 0; i < m_predicates.length; i++)
    171       	{
    172       		m_predicates[i].exprSetParent(this);
    173       	}
    174       }
    175     }
    176   }
    177 
    178   /**
    179    * Get a predicate expression at the given index.
    180    *
    181    *
    182    * @param index Index of the predicate.
    183    *
    184    * @return A predicate expression.
    185    */
    186   public Expression getPredicate(int index)
    187   {
    188     return m_predicates[index];
    189   }
    190 
    191   /**
    192    * Get the current sub-context position.
    193    *
    194    * @return The node position of this walker in the sub-context node list.
    195    */
    196   public int getProximityPosition()
    197   {
    198 
    199     // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex);
    200     return getProximityPosition(m_predicateIndex);
    201   }
    202 
    203   /**
    204    * Get the current sub-context position.
    205    *
    206    * @param xctxt The XPath runtime context.
    207    *
    208    * @return The node position of this walker in the sub-context node list.
    209    */
    210   public int getProximityPosition(XPathContext xctxt)
    211   {
    212     return getProximityPosition();
    213   }
    214 
    215   /**
    216    * Get the index of the last node that can be itterated to.
    217    *
    218    *
    219    * @param xctxt XPath runtime context.
    220    *
    221    * @return the index of the last node that can be itterated to.
    222    */
    223   public abstract int getLastPos(XPathContext xctxt);
    224 
    225   /**
    226    * Get the current sub-context position.
    227    *
    228    * @param predicateIndex The index of the predicate where the proximity
    229    *                       should be taken from.
    230    *
    231    * @return The node position of this walker in the sub-context node list.
    232    */
    233   protected int getProximityPosition(int predicateIndex)
    234   {
    235     return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0;
    236   }
    237 
    238   /**
    239    * Reset the proximity positions counts.
    240    */
    241   public void resetProximityPositions()
    242   {
    243     int nPredicates = getPredicateCount();
    244     if (nPredicates > 0)
    245     {
    246       if (null == m_proximityPositions)
    247         m_proximityPositions = new int[nPredicates];
    248 
    249       for (int i = 0; i < nPredicates; i++)
    250       {
    251         try
    252         {
    253           initProximityPosition(i);
    254         }
    255         catch(Exception e)
    256         {
    257           // TODO: Fix this...
    258           throw new org.apache.xml.utils.WrappedRuntimeException(e);
    259         }
    260       }
    261     }
    262   }
    263 
    264   /**
    265    * Init the proximity position to zero for a forward axes.
    266    *
    267    * @param i The index into the m_proximityPositions array.
    268    *
    269    * @throws javax.xml.transform.TransformerException
    270    */
    271   public void initProximityPosition(int i) throws javax.xml.transform.TransformerException
    272   {
    273     m_proximityPositions[i] = 0;
    274   }
    275 
    276   /**
    277    * Count forward one proximity position.
    278    *
    279    * @param i The index into the m_proximityPositions array, where the increment
    280    *          will occur.
    281    */
    282   protected void countProximityPosition(int i)
    283   {
    284   	// Note that in the case of a UnionChildIterator, this may be a
    285   	// static object and so m_proximityPositions may indeed be null!
    286   	int[] pp = m_proximityPositions;
    287     if ((null != pp) && (i < pp.length))
    288       pp[i]++;
    289   }
    290 
    291   /**
    292    * Tells if this is a reverse axes.
    293    *
    294    * @return false, unless a derived class overrides.
    295    */
    296   public boolean isReverseAxes()
    297   {
    298     return false;
    299   }
    300 
    301   /**
    302    * Get which predicate is executing.
    303    *
    304    * @return The current predicate index, or -1 if no predicate is executing.
    305    */
    306   public int getPredicateIndex()
    307   {
    308     return m_predicateIndex;
    309   }
    310 
    311   /**
    312    * Process the predicates.
    313    *
    314    * @param context The current context node.
    315    * @param xctxt The XPath runtime context.
    316    *
    317    * @return the result of executing the predicate expressions.
    318    *
    319    * @throws javax.xml.transform.TransformerException
    320    */
    321   boolean executePredicates(int context, XPathContext xctxt)
    322           throws javax.xml.transform.TransformerException
    323   {
    324 
    325     int nPredicates = getPredicateCount();
    326     // System.out.println("nPredicates: "+nPredicates);
    327     if (nPredicates == 0)
    328       return true;
    329 
    330     PrefixResolver savedResolver = xctxt.getNamespaceContext();
    331 
    332     try
    333     {
    334       m_predicateIndex = 0;
    335       xctxt.pushSubContextList(this);
    336       xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
    337       xctxt.pushCurrentNode(context);
    338 
    339       for (int i = 0; i < nPredicates; i++)
    340       {
    341         // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
    342         XObject pred = m_predicates[i].execute(xctxt);
    343         // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
    344         // System.out.println("pred.getType(): "+pred.getType());
    345         if (XObject.CLASS_NUMBER == pred.getType())
    346         {
    347           if (DEBUG_PREDICATECOUNTING)
    348           {
    349             System.out.flush();
    350             System.out.println("\n===== start predicate count ========");
    351             System.out.println("m_predicateIndex: " + m_predicateIndex);
    352             // System.out.println("getProximityPosition(m_predicateIndex): "
    353             //                   + getProximityPosition(m_predicateIndex));
    354             System.out.println("pred.num(): " + pred.num());
    355           }
    356 
    357           int proxPos = this.getProximityPosition(m_predicateIndex);
    358           int predIndex = (int) pred.num();
    359           if (proxPos != predIndex)
    360           {
    361             if (DEBUG_PREDICATECOUNTING)
    362             {
    363               System.out.println("\nnode context: "+nodeToString(context));
    364               System.out.println("index predicate is false: "+proxPos);
    365               System.out.println("\n===== end predicate count ========");
    366             }
    367             return false;
    368           }
    369           else if (DEBUG_PREDICATECOUNTING)
    370           {
    371             System.out.println("\nnode context: "+nodeToString(context));
    372             System.out.println("index predicate is true: "+proxPos);
    373             System.out.println("\n===== end predicate count ========");
    374           }
    375 
    376           // If there is a proximity index that will not change during the
    377           // course of itteration, then we know there can be no more true
    378           // occurances of this predicate, so flag that we're done after
    379           // this.
    380           //
    381           // bugzilla 14365
    382           // We can't set m_foundLast = true unless we're sure that -all-
    383           // remaining parameters are stable, or else last() fails. Fixed so
    384           // only sets m_foundLast if on the last predicate
    385           if(m_predicates[i].isStableNumber() && i == nPredicates - 1)
    386           {
    387             m_foundLast = true;
    388           }
    389         }
    390         else if (!pred.bool())
    391           return false;
    392 
    393         countProximityPosition(++m_predicateIndex);
    394       }
    395     }
    396     finally
    397     {
    398       xctxt.popCurrentNode();
    399       xctxt.popNamespaceContext();
    400       xctxt.popSubContextList();
    401       m_predicateIndex = -1;
    402     }
    403 
    404     return true;
    405   }
    406 
    407   /**
    408    * This function is used to fixup variables from QNames to stack frame
    409    * indexes at stylesheet build time.
    410    * @param vars List of QNames that correspond to variables.  This list
    411    * should be searched backwards for the first qualified name that
    412    * corresponds to the variable reference qname.  The position of the
    413    * QName in the vector from the start of the vector will be its position
    414    * in the stack frame (but variables above the globalsTop value will need
    415    * to be offset to the current stack frame).
    416    */
    417   public void fixupVariables(java.util.Vector vars, int globalsSize)
    418   {
    419     super.fixupVariables(vars, globalsSize);
    420 
    421     int nPredicates = getPredicateCount();
    422 
    423     for (int i = 0; i < nPredicates; i++)
    424     {
    425       m_predicates[i].fixupVariables(vars, globalsSize);
    426     }
    427   }
    428 
    429 
    430   /**
    431    * Diagnostics.
    432    *
    433    * @param n Node to give diagnostic information about, or null.
    434    *
    435    * @return Informative string about the argument.
    436    */
    437   protected String nodeToString(int n)
    438   {
    439     if(DTM.NULL != n)
    440     {
    441       DTM dtm = m_lpi.getXPathContext().getDTM(n);
    442       return dtm.getNodeName(n) + "{" + (n+1) + "}";
    443     }
    444     else
    445     {
    446       return "null";
    447     }
    448   }
    449 
    450   //=============== NodeFilter Implementation ===============
    451 
    452   /**
    453    *  Test whether a specified node is visible in the logical view of a
    454    * TreeWalker or NodeIterator. This function will be called by the
    455    * implementation of TreeWalker and NodeIterator; it is not intended to
    456    * be called directly from user code.
    457    * @param n  The node to check to see if it passes the filter or not.
    458    * @return  a constant to determine whether the node is accepted,
    459    *   rejected, or skipped, as defined  above .
    460    */
    461   public short acceptNode(int n)
    462   {
    463 
    464     XPathContext xctxt = m_lpi.getXPathContext();
    465 
    466     try
    467     {
    468       xctxt.pushCurrentNode(n);
    469 
    470       XObject score = execute(xctxt, n);
    471 
    472       // System.out.println("\n::acceptNode - score: "+score.num()+"::");
    473       if (score != NodeTest.SCORE_NONE)
    474       {
    475         if (getPredicateCount() > 0)
    476         {
    477           countProximityPosition(0);
    478 
    479           if (!executePredicates(n, xctxt))
    480             return DTMIterator.FILTER_SKIP;
    481         }
    482 
    483         return DTMIterator.FILTER_ACCEPT;
    484       }
    485     }
    486     catch (javax.xml.transform.TransformerException se)
    487     {
    488 
    489       // TODO: Fix this.
    490       throw new RuntimeException(se.getMessage());
    491     }
    492     finally
    493     {
    494       xctxt.popCurrentNode();
    495     }
    496 
    497     return DTMIterator.FILTER_SKIP;
    498   }
    499 
    500 
    501   /**
    502    * Get the owning location path iterator.
    503    *
    504    * @return the owning location path iterator, which should not be null.
    505    */
    506   public LocPathIterator getLocPathIterator()
    507   {
    508     return m_lpi;
    509   }
    510 
    511   /**
    512    * Set the location path iterator owner for this walker.  Besides
    513    * initialization, this function is called during cloning operations.
    514    *
    515    * @param li non-null reference to the owning location path iterator.
    516    */
    517   public void setLocPathIterator(LocPathIterator li)
    518   {
    519     m_lpi = li;
    520     if(this != li)
    521       li.exprSetParent(this);
    522   }
    523 
    524   /**
    525    * Tell if this expression or it's subexpressions can traverse outside
    526    * the current subtree.
    527    *
    528    * @return true if traversal outside the context node's subtree can occur.
    529    */
    530    public boolean canTraverseOutsideSubtree()
    531    {
    532     int n = getPredicateCount();
    533     for (int i = 0; i < n; i++)
    534     {
    535       if(getPredicate(i).canTraverseOutsideSubtree())
    536         return true;
    537     }
    538     return false;
    539    }
    540 
    541 	/**
    542 	 * This will traverse the heararchy, calling the visitor for
    543 	 * each member.  If the called visitor method returns
    544 	 * false, the subtree should not be called.
    545 	 *
    546 	 * @param visitor The visitor whose appropriate method will be called.
    547 	 */
    548 	public void callPredicateVisitors(XPathVisitor visitor)
    549 	{
    550 	  if (null != m_predicates)
    551 	    {
    552 	    int n = m_predicates.length;
    553 	    for (int i = 0; i < n; i++)
    554 	      {
    555 	      ExpressionOwner predOwner = new PredOwner(i);
    556 	      if (visitor.visitPredicate(predOwner, m_predicates[i]))
    557 	        {
    558 	        m_predicates[i].callVisitors(predOwner, visitor);
    559 	      }
    560 
    561 	    }
    562 	  }
    563 	}
    564 
    565     /**
    566      * @see Expression#deepEquals(Expression)
    567      */
    568     public boolean deepEquals(Expression expr)
    569     {
    570       if (!super.deepEquals(expr))
    571             return false;
    572 
    573       PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
    574       if (null != m_predicates)
    575       {
    576 
    577         int n = m_predicates.length;
    578         if ((null == pnt.m_predicates) || (pnt.m_predicates.length != n))
    579               return false;
    580         for (int i = 0; i < n; i++)
    581         {
    582           if (!m_predicates[i].deepEquals(pnt.m_predicates[i]))
    583           	return false;
    584         }
    585       }
    586       else if (null != pnt.m_predicates)
    587               return false;
    588 
    589       return true;
    590     }
    591 
    592   /** This is true if nextNode returns null. */
    593   transient protected boolean m_foundLast = false;
    594 
    595   /** The owning location path iterator.
    596    *  @serial */
    597   protected LocPathIterator m_lpi;
    598 
    599   /**
    600    * Which predicate we are executing.
    601    */
    602   transient int m_predicateIndex = -1;
    603 
    604   /** The list of predicate expressions. Is static and does not need
    605    *  to be deep cloned.
    606    *  @serial
    607    */
    608   private Expression[] m_predicates;
    609 
    610   /**
    611    * An array of counts that correspond to the number
    612    * of predicates the step contains.
    613    */
    614   transient protected int[] m_proximityPositions;
    615 
    616   /** If true, diagnostic messages about predicate execution will be posted.  */
    617   static final boolean DEBUG_PREDICATECOUNTING = false;
    618 
    619   class PredOwner implements ExpressionOwner
    620   {
    621   	int m_index;
    622 
    623   	PredOwner(int index)
    624   	{
    625   		m_index = index;
    626   	}
    627 
    628     /**
    629      * @see ExpressionOwner#getExpression()
    630      */
    631     public Expression getExpression()
    632     {
    633       return m_predicates[m_index];
    634     }
    635 
    636 
    637     /**
    638      * @see ExpressionOwner#setExpression(Expression)
    639      */
    640     public void setExpression(Expression exp)
    641     {
    642     	exp.exprSetParent(PredicatedNodeTest.this);
    643     	m_predicates[m_index] = exp;
    644     }
    645   }
    646 
    647 }
    648