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: DescendantIterator.java 469314 2006-10-30 23:31:59Z minchau $ 20 */ 21 package org.apache.xpath.axes; 22 23 import org.apache.xml.dtm.Axis; 24 import org.apache.xml.dtm.DTM; 25 import org.apache.xml.dtm.DTMAxisTraverser; 26 import org.apache.xml.dtm.DTMFilter; 27 import org.apache.xml.dtm.DTMIterator; 28 import org.apache.xpath.Expression; 29 import org.apache.xpath.XPathContext; 30 import org.apache.xpath.compiler.Compiler; 31 import org.apache.xpath.compiler.OpCodes; 32 import org.apache.xpath.compiler.OpMap; 33 import org.apache.xpath.patterns.NodeTest; 34 35 /** 36 * This class implements an optimized iterator for 37 * descendant, descendant-or-self, or "//foo" patterns. 38 * @see org.apache.xpath.axes.LocPathIterator 39 * @xsl.usage advanced 40 */ 41 public class DescendantIterator extends LocPathIterator 42 { 43 static final long serialVersionUID = -1190338607743976938L; 44 /** 45 * Create a DescendantIterator object. 46 * 47 * @param compiler A reference to the Compiler that contains the op map. 48 * @param opPos The position within the op map, which contains the 49 * location path expression for this itterator. 50 * 51 * @throws javax.xml.transform.TransformerException 52 */ 53 DescendantIterator(Compiler compiler, int opPos, int analysis) 54 throws javax.xml.transform.TransformerException 55 { 56 57 super(compiler, opPos, analysis, false); 58 59 int firstStepPos = OpMap.getFirstChildPos(opPos); 60 int stepType = compiler.getOp(firstStepPos); 61 62 boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType); 63 boolean fromRoot = false; 64 if (OpCodes.FROM_SELF == stepType) 65 { 66 orSelf = true; 67 // firstStepPos += 8; 68 } 69 else if(OpCodes.FROM_ROOT == stepType) 70 { 71 fromRoot = true; 72 // Ugly code... will go away when AST work is done. 73 int nextStepPos = compiler.getNextStepPos(firstStepPos); 74 if(compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF) 75 orSelf = true; 76 // firstStepPos += 8; 77 } 78 79 // Find the position of the last step. 80 int nextStepPos = firstStepPos; 81 while(true) 82 { 83 nextStepPos = compiler.getNextStepPos(nextStepPos); 84 if(nextStepPos > 0) 85 { 86 int stepOp = compiler.getOp(nextStepPos); 87 if(OpCodes.ENDOP != stepOp) 88 firstStepPos = nextStepPos; 89 else 90 break; 91 } 92 else 93 break; 94 95 } 96 97 // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336 98 if((analysis & WalkerFactory.BIT_CHILD) != 0) 99 orSelf = false; 100 101 if(fromRoot) 102 { 103 if(orSelf) 104 m_axis = Axis.DESCENDANTSORSELFFROMROOT; 105 else 106 m_axis = Axis.DESCENDANTSFROMROOT; 107 } 108 else if(orSelf) 109 m_axis = Axis.DESCENDANTORSELF; 110 else 111 m_axis = Axis.DESCENDANT; 112 113 int whatToShow = compiler.getWhatToShow(firstStepPos); 114 115 if ((0 == (whatToShow 116 & (DTMFilter.SHOW_ATTRIBUTE | DTMFilter.SHOW_ELEMENT 117 | DTMFilter.SHOW_PROCESSING_INSTRUCTION))) || 118 (whatToShow == DTMFilter.SHOW_ALL)) 119 initNodeTest(whatToShow); 120 else 121 { 122 initNodeTest(whatToShow, compiler.getStepNS(firstStepPos), 123 compiler.getStepLocalName(firstStepPos)); 124 } 125 initPredicateInfo(compiler, firstStepPos); 126 } 127 128 /** 129 * Create a DescendantIterator object. 130 * 131 */ 132 public DescendantIterator() 133 { 134 super(null); 135 m_axis = Axis.DESCENDANTSORSELFFROMROOT; 136 int whatToShow = DTMFilter.SHOW_ALL; 137 initNodeTest(whatToShow); 138 } 139 140 141 /** 142 * Get a cloned Iterator that is reset to the beginning 143 * of the query. 144 * 145 * @return A cloned NodeIterator set of the start of the query. 146 * 147 * @throws CloneNotSupportedException 148 */ 149 public DTMIterator cloneWithReset() throws CloneNotSupportedException 150 { 151 152 DescendantIterator clone = (DescendantIterator) super.cloneWithReset(); 153 clone.m_traverser = m_traverser; 154 155 clone.resetProximityPositions(); 156 157 return clone; 158 } 159 160 /** 161 * Returns the next node in the set and advances the position of the 162 * iterator in the set. After a NodeIterator is created, the first call 163 * to nextNode() returns the first node in the set. 164 * 165 * @return The next <code>Node</code> in the set being iterated over, or 166 * <code>null</code> if there are no more members in that set. 167 * 168 * @throws DOMException 169 * INVALID_STATE_ERR: Raised if this method is called after the 170 * <code>detach</code> method was invoked. 171 */ 172 public int nextNode() 173 { 174 if(m_foundLast) 175 return DTM.NULL; 176 177 if(DTM.NULL == m_lastFetched) 178 { 179 resetProximityPositions(); 180 } 181 182 int next; 183 184 org.apache.xpath.VariableStack vars; 185 int savedStart; 186 if (-1 != m_stackFrame) 187 { 188 vars = m_execContext.getVarStack(); 189 190 // These three statements need to be combined into one operation. 191 savedStart = vars.getStackFrame(); 192 193 vars.setStackFrame(m_stackFrame); 194 } 195 else 196 { 197 // Yuck. Just to shut up the compiler! 198 vars = null; 199 savedStart = 0; 200 } 201 202 try 203 { 204 do 205 { 206 if(0 == m_extendedTypeID) 207 { 208 next = m_lastFetched = (DTM.NULL == m_lastFetched) 209 ? m_traverser.first(m_context) 210 : m_traverser.next(m_context, m_lastFetched); 211 } 212 else 213 { 214 next = m_lastFetched = (DTM.NULL == m_lastFetched) 215 ? m_traverser.first(m_context, m_extendedTypeID) 216 : m_traverser.next(m_context, m_lastFetched, 217 m_extendedTypeID); 218 } 219 220 if (DTM.NULL != next) 221 { 222 if(DTMIterator.FILTER_ACCEPT == acceptNode(next)) 223 break; 224 else 225 continue; 226 } 227 else 228 break; 229 } 230 while (next != DTM.NULL); 231 232 if (DTM.NULL != next) 233 { 234 m_pos++; 235 return next; 236 } 237 else 238 { 239 m_foundLast = true; 240 241 return DTM.NULL; 242 } 243 } 244 finally 245 { 246 if (-1 != m_stackFrame) 247 { 248 // These two statements need to be combined into one operation. 249 vars.setStackFrame(savedStart); 250 } 251 } 252 } 253 254 /** 255 * Initialize the context values for this expression 256 * after it is cloned. 257 * 258 * @param context The XPath runtime context for this 259 * transformation. 260 */ 261 public void setRoot(int context, Object environment) 262 { 263 super.setRoot(context, environment); 264 m_traverser = m_cdtm.getAxisTraverser(m_axis); 265 266 String localName = getLocalName(); 267 String namespace = getNamespace(); 268 int what = m_whatToShow; 269 // System.out.println("what: "); 270 // NodeTest.debugWhatToShow(what); 271 if(DTMFilter.SHOW_ALL == what 272 || NodeTest.WILD.equals(localName) 273 || NodeTest.WILD.equals(namespace)) 274 { 275 m_extendedTypeID = 0; 276 } 277 else 278 { 279 int type = getNodeTypeTest(what); 280 m_extendedTypeID = m_cdtm.getExpandedTypeID(namespace, localName, type); 281 } 282 283 } 284 285 /** 286 * Return the first node out of the nodeset, if this expression is 287 * a nodeset expression. This is the default implementation for 288 * nodesets. 289 * <p>WARNING: Do not mutate this class from this function!</p> 290 * @param xctxt The XPath runtime context. 291 * @return the first node out of the nodeset, or DTM.NULL. 292 */ 293 public int asNode(XPathContext xctxt) 294 throws javax.xml.transform.TransformerException 295 { 296 if(getPredicateCount() > 0) 297 return super.asNode(xctxt); 298 299 int current = xctxt.getCurrentNode(); 300 301 DTM dtm = xctxt.getDTM(current); 302 DTMAxisTraverser traverser = dtm.getAxisTraverser(m_axis); 303 304 String localName = getLocalName(); 305 String namespace = getNamespace(); 306 int what = m_whatToShow; 307 308 // System.out.print(" (DescendantIterator) "); 309 310 // System.out.println("what: "); 311 // NodeTest.debugWhatToShow(what); 312 if(DTMFilter.SHOW_ALL == what 313 || localName == NodeTest.WILD 314 || namespace == NodeTest.WILD) 315 { 316 return traverser.first(current); 317 } 318 else 319 { 320 int type = getNodeTypeTest(what); 321 int extendedType = dtm.getExpandedTypeID(namespace, localName, type); 322 return traverser.first(current, extendedType); 323 } 324 } 325 326 /** 327 * Detaches the iterator from the set which it iterated over, releasing 328 * any computational resources and placing the iterator in the INVALID 329 * state. After<code>detach</code> has been invoked, calls to 330 * <code>nextNode</code> or<code>previousNode</code> will raise the 331 * exception INVALID_STATE_ERR. 332 */ 333 public void detach() 334 { 335 if (m_allowDetach) { 336 m_traverser = null; 337 m_extendedTypeID = 0; 338 339 // Always call the superclass detach last! 340 super.detach(); 341 } 342 } 343 344 /** 345 * Returns the axis being iterated, if it is known. 346 * 347 * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 348 * types. 349 */ 350 public int getAxis() 351 { 352 return m_axis; 353 } 354 355 356 /** The traverser to use to navigate over the descendants. */ 357 transient protected DTMAxisTraverser m_traverser; 358 359 /** The axis that we are traversing. */ 360 protected int m_axis; 361 362 /** The extended type ID, not set until setRoot. */ 363 protected int m_extendedTypeID; 364 365 /** 366 * @see Expression#deepEquals(Expression) 367 */ 368 public boolean deepEquals(Expression expr) 369 { 370 if(!super.deepEquals(expr)) 371 return false; 372 373 if(m_axis != ((DescendantIterator)expr).m_axis) 374 return false; 375 376 return true; 377 } 378 379 380 } 381