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  */
21 package org.apache.xpath.axes;
22 
23 import org.apache.xml.dtm.DTM;
24 import org.apache.xml.dtm.DTMAxisIterator;
25 import 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  */
31 public 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    */
ReverseAxesWalker(LocPathIterator locPathIterator, int axis)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    */
setRoot(int root)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    */
detach()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    */
getNextNode()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    */
isReverseAxes()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    */
getProximityPosition(int predicateIndex)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    */
countProximityPosition(int i)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    */
getLastPos(XPathContext xctxt)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    */
isDocOrdered()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