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.xalan.internal.res.XSLMessages;
  24 import com.sun.org.apache.xml.internal.dtm.DTM;
  25 import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser;
  26 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
  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.res.XPATHErrorResources;
  33 import java.util.List;
  34 
  35 /**
  36  * Serves as common interface for axes Walkers, and stores common
  37  * state variables.
  38  *
  39  * @LastModified: Oct 2017
  40  */
  41 public class AxesWalker extends PredicatedNodeTest
  42         implements Cloneable, PathComponent, ExpressionOwner
  43 {
  44     static final long serialVersionUID = -2966031951306601247L;
  45 
  46   /**
  47    * Construct an AxesWalker using a LocPathIterator.
  48    *
  49    * @param locPathIterator non-null reference to the parent iterator.
  50    */
  51   public AxesWalker(LocPathIterator locPathIterator, int axis)
  52   {
  53     super( locPathIterator );
  54     m_axis = axis;
  55   }
  56 
  57   public final WalkingIterator wi()
  58   {
  59     return (WalkingIterator)m_lpi;
  60   }
  61 
  62   /**
  63    * Initialize an AxesWalker during the parse of the XPath expression.
  64    *
  65    * @param compiler The Compiler object that has information about this
  66    *                 walker in the op map.
  67    * @param opPos The op code position of this location step.
  68    * @param stepType  The type of location step.
  69    *
  70    * @throws javax.xml.transform.TransformerException
  71    */
  72   public void init(Compiler compiler, int opPos, int stepType)
  73           throws javax.xml.transform.TransformerException
  74   {
  75 
  76     initPredicateInfo(compiler, opPos);
  77 
  78     // int testType = compiler.getOp(nodeTestOpPos);
  79   }
  80 
  81   /**
  82    * Get a cloned AxesWalker.
  83    *
  84    * @return A new AxesWalker that can be used without mutating this one.
  85    *
  86    * @throws CloneNotSupportedException
  87    */
  88   public Object clone() throws CloneNotSupportedException
  89   {
  90     // Do not access the location path itterator during this operation!
  91 
  92     AxesWalker clone = (AxesWalker) super.clone();
  93 
  94     //clone.setCurrentNode(clone.m_root);
  95 
  96     // clone.m_isFresh = true;
  97 
  98     return clone;
  99   }
 100 
 101   /**
 102    * Do a deep clone of this walker, including next and previous walkers.
 103    * If the this AxesWalker is on the clone list, don't clone but
 104    * return the already cloned version.
 105    *
 106    * @param cloneOwner non-null reference to the cloned location path
 107    *                   iterator to which this clone will be added.
 108    * @param cloneList non-null vector of sources in odd elements, and the
 109    *                  corresponding clones in even vectors.
 110    *
 111    * @return non-null clone, which may be a new clone, or may be a clone
 112    *         contained on the cloneList.
 113    */
 114   AxesWalker cloneDeep(WalkingIterator cloneOwner, List<AxesWalker> cloneList)
 115      throws CloneNotSupportedException
 116   {
 117     AxesWalker clone = findClone(this, cloneList);
 118     if(null != clone)
 119       return clone;
 120     clone = (AxesWalker)this.clone();
 121     clone.setLocPathIterator(cloneOwner);
 122     if(null != cloneList)
 123     {
 124       cloneList.add(this);
 125       cloneList.add(clone);
 126     }
 127 
 128     if(wi().m_lastUsedWalker == this)
 129       cloneOwner.m_lastUsedWalker = clone;
 130 
 131     if(null != m_nextWalker)
 132       clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner, cloneList);
 133 
 134     // If you don't check for the cloneList here, you'll go into an
 135     // recursive infinate loop.
 136     if(null != cloneList)
 137     {
 138       if(null != m_prevWalker)
 139         clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner, cloneList);
 140     }
 141     else
 142     {
 143       if(null != m_nextWalker)
 144         clone.m_nextWalker.m_prevWalker = clone;
 145     }
 146     return clone;
 147   }
 148 
 149   /**
 150    * Find a clone that corresponds to the key argument.
 151    *
 152    * @param key The original AxesWalker for which there may be a clone.
 153    * @param cloneList vector of sources in odd elements, and the
 154    *                  corresponding clones in even vectors, may be null.
 155    *
 156    * @return A clone that corresponds to the key, or null if key not found.
 157    */
 158   static AxesWalker findClone(AxesWalker key, List<AxesWalker> cloneList)
 159   {
 160     if(null != cloneList)
 161     {
 162       // First, look for clone on list.
 163       int n = cloneList.size();
 164       for (int i = 0; i < n; i+=2)
 165       {
 166         if(key == cloneList.get(i))
 167           return cloneList.get(i+1);
 168       }
 169     }
 170     return null;
 171   }
 172 
 173   /**
 174    * Detaches the walker from the set which it iterated over, releasing
 175    * any computational resources and placing the iterator in the INVALID
 176    * state.
 177    */
 178   public void detach()
 179   {
 180         m_currentNode = DTM.NULL;
 181         m_dtm = null;
 182         m_traverser = null;
 183         m_isFresh = true;
 184         m_root = DTM.NULL;
 185   }
 186 
 187   //=============== TreeWalker Implementation ===============
 188 
 189   /**
 190    * The root node of the TreeWalker, as specified in setRoot(int root).
 191    * Note that this may actually be below the current node.
 192    *
 193    * @return The context node of the step.
 194    */
 195   public int getRoot()
 196   {
 197     return m_root;
 198   }
 199 
 200   /**
 201    * Get the analysis bits for this walker, as defined in the WalkerFactory.
 202    * @return One of WalkerFactory#BIT_DESCENDANT, etc.
 203    */
 204   public int getAnalysisBits()
 205   {
 206         int axis = getAxis();
 207         int bit = WalkerFactory.getAnalysisBitFromAxes(axis);
 208         return bit;
 209   }
 210 
 211   /**
 212    * Set the root node of the TreeWalker.
 213    * (Not part of the DOM2 TreeWalker interface).
 214    *
 215    * @param root The context node of this step.
 216    */
 217   public void setRoot(int root)
 218   {
 219     // %OPT% Get this directly from the lpi.
 220     XPathContext xctxt = wi().getXPathContext();
 221     m_dtm = xctxt.getDTM(root);
 222     m_traverser = m_dtm.getAxisTraverser(m_axis);
 223     m_isFresh = true;
 224     m_foundLast = false;
 225     m_root = root;
 226     m_currentNode = root;
 227 
 228     if (DTM.NULL == root)
 229     {
 230       throw new RuntimeException(
 231         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL, null)); //"\n !!!! Error! Setting the root of a walker to null!!!");
 232     }
 233 
 234     resetProximityPositions();
 235   }
 236 
 237   /**
 238    * The node at which the TreeWalker is currently positioned.
 239    * <br> The value must not be null. Alterations to the DOM tree may cause
 240    * the current node to no longer be accepted by the TreeWalker's
 241    * associated filter. currentNode may also be explicitly set to any node,
 242    * whether or not it is within the subtree specified by the root node or
 243    * would be accepted by the filter and whatToShow flags. Further
 244    * traversal occurs relative to currentNode even if it is not part of the
 245    * current view by applying the filters in the requested direction (not
 246    * changing currentNode where no traversal is possible).
 247    *
 248    * @return The node at which the TreeWalker is currently positioned, only null
 249    * if setRoot has not yet been called.
 250    */
 251   public final int getCurrentNode()
 252   {
 253     return m_currentNode;
 254   }
 255 
 256   /**
 257    * Set the next walker in the location step chain.
 258    *
 259    *
 260    * @param walker Reference to AxesWalker derivative, or may be null.
 261    */
 262   public void setNextWalker(AxesWalker walker)
 263   {
 264     m_nextWalker = walker;
 265   }
 266 
 267   /**
 268    * Get the next walker in the location step chain.
 269    *
 270    *
 271    * @return Reference to AxesWalker derivative, or null.
 272    */
 273   public AxesWalker getNextWalker()
 274   {
 275     return m_nextWalker;
 276   }
 277 
 278   /**
 279    * Set or clear the previous walker reference in the location step chain.
 280    *
 281    *
 282    * @param walker Reference to previous walker reference in the location
 283    *               step chain, or null.
 284    */
 285   public void setPrevWalker(AxesWalker walker)
 286   {
 287     m_prevWalker = walker;
 288   }
 289 
 290   /**
 291    * Get the previous walker reference in the location step chain.
 292    *
 293    *
 294    * @return Reference to previous walker reference in the location
 295    *               step chain, or null.
 296    */
 297   public AxesWalker getPrevWalker()
 298   {
 299     return m_prevWalker;
 300   }
 301 
 302   /**
 303    * This is simply a way to bottle-neck the return of the next node, for
 304    * diagnostic purposes.
 305    *
 306    * @param n Node to return, or null.
 307    *
 308    * @return The argument.
 309    */
 310   private int returnNextNode(int n)
 311   {
 312 
 313     return n;
 314   }
 315 
 316   /**
 317    * Get the next node in document order on the axes.
 318    *
 319    * @return the next node in document order on the axes, or null.
 320    */
 321   protected int getNextNode()
 322   {
 323     if (m_foundLast)
 324       return DTM.NULL;
 325 
 326     if (m_isFresh)
 327     {
 328       m_currentNode = m_traverser.first(m_root);
 329       m_isFresh = false;
 330     }
 331     // I shouldn't have to do this the check for current node, I think.
 332     // numbering\numbering24.xsl fails if I don't do this.  I think
 333     // it occurs as the walkers are backing up. -sb
 334     else if(DTM.NULL != m_currentNode)
 335     {
 336       m_currentNode = m_traverser.next(m_root, m_currentNode);
 337     }
 338 
 339     if (DTM.NULL == m_currentNode)
 340       this.m_foundLast = true;
 341 
 342     return m_currentNode;
 343   }
 344 
 345   /**
 346    *  Moves the <code>TreeWalker</code> to the next visible node in document
 347    * order relative to the current node, and returns the new node. If the
 348    * current node has no next node,  or if the search for nextNode attempts
 349    * to step upward from the TreeWalker's root node, returns
 350    * <code>null</code> , and retains the current node.
 351    * @return  The new node, or <code>null</code> if the current node has no
 352    *   next node  in the TreeWalker's logical view.
 353    */
 354   public int nextNode()
 355   {
 356     int nextNode = DTM.NULL;
 357     AxesWalker walker = wi().getLastUsedWalker();
 358 
 359     while (true)
 360     {
 361       if (null == walker)
 362         break;
 363 
 364       nextNode = walker.getNextNode();
 365 
 366       if (DTM.NULL == nextNode)
 367       {
 368 
 369         walker = walker.m_prevWalker;
 370       }
 371       else
 372       {
 373         if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT)
 374         {
 375           continue;
 376         }
 377 
 378         if (null == walker.m_nextWalker)
 379         {
 380           wi().setLastUsedWalker(walker);
 381 
 382           // return walker.returnNextNode(nextNode);
 383           break;
 384         }
 385         else
 386         {
 387           AxesWalker prev = walker;
 388 
 389           walker = walker.m_nextWalker;
 390 
 391           walker.setRoot(nextNode);
 392 
 393           walker.m_prevWalker = prev;
 394 
 395           continue;
 396         }
 397       }  // if(null != nextNode)
 398     }  // while(null != walker)
 399 
 400     return nextNode;
 401   }
 402 
 403   //============= End TreeWalker Implementation =============
 404 
 405   /**
 406    * Get the index of the last node that can be itterated to.
 407    *
 408    *
 409    * @param xctxt XPath runtime context.
 410    *
 411    * @return the index of the last node that can be itterated to.
 412    */
 413   public int getLastPos(XPathContext xctxt)
 414   {
 415 
 416     int pos = getProximityPosition();
 417 
 418     AxesWalker walker;
 419 
 420     try
 421     {
 422       walker = (AxesWalker) clone();
 423     }
 424     catch (CloneNotSupportedException cnse)
 425     {
 426       return -1;
 427     }
 428 
 429     walker.setPredicateCount(m_predicateIndex);
 430     walker.setNextWalker(null);
 431     walker.setPrevWalker(null);
 432 
 433     WalkingIterator lpi = wi();
 434     AxesWalker savedWalker = lpi.getLastUsedWalker();
 435 
 436     try
 437     {
 438       lpi.setLastUsedWalker(walker);
 439 
 440       int next;
 441 
 442       while (DTM.NULL != (next = walker.nextNode()))
 443       {
 444         pos++;
 445       }
 446 
 447       // TODO: Should probably save this in the iterator.
 448     }
 449     finally
 450     {
 451       lpi.setLastUsedWalker(savedWalker);
 452     }
 453 
 454     // System.out.println("pos: "+pos);
 455     return pos;
 456   }
 457 
 458   //============= State Data =============
 459 
 460   /**
 461    * The DTM for the root.  This can not be used, or must be changed,
 462    * for the filter walker, or any walker that can have nodes
 463    * from multiple documents.
 464    * Never, ever, access this value without going through getDTM(int node).
 465    */
 466   private DTM m_dtm;
 467 
 468   /**
 469    * Set the DTM for this walker.
 470    *
 471    * @param dtm Non-null reference to a DTM.
 472    */
 473   public void setDefaultDTM(DTM dtm)
 474   {
 475     m_dtm = dtm;
 476   }
 477 
 478   /**
 479    * Get the DTM for this walker.
 480    *
 481    * @return Non-null reference to a DTM.
 482    */
 483   public DTM getDTM(int node)
 484   {
 485     //
 486     return wi().getXPathContext().getDTM(node);
 487   }
 488 
 489   /**
 490    * Returns true if all the nodes in the iteration well be returned in document
 491    * order.
 492    * Warning: This can only be called after setRoot has been called!
 493    *
 494    * @return true as a default.
 495    */
 496   public boolean isDocOrdered()
 497   {
 498     return true;
 499   }
 500 
 501   /**
 502    * Returns the axis being iterated, if it is known.
 503    *
 504    * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
 505    * types.
 506    */
 507   public int getAxis()
 508   {
 509     return m_axis;
 510   }
 511 
 512   /**
 513    * This will traverse the heararchy, calling the visitor for
 514    * each member.  If the called visitor method returns
 515    * false, the subtree should not be called.
 516    *
 517    * @param owner The owner of the visitor, where that path may be
 518    *              rewritten if needed.
 519    * @param visitor The visitor whose appropriate method will be called.
 520    */
 521   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
 522   {
 523         if(visitor.visitStep(owner, this))
 524         {
 525                 callPredicateVisitors(visitor);
 526                 if(null != m_nextWalker)
 527                 {
 528                         m_nextWalker.callVisitors(this, visitor);
 529                 }
 530         }
 531   }
 532 
 533   /**
 534    * @see ExpressionOwner#getExpression()
 535    */
 536   public Expression getExpression()
 537   {
 538     return m_nextWalker;
 539   }
 540 
 541   /**
 542    * @see ExpressionOwner#setExpression(Expression)
 543    */
 544   public void setExpression(Expression exp)
 545   {
 546         exp.exprSetParent(this);
 547         m_nextWalker = (AxesWalker)exp;
 548   }
 549 
 550     /**
 551      * @see Expression#deepEquals(Expression)
 552      */
 553     public boolean deepEquals(Expression expr)
 554     {
 555       if (!super.deepEquals(expr))
 556                 return false;
 557 
 558       AxesWalker walker = (AxesWalker)expr;
 559       if(this.m_axis != walker.m_axis)
 560         return false;
 561 
 562       return true;
 563     }
 564 
 565   /**
 566    *  The root node of the TreeWalker, as specified when it was created.
 567    */
 568   transient int m_root = DTM.NULL;
 569 
 570   /**
 571    *  The node at which the TreeWalker is currently positioned.
 572    */
 573   private transient int m_currentNode = DTM.NULL;
 574 
 575   /** True if an itteration has not begun.  */
 576   transient boolean m_isFresh;
 577 
 578   /** The next walker in the location step chain.
 579    *  @serial  */
 580   protected AxesWalker m_nextWalker;
 581 
 582   /** The previous walker in the location step chain, or null.
 583    *  @serial   */
 584   AxesWalker m_prevWalker;
 585 
 586   /** The traversal axis from where the nodes will be filtered. */
 587   protected int m_axis = -1;
 588 
 589   /** The DTM inner traversal class, that corresponds to the super axis. */
 590   protected DTMAxisTraverser m_traverser;
 591 }