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: MatchPatternIterator.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.DTMIterator; 027 import org.apache.xpath.XPathContext; 028 import org.apache.xpath.compiler.Compiler; 029 import org.apache.xpath.compiler.OpMap; 030 import org.apache.xpath.objects.XObject; 031 import org.apache.xpath.patterns.NodeTest; 032 import org.apache.xpath.patterns.StepPattern; 033 034 /** 035 * This class treats a 036 * <a href="http://www.w3.org/TR/xpath#location-paths">LocationPath</a> as a 037 * filtered iteration over the tree, evaluating each node in a super axis 038 * traversal against the LocationPath interpreted as a match pattern. This 039 * class is useful to find nodes in document order that are complex paths 040 * whose steps probably criss-cross each other. 041 */ 042 public class MatchPatternIterator extends LocPathIterator 043 { 044 static final long serialVersionUID = -5201153767396296474L; 045 046 /** This is the select pattern, translated into a match pattern. */ 047 protected StepPattern m_pattern; 048 049 /** The traversal axis from where the nodes will be filtered. */ 050 protected int m_superAxis = -1; 051 052 /** The DTM inner traversal class, that corresponds to the super axis. */ 053 protected DTMAxisTraverser m_traverser; 054 055 /** DEBUG flag for diagnostic dumps. */ 056 private static final boolean DEBUG = false; 057 058 // protected int m_nsElemBase = DTM.NULL; 059 060 /** 061 * Create a LocPathIterator object, including creation 062 * of step walkers from the opcode list, and call back 063 * into the Compiler to create predicate expressions. 064 * 065 * @param compiler The Compiler which is creating 066 * this expression. 067 * @param opPos The position of this iterator in the 068 * opcode list from the compiler. 069 * @param analysis Analysis bits that give general information about the 070 * LocationPath. 071 * 072 * @throws javax.xml.transform.TransformerException 073 */ 074 MatchPatternIterator(Compiler compiler, int opPos, int analysis) 075 throws javax.xml.transform.TransformerException 076 { 077 078 super(compiler, opPos, analysis, false); 079 080 int firstStepPos = OpMap.getFirstChildPos(opPos); 081 082 m_pattern = WalkerFactory.loadSteps(this, compiler, firstStepPos, 0); 083 084 boolean fromRoot = false; 085 boolean walkBack = false; 086 boolean walkDescendants = false; 087 boolean walkAttributes = false; 088 089 if (0 != (analysis & (WalkerFactory.BIT_ROOT | 090 WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT))) 091 fromRoot = true; 092 093 if (0 != (analysis 094 & (WalkerFactory.BIT_ANCESTOR 095 | WalkerFactory.BIT_ANCESTOR_OR_SELF 096 | WalkerFactory.BIT_PRECEDING 097 | WalkerFactory.BIT_PRECEDING_SIBLING 098 | WalkerFactory.BIT_FOLLOWING 099 | WalkerFactory.BIT_FOLLOWING_SIBLING 100 | WalkerFactory.BIT_PARENT | WalkerFactory.BIT_FILTER))) 101 walkBack = true; 102 103 if (0 != (analysis 104 & (WalkerFactory.BIT_DESCENDANT_OR_SELF 105 | WalkerFactory.BIT_DESCENDANT 106 | WalkerFactory.BIT_CHILD))) 107 walkDescendants = true; 108 109 if (0 != (analysis 110 & (WalkerFactory.BIT_ATTRIBUTE | WalkerFactory.BIT_NAMESPACE))) 111 walkAttributes = true; 112 113 if(false || DEBUG) 114 { 115 System.out.print("analysis: "+Integer.toBinaryString(analysis)); 116 System.out.println(", "+WalkerFactory.getAnalysisString(analysis)); 117 } 118 119 if(fromRoot || walkBack) 120 { 121 if(walkAttributes) 122 { 123 m_superAxis = Axis.ALL; 124 } 125 else 126 { 127 m_superAxis = Axis.DESCENDANTSFROMROOT; 128 } 129 } 130 else if(walkDescendants) 131 { 132 if(walkAttributes) 133 { 134 m_superAxis = Axis.ALLFROMNODE; 135 } 136 else 137 { 138 m_superAxis = Axis.DESCENDANTORSELF; 139 } 140 } 141 else 142 { 143 m_superAxis = Axis.ALL; 144 } 145 if(false || DEBUG) 146 { 147 System.out.println("axis: "+Axis.getNames(m_superAxis)); 148 } 149 150 } 151 152 153 /** 154 * Initialize the context values for this expression 155 * after it is cloned. 156 * 157 * @param context The XPath runtime context for this 158 * transformation. 159 */ 160 public void setRoot(int context, Object environment) 161 { 162 super.setRoot(context, environment); 163 m_traverser = m_cdtm.getAxisTraverser(m_superAxis); 164 } 165 166 /** 167 * Detaches the iterator from the set which it iterated over, releasing 168 * any computational resources and placing the iterator in the INVALID 169 * state. After<code>detach</code> has been invoked, calls to 170 * <code>nextNode</code> or<code>previousNode</code> will raise the 171 * exception INVALID_STATE_ERR. 172 */ 173 public void detach() 174 { 175 if(m_allowDetach) 176 { 177 m_traverser = null; 178 179 // Always call the superclass detach last! 180 super.detach(); 181 } 182 } 183 184 /** 185 * Get the next node via getNextXXX. Bottlenecked for derived class override. 186 * @return The next node on the axis, or DTM.NULL. 187 */ 188 protected int getNextNode() 189 { 190 m_lastFetched = (DTM.NULL == m_lastFetched) 191 ? m_traverser.first(m_context) 192 : m_traverser.next(m_context, m_lastFetched); 193 return m_lastFetched; 194 } 195 196 /** 197 * Returns the next node in the set and advances the position of the 198 * iterator in the set. After a NodeIterator is created, the first call 199 * to nextNode() returns the first node in the set. 200 * @return The next <code>Node</code> in the set being iterated over, or 201 * <code>null</code> if there are no more members in that set. 202 */ 203 public int nextNode() 204 { 205 if(m_foundLast) 206 return DTM.NULL; 207 208 int next; 209 210 org.apache.xpath.VariableStack vars; 211 int savedStart; 212 if (-1 != m_stackFrame) 213 { 214 vars = m_execContext.getVarStack(); 215 216 // These three statements need to be combined into one operation. 217 savedStart = vars.getStackFrame(); 218 219 vars.setStackFrame(m_stackFrame); 220 } 221 else 222 { 223 // Yuck. Just to shut up the compiler! 224 vars = null; 225 savedStart = 0; 226 } 227 228 try 229 { 230 if(DEBUG) 231 System.out.println("m_pattern"+m_pattern.toString()); 232 233 do 234 { 235 next = getNextNode(); 236 237 if (DTM.NULL != next) 238 { 239 if(DTMIterator.FILTER_ACCEPT == acceptNode(next, m_execContext)) 240 break; 241 else 242 continue; 243 } 244 else 245 break; 246 } 247 while (next != DTM.NULL); 248 249 if (DTM.NULL != next) 250 { 251 if(DEBUG) 252 { 253 System.out.println("next: "+next); 254 System.out.println("name: "+m_cdtm.getNodeName(next)); 255 } 256 incrementCurrentPos(); 257 258 return next; 259 } 260 else 261 { 262 m_foundLast = true; 263 264 return DTM.NULL; 265 } 266 } 267 finally 268 { 269 if (-1 != m_stackFrame) 270 { 271 // These two statements need to be combined into one operation. 272 vars.setStackFrame(savedStart); 273 } 274 } 275 276 } 277 278 /** 279 * Test whether a specified node is visible in the logical view of a 280 * TreeWalker or NodeIterator. This function will be called by the 281 * implementation of TreeWalker and NodeIterator; it is not intended to 282 * be called directly from user code. 283 * @param n The node to check to see if it passes the filter or not. 284 * @return a constant to determine whether the node is accepted, 285 * rejected, or skipped, as defined above . 286 */ 287 public short acceptNode(int n, XPathContext xctxt) 288 { 289 290 try 291 { 292 xctxt.pushCurrentNode(n); 293 xctxt.pushIteratorRoot(m_context); 294 if(DEBUG) 295 { 296 System.out.println("traverser: "+m_traverser); 297 System.out.print("node: "+n); 298 System.out.println(", "+m_cdtm.getNodeName(n)); 299 // if(m_cdtm.getNodeName(n).equals("near-east")) 300 System.out.println("pattern: "+m_pattern.toString()); 301 m_pattern.debugWhatToShow(m_pattern.getWhatToShow()); 302 } 303 304 XObject score = m_pattern.execute(xctxt); 305 306 if(DEBUG) 307 { 308 // System.out.println("analysis: "+Integer.toBinaryString(m_analysis)); 309 System.out.println("score: "+score); 310 System.out.println("skip: "+(score == NodeTest.SCORE_NONE)); 311 } 312 313 // System.out.println("\n::acceptNode - score: "+score.num()+"::"); 314 return (score == NodeTest.SCORE_NONE) ? DTMIterator.FILTER_SKIP 315 : DTMIterator.FILTER_ACCEPT; 316 } 317 catch (javax.xml.transform.TransformerException se) 318 { 319 320 // TODO: Fix this. 321 throw new RuntimeException(se.getMessage()); 322 } 323 finally 324 { 325 xctxt.popCurrentNode(); 326 xctxt.popIteratorRoot(); 327 } 328 329 } 330 331 }