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