KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > pull > TreeWalker


1 package net.sf.saxon.pull;
2
3 import net.sf.saxon.event.PipelineConfiguration;
4 import net.sf.saxon.om.*;
5 import net.sf.saxon.tinytree.TinyNodeImpl;
6 import net.sf.saxon.tinytree.TinyTreeWalker;
7 import net.sf.saxon.trans.XPathException;
8 import net.sf.saxon.type.Type;
9 import net.sf.saxon.value.AtomicValue;
10
11 import javax.xml.transform.SourceLocator JavaDoc;
12 import java.util.Stack JavaDoc;
13
14 /**
15  * This implementation of the Saxon pull interface starts from any NodeInfo,
16  * and returns the events corresponding to that node and its descendants (including
17  * their attributes and namespaces). This works with any tree model: alternative
18  * implementations may be available that take advantage of particular implementations
19  * of the tree model.
20  */

21
22 public class TreeWalker implements PullProvider, SourceLocator JavaDoc {
23
24     private NodeInfo startNode;
25     private NodeInfo currentNode;
26     private int currentEvent;
27     private Stack JavaDoc iteratorStack = new Stack JavaDoc();
28     private PipelineConfiguration pipe;
29
30     /**
31      * Factory method to get a tree walker starting an a given node
32      * @param startNode the start node
33      * @return a PullProvider that delivers events associated with the subtree starting at the given node
34      */

35
36     public static PullProvider makeTreeWalker(NodeInfo startNode) {
37         if (startNode instanceof UnconstructedParent) {
38             return ((UnconstructedParent)startNode).getPuller();
39         }
40         if (startNode instanceof TinyNodeImpl) {
41             switch (startNode.getNodeKind()) {
42                 case Type.DOCUMENT:
43                 case Type.ELEMENT:
44                     return new TinyTreeWalker((TinyNodeImpl)startNode);
45                 default:
46                     return new PullFromIterator(SingletonIterator.makeIterator(startNode));
47             }
48
49         } else {
50             return new TreeWalker(startNode);
51         }
52     }
53
54     /**
55      * Private constructor: the class should be instantiated using the static factory method
56      * @param startNode
57      */

58
59     private TreeWalker(NodeInfo startNode) {
60         this.startNode = startNode;
61     }
62
63     /**
64      * Set configuration information. This must only be called before any events
65      * have been read.
66      */

67
68     public void setPipelineConfiguration(PipelineConfiguration pipe) {
69         this.pipe = pipe;
70     }
71
72     /**
73      * Get configuration information.
74      */

75
76     public PipelineConfiguration getPipelineConfiguration() {
77         return pipe;
78     }
79
80     /**
81      * Get the next event
82      *
83      * @return an integer code indicating the type of event. The code
84      * {@link #END_OF_INPUT} is returned if there are no more events to return.
85      */

86
87     public int next() throws XPathException {
88         switch(currentEvent) {
89             case START_OF_INPUT:
90                 currentNode = startNode;
91                 switch (currentNode.getNodeKind()) {
92                     case Type.DOCUMENT:
93                         currentEvent = START_DOCUMENT;
94                         break;
95                     case Type.ELEMENT:
96                         currentEvent = START_ELEMENT;
97                         break;
98                     case Type.TEXT:
99                         currentEvent = TEXT;
100                         break;
101                     case Type.COMMENT:
102                         currentEvent = COMMENT;
103                         break;
104                     case Type.PROCESSING_INSTRUCTION:
105                         currentEvent = PROCESSING_INSTRUCTION;
106                         break;
107                     case Type.ATTRIBUTE:
108                         currentEvent = ATTRIBUTE;
109                         break;
110                     case Type.NAMESPACE:
111                         currentEvent = NAMESPACE;
112                         break;
113                 }
114                 return currentEvent;
115
116             case START_DOCUMENT:
117             case START_ELEMENT:
118                 AxisIterator kids = currentNode.iterateAxis(Axis.CHILD);
119                 iteratorStack.push(kids);
120
121                 currentNode = (NodeInfo)kids.next();
122                 if (currentNode != null) {
123                     switch (currentNode.getNodeKind()) {
124                         case Type.ELEMENT:
125                             currentEvent = START_ELEMENT;
126                             break;
127                         case Type.TEXT:
128                             currentEvent = TEXT;
129                             break;
130                         case Type.COMMENT:
131                             currentEvent = COMMENT;
132                             break;
133                         case Type.PROCESSING_INSTRUCTION:
134                             currentEvent = PROCESSING_INSTRUCTION;
135                             break;
136                     }
137                     return currentEvent;
138                 } else {
139                     iteratorStack.pop();
140                     if (iteratorStack.isEmpty()) {
141                         currentNode = startNode;
142                     }
143                     if (currentEvent == START_DOCUMENT) {
144                         currentEvent = END_DOCUMENT;
145                     } else {
146                         currentEvent = END_ELEMENT;
147                     }
148                     return currentEvent;
149                 }
150             case TEXT:
151             case COMMENT:
152             case PROCESSING_INSTRUCTION:
153             case END_ELEMENT:
154                 if (iteratorStack.isEmpty()) {
155                     if (currentNode == startNode) {
156                         currentNode = null;
157                         currentEvent = END_OF_INPUT;
158                     } else {
159                         currentNode = startNode;
160                         if (currentNode.getNodeKind() == Type.ELEMENT) {
161                             currentEvent = END_ELEMENT;
162                         } else {
163                             currentEvent = END_DOCUMENT;
164                         }
165                     }
166                     return currentEvent;
167                 }
168                 do {
169                     AxisIterator siblings = (AxisIterator)iteratorStack.peek();
170                     currentNode = (NodeInfo)siblings.next();
171                     if (currentNode == null) {
172                         iteratorStack.pop();
173                         if (iteratorStack.isEmpty()) {
174                             currentNode = startNode;
175                             if (currentNode.getNodeKind() == Type.ELEMENT) {
176                                 currentEvent = END_ELEMENT;
177                             } else {
178                                 currentEvent = END_DOCUMENT;
179                             }
180                             return currentEvent;
181                         }
182                         AxisIterator uncles = (AxisIterator)iteratorStack.peek();
183                         currentNode = (NodeInfo)uncles.current();
184                         if (currentNode.getNodeKind() == Type.DOCUMENT) {
185                             currentEvent = END_DOCUMENT;
186                         } else {
187                             currentEvent = END_ELEMENT;
188                         }
189                         return currentEvent;
190                     } else {
191                         switch (currentNode.getNodeKind()) {
192                             case Type.ELEMENT:
193                                 currentEvent = START_ELEMENT;
194                                 break;
195                             case Type.TEXT:
196                                 currentEvent = TEXT;
197                                 break;
198                             case Type.COMMENT:
199                                 currentEvent = COMMENT;
200                                 break;
201                             case Type.PROCESSING_INSTRUCTION:
202                                 currentEvent = PROCESSING_INSTRUCTION;
203                                 break;
204                         }
205                         return currentEvent;
206                     }
207                 } while (true);
208
209             case ATTRIBUTE:
210             case NAMESPACE:
211             case END_DOCUMENT:
212                 currentEvent = END_OF_INPUT;
213                 return currentEvent;
214
215             case END_OF_INPUT:
216                 throw new IllegalStateException JavaDoc("Cannot call next() when input is exhausted");
217
218             default:
219                 throw new IllegalStateException JavaDoc("Unrecognized event " + currentEvent);
220
221         }
222     }
223
224     /**
225      * Get the event most recently returned by next(), or by other calls that change
226      * the position, for example getStringValue() and skipToMatchingEnd(). This
227      * method does not change the position of the PullProvider.
228      *
229      * @return the current event
230      */

231
232     public int current() {
233         return currentEvent;
234     }
235
236     /**
237      * Get the attributes associated with the current element. This method must
238      * be called only after a START_ELEMENT event has been notified. The contents
239      * of the returned AttributeCollection are guaranteed to remain unchanged
240      * until the next START_ELEMENT event, but may be modified thereafter. The object
241      * should not be modified by the client.
242      * <p/>
243      * <p>Attributes may be read before or after reading the namespaces of an element,
244      * but must not be read after the first child node has been read, or after calling
245      * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
246      *
247      * @return an AttributeCollection representing the attributes of the element
248      * that has just been notified.
249      */

250
251     public AttributeCollection getAttributes() throws XPathException {
252         if (currentNode.getNodeKind() == Type.ELEMENT) {
253             AttributeCollectionImpl atts = new AttributeCollectionImpl(getNamePool());
254             SequenceIterator iter = currentNode.iterateAxis(Axis.ATTRIBUTE);
255             while (true) {
256                 NodeInfo node = (NodeInfo)iter.next();
257                 if (node == null) {
258                     break;
259                 }
260                 atts.addAttribute(node.getNameCode(), node.getTypeAnnotation(), node.getStringValue(), 0, 0);
261             }
262             return atts;
263         } else {
264             throw new IllegalStateException JavaDoc("getAttributes() called when current event is not ELEMENT_START");
265         }
266     }
267
268     /**
269      * Get the namespace declarations associated with the current element. This method must
270      * be called only after a START_ELEMENT event has been notified. In the case of a top-level
271      * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
272      * is not included in the sequence being read), the NamespaceDeclarations object returned
273      * will contain a namespace declaration for each namespace that is in-scope for this element
274      * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
275      * a set of namespace declarations and undeclarations, representing the differences between
276      * this element and its parent.
277      * <p/>
278      * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
279      * <p/>
280      * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
281      * event, but may then be overwritten. The object should not be modified by the client.</p>
282      * <p/>
283      * <p>Namespaces may be read before or after reading the attributes of an element,
284      * but must not be read after the first child node has been read, or after calling
285      * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
286      */

287
288     public NamespaceDeclarations getNamespaceDeclarations() throws XPathException {
289         if (currentNode.getNodeKind() == Type.ELEMENT) {
290             if (iteratorStack.size() == 0) {
291                 // get all inscope namespaces for a top-level element in the sequence.
292
int[] codes = new NamespaceIterator(currentNode, null).getInScopeNamespaceCodes();
293                 return new NamespaceDeclarationsImpl(getNamePool(), codes);
294             } else {
295                 // only namespace declarations (and undeclarations) on this element are required
296
return new NamespaceDeclarationsImpl(getNamePool(), currentNode.getDeclaredNamespaces(nsBuffer));
297             }
298         }
299         throw new IllegalStateException JavaDoc("getNamespaceDeclarations() called when current event is not ELEMENT_START");
300     }
301
302     private int[] nsBuffer = new int[10];
303
304     /**
305      * Skip the current subtree. This method may be called only immediately after
306      * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
307      * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
308      * the event following the END_DOCUMENT or END_ELEMENT.
309      */

310
311     public int skipToMatchingEnd() throws XPathException {
312         // For this implementation, we simply leave the current node unchanged, and change
313
// the current event
314
switch (currentEvent) {
315             case START_DOCUMENT:
316                 currentEvent = END_DOCUMENT;
317                 return currentEvent;
318             case START_ELEMENT:
319                 currentEvent = END_ELEMENT;
320                 return currentEvent;
321             default:
322                 throw new IllegalStateException JavaDoc(
323                         "Cannot call skipToMatchingEnd() except when at start of element or document");
324         }
325     }
326
327     /**
328      * Close the event reader. This indicates that no further events are required.
329      * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
330      * been reported, but it is recommended to close it if reading terminates
331      * prematurely. Once an event reader has been closed, the effect of further
332      * calls on next() is undefined.
333      */

334
335     public void close() {
336         // no action
337
}
338
339     /**
340      * Get the namePool used to lookup all name codes and namespace codes
341      *
342      * @return the namePool
343      */

344
345     public NamePool getNamePool() {
346         return pipe.getConfiguration().getNamePool();
347     }
348
349     /**
350      * Get the nameCode identifying the name of the current node. This method
351      * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
352      * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
353      * including this one, it can also be used after {@link #END_ELEMENT}.
354      * If called at other times, the result is undefined and may result in an IllegalStateException.
355      * If called when the current node is an unnamed namespace node (a node representing the default namespace)
356      * the returned value is -1.
357      * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
358      * and namespace URI from the name pool.
359      */

360
361     public int getNameCode() {
362         return currentNode.getNameCode();
363     }
364
365     /**
366      * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
367      * it does not contain any information about the prefix: so two elements with the same fingerprint
368      * have the same name, excluding prefix. This method
369      * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
370      * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
371      * If called at other times, the result is undefined and may result in an IllegalStateException.
372      * If called when the current node is an unnamed namespace node (a node representing the default namespace)
373      * the returned value is -1.
374      *
375      * @return the fingerprint. The fingerprint can be used to obtain the local name
376      * and namespace URI from the name pool.
377      */

378
379     public int getFingerprint() {
380         return currentNode.getFingerprint();
381     }
382
383     /**
384      * Get the string value of the current attribute, text node, processing-instruction,
385      * or atomic value.
386      * This method cannot be used to obtain the string value of an element, or of a namespace
387      * node. If the most recent event was anything other than {@link #START_ELEMENT}, {@link #TEXT},
388      * {@link #PROCESSING_INSTRUCTION}, or {@link #ATOMIC_VALUE}, the result is undefined.
389      */

390
391     public CharSequence JavaDoc getStringValue() throws XPathException {
392         if (currentNode.getNodeKind() == Type.ELEMENT) {
393             skipToMatchingEnd();
394         }
395         return currentNode.getStringValueCS();
396     }
397
398     /**
399      * Get the type annotation of the current attribute or element node, or atomic value.
400      * The result of this method is undefined unless the most recent event was START_ELEMENT,
401      * START_CONTENT, ATTRIBUTE, or ATOMIC_VALUE.
402      *
403      * @return the type code. This code is the fingerprint of a type name, which may be
404      * resolved to a {@link net.sf.saxon.type.SchemaType} by access to the Configuration.
405      */

406
407     public int getTypeAnnotation() {
408         return currentNode.getTypeAnnotation();
409     }
410
411     /**
412      * Get an atomic value. This call may be used only when the last event reported was
413      * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
414      * a free-standing atomic value; it is never used when reading the content of a node.
415      */

416
417     public AtomicValue getAtomicValue() {
418         throw new IllegalStateException JavaDoc();
419     }
420
421     /**
422      * Get the location of the current event.
423      * For an event stream representing a real document, the location information
424      * should identify the location in the lexical XML source. For a constructed document, it should
425      * identify the location in the query or stylesheet that caused the node to be created.
426      * A value of null can be returned if no location information is available.
427      */

428
429     public SourceLocator JavaDoc getSourceLocator() {
430         return this;
431     }
432
433     /**
434      * Return the public identifier for the current document event.
435      * <p/>
436      * <p>The return value is the public identifier of the document
437      * entity or of the external parsed entity in which the markup that
438      * triggered the event appears.</p>
439      *
440      * @return A string containing the public identifier, or
441      * null if none is available.
442      * @see #getSystemId
443      */

444     public String JavaDoc getPublicId() {
445         return null;
446     }
447
448     /**
449      * Return the system identifier for the current document event.
450      * <p/>
451      * <p>The return value is the system identifier of the document
452      * entity or of the external parsed entity in which the markup that
453      * triggered the event appears.</p>
454      * <p/>
455      * <p>If the system identifier is a URL, the parser must resolve it
456      * fully before passing it to the application.</p>
457      *
458      * @return A string containing the system identifier, or null
459      * if none is available.
460      * @see #getPublicId
461      */

462     public String JavaDoc getSystemId() {
463         return currentNode.getSystemId();
464     }
465
466     /**
467      * Return the line number where the current document event ends.
468      * <p/>
469      * <p><strong>Warning:</strong> The return value from the method
470      * is intended only as an approximation for the sake of error
471      * reporting; it is not intended to provide sufficient information
472      * to edit the character content of the original XML document.</p>
473      * <p/>
474      * <p>The return value is an approximation of the line number
475      * in the document entity or external parsed entity where the
476      * markup that triggered the event appears.</p>
477      *
478      * @return The line number, or -1 if none is available.
479      * @see #getColumnNumber
480      */

481     public int getLineNumber() {
482         return currentNode.getLineNumber();
483     }
484
485     /**
486      * Return the character position where the current document event ends.
487      * <p/>
488      * <p><strong>Warning:</strong> The return value from the method
489      * is intended only as an approximation for the sake of error
490      * reporting; it is not intended to provide sufficient information
491      * to edit the character content of the original XML document.</p>
492      * <p/>
493      * <p>The return value is an approximation of the column number
494      * in the document entity or external parsed entity where the
495      * markup that triggered the event appears.</p>
496      *
497      * @return The column number, or -1 if none is available.
498      * @see #getLineNumber
499      */

500     public int getColumnNumber() {
501         return -1;
502     }
503 }
504
505 //
506
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
507
// you may not use this file except in compliance with the License. You may obtain a copy of the
508
// License at http://www.mozilla.org/MPL/
509
//
510
// Software distributed under the License is distributed on an "AS IS" basis,
511
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
512
// See the License for the specific language governing rights and limitations under the License.
513
//
514
// The Original Code is: all this file.
515
//
516
// The Initial Developer of the Original Code is Michael H. Kay.
517
//
518
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
519
//
520
// Contributor(s): none.
521
//
Popular Tags