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