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