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: OneStepIterator.java 469314 2006-10-30 23:31:59Z 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.xml.dtm.DTMFilter; 26 import org.apache.xml.dtm.DTMIterator; 27 import org.apache.xpath.Expression; 28 import org.apache.xpath.XPathContext; 29 import org.apache.xpath.compiler.Compiler; 30 import org.apache.xpath.compiler.OpMap; 31 32 /** 33 * This class implements a general iterator for 34 * those LocationSteps with only one step, and perhaps a predicate. 35 * @see org.apache.xpath.axes#LocPathIterator 36 * @xsl.usage advanced 37 */ 38 public class OneStepIterator extends ChildTestIterator 39 { 40 static final long serialVersionUID = 4623710779664998283L; 41 /** The traversal axis from where the nodes will be filtered. */ 42 protected int m_axis = -1; 43 44 /** The DTM inner traversal class, that corresponds to the super axis. */ 45 protected DTMAxisIterator m_iterator; 46 47 /** 48 * Create a OneStepIterator object. 49 * 50 * @param compiler A reference to the Compiler that contains the op map. 51 * @param opPos The position within the op map, which contains the 52 * location path expression for this itterator. 53 * 54 * @throws javax.xml.transform.TransformerException 55 */ 56 OneStepIterator(Compiler compiler, int opPos, int analysis) 57 throws javax.xml.transform.TransformerException 58 { 59 super(compiler, opPos, analysis); 60 int firstStepPos = OpMap.getFirstChildPos(opPos); 61 62 m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos); 63 64 } 65 66 67 /** 68 * Create a OneStepIterator object. 69 * 70 * @param iterator The DTM iterator which this iterator will use. 71 * @param axis One of Axis.Child, etc., or -1 if the axis is unknown. 72 * 73 * @throws javax.xml.transform.TransformerException 74 */ 75 public OneStepIterator(DTMAxisIterator iterator, int axis) 76 throws javax.xml.transform.TransformerException 77 { 78 super(null); 79 80 m_iterator = iterator; 81 m_axis = axis; 82 int whatToShow = DTMFilter.SHOW_ALL; 83 initNodeTest(whatToShow); 84 } 85 86 /** 87 * Initialize the context values for this expression 88 * after it is cloned. 89 * 90 * @param context The XPath runtime context for this 91 * transformation. 92 */ 93 public void setRoot(int context, Object environment) 94 { 95 super.setRoot(context, environment); 96 if(m_axis > -1) 97 m_iterator = m_cdtm.getAxisIterator(m_axis); 98 m_iterator.setStartNode(m_context); 99 } 100 101 /** 102 * Detaches the iterator from the set which it iterated over, releasing 103 * any computational resources and placing the iterator in the INVALID 104 * state. After<code>detach</code> has been invoked, calls to 105 * <code>nextNode</code> or<code>previousNode</code> will raise the 106 * exception INVALID_STATE_ERR. 107 */ 108 public void detach() 109 { 110 if(m_allowDetach) 111 { 112 if(m_axis > -1) 113 m_iterator = null; 114 115 // Always call the superclass detach last! 116 super.detach(); 117 } 118 } 119 120 /** 121 * Get the next node via getFirstAttribute && getNextAttribute. 122 */ 123 protected int getNextNode() 124 { 125 return m_lastFetched = m_iterator.next(); 126 } 127 128 /** 129 * Get a cloned iterator. 130 * 131 * @return A new iterator that can be used without mutating this one. 132 * 133 * @throws CloneNotSupportedException 134 */ 135 public Object clone() throws CloneNotSupportedException 136 { 137 // Do not access the location path itterator during this operation! 138 139 OneStepIterator clone = (OneStepIterator) super.clone(); 140 141 if(m_iterator != null) 142 { 143 clone.m_iterator = m_iterator.cloneIterator(); 144 } 145 return clone; 146 } 147 148 /** 149 * Get a cloned Iterator that is reset to the beginning 150 * of the query. 151 * 152 * @return A cloned NodeIterator set of the start of the query. 153 * 154 * @throws CloneNotSupportedException 155 */ 156 public DTMIterator cloneWithReset() throws CloneNotSupportedException 157 { 158 159 OneStepIterator clone = (OneStepIterator) super.cloneWithReset(); 160 clone.m_iterator = m_iterator; 161 162 return clone; 163 } 164 165 166 167 /** 168 * Tells if this is a reverse axes. Overrides AxesWalker#isReverseAxes. 169 * 170 * @return true for this class. 171 */ 172 public boolean isReverseAxes() 173 { 174 return m_iterator.isReverse(); 175 } 176 177 /** 178 * Get the current sub-context position. In order to do the 179 * reverse axes count, for the moment this re-searches the axes 180 * up to the predicate. An optimization on this is to cache 181 * the nodes searched, but, for the moment, this case is probably 182 * rare enough that the added complexity isn't worth it. 183 * 184 * @param predicateIndex The predicate index of the proximity position. 185 * 186 * @return The pridicate index, or -1. 187 */ 188 protected int getProximityPosition(int predicateIndex) 189 { 190 if(!isReverseAxes()) 191 return super.getProximityPosition(predicateIndex); 192 193 // A negative predicate index seems to occur with 194 // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()] 195 // -sb 196 if(predicateIndex < 0) 197 return -1; 198 199 if (m_proximityPositions[predicateIndex] <= 0) 200 { 201 XPathContext xctxt = getXPathContext(); 202 try 203 { 204 OneStepIterator clone = (OneStepIterator) this.clone(); 205 206 int root = getRoot(); 207 xctxt.pushCurrentNode(root); 208 clone.setRoot(root, xctxt); 209 210 // clone.setPredicateCount(predicateIndex); 211 clone.m_predCount = predicateIndex; 212 213 // Count 'em all 214 int count = 1; 215 int next; 216 217 while (DTM.NULL != (next = clone.nextNode())) 218 { 219 count++; 220 } 221 222 m_proximityPositions[predicateIndex] += count; 223 } 224 catch (CloneNotSupportedException cnse) 225 { 226 227 // can't happen 228 } 229 finally 230 { 231 xctxt.popCurrentNode(); 232 } 233 } 234 235 return m_proximityPositions[predicateIndex]; 236 } 237 238 /** 239 * The number of nodes in the list. The range of valid child node indices 240 * is 0 to <code>length-1</code> inclusive. 241 * 242 * @return The number of nodes in the list, always greater or equal to zero. 243 */ 244 public int getLength() 245 { 246 if(!isReverseAxes()) 247 return super.getLength(); 248 249 // Tell if this is being called from within a predicate. 250 boolean isPredicateTest = (this == m_execContext.getSubContextList()); 251 252 // And get how many total predicates are part of this step. 253 int predCount = getPredicateCount(); 254 255 // If we have already calculated the length, and the current predicate 256 // is the first predicate, then return the length. We don't cache 257 // the anything but the length of the list to the first predicate. 258 if (-1 != m_length && isPredicateTest && m_predicateIndex < 1) 259 return m_length; 260 261 int count = 0; 262 263 XPathContext xctxt = getXPathContext(); 264 try 265 { 266 OneStepIterator clone = (OneStepIterator) this.cloneWithReset(); 267 268 int root = getRoot(); 269 xctxt.pushCurrentNode(root); 270 clone.setRoot(root, xctxt); 271 272 clone.m_predCount = m_predicateIndex; 273 274 int next; 275 276 while (DTM.NULL != (next = clone.nextNode())) 277 { 278 count++; 279 } 280 } 281 catch (CloneNotSupportedException cnse) 282 { 283 // can't happen 284 } 285 finally 286 { 287 xctxt.popCurrentNode(); 288 } 289 if (isPredicateTest && m_predicateIndex < 1) 290 m_length = count; 291 292 return count; 293 } 294 295 /** 296 * Count backwards one proximity position. 297 * 298 * @param i The predicate index. 299 */ 300 protected void countProximityPosition(int i) 301 { 302 if(!isReverseAxes()) 303 super.countProximityPosition(i); 304 else if (i < m_proximityPositions.length) 305 m_proximityPositions[i]--; 306 } 307 308 /** 309 * Reset the iterator. 310 */ 311 public void reset() 312 { 313 314 super.reset(); 315 if(null != m_iterator) 316 m_iterator.reset(); 317 } 318 319 /** 320 * Returns the axis being iterated, if it is known. 321 * 322 * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 323 * types. 324 */ 325 public int getAxis() 326 { 327 return m_axis; 328 } 329 330 /** 331 * @see Expression#deepEquals(Expression) 332 */ 333 public boolean deepEquals(Expression expr) 334 { 335 if(!super.deepEquals(expr)) 336 return false; 337 338 if(m_axis != ((OneStepIterator)expr).m_axis) 339 return false; 340 341 return true; 342 } 343 344 345 } 346