001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: OneStepIterator.java 469314 2006-10-30 23:31:59Z minchau $ 020 */ 021 package org.apache.xpath.axes; 022 023 import org.apache.xml.dtm.DTM; 024 import org.apache.xml.dtm.DTMAxisIterator; 025 import org.apache.xml.dtm.DTMFilter; 026 import org.apache.xml.dtm.DTMIterator; 027 import org.apache.xpath.Expression; 028 import org.apache.xpath.XPathContext; 029 import org.apache.xpath.compiler.Compiler; 030 import org.apache.xpath.compiler.OpMap; 031 032 /** 033 * This class implements a general iterator for 034 * those LocationSteps with only one step, and perhaps a predicate. 035 * @see org.apache.xpath.axes#LocPathIterator 036 * @xsl.usage advanced 037 */ 038 public class OneStepIterator extends ChildTestIterator 039 { 040 static final long serialVersionUID = 4623710779664998283L; 041 /** The traversal axis from where the nodes will be filtered. */ 042 protected int m_axis = -1; 043 044 /** The DTM inner traversal class, that corresponds to the super axis. */ 045 protected DTMAxisIterator m_iterator; 046 047 /** 048 * Create a OneStepIterator object. 049 * 050 * @param compiler A reference to the Compiler that contains the op map. 051 * @param opPos The position within the op map, which contains the 052 * location path expression for this itterator. 053 * 054 * @throws javax.xml.transform.TransformerException 055 */ 056 OneStepIterator(Compiler compiler, int opPos, int analysis) 057 throws javax.xml.transform.TransformerException 058 { 059 super(compiler, opPos, analysis); 060 int firstStepPos = OpMap.getFirstChildPos(opPos); 061 062 m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos); 063 064 } 065 066 067 /** 068 * Create a OneStepIterator object. 069 * 070 * @param iterator The DTM iterator which this iterator will use. 071 * @param axis One of Axis.Child, etc., or -1 if the axis is unknown. 072 * 073 * @throws javax.xml.transform.TransformerException 074 */ 075 public OneStepIterator(DTMAxisIterator iterator, int axis) 076 throws javax.xml.transform.TransformerException 077 { 078 super(null); 079 080 m_iterator = iterator; 081 m_axis = axis; 082 int whatToShow = DTMFilter.SHOW_ALL; 083 initNodeTest(whatToShow); 084 } 085 086 /** 087 * Initialize the context values for this expression 088 * after it is cloned. 089 * 090 * @param context The XPath runtime context for this 091 * transformation. 092 */ 093 public void setRoot(int context, Object environment) 094 { 095 super.setRoot(context, environment); 096 if(m_axis > -1) 097 m_iterator = m_cdtm.getAxisIterator(m_axis); 098 m_iterator.setStartNode(m_context); 099 } 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 }