001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: MatchPatternIterator.java 469314 2006-10-30 23:31:59Z minchau $
020     */
021    package org.apache.xpath.axes;
022    
023    import org.apache.xml.dtm.Axis;
024    import org.apache.xml.dtm.DTM;
025    import org.apache.xml.dtm.DTMAxisTraverser;
026    import org.apache.xml.dtm.DTMIterator;
027    import org.apache.xpath.XPathContext;
028    import org.apache.xpath.compiler.Compiler;
029    import org.apache.xpath.compiler.OpMap;
030    import org.apache.xpath.objects.XObject;
031    import org.apache.xpath.patterns.NodeTest;
032    import org.apache.xpath.patterns.StepPattern;
033    
034    /**
035     * This class treats a 
036     * <a href="http://www.w3.org/TR/xpath#location-paths">LocationPath</a> as a 
037     * filtered iteration over the tree, evaluating each node in a super axis 
038     * traversal against the LocationPath interpreted as a match pattern.  This 
039     * class is useful to find nodes in document order that are complex paths 
040     * whose steps probably criss-cross each other.
041     */
042    public class MatchPatternIterator extends LocPathIterator
043    {
044        static final long serialVersionUID = -5201153767396296474L;
045    
046      /** This is the select pattern, translated into a match pattern. */
047      protected StepPattern m_pattern;
048    
049      /** The traversal axis from where the nodes will be filtered. */
050      protected int m_superAxis = -1;
051    
052      /** The DTM inner traversal class, that corresponds to the super axis. */
053      protected DTMAxisTraverser m_traverser;
054      
055      /** DEBUG flag for diagnostic dumps. */
056      private static final boolean DEBUG = false;
057      
058    //  protected int m_nsElemBase = DTM.NULL;
059    
060      /**
061       * Create a LocPathIterator object, including creation
062       * of step walkers from the opcode list, and call back
063       * into the Compiler to create predicate expressions.
064       *
065       * @param compiler The Compiler which is creating
066       * this expression.
067       * @param opPos The position of this iterator in the
068       * opcode list from the compiler.
069       * @param analysis Analysis bits that give general information about the 
070       * LocationPath.
071       *
072       * @throws javax.xml.transform.TransformerException
073       */
074      MatchPatternIterator(Compiler compiler, int opPos, int analysis)
075              throws javax.xml.transform.TransformerException
076      {
077    
078        super(compiler, opPos, analysis, false);
079    
080        int firstStepPos = OpMap.getFirstChildPos(opPos);
081    
082        m_pattern = WalkerFactory.loadSteps(this, compiler, firstStepPos, 0); 
083    
084        boolean fromRoot = false;
085        boolean walkBack = false;
086        boolean walkDescendants = false;
087        boolean walkAttributes = false;
088    
089        if (0 != (analysis & (WalkerFactory.BIT_ROOT | 
090                              WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT)))
091          fromRoot = true;
092          
093        if (0 != (analysis
094                  & (WalkerFactory.BIT_ANCESTOR
095                     | WalkerFactory.BIT_ANCESTOR_OR_SELF
096                     | WalkerFactory.BIT_PRECEDING
097                     | WalkerFactory.BIT_PRECEDING_SIBLING 
098                     | WalkerFactory.BIT_FOLLOWING
099                     | WalkerFactory.BIT_FOLLOWING_SIBLING
100                     | WalkerFactory.BIT_PARENT | WalkerFactory.BIT_FILTER)))
101          walkBack = true;
102    
103        if (0 != (analysis
104                  & (WalkerFactory.BIT_DESCENDANT_OR_SELF
105                     | WalkerFactory.BIT_DESCENDANT
106                     | WalkerFactory.BIT_CHILD)))
107          walkDescendants = true;
108    
109        if (0 != (analysis
110                  & (WalkerFactory.BIT_ATTRIBUTE | WalkerFactory.BIT_NAMESPACE)))
111          walkAttributes = true;
112          
113        if(false || DEBUG)
114        {
115          System.out.print("analysis: "+Integer.toBinaryString(analysis));
116          System.out.println(", "+WalkerFactory.getAnalysisString(analysis));
117        }
118          
119        if(fromRoot || walkBack)
120        {
121          if(walkAttributes)
122          {
123            m_superAxis = Axis.ALL;
124          }
125          else
126          {
127            m_superAxis = Axis.DESCENDANTSFROMROOT;
128          }
129        }
130        else if(walkDescendants)
131        {
132          if(walkAttributes)
133          {
134            m_superAxis = Axis.ALLFROMNODE;
135          }
136          else
137          {
138            m_superAxis = Axis.DESCENDANTORSELF;
139          }
140        }
141        else
142        {
143          m_superAxis = Axis.ALL;
144        }
145        if(false || DEBUG)
146        {
147          System.out.println("axis: "+Axis.getNames(m_superAxis));
148        }
149        
150      }
151      
152      
153      /**
154       * Initialize the context values for this expression
155       * after it is cloned.
156       *
157       * @param context The XPath runtime context for this
158       * transformation.
159       */
160      public void setRoot(int context, Object environment)
161      {
162        super.setRoot(context, environment);
163        m_traverser = m_cdtm.getAxisTraverser(m_superAxis);
164      }
165    
166      /**
167       *  Detaches the iterator from the set which it iterated over, releasing
168       * any computational resources and placing the iterator in the INVALID
169       * state. After<code>detach</code> has been invoked, calls to
170       * <code>nextNode</code> or<code>previousNode</code> will raise the
171       * exception INVALID_STATE_ERR.
172       */
173      public void detach()
174      {    
175        if(m_allowDetach)
176        {
177          m_traverser = null;
178          
179          // Always call the superclass detach last!
180          super.detach();
181        }
182      }
183      
184      /**
185       * Get the next node via getNextXXX.  Bottlenecked for derived class override.
186       * @return The next node on the axis, or DTM.NULL.
187       */
188      protected int getNextNode()
189      {
190        m_lastFetched = (DTM.NULL == m_lastFetched)
191                         ? m_traverser.first(m_context)
192                         : m_traverser.next(m_context, m_lastFetched);
193        return m_lastFetched;
194      }
195    
196      /**
197       *  Returns the next node in the set and advances the position of the
198       * iterator in the set. After a NodeIterator is created, the first call
199       * to nextNode() returns the first node in the set.
200       * @return  The next <code>Node</code> in the set being iterated over, or
201       *   <code>null</code> if there are no more members in that set.
202       */
203      public int nextNode()
204      {      
205            if(m_foundLast)
206                    return DTM.NULL;
207    
208        int next;
209        
210        org.apache.xpath.VariableStack vars;
211        int savedStart;
212        if (-1 != m_stackFrame)
213        {
214          vars = m_execContext.getVarStack();
215    
216          // These three statements need to be combined into one operation.
217          savedStart = vars.getStackFrame();
218    
219          vars.setStackFrame(m_stackFrame);
220        }
221        else
222        {
223          // Yuck.  Just to shut up the compiler!
224          vars = null;
225          savedStart = 0;
226        }
227        
228        try
229        {
230          if(DEBUG)
231            System.out.println("m_pattern"+m_pattern.toString());
232    
233          do
234          {
235            next = getNextNode();
236      
237            if (DTM.NULL != next)
238            {
239              if(DTMIterator.FILTER_ACCEPT == acceptNode(next, m_execContext))
240                break;
241              else
242                continue;
243            }
244            else
245              break;
246          }
247          while (next != DTM.NULL);
248          
249          if (DTM.NULL != next)
250          {
251            if(DEBUG)
252            {
253              System.out.println("next: "+next);
254              System.out.println("name: "+m_cdtm.getNodeName(next));
255            }
256            incrementCurrentPos();
257      
258            return next;
259          }
260          else
261          {
262            m_foundLast = true;
263      
264            return DTM.NULL;
265          }
266        }
267        finally
268        {
269          if (-1 != m_stackFrame)
270          {
271            // These two statements need to be combined into one operation.
272            vars.setStackFrame(savedStart);
273          }
274        }
275    
276      }
277      
278      /**
279       *  Test whether a specified node is visible in the logical view of a
280       * TreeWalker or NodeIterator. This function will be called by the
281       * implementation of TreeWalker and NodeIterator; it is not intended to
282       * be called directly from user code.
283       * @param n  The node to check to see if it passes the filter or not.
284       * @return  a constant to determine whether the node is accepted,
285       *   rejected, or skipped, as defined  above .
286       */
287      public short acceptNode(int n, XPathContext xctxt)
288      {
289    
290        try
291        {
292          xctxt.pushCurrentNode(n);
293          xctxt.pushIteratorRoot(m_context);
294          if(DEBUG)
295          {
296            System.out.println("traverser: "+m_traverser);
297            System.out.print("node: "+n);
298            System.out.println(", "+m_cdtm.getNodeName(n));
299            // if(m_cdtm.getNodeName(n).equals("near-east"))
300            System.out.println("pattern: "+m_pattern.toString());
301            m_pattern.debugWhatToShow(m_pattern.getWhatToShow());
302          }
303          
304          XObject score = m_pattern.execute(xctxt);
305          
306          if(DEBUG)
307          {
308            // System.out.println("analysis: "+Integer.toBinaryString(m_analysis));
309            System.out.println("score: "+score);
310            System.out.println("skip: "+(score == NodeTest.SCORE_NONE));
311          }
312    
313          // System.out.println("\n::acceptNode - score: "+score.num()+"::");
314          return (score == NodeTest.SCORE_NONE) ? DTMIterator.FILTER_SKIP 
315                        : DTMIterator.FILTER_ACCEPT;
316        }
317        catch (javax.xml.transform.TransformerException se)
318        {
319    
320          // TODO: Fix this.
321          throw new RuntimeException(se.getMessage());
322        }
323        finally
324        {
325          xctxt.popCurrentNode();
326          xctxt.popIteratorRoot();
327        }
328    
329      }
330    
331    }