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