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