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: ReverseAxesWalker.java 513117 2007-03-01 03:28:52Z minchau $
20 */
21package org.apache.xpath.axes;
22
23import org.apache.xml.dtm.DTM;
24import org.apache.xml.dtm.DTMAxisIterator;
25import org.apache.xpath.XPathContext;
26
27/**
28 * Walker for a reverse axes.
29 * @see <a href="http://www.w3.org/TR/xpath#predicates">XPath 2.4 Predicates</a>
30 */
31public class ReverseAxesWalker extends AxesWalker
32{
33    static final long serialVersionUID = 2847007647832768941L;
34
35  /**
36   * Construct an AxesWalker using a LocPathIterator.
37   *
38   * @param locPathIterator The location path iterator that 'owns' this walker.
39   */
40  ReverseAxesWalker(LocPathIterator locPathIterator, int axis)
41  {
42    super(locPathIterator, axis);
43  }
44
45  /**
46   * Set the root node of the TreeWalker.
47   * (Not part of the DOM2 TreeWalker interface).
48   *
49   * @param root The context node of this step.
50   */
51  public void setRoot(int root)
52  {
53    super.setRoot(root);
54    m_iterator = getDTM(root).getAxisIterator(m_axis);
55    m_iterator.setStartNode(root);
56  }
57
58  /**
59   * Detaches the walker from the set which it iterated over, releasing
60   * any computational resources and placing the iterator in the INVALID
61   * state.
62   */
63  public void detach()
64  {
65    m_iterator = null;
66    super.detach();
67  }
68
69  /**
70   * Get the next node in document order on the axes.
71   *
72   * @return the next node in document order on the axes, or null.
73   */
74  protected int getNextNode()
75  {
76    if (m_foundLast)
77      return DTM.NULL;
78
79    int next = m_iterator.next();
80
81    if (m_isFresh)
82      m_isFresh = false;
83
84    if (DTM.NULL == next)
85      this.m_foundLast = true;
86
87    return next;
88  }
89
90
91  /**
92   * Tells if this is a reverse axes.  Overrides AxesWalker#isReverseAxes.
93   *
94   * @return true for this class.
95   */
96  public boolean isReverseAxes()
97  {
98    return true;
99  }
100
101//  /**
102//   *  Set the root node of the TreeWalker.
103//   *
104//   * @param root The context node of this step.
105//   */
106//  public void setRoot(int root)
107//  {
108//    super.setRoot(root);
109//  }
110
111  /**
112   * Get the current sub-context position.  In order to do the
113   * reverse axes count, for the moment this re-searches the axes
114   * up to the predicate.  An optimization on this is to cache
115   * the nodes searched, but, for the moment, this case is probably
116   * rare enough that the added complexity isn't worth it.
117   *
118   * @param predicateIndex The predicate index of the proximity position.
119   *
120   * @return The pridicate index, or -1.
121   */
122  protected int getProximityPosition(int predicateIndex)
123  {
124    // A negative predicate index seems to occur with
125    // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()]
126    // -sb
127    if(predicateIndex < 0)
128      return -1;
129
130    int count = m_proximityPositions[predicateIndex];
131
132    if (count <= 0)
133    {
134      AxesWalker savedWalker = wi().getLastUsedWalker();
135
136      try
137      {
138        ReverseAxesWalker clone = (ReverseAxesWalker) this.clone();
139
140        clone.setRoot(this.getRoot());
141
142        clone.setPredicateCount(predicateIndex);
143
144        clone.setPrevWalker(null);
145        clone.setNextWalker(null);
146        wi().setLastUsedWalker(clone);
147
148        // Count 'em all
149        count++;
150        int next;
151
152        while (DTM.NULL != (next = clone.nextNode()))
153        {
154          count++;
155        }
156
157        m_proximityPositions[predicateIndex] = count;
158      }
159      catch (CloneNotSupportedException cnse)
160      {
161
162        // can't happen
163      }
164      finally
165      {
166        wi().setLastUsedWalker(savedWalker);
167      }
168    }
169
170    return count;
171  }
172
173  /**
174   * Count backwards one proximity position.
175   *
176   * @param i The predicate index.
177   */
178  protected void countProximityPosition(int i)
179  {
180    if (i < m_proximityPositions.length)
181      m_proximityPositions[i]--;
182  }
183
184  /**
185   * Get the number of nodes in this node list.  The function is probably ill
186   * named?
187   *
188   *
189   * @param xctxt The XPath runtime context.
190   *
191   * @return the number of nodes in this node list.
192   */
193  public int getLastPos(XPathContext xctxt)
194  {
195
196    int count = 0;
197    AxesWalker savedWalker = wi().getLastUsedWalker();
198
199    try
200    {
201      ReverseAxesWalker clone = (ReverseAxesWalker) this.clone();
202
203      clone.setRoot(this.getRoot());
204
205      clone.setPredicateCount(m_predicateIndex);
206
207      clone.setPrevWalker(null);
208      clone.setNextWalker(null);
209      wi().setLastUsedWalker(clone);
210
211      // Count 'em all
212      // count = 1;
213      int next;
214
215      while (DTM.NULL != (next = clone.nextNode()))
216      {
217        count++;
218      }
219    }
220    catch (CloneNotSupportedException cnse)
221    {
222
223      // can't happen
224    }
225    finally
226    {
227      wi().setLastUsedWalker(savedWalker);
228    }
229
230    return count;
231  }
232
233  /**
234   * Returns true if all the nodes in the iteration well be returned in document
235   * order.
236   * Warning: This can only be called after setRoot has been called!
237   *
238   * @return false.
239   */
240  public boolean isDocOrdered()
241  {
242    return false;  // I think.
243  }
244
245  /** The DTM inner traversal class, that corresponds to the super axis. */
246  protected DTMAxisIterator m_iterator;
247}
248