001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: PredicatedNodeTest.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath.axes;
022    
023    import org.apache.xml.dtm.DTM;
024    import org.apache.xml.dtm.DTMIterator;
025    import org.apache.xml.utils.PrefixResolver;
026    import org.apache.xpath.Expression;
027    import org.apache.xpath.ExpressionOwner;
028    import org.apache.xpath.XPathContext;
029    import org.apache.xpath.XPathVisitor;
030    import org.apache.xpath.compiler.Compiler;
031    import org.apache.xpath.objects.XObject;
032    import org.apache.xpath.patterns.NodeTest;
033    
034    public abstract class PredicatedNodeTest extends NodeTest implements SubContextList
035    {
036        static final long serialVersionUID = -6193530757296377351L;
037    
038      /**
039       * Construct an AxesWalker using a LocPathIterator.
040       *
041       * @param locPathIterator non-null reference to the parent iterator.
042       */
043      PredicatedNodeTest(LocPathIterator locPathIterator)
044      {
045        m_lpi = locPathIterator;
046      }
047      
048      /**
049       * Construct an AxesWalker.  The location path iterator will have to be set
050       * before use.
051       */
052      PredicatedNodeTest()
053      {
054      }
055      
056      /**
057       * Read the object from a serialization stream.
058       *
059       * @param stream Input stream to read from
060       *
061       * @throws java.io.IOException
062       * @throws javax.xml.transform.TransformerException
063       */
064      private void readObject(java.io.ObjectInputStream stream)
065              throws java.io.IOException, javax.xml.transform.TransformerException
066      {
067        try
068        {
069          stream.defaultReadObject();
070          m_predicateIndex = -1;
071          resetProximityPositions();
072        }
073        catch (ClassNotFoundException cnfe)
074        {
075          throw new javax.xml.transform.TransformerException(cnfe);
076        }
077      }
078      
079      /**
080       * Get a cloned PrdicatedNodeTest.
081       *
082       * @return A new PredicatedNodeTest that can be used without mutating this one.
083       *
084       * @throws CloneNotSupportedException
085       */
086      public Object clone() throws CloneNotSupportedException
087      {
088        // Do not access the location path itterator during this operation!
089        
090        PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();
091    
092        if ((null != this.m_proximityPositions)
093                && (this.m_proximityPositions == clone.m_proximityPositions))
094        {
095          clone.m_proximityPositions = new int[this.m_proximityPositions.length];
096    
097          System.arraycopy(this.m_proximityPositions, 0,
098                           clone.m_proximityPositions, 0,
099                           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    }