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: DescendantIterator.java 469314 2006-10-30 23:31:59Z minchau $ 020 */ 021 package org.apache.xpath.axes; 022 023 import org.apache.xml.dtm.Axis; 024 import org.apache.xml.dtm.DTM; 025 import org.apache.xml.dtm.DTMAxisTraverser; 026 import org.apache.xml.dtm.DTMFilter; 027 import org.apache.xml.dtm.DTMIterator; 028 import org.apache.xpath.Expression; 029 import org.apache.xpath.XPathContext; 030 import org.apache.xpath.compiler.Compiler; 031 import org.apache.xpath.compiler.OpCodes; 032 import org.apache.xpath.compiler.OpMap; 033 import org.apache.xpath.patterns.NodeTest; 034 035 /** 036 * This class implements an optimized iterator for 037 * descendant, descendant-or-self, or "//foo" patterns. 038 * @see org.apache.xpath.axes.LocPathIterator 039 * @xsl.usage advanced 040 */ 041 public class DescendantIterator extends LocPathIterator 042 { 043 static final long serialVersionUID = -1190338607743976938L; 044 /** 045 * Create a DescendantIterator object. 046 * 047 * @param compiler A reference to the Compiler that contains the op map. 048 * @param opPos The position within the op map, which contains the 049 * location path expression for this itterator. 050 * 051 * @throws javax.xml.transform.TransformerException 052 */ 053 DescendantIterator(Compiler compiler, int opPos, int analysis) 054 throws javax.xml.transform.TransformerException 055 { 056 057 super(compiler, opPos, analysis, false); 058 059 int firstStepPos = OpMap.getFirstChildPos(opPos); 060 int stepType = compiler.getOp(firstStepPos); 061 062 boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType); 063 boolean fromRoot = false; 064 if (OpCodes.FROM_SELF == stepType) 065 { 066 orSelf = true; 067 // firstStepPos += 8; 068 } 069 else if(OpCodes.FROM_ROOT == stepType) 070 { 071 fromRoot = true; 072 // Ugly code... will go away when AST work is done. 073 int nextStepPos = compiler.getNextStepPos(firstStepPos); 074 if(compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF) 075 orSelf = true; 076 // firstStepPos += 8; 077 } 078 079 // Find the position of the last step. 080 int nextStepPos = firstStepPos; 081 while(true) 082 { 083 nextStepPos = compiler.getNextStepPos(nextStepPos); 084 if(nextStepPos > 0) 085 { 086 int stepOp = compiler.getOp(nextStepPos); 087 if(OpCodes.ENDOP != stepOp) 088 firstStepPos = nextStepPos; 089 else 090 break; 091 } 092 else 093 break; 094 095 } 096 097 // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336 098 if((analysis & WalkerFactory.BIT_CHILD) != 0) 099 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 }