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