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.Axis;
  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.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.XPathVisitor;
  30 import com.sun.org.apache.xpath.internal.compiler.Compiler;
  31 import com.sun.org.apache.xpath.internal.compiler.OpCodes;
  32 import com.sun.org.apache.xpath.internal.compiler.OpMap;
  33 import java.util.List;
  34 
  35 /**
  36  * This class extends NodeSetDTM, which implements DTMIterator,
  37  * and fetches nodes one at a time in document order based on a XPath
  38  * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
  39  * As each node is iterated via nextNode(), the node is also stored
  40  * in the NodeVector, so that previousNode() can easily be done.
  41  * @xsl.usage advanced
  42  * @LastModified: Oct 2017
  43  */
  44 public class UnionPathIterator extends LocPathIterator
  45         implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
  46 {
  47     static final long serialVersionUID = -3910351546843826781L;
  48 
  49   /**
  50    * Constructor to create an instance which you can add location paths to.
  51    */
  52   public UnionPathIterator()
  53   {
  54 
  55     super();
  56 
  57     // m_mutable = false;
  58     // m_cacheNodes = false;
  59     m_iterators = null;
  60     m_exprs = null;
  61   }
  62 
  63   /**
  64    * Initialize the context values for this expression
  65    * after it is cloned.
  66    *
  67    * @param context The XPath runtime context for this
  68    * transformation.
  69    */
  70   public void setRoot(int context, Object environment)
  71   {
  72     super.setRoot(context, environment);
  73 
  74     try
  75     {
  76       if (null != m_exprs)
  77       {
  78         int n = m_exprs.length;
  79         DTMIterator newIters[] = new DTMIterator[n];
  80 
  81         for (int i = 0; i < n; i++)
  82         {
  83           DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
  84           newIters[i] = iter;
  85           iter.nextNode();
  86         }
  87         m_iterators = newIters;
  88       }
  89     }
  90     catch(Exception e)
  91     {
  92       throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
  93     }
  94   }
  95 
  96   /**
  97    * Add an iterator to the union list.
  98    *
  99    * @param expr non-null reference to a location path iterator.
 100    */
 101   public void addIterator(DTMIterator expr)
 102   {
 103 
 104     // Increase array size by only 1 at a time.  Fix this
 105     // if it looks to be a problem.
 106     if (null == m_iterators)
 107     {
 108       m_iterators = new DTMIterator[1];
 109       m_iterators[0] = expr;
 110     }
 111     else
 112     {
 113       DTMIterator[] exprs = m_iterators;
 114       int len = m_iterators.length;
 115 
 116       m_iterators = new DTMIterator[len + 1];
 117 
 118       System.arraycopy(exprs, 0, m_iterators, 0, len);
 119 
 120       m_iterators[len] = expr;
 121     }
 122     expr.nextNode();
 123     if(expr instanceof Expression)
 124         ((Expression)expr).exprSetParent(this);
 125   }
 126 
 127   /**
 128    *  Detaches the iterator from the set which it iterated over, releasing
 129    * any computational resources and placing the iterator in the INVALID
 130    * state. After<code>detach</code> has been invoked, calls to
 131    * <code>nextNode</code> or<code>previousNode</code> will raise the
 132    * exception INVALID_STATE_ERR.
 133    */
 134   public void detach()
 135   {
 136           if(m_allowDetach && null != m_iterators){
 137                   int n = m_iterators.length;
 138                   for(int i = 0; i < n; i++)
 139                   {
 140                           m_iterators[i].detach();
 141                   }
 142                   m_iterators = null;
 143           }
 144   }
 145 
 146 
 147   /**
 148    * Create a UnionPathIterator object, including creation
 149    * of location path iterators from the opcode list, and call back
 150    * into the Compiler to create predicate expressions.
 151    *
 152    * @param compiler The Compiler which is creating
 153    * this expression.
 154    * @param opPos The position of this iterator in the
 155    * opcode list from the compiler.
 156    *
 157    * @throws javax.xml.transform.TransformerException
 158    */
 159   public UnionPathIterator(Compiler compiler, int opPos)
 160           throws javax.xml.transform.TransformerException
 161   {
 162 
 163     super();
 164 
 165     opPos = OpMap.getFirstChildPos(opPos);
 166 
 167     loadLocationPaths(compiler, opPos, 0);
 168   }
 169 
 170   /**
 171    * This will return an iterator capable of handling the union of paths given.
 172    *
 173    * @param compiler The Compiler which is creating
 174    * this expression.
 175    * @param opPos The position of this iterator in the
 176    * opcode list from the compiler.
 177    *
 178    * @return Object that is derived from LocPathIterator.
 179    *
 180    * @throws javax.xml.transform.TransformerException
 181    */
 182   public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
 183           throws javax.xml.transform.TransformerException
 184   {
 185         // For the moment, I'm going to first create a full UnionPathIterator, and
 186         // then see if I can reduce it to a UnionChildIterator.  It would obviously
 187         // be more effecient to just test for the conditions for a UnionChildIterator,
 188         // and then create that directly.
 189         UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
 190         int nPaths = upi.m_exprs.length;
 191         boolean isAllChildIterators = true;
 192         for(int i = 0; i < nPaths; i++)
 193         {
 194                 LocPathIterator lpi = upi.m_exprs[i];
 195 
 196                 if(lpi.getAxis() != Axis.CHILD)
 197                 {
 198                         isAllChildIterators = false;
 199                         break;
 200                 }
 201                 else
 202                 {
 203                         // check for positional predicates or position function, which won't work.
 204                         if(HasPositionalPredChecker.check(lpi))
 205                         {
 206                                 isAllChildIterators = false;
 207                                 break;
 208                         }
 209                 }
 210         }
 211         if(isAllChildIterators)
 212         {
 213                 UnionChildIterator uci = new UnionChildIterator();
 214 
 215                 for(int i = 0; i < nPaths; i++)
 216                 {
 217                         PredicatedNodeTest lpi = upi.m_exprs[i];
 218                         // I could strip the lpi down to a pure PredicatedNodeTest, but
 219                         // I don't think it's worth it.  Note that the test can be used
 220                         // as a static object... so it doesn't have to be cloned.
 221                         uci.addNodeTest(lpi);
 222                 }
 223                 return uci;
 224 
 225         }
 226         else
 227                 return upi;
 228   }
 229 
 230   /**
 231    * Get the analysis bits for this walker, as defined in the WalkerFactory.
 232    * @return One of WalkerFactory#BIT_DESCENDANT, etc.
 233    */
 234   public int getAnalysisBits()
 235   {
 236     int bits = 0;
 237 
 238     if (m_exprs != null)
 239     {
 240       int n = m_exprs.length;
 241 
 242       for (int i = 0; i < n; i++)
 243       {
 244         int bit = m_exprs[i].getAnalysisBits();
 245         bits |= bit;
 246       }
 247     }
 248 
 249     return bits;
 250   }
 251 
 252   /**
 253    * Read the object from a serialization stream.
 254    *
 255    * @param stream Input stream to read from
 256    *
 257    * @throws java.io.IOException
 258    * @throws javax.xml.transform.TransformerException
 259    */
 260   private void readObject(java.io.ObjectInputStream stream)
 261           throws java.io.IOException, javax.xml.transform.TransformerException
 262   {
 263     try
 264     {
 265       stream.defaultReadObject();
 266       m_clones =  new IteratorPool(this);
 267     }
 268     catch (ClassNotFoundException cnfe)
 269     {
 270       throw new javax.xml.transform.TransformerException(cnfe);
 271     }
 272   }
 273 
 274   /**
 275    * Get a cloned LocPathIterator that holds the same
 276    * position as this iterator.
 277    *
 278    * @return A clone of this iterator that holds the same node position.
 279    *
 280    * @throws CloneNotSupportedException
 281    */
 282   public Object clone() throws CloneNotSupportedException
 283   {
 284 
 285     UnionPathIterator clone = (UnionPathIterator) super.clone();
 286     if (m_iterators != null)
 287     {
 288       int n = m_iterators.length;
 289 
 290       clone.m_iterators = new DTMIterator[n];
 291 
 292       for (int i = 0; i < n; i++)
 293       {
 294         clone.m_iterators[i] = (DTMIterator)m_iterators[i].clone();
 295       }
 296     }
 297 
 298     return clone;
 299   }
 300 
 301 
 302   /**
 303    * Create a new location path iterator.
 304    *
 305    * @param compiler The Compiler which is creating
 306    * this expression.
 307    * @param opPos The position of this iterator in the
 308    *
 309    * @return New location path iterator.
 310    *
 311    * @throws javax.xml.transform.TransformerException
 312    */
 313   protected LocPathIterator createDTMIterator(
 314           Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
 315   {
 316     LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos,
 317                                       (compiler.getLocationPathDepth() <= 0));
 318     return lpi;
 319   }
 320 
 321   /**
 322    * Initialize the location path iterators.  Recursive.
 323    *
 324    * @param compiler The Compiler which is creating
 325    * this expression.
 326    * @param opPos The position of this iterator in the
 327    * opcode list from the compiler.
 328    * @param count The insert position of the iterator.
 329    *
 330    * @throws javax.xml.transform.TransformerException
 331    */
 332   protected void loadLocationPaths(Compiler compiler, int opPos, int count)
 333           throws javax.xml.transform.TransformerException
 334   {
 335 
 336     // TODO: Handle unwrapped FilterExpr
 337     int steptype = compiler.getOp(opPos);
 338 
 339     if (steptype == OpCodes.OP_LOCATIONPATH)
 340     {
 341       loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
 342 
 343       m_exprs[count] = createDTMIterator(compiler, opPos);
 344       m_exprs[count].exprSetParent(this);
 345     }
 346     else
 347     {
 348 
 349       // Have to check for unwrapped functions, which the LocPathIterator
 350       // doesn't handle.
 351       switch (steptype)
 352       {
 353       case OpCodes.OP_VARIABLE :
 354       case OpCodes.OP_EXTFUNCTION :
 355       case OpCodes.OP_FUNCTION :
 356       case OpCodes.OP_GROUP :
 357         loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
 358 
 359         WalkingIterator iter =
 360           new WalkingIterator(compiler.getNamespaceContext());
 361         iter.exprSetParent(this);
 362 
 363         if(compiler.getLocationPathDepth() <= 0)
 364           iter.setIsTopLevel(true);
 365 
 366         iter.m_firstWalker = new com.sun.org.apache.xpath.internal.axes.FilterExprWalker(iter);
 367 
 368         iter.m_firstWalker.init(compiler, opPos, steptype);
 369 
 370         m_exprs[count] = iter;
 371         break;
 372       default :
 373         m_exprs = new LocPathIterator[count];
 374       }
 375     }
 376   }
 377 
 378   /**
 379    *  Returns the next node in the set and advances the position of the
 380    * iterator in the set. After a DTMIterator is created, the first call
 381    * to nextNode() returns the first node in the set.
 382    * @return  The next <code>Node</code> in the set being iterated over, or
 383    *   <code>null</code> if there are no more members in that set.
 384    */
 385   public int nextNode()
 386   {
 387         if(m_foundLast)
 388                 return DTM.NULL;
 389 
 390     // Loop through the iterators getting the current fetched
 391     // node, and get the earliest occuring in document order
 392     int earliestNode = DTM.NULL;
 393 
 394     if (null != m_iterators)
 395     {
 396       int n = m_iterators.length;
 397       int iteratorUsed = -1;
 398 
 399       for (int i = 0; i < n; i++)
 400       {
 401         int node = m_iterators[i].getCurrentNode();
 402 
 403         if (DTM.NULL == node)
 404           continue;
 405         else if (DTM.NULL == earliestNode)
 406         {
 407           iteratorUsed = i;
 408           earliestNode = node;
 409         }
 410         else
 411         {
 412           if (node == earliestNode)
 413           {
 414 
 415             // Found a duplicate, so skip past it.
 416             m_iterators[i].nextNode();
 417           }
 418           else
 419           {
 420             DTM dtm = getDTM(node);
 421 
 422             if (dtm.isNodeAfter(node, earliestNode))
 423             {
 424               iteratorUsed = i;
 425               earliestNode = node;
 426             }
 427           }
 428         }
 429       }
 430 
 431       if (DTM.NULL != earliestNode)
 432       {
 433         m_iterators[iteratorUsed].nextNode();
 434 
 435         incrementCurrentPos();
 436       }
 437       else
 438         m_foundLast = true;
 439     }
 440 
 441     m_lastFetched = earliestNode;
 442 
 443     return earliestNode;
 444   }
 445 
 446   /**
 447    * This function is used to fixup variables from QNames to stack frame
 448    * indexes at stylesheet build time.
 449    * @param vars List of QNames that correspond to variables.  This list
 450    * should be searched backwards for the first qualified name that
 451    * corresponds to the variable reference qname.  The position of the
 452    * QName in the vector from the start of the vector will be its position
 453    * in the stack frame (but variables above the globalsTop value will need
 454    * to be offset to the current stack frame).
 455    */
 456   public void fixupVariables(List<QName> vars, int globalsSize)
 457   {
 458     for (int i = 0; i < m_exprs.length; i++)
 459     {
 460       m_exprs[i].fixupVariables(vars, globalsSize);
 461     }
 462 
 463   }
 464 
 465   /**
 466    * The location path iterators, one for each
 467    * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
 468    * path</a> contained in the union expression.
 469    * @serial
 470    */
 471   protected LocPathIterator[] m_exprs;
 472 
 473 
 474   /**
 475    * The location path iterators, one for each
 476    * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
 477    * path</a> contained in the union expression.
 478    * @serial
 479    */
 480   protected DTMIterator[] m_iterators;
 481 
 482   /**
 483    * Returns the axis being iterated, if it is known.
 484    *
 485    * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
 486    * types.
 487    */
 488   public int getAxis()
 489   {
 490     // Could be smarter.
 491     return -1;
 492   }
 493 
 494   class iterOwner implements ExpressionOwner
 495   {
 496         int m_index;
 497 
 498         iterOwner(int index)
 499         {
 500                 m_index = index;
 501         }
 502 
 503     /**
 504      * @see ExpressionOwner#getExpression()
 505      */
 506     public Expression getExpression()
 507     {
 508       return m_exprs[m_index];
 509     }
 510 
 511     /**
 512      * @see ExpressionOwner#setExpression(Expression)
 513      */
 514     public void setExpression(Expression exp)
 515     {
 516 
 517         if(!(exp instanceof LocPathIterator))
 518         {
 519                 // Yuck.  Need FilterExprIter.  Or make it so m_exprs can be just
 520                 // plain expressions?
 521                 WalkingIterator wi = new WalkingIterator(getPrefixResolver());
 522                 FilterExprWalker few = new FilterExprWalker(wi);
 523                 wi.setFirstWalker(few);
 524                 few.setInnerExpression(exp);
 525                 wi.exprSetParent(UnionPathIterator.this);
 526                 few.exprSetParent(wi);
 527                 exp.exprSetParent(few);
 528                 exp = wi;
 529         }
 530         else
 531                 exp.exprSetParent(UnionPathIterator.this);
 532         m_exprs[m_index] = (LocPathIterator)exp;
 533     }
 534 
 535   }
 536 
 537   /**
 538    * @see com.sun.org.apache.xpath.internal.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
 539    */
 540   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
 541   {
 542                 if(visitor.visitUnionPath(owner, this))
 543                 {
 544                         if(null != m_exprs)
 545                         {
 546                                 int n = m_exprs.length;
 547                                 for(int i = 0; i < n; i++)
 548                                 {
 549                                         m_exprs[i].callVisitors(new iterOwner(i), visitor);
 550                                 }
 551                         }
 552                 }
 553   }
 554 
 555     /**
 556      * @see Expression#deepEquals(Expression)
 557      */
 558     public boolean deepEquals(Expression expr)
 559     {
 560       if (!super.deepEquals(expr))
 561             return false;
 562 
 563       UnionPathIterator upi = (UnionPathIterator) expr;
 564 
 565       if (null != m_exprs)
 566       {
 567         int n = m_exprs.length;
 568 
 569         if((null == upi.m_exprs) || (upi.m_exprs.length != n))
 570                 return false;
 571 
 572         for (int i = 0; i < n; i++)
 573         {
 574           if(!m_exprs[i].deepEquals(upi.m_exprs[i]))
 575                 return false;
 576         }
 577       }
 578       else if (null != upi.m_exprs)
 579       {
 580           return false;
 581       }
 582 
 583       return true;
 584     }
 585 
 586 
 587 }