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.Axis;
  25 import com.sun.org.apache.xml.internal.dtm.DTM;
  26 import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser;
  27 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
  28 import com.sun.org.apache.xpath.internal.XPathContext;
  29 import com.sun.org.apache.xpath.internal.compiler.Compiler;
  30 import com.sun.org.apache.xpath.internal.compiler.OpMap;
  31 import com.sun.org.apache.xpath.internal.objects.XObject;
  32 import com.sun.org.apache.xpath.internal.patterns.NodeTest;
  33 import com.sun.org.apache.xpath.internal.patterns.StepPattern;
  34 
  35 /**
  36  * This class treats a
  37  * <a href="http://www.w3.org/TR/xpath#location-paths">LocationPath</a> as a
  38  * filtered iteration over the tree, evaluating each node in a super axis
  39  * traversal against the LocationPath interpreted as a match pattern.  This
  40  * class is useful to find nodes in document order that are complex paths
  41  * whose steps probably criss-cross each other.
  42  */
  43 public class MatchPatternIterator extends LocPathIterator
  44 {
  45     static final long serialVersionUID = -5201153767396296474L;
  46 
  47   /** This is the select pattern, translated into a match pattern. */
  48   protected StepPattern m_pattern;
  49 
  50   /** The traversal axis from where the nodes will be filtered. */
  51   protected int m_superAxis = -1;
  52 
  53   /** The DTM inner traversal class, that corresponds to the super axis. */
  54   protected DTMAxisTraverser m_traverser;
  55 
  56   /** DEBUG flag for diagnostic dumps. */
  57   private static final boolean DEBUG = false;
  58 
  59 //  protected int m_nsElemBase = DTM.NULL;
  60 
  61   /**
  62    * Create a LocPathIterator object, including creation
  63    * of step walkers from the opcode list, and call back
  64    * into the Compiler to create predicate expressions.
  65    *
  66    * @param compiler The Compiler which is creating
  67    * this expression.
  68    * @param opPos The position of this iterator in the
  69    * opcode list from the compiler.
  70    * @param analysis Analysis bits that give general information about the
  71    * LocationPath.
  72    *
  73    * @throws javax.xml.transform.TransformerException
  74    */
  75   MatchPatternIterator(Compiler compiler, int opPos, int analysis)
  76           throws javax.xml.transform.TransformerException
  77   {
  78 
  79     super(compiler, opPos, analysis, false);
  80 
  81     int firstStepPos = OpMap.getFirstChildPos(opPos);
  82 
  83     m_pattern = WalkerFactory.loadSteps(this, compiler, firstStepPos, 0);
  84 
  85     boolean fromRoot = false;
  86     boolean walkBack = false;
  87     boolean walkDescendants = false;
  88     boolean walkAttributes = false;
  89 
  90     if (0 != (analysis & (WalkerFactory.BIT_ROOT |
  91                           WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT)))
  92       fromRoot = true;
  93 
  94     if (0 != (analysis
  95               & (WalkerFactory.BIT_ANCESTOR
  96                  | WalkerFactory.BIT_ANCESTOR_OR_SELF
  97                  | WalkerFactory.BIT_PRECEDING
  98                  | WalkerFactory.BIT_PRECEDING_SIBLING
  99                  | WalkerFactory.BIT_FOLLOWING
 100                  | WalkerFactory.BIT_FOLLOWING_SIBLING
 101                  | WalkerFactory.BIT_PARENT | WalkerFactory.BIT_FILTER)))
 102       walkBack = true;
 103 
 104     if (0 != (analysis
 105               & (WalkerFactory.BIT_DESCENDANT_OR_SELF
 106                  | WalkerFactory.BIT_DESCENDANT
 107                  | WalkerFactory.BIT_CHILD)))
 108       walkDescendants = true;
 109 
 110     if (0 != (analysis
 111               & (WalkerFactory.BIT_ATTRIBUTE | WalkerFactory.BIT_NAMESPACE)))
 112       walkAttributes = true;
 113 
 114     if(false || DEBUG)
 115     {
 116       System.out.print("analysis: "+Integer.toBinaryString(analysis));
 117       System.out.println(", "+WalkerFactory.getAnalysisString(analysis));
 118     }
 119 
 120     if(fromRoot || walkBack)
 121     {
 122       if(walkAttributes)
 123       {
 124         m_superAxis = Axis.ALL;
 125       }
 126       else
 127       {
 128         m_superAxis = Axis.DESCENDANTSFROMROOT;
 129       }
 130     }
 131     else if(walkDescendants)
 132     {
 133       if(walkAttributes)
 134       {
 135         m_superAxis = Axis.ALLFROMNODE;
 136       }
 137       else
 138       {
 139         m_superAxis = Axis.DESCENDANTORSELF;
 140       }
 141     }
 142     else
 143     {
 144       m_superAxis = Axis.ALL;
 145     }
 146     if(false || DEBUG)
 147     {
 148       System.out.println("axis: "+Axis.getNames(m_superAxis));
 149     }
 150 
 151   }
 152 
 153 
 154   /**
 155    * Initialize the context values for this expression
 156    * after it is cloned.
 157    *
 158    * @param context The XPath runtime context for this
 159    * transformation.
 160    */
 161   public void setRoot(int context, Object environment)
 162   {
 163     super.setRoot(context, environment);
 164     m_traverser = m_cdtm.getAxisTraverser(m_superAxis);
 165   }
 166 
 167   /**
 168    *  Detaches the iterator from the set which it iterated over, releasing
 169    * any computational resources and placing the iterator in the INVALID
 170    * state. After<code>detach</code> has been invoked, calls to
 171    * <code>nextNode</code> or<code>previousNode</code> will raise the
 172    * exception INVALID_STATE_ERR.
 173    */
 174   public void detach()
 175   {
 176     if(m_allowDetach)
 177     {
 178       m_traverser = null;
 179 
 180       // Always call the superclass detach last!
 181       super.detach();
 182     }
 183   }
 184 
 185   /**
 186    * Get the next node via getNextXXX.  Bottlenecked for derived class override.
 187    * @return The next node on the axis, or DTM.NULL.
 188    */
 189   protected int getNextNode()
 190   {
 191     m_lastFetched = (DTM.NULL == m_lastFetched)
 192                      ? m_traverser.first(m_context)
 193                      : m_traverser.next(m_context, m_lastFetched);
 194     return m_lastFetched;
 195   }
 196 
 197   /**
 198    *  Returns the next node in the set and advances the position of the
 199    * iterator in the set. After a NodeIterator is created, the first call
 200    * to nextNode() returns the first node in the set.
 201    * @return  The next <code>Node</code> in the set being iterated over, or
 202    *   <code>null</code> if there are no more members in that set.
 203    */
 204   public int nextNode()
 205   {
 206         if(m_foundLast)
 207                 return DTM.NULL;
 208 
 209     int next;
 210 
 211     com.sun.org.apache.xpath.internal.VariableStack vars;
 212     int savedStart;
 213     if (-1 != m_stackFrame)
 214     {
 215       vars = m_execContext.getVarStack();
 216 
 217       // These three statements need to be combined into one operation.
 218       savedStart = vars.getStackFrame();
 219 
 220       vars.setStackFrame(m_stackFrame);
 221     }
 222     else
 223     {
 224       // Yuck.  Just to shut up the compiler!
 225       vars = null;
 226       savedStart = 0;
 227     }
 228 
 229     try
 230     {
 231       if(DEBUG)
 232         System.out.println("m_pattern"+m_pattern.toString());
 233 
 234       do
 235       {
 236         next = getNextNode();
 237 
 238         if (DTM.NULL != next)
 239         {
 240           if(DTMIterator.FILTER_ACCEPT == acceptNode(next, m_execContext))
 241             break;
 242           else
 243             continue;
 244         }
 245         else
 246           break;
 247       }
 248       while (next != DTM.NULL);
 249 
 250       if (DTM.NULL != next)
 251       {
 252         if(DEBUG)
 253         {
 254           System.out.println("next: "+next);
 255           System.out.println("name: "+m_cdtm.getNodeName(next));
 256         }
 257         incrementCurrentPos();
 258 
 259         return next;
 260       }
 261       else
 262       {
 263         m_foundLast = true;
 264 
 265         return DTM.NULL;
 266       }
 267     }
 268     finally
 269     {
 270       if (-1 != m_stackFrame)
 271       {
 272         // These two statements need to be combined into one operation.
 273         vars.setStackFrame(savedStart);
 274       }
 275     }
 276 
 277   }
 278 
 279   /**
 280    *  Test whether a specified node is visible in the logical view of a
 281    * TreeWalker or NodeIterator. This function will be called by the
 282    * implementation of TreeWalker and NodeIterator; it is not intended to
 283    * be called directly from user code.
 284    * @param n  The node to check to see if it passes the filter or not.
 285    * @return  a constant to determine whether the node is accepted,
 286    *   rejected, or skipped, as defined  above .
 287    */
 288   public short acceptNode(int n, XPathContext xctxt)
 289   {
 290 
 291     try
 292     {
 293       xctxt.pushCurrentNode(n);
 294       xctxt.pushIteratorRoot(m_context);
 295       if(DEBUG)
 296       {
 297         System.out.println("traverser: "+m_traverser);
 298         System.out.print("node: "+n);
 299         System.out.println(", "+m_cdtm.getNodeName(n));
 300         // if(m_cdtm.getNodeName(n).equals("near-east"))
 301         System.out.println("pattern: "+m_pattern.toString());
 302         NodeTest.debugWhatToShow(m_pattern.getWhatToShow());
 303       }
 304 
 305       XObject score = m_pattern.execute(xctxt);
 306 
 307       if(DEBUG)
 308       {
 309         // System.out.println("analysis: "+Integer.toBinaryString(m_analysis));
 310         System.out.println("score: "+score);
 311         System.out.println("skip: "+(score == NodeTest.SCORE_NONE));
 312       }
 313 
 314       // System.out.println("\n::acceptNode - score: "+score.num()+"::");
 315       return (score == NodeTest.SCORE_NONE) ? DTMIterator.FILTER_SKIP
 316                     : DTMIterator.FILTER_ACCEPT;
 317     }
 318     catch (javax.xml.transform.TransformerException se)
 319     {
 320 
 321       // TODO: Fix this.
 322       throw new RuntimeException(se.getMessage());
 323     }
 324     finally
 325     {
 326       xctxt.popCurrentNode();
 327       xctxt.popIteratorRoot();
 328     }
 329 
 330   }
 331 
 332 }