KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > tinytree > TinyTreeWalker


1 package net.sf.saxon.tinytree;
2
3 import net.sf.saxon.event.PipelineConfiguration;
4 import net.sf.saxon.om.*;
5 import net.sf.saxon.pull.PullProvider;
6 import net.sf.saxon.trans.XPathException;
7 import net.sf.saxon.type.Type;
8 import net.sf.saxon.value.AtomicValue;
9 import net.sf.saxon.style.StandardNames;
10
11 import javax.xml.transform.SourceLocator JavaDoc;
12
13 /**
14  * This implementation of the Saxon pull interface starts from a document, element,
15  * text, comment, or processing-instruction node in a TinyTree,
16  * and returns the events corresponding to that node and its descendants (including
17  * their attributes and namespaces). The class performs the same function as
18  * the general-purpose {@link net.sf.saxon.pull.TreeWalker} class, but is
19  * specialized to exploit the TinyTree data structure: in particular, it never
20  * materializes any Node objects.
21  */

22
23 public class TinyTreeWalker implements PullProvider, SourceLocator JavaDoc {
24
25     private int startNode;
26     private int currentNode;
27     private int currentEvent;
28     private TinyTree tree;
29     private PipelineConfiguration pipe;
30     private NamespaceDeclarationsImpl nsDeclarations;
31     private int[] nsBuffer = new int[10];
32
33     /**
34      * Create a TinyTreeWalker to return events associated with a tree or subtree
35      * @param startNode the root of the tree or subtree. Must be a document, element, text,
36      * comment, or processing-instruction node.
37      * @throws IllegalArgumentException if the start node is an attribute or namespace node.
38      */

39
40     public TinyTreeWalker(TinyNodeImpl startNode) {
41         int kind = startNode.getNodeKind();
42         if (kind == Type.ATTRIBUTE || kind == Type.NAMESPACE) {
43             throw new IllegalArgumentException JavaDoc("TinyTreeWalker cannot start at an attribute or namespace node");
44         }
45         this.startNode = startNode.nodeNr;
46         this.tree = startNode.tree;
47         this.nsDeclarations = new NamespaceDeclarationsImpl();
48         nsDeclarations.setNamePool(startNode.getNamePool());
49     }
50
51     /**
52      * Set configuration information. This must only be called before any events
53      * have been read.
54      */

55
56     public void setPipelineConfiguration(PipelineConfiguration pipe) {
57         this.pipe = pipe;
58     }
59
60     /**
61      * Get configuration information.
62      */

63
64     public PipelineConfiguration getPipelineConfiguration() {
65         return pipe;
66     }
67
68     /**
69      * Get the next event
70      *
71      * @return an integer code indicating the type of event. The code
72      * {@link #END_OF_INPUT} is returned if there are no more events to return.
73      */

74
75     public int next() throws XPathException {
76         switch(currentEvent) {
77             case START_OF_INPUT:
78                 currentNode = startNode;
79                 switch (tree.nodeKind[currentNode]) {
80                     case Type.DOCUMENT:
81                         currentEvent = START_DOCUMENT;
82                         break;
83                     case Type.ELEMENT:
84                         currentEvent = START_ELEMENT;
85                         break;
86                     case Type.TEXT:
87                         currentEvent = TEXT;
88                         break;
89                     case Type.COMMENT:
90                         currentEvent = COMMENT;
91                         break;
92                     case Type.PROCESSING_INSTRUCTION:
93                         currentEvent = PROCESSING_INSTRUCTION;
94                         break;
95                     case Type.PARENT_POINTER:
96                         throw new IllegalStateException JavaDoc("Current node is a parent-pointer pseudo-node");
97                 }
98                 return currentEvent;
99
100             case START_DOCUMENT:
101             case START_ELEMENT:
102
103                 if (tree.depth[currentNode+1] > tree.depth[currentNode]) {
104                     // the current element or document has children: move to the first child
105
switch (tree.nodeKind[++currentNode]) {
106                         case Type.ELEMENT:
107                             currentEvent = START_ELEMENT;
108                             break;
109                         case Type.TEXT:
110                             currentEvent = TEXT;
111                             break;
112                         case Type.COMMENT:
113                             currentEvent = COMMENT;
114                             break;
115                         case Type.PROCESSING_INSTRUCTION:
116                             currentEvent = PROCESSING_INSTRUCTION;
117                             break;
118                         case Type.PARENT_POINTER:
119                             throw new IllegalStateException JavaDoc("First child node must not be a parent-pointer pseudo-node");
120                     }
121                     return currentEvent;
122                 } else {
123                     if (currentEvent == START_DOCUMENT) {
124                         currentEvent = END_DOCUMENT;
125                     } else {
126                         currentEvent = END_ELEMENT;
127                     }
128                     return currentEvent;
129                 }
130             case END_ELEMENT:
131             case TEXT:
132             case COMMENT:
133             case PROCESSING_INSTRUCTION:
134                 if (currentNode == startNode) {
135                     currentEvent = END_OF_INPUT;
136                     return currentEvent;
137                 }
138                 int next = tree.next[currentNode];
139                 if (next > currentNode) {
140                     // this node has a following sibling
141
currentNode = tree.next[currentNode];
142                     do {
143                         switch (tree.nodeKind[currentNode]) {
144                             case Type.ELEMENT:
145                                 currentEvent = START_ELEMENT;
146                                 break;
147                             case Type.TEXT:
148                                 currentEvent = TEXT;
149                                 break;
150                             case Type.COMMENT:
151                                 currentEvent = COMMENT;
152                                 break;
153                             case Type.PROCESSING_INSTRUCTION:
154                                 currentEvent = PROCESSING_INSTRUCTION;
155                                 break;
156                             case Type.PARENT_POINTER:
157                                 // skip this pseudo-node
158
currentEvent = -1;
159                                 currentNode++;
160                                 break;
161                         }
162                     } while (currentEvent == -1);
163                     return currentEvent;
164                 } else {
165                     // return to the parent element or document
166
currentNode = next;
167                     if (currentNode == -1) {
168                         // indicates we were at the END_ELEMENT of a parentless element node
169
currentEvent = END_OF_INPUT;
170                         return currentEvent;
171                     }
172                     switch (tree.nodeKind[currentNode]) {
173                         case Type.ELEMENT:
174                             currentEvent = END_ELEMENT;
175                             break;
176                         case Type.DOCUMENT:
177                             currentEvent = END_DOCUMENT;
178                             break;
179                     }
180                     return currentEvent;
181                 }
182
183             case ATTRIBUTE:
184             case NAMESPACE:
185             case END_DOCUMENT:
186                 currentEvent = END_OF_INPUT;
187                 return currentEvent;
188
189             case END_OF_INPUT:
190                 throw new IllegalStateException JavaDoc("Cannot call next() when input is exhausted");
191
192             default:
193                 throw new IllegalStateException JavaDoc("Unrecognized event " + currentEvent);
194
195         }
196     }
197
198     /**
199      * Get the event most recently returned by next(), or by other calls that change
200      * the position, for example getStringValue() and skipToMatchingEnd(). This
201      * method does not change the position of the PullProvider.
202      *
203      * @return the current event
204      */

205
206     public int current() {
207         return currentEvent;
208     }
209
210     /**
211      * Get the attributes associated with the current element. This method must
212      * be called only after a START_ELEMENT event has been notified. The contents
213      * of the returned AttributeCollection are guaranteed to remain unchanged
214      * until the next START_ELEMENT event, but may be modified thereafter. The object
215      * should not be modified by the client.
216      * <p/>
217      * <p>Attributes may be read before or after reading the namespaces of an element,
218      * but must not be read after the first child node has been read, or after calling
219      * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
220      *
221      * @return an AttributeCollection representing the attributes of the element
222      * that has just been notified.
223      */

224
225     public AttributeCollection getAttributes() throws XPathException {
226         if (tree.nodeKind[currentNode] == Type.ELEMENT) {
227             if (tree.alpha[currentNode] == -1) {
228                 return AttributeCollectionImpl.EMPTY_ATTRIBUTE_COLLECTION;
229             }
230             return new TinyAttributeCollection(tree, currentNode);
231         } else {
232             throw new IllegalStateException JavaDoc("getAttributes() called when current event is not ELEMENT_START");
233         }
234     }
235
236     /**
237      * Get the namespace declarations associated with the current element. This method must
238      * be called only after a START_ELEMENT event has been notified. In the case of a top-level
239      * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
240      * is not included in the sequence being read), the NamespaceDeclarations object returned
241      * will contain a namespace declaration for each namespace that is in-scope for this element
242      * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
243      * a set of namespace declarations and undeclarations, representing the differences between
244      * this element and its parent.
245      * <p/>
246      * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
247      * <p/>
248      * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
249      * event, but may then be overwritten. The object should not be modified by the client.</p>
250      * <p/>
251      * <p>Namespaces may be read before or after reading the attributes of an element,
252      * but must not be read after the first child node has been read, or after calling
253      * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
254      */

255
256     public NamespaceDeclarations getNamespaceDeclarations() throws XPathException {
257         if (tree.nodeKind[currentNode] == Type.ELEMENT) {
258             int[] decl;
259             if (currentNode == startNode) {
260                 // get all inscope namespaces for a top-level element in the sequence.
261
decl = TinyElementImpl.getInScopeNamespaces(tree, currentNode, nsBuffer);
262             } else {
263                 // only namespace declarations (and undeclarations) on this element are required
264
decl = TinyElementImpl.getDeclaredNamespaces(tree, currentNode, nsBuffer);
265             }
266             nsDeclarations.setNamespaceCodes(decl);
267             return nsDeclarations;
268         }
269         throw new IllegalStateException JavaDoc("getNamespaceDeclarations() called when current event is not START_ELEMENT");
270     }
271
272     /**
273      * Skip the current subtree. This method may be called only immediately after
274      * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
275      * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
276      * the event following the END_DOCUMENT or END_ELEMENT.
277      */

278
279     public int skipToMatchingEnd() throws XPathException {
280         // For this implementation, we simply leave the current node unchanged, and change
281
// the current event
282
switch (currentEvent) {
283             case START_DOCUMENT:
284                 currentEvent = END_DOCUMENT;
285                 return currentEvent;
286             case START_ELEMENT:
287                 currentEvent = END_ELEMENT;
288                 return currentEvent;
289             default:
290                 throw new IllegalStateException JavaDoc(
291                         "Cannot call skipToMatchingEnd() except when at start of element or document");
292
293         }
294     }
295
296     /**
297      * Close the event reader. This indicates that no further events are required.
298      * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
299      * been reported, but it is recommended to close it if reading terminates
300      * prematurely. Once an event reader has been closed, the effect of further
301      * calls on next() is undefined.
302      */

303
304     public void close() {
305         // no action
306
}
307
308     /**
309      * Get the namePool used to lookup all name codes and namespace codes
310      *
311      * @return the namePool
312      */

313
314     public NamePool getNamePool() {
315         return pipe.getConfiguration().getNamePool();
316     }
317
318     /**
319      * Get the nameCode identifying the name of the current node. This method
320      * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
321      * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
322      * including this one, it can also be used after {@link #END_ELEMENT}.
323      * If called at other times, the result is undefined and may result in an IllegalStateException.
324      * If called when the current node is an unnamed namespace node (a node representing the default namespace)
325      * the returned value is -1.
326      * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
327      * and namespace URI from the name pool.
328      */

329
330     public int getNameCode() {
331         switch (currentEvent) {
332             case START_ELEMENT:
333             case PROCESSING_INSTRUCTION:
334             case END_ELEMENT:
335                 return tree.nameCode[currentNode];
336             default:
337                 throw new IllegalStateException JavaDoc("getNameCode() called when its value is undefined");
338         }
339     }
340
341     /**
342      * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
343      * it does not contain any information about the prefix: so two elements with the same fingerprint
344      * have the same name, excluding prefix. This method
345      * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
346      * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
347      * If called at other times, the result is undefined and may result in an IllegalStateException.
348      * If called when the current node is an unnamed namespace node (a node representing the default namespace)
349      * the returned value is -1.
350      *
351      * @return the fingerprint. The fingerprint can be used to obtain the local name
352      * and namespace URI from the name pool.
353      */

354
355     public int getFingerprint() {
356         int nc = getNameCode();
357         if (nc == -1) {
358             return -1;
359         } else {
360             return nc & NamePool.FP_MASK;
361         }
362     }
363
364     /**
365      * Get the string value of the current attribute, text node, processing-instruction,
366      * or atomic value.
367      * This method cannot be used to obtain the string value of an element, or of a namespace
368      * node. If the most recent event was anything other than {@link #START_ELEMENT}, {@link #TEXT},
369      * {@link #PROCESSING_INSTRUCTION}, or {@link #ATOMIC_VALUE}, the result is undefined.
370      */

371
372     public CharSequence JavaDoc getStringValue() throws XPathException {
373         switch (tree.nodeKind[currentNode]) {
374             case Type.TEXT:
375                 return TinyTextImpl.getStringValue(tree, currentNode);
376             case Type.COMMENT:
377             case Type.PROCESSING_INSTRUCTION:
378                 // sufficiently rare that instantiating the node is OK
379
return tree.getNode(currentNode).getStringValue();
380             case Type.ELEMENT:
381                 currentEvent = END_ELEMENT;
382                 return TinyParentNodeImpl.getStringValue(tree, currentNode);
383             case Type.PARENT_POINTER:
384                 throw new IllegalStateException JavaDoc("Trying to get string value of a parent-pointer pseudo node");
385         }
386         return null;
387     }
388
389     /**
390      * Get an atomic value. This call may be used only when the last event reported was
391      * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
392      * a free-standing atomic value; it is never used when reading the content of a node.
393      */

394
395     public AtomicValue getAtomicValue() {
396         throw new IllegalStateException JavaDoc();
397     }
398
399     /**
400      * Get the type annotation of the current attribute or element node, or atomic value.
401      * The result of this method is undefined unless the most recent event was START_ELEMENT,
402      * START_CONTENT, ATTRIBUTE, or ATOMIC_VALUE.
403      *
404      * @return the type code. This code is the fingerprint of a type name, which may be
405      * resolved to a {@link net.sf.saxon.type.SchemaType} by access to the Configuration.
406      */

407
408     public int getTypeAnnotation() {
409         if (tree.nodeKind[currentNode] != Type.ELEMENT) {
410             throw new IllegalStateException JavaDoc("getTypeAnnotation() called when current event is not ELEMENT_START or ");
411         }
412         if (tree.typeCodeArray == null) {
413             return StandardNames.XDT_UNTYPED;
414         }
415         return tree.typeCodeArray[currentNode];
416     }
417
418     /**
419      * Get the location of the current event.
420      * For an event stream representing a real document, the location information
421      * should identify the location in the lexical XML source. For a constructed document, it should
422      * identify the location in the query or stylesheet that caused the node to be created.
423      * A value of null can be returned if no location information is available.
424      */

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

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

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

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

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