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 }