KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jxpath > ri > EvalContext


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.jxpath.ri;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Collections JavaDoc;
20 import java.util.Comparator JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.NoSuchElementException JavaDoc;
25
26 import org.apache.commons.jxpath.BasicNodeSet;
27 import org.apache.commons.jxpath.ExpressionContext;
28 import org.apache.commons.jxpath.JXPathContext;
29 import org.apache.commons.jxpath.JXPathException;
30 import org.apache.commons.jxpath.NodeSet;
31 import org.apache.commons.jxpath.Pointer;
32 import org.apache.commons.jxpath.ri.axes.RootContext;
33 import org.apache.commons.jxpath.ri.model.NodePointer;
34
35 /**
36  * An XPath evaluation context.
37  *
38  * When evaluating a path, a chain of EvalContexts is created, each context in
39  * the chain representing a step of the path. Subclasses of EvalContext
40  * implement behavior of various XPath axes: "child::", "parent::" etc.
41  *
42  * @author Dmitri Plotnikov
43  * @version $Revision: 1.30 $ $Date: 2004/03/25 05:42:01 $
44  */

45 public abstract class EvalContext implements ExpressionContext, Iterator JavaDoc {
46     protected EvalContext parentContext;
47     protected RootContext rootContext;
48     protected int position = 0;
49     private boolean startedSetIteration = false;
50     private boolean done = false;
51     private boolean hasPerformedIteratorStep = false;
52     private Iterator JavaDoc pointerIterator;
53
54     // Sorts in the reverse order to the one defined by the Comparable
55
// interface.
56
private static final Comparator JavaDoc REVERSE_COMPARATOR = new Comparator JavaDoc() {
57         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
58             return ((Comparable JavaDoc) o2).compareTo(o1);
59         }
60     };
61
62     public EvalContext(EvalContext parentContext) {
63         this.parentContext = parentContext;
64     }
65
66     public Pointer getContextNodePointer() {
67         return getCurrentNodePointer();
68     }
69
70     public JXPathContext getJXPathContext() {
71         return getRootContext().getJXPathContext();
72     }
73
74     public int getPosition() {
75         return position;
76     }
77
78     /**
79      * Determines the document order for this context.
80      *
81      * @return 1 ascending order, -1 descending order,
82      * 0 - does not require ordering
83      */

84     public int getDocumentOrder() {
85         if (parentContext != null && parentContext.isChildOrderingRequired()) {
86             return 1;
87         }
88         return 0;
89     }
90     
91     /**
92      * Even if this context has the natural ordering and therefore does
93      * not require collecting and sorting all nodes prior to returning them,
94      * such operation may be required for any child context.
95      */

96     public boolean isChildOrderingRequired() {
97         // Default behavior: if this context needs to be ordered,
98
// the children need to be ordered too
99
if (getDocumentOrder() != 0) {
100             return true;
101         }
102         return false;
103     }
104
105     /**
106      * Returns true if there are mode nodes matching the context's constraints.
107      */

108     public boolean hasNext() {
109         if (pointerIterator != null) {
110             return pointerIterator.hasNext();
111         }
112
113         if (getDocumentOrder() != 0) {
114             return constructIterator();
115         }
116         else {
117             if (!done && !hasPerformedIteratorStep) {
118                 performIteratorStep();
119             }
120             return !done;
121         }
122     }
123
124     /**
125      * Returns the next node pointer in the context
126      */

127     public Object JavaDoc next() {
128         if (pointerIterator != null) {
129             return pointerIterator.next();
130         }
131
132         if (getDocumentOrder() != 0) {
133             if (!constructIterator()) {
134                 throw new NoSuchElementException JavaDoc();
135             }
136             return pointerIterator.next();
137         }
138         else {
139             if (!done && !hasPerformedIteratorStep) {
140                 performIteratorStep();
141             }
142             if (done) {
143                 throw new NoSuchElementException JavaDoc();
144             }
145             hasPerformedIteratorStep = false;
146             return getCurrentNodePointer();
147         }
148     }
149
150     /**
151      * Moves the iterator forward by one position
152      */

153     private void performIteratorStep() {
154         done = true;
155         if (position != 0 && nextNode()) {
156             done = false;
157         }
158         else {
159             while (nextSet()) {
160                 if (nextNode()) {
161                     done = false;
162                     break;
163                 }
164             }
165         }
166         hasPerformedIteratorStep = true;
167     }
168
169     /**
170      * Operation is not supported
171      */

172     public void remove() {
173         throw new UnsupportedOperationException JavaDoc(
174             "JXPath iterators cannot remove nodes");
175     }
176
177     private boolean constructIterator() {
178         HashSet JavaDoc set = new HashSet JavaDoc();
179         ArrayList JavaDoc list = new ArrayList JavaDoc();
180         while (nextSet()) {
181             while (nextNode()) {
182                 NodePointer pointer = getCurrentNodePointer();
183                 if (!set.contains(pointer)) {
184 // Pointer cln = (Pointer) pointer.clone();
185
set.add(pointer);
186                     list.add(pointer);
187                 }
188             }
189         }
190         if (list.isEmpty()) {
191             return false;
192         }
193
194         if (getDocumentOrder() == 1) {
195             Collections.sort(list);
196         }
197         else {
198             Collections.sort(list, REVERSE_COMPARATOR);
199         }
200         pointerIterator = list.iterator();
201         return true;
202     }
203
204     /**
205      * Returns the list of all Pointers in this context for the current
206      * position of the parent context.
207      */

208     public List JavaDoc getContextNodeList() {
209         int pos = position;
210         if (pos != 0) {
211             reset();
212         }
213         List JavaDoc list = new ArrayList JavaDoc();
214         while (nextNode()) {
215             list.add(getCurrentNodePointer());
216         }
217         if (pos != 0) {
218             setPosition(pos);
219         }
220         else {
221             reset();
222         }
223         return list;
224     }
225
226     /**
227      * Returns the list of all Pointers in this context for all positions
228      * of the parent contexts. If there was an ongoing iteration over
229      * this context, the method should not be called.
230      */

231     public NodeSet getNodeSet() {
232         if (position != 0) {
233             throw new JXPathException(
234                 "Simultaneous operations: "
235                     + "should not request pointer list while "
236                     + "iterating over an EvalContext");
237         }
238         BasicNodeSet set = new BasicNodeSet();
239         while (nextSet()) {
240             while (nextNode()) {
241                 set.add((Pointer)getCurrentNodePointer().clone());
242             }
243         }
244
245         return set;
246     }
247     
248     /**
249      * Typically returns the NodeSet by calling getNodeSet(),
250      * but will be overridden for contexts that more naturally produce
251      * individual values, e.g. VariableContext
252      */

253     public Object JavaDoc getValue() {
254         return getNodeSet();
255     }
256
257     public String JavaDoc toString() {
258         Pointer ptr = getContextNodePointer();
259         if (ptr == null) {
260             return "Empty expression context";
261         }
262         else {
263             return "Expression context [" + getPosition() + "] " + ptr.asPath();
264         }
265     }
266
267     /**
268      * Returns the root context of the path, which provides easy
269      * access to variables and functions.
270      */

271     public RootContext getRootContext() {
272         if (rootContext == null) {
273             rootContext = parentContext.getRootContext();
274         }
275         return rootContext;
276     }
277
278     /**
279      * Sets current position = 0, which is the pre-iteration state.
280      */

281     public void reset() {
282         position = 0;
283     }
284
285     public int getCurrentPosition() {
286         return position;
287     }
288
289     /**
290      * Returns the first encountered Pointer that matches the current
291      * context's criteria.
292      */

293     public Pointer getSingleNodePointer() {
294         reset();
295         while (nextSet()) {
296             if (nextNode()) {
297                 return getCurrentNodePointer();
298             }
299         }
300         return null;
301     }
302
303     /**
304      * Returns the current context node. Undefined before the beginning
305      * of the iteration.
306      */

307     public abstract NodePointer getCurrentNodePointer();
308
309     /**
310      * Returns true if there is another sets of objects to interate over.
311      * Resets the current position and node.
312      */

313     public boolean nextSet() {
314         reset(); // Restart iteration within the set
315

316         // Most of the time you have one set per parent node
317
// First time this method is called, we should look for
318
// the first parent set that contains at least one node.
319
if (!startedSetIteration) {
320             startedSetIteration = true;
321             while (parentContext.nextSet()) {
322                 if (parentContext.nextNode()) {
323                     return true;
324                 }
325             }
326             return false;
327         }
328
329         // In subsequent calls, we see if the parent context
330
// has any nodes left in the current set
331
if (parentContext.nextNode()) {
332             return true;
333         }
334
335         // If not, we look for the next set that contains
336
// at least one node
337
while (parentContext.nextSet()) {
338             if (parentContext.nextNode()) {
339                 return true;
340             }
341         }
342         return false;
343     }
344
345     /**
346      * Returns true if there is another object in the current set.
347      * Switches the current position and node to the next object.
348      */

349     public abstract boolean nextNode();
350
351     /**
352      * Moves the current position to the specified index. Used with integer
353      * predicates to quickly get to the n'th element of the node set.
354      * Returns false if the position is out of the node set range.
355      * You can call it with 0 as the position argument to restart the iteration.
356      */

357     public boolean setPosition(int position) {
358         this.position = position;
359         return true;
360     }
361 }
Popular Tags