KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > xom > NodeWrapper


1 package net.sf.saxon.xom;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.trans.XPathException;
5 import net.sf.saxon.event.Receiver;
6 import net.sf.saxon.om.*;
7 import net.sf.saxon.pattern.AnyNodeTest;
8 import net.sf.saxon.pattern.NameTest;
9 import net.sf.saxon.pattern.NodeKindTest;
10 import net.sf.saxon.pattern.NodeTest;
11 import net.sf.saxon.type.Type;
12 import net.sf.saxon.value.UntypedAtomicValue;
13 import net.sf.saxon.value.Value;
14 import nu.xom.*;
15
16 /**
17  * A node in the XML parse tree representing an XML element, character content,
18  * or attribute.
19  * <P>
20  * This is the implementation of the NodeInfo interface used as a wrapper for
21  * XOM nodes.
22  *
23  * @author Michael H. Kay
24  * @author Wolfgang Hoschek (ported net.sf.saxon.jdom to XOM)
25  */

26
27 public class NodeWrapper implements NodeInfo, VirtualNode, SiblingCountingNode {
28
29     protected Node node;
30
31     protected short nodeKind;
32
33     private NodeWrapper parent; // null means unknown
34

35     protected DocumentWrapper docWrapper;
36
37     protected int index; // -1 means unknown
38

39     /**
40      * This constructor is protected: nodes should be created using the wrap
41      * factory method on the DocumentWrapper class
42      *
43      * @param node
44      * The XOM node to be wrapped
45      * @param parent
46      * The NodeWrapper that wraps the parent of this node
47      * @param index
48      * Position of this node among its siblings
49      */

50     protected NodeWrapper(Node node, NodeWrapper parent, int index) {
51         this.node = node;
52         this.parent = parent;
53         this.index = index;
54     }
55
56     /**
57      * Factory method to wrap a XOM node with a wrapper that implements the
58      * Saxon NodeInfo interface.
59      *
60      * @param node
61      * The XOM node
62      * @param docWrapper
63      * The wrapper for the Document containing this node
64      * @return The new wrapper for the supplied node
65      */

66     protected final NodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper) {
67         return makeWrapper(node, docWrapper, null, -1);
68     }
69
70     /**
71      * Factory method to wrap a XOM node with a wrapper that implements the
72      * Saxon NodeInfo interface.
73      *
74      * @param node
75      * The XOM node
76      * @param docWrapper
77      * The wrapper for the Document containing this node
78      * @param parent
79      * The wrapper for the parent of the XOM node
80      * @param index
81      * The position of this node relative to its siblings
82      * @return The new wrapper for the supplied node
83      */

84
85     protected final NodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper,
86             NodeWrapper parent, int index) {
87
88         short kind;
89         if (node instanceof Element) {
90             kind = Type.ELEMENT;
91         } else if (node instanceof Attribute) {
92             kind = Type.ATTRIBUTE;
93         } else if (node instanceof Text) {
94             kind = Type.TEXT;
95         } else if (node instanceof Comment) {
96             kind = Type.COMMENT;
97         } else if (node instanceof ProcessingInstruction) {
98             kind = Type.PROCESSING_INSTRUCTION;
99         } else if (node instanceof Document) {
100             return docWrapper;
101         } else {
102             throwIllegalNode(node); // moved out of fast path to enable better inlining
103
return null; // keep compiler happy
104
}
105
106         NodeWrapper wrapper = new NodeWrapper(node, parent, index);
107         wrapper.nodeKind = kind;
108         wrapper.docWrapper = docWrapper;
109         return wrapper;
110     }
111
112     private static void throwIllegalNode(Node node) {
113         String JavaDoc str = node == null ?
114                 "NULL" :
115                 node.getClass() + " instance " + node.toString();
116         throw new IllegalArgumentException JavaDoc("Bad node type in XOM! " + str);
117     }
118
119     /**
120      * Get the configuration
121      */

122
123     public Configuration getConfiguration() {
124         return docWrapper.getConfiguration();
125     }
126
127     /**
128      * Get the underlying XOM node, to implement the VirtualNode interface
129      */

130
131     public Object JavaDoc getUnderlyingNode() {
132         return node;
133     }
134
135     /**
136      * Get the name pool for this node
137      *
138      * @return the NamePool
139      */

140
141     public NamePool getNamePool() {
142         return docWrapper.getNamePool();
143     }
144
145     /**
146      * Return the type of node.
147      *
148      * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
149      */

150
151     public int getNodeKind() {
152         return nodeKind;
153     }
154
155     /**
156      * Get the typed value of the item
157      */

158
159     public SequenceIterator getTypedValue() {
160         return SingletonIterator.makeIterator(new UntypedAtomicValue(
161                 getStringValueCS()));
162     }
163
164     /**
165      * Get the typed value. The result of this method will always be consistent with the method
166      * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
167      * more efficient, especially in the common case where the value is expected to be a singleton.
168      *
169      * @return the typed value. If requireSingleton is set to true, the result will always be an
170      * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
171      * values.
172      * @since 8.5
173      */

174
175     public Value atomize() throws XPathException {
176         return new UntypedAtomicValue(getStringValueCS());
177     }
178
179     /**
180      * Get the type annotation of this node, if any. Returns -1 for kinds of
181      * nodes that have no annotation, and for elements annotated as untyped, and
182      * attributes annotated as untypedAtomic.
183      *
184      * @return -1 (there is no type annotation)
185      * @return the type annotation of the node.
186      * @see net.sf.saxon.type.Type
187      */

188
189     public int getTypeAnnotation() {
190         return -1;
191     }
192
193     /**
194      * Determine whether this is the same node as another node. <br />
195      * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
196      *
197      * @return true if this Node object and the supplied Node object represent
198      * the same node in the tree.
199      */

200
201     public boolean isSameNodeInfo(NodeInfo other) {
202         if (other instanceof NodeWrapper) {
203             return node == ((NodeWrapper) other).node; // In XOM equality means identity
204
}
205         return false;
206     }
207
208     /**
209      * Get the System ID for the node.
210      *
211      * @return the System Identifier of the entity in the source document
212      * containing the node, or null if not known. Note this is not the
213      * same as the base URI: the base URI can be modified by xml:base,
214      * but the system ID cannot.
215      */

216
217     public String JavaDoc getSystemId() {
218         return docWrapper.baseURI;
219     }
220
221     public void setSystemId(String JavaDoc uri) {
222         docWrapper.baseURI = uri;
223     }
224
225     /**
226      * Get the Base URI for the node, that is, the URI used for resolving a
227      * relative URI contained in the node.
228      */

229
230     public String JavaDoc getBaseURI() {
231         return node.getBaseURI();
232     }
233
234     /**
235      * Get line number
236      *
237      * @return the line number of the node in its original source document; or
238      * -1 if not available
239      */

240
241     public int getLineNumber() {
242         return -1;
243     }
244
245     /**
246      * Determine the relative position of this node and another node, in
247      * document order. The other node will always be in the same document.
248      *
249      * @param other
250      * The other node, whose position is to be compared with this
251      * node
252      * @return -1 if this node precedes the other node, +1 if it follows the
253      * other node, or 0 if they are the same node. (In this case,
254      * isSameNode() will always return true, and the two nodes will
255      * produce the same result for generateId())
256      */

257
258     public int compareOrder(NodeInfo other) {
259         if (other instanceof NodeWrapper) {
260             return compareOrderFast(node,((NodeWrapper) other).node);
261 // }
262
// if (other instanceof SiblingCountingNode) {
263
// return Navigator.compareOrder(this, (SiblingCountingNode) other);
264
} else {
265             // it must be a namespace node
266
return -other.compareOrder(this);
267         }
268     }
269
270     private static int compareOrderFast(Node first, Node second) {
271         /*
272          * Unfortunately we do not have a sequence number for each node at hand;
273          * this would allow to turn the comparison into a simple sequence number
274          * subtraction. Walking the entire tree and batch-generating sequence
275          * numbers on the fly is no good option either. However, this rewritten
276          * implementation turns out to be more than fast enough.
277          */

278
279         // assert first != null && second != null
280
// assert first or second MUST NOT be namespace nodes
281
if (first == second) return 0;
282
283         ParentNode firstParent = first.getParent();
284         ParentNode secondParent = second.getParent();
285         if (firstParent == null) {
286             if (secondParent != null) return -1; // first node is the root
287
// both nodes are parentless, use arbitrary but fixed order:
288
return first.hashCode() - second.hashCode();
289         }
290
291         if (secondParent == null) return +1; // second node is the root
292

293         // do they have the same parent (common case)?
294
if (firstParent == secondParent) {
295             int i1 = firstParent.indexOf(first);
296             int i2 = firstParent.indexOf(second);
297
298             // note that attributes and namespaces are not children
299
// of their own parent (i = -1).
300
// attribute (if any) comes before child
301
if (i1 != -1) return (i2 != -1) ? i1 - i2 : +1;
302             if (i2 != -1) return -1;
303
304             // assert: i1 == -1 && i2 == -1
305
// i.e. both nodes are attributes
306
Element elem = (Element) firstParent;
307             for (int i = elem.getAttributeCount(); --i >= 0;) {
308                 Attribute attr = elem.getAttribute(i);
309                 if (attr == second) return -1;
310                 if (attr == first) return +1;
311             }
312             throw new IllegalStateException JavaDoc("should be unreachable");
313         }
314
315         // find the depths of both nodes in the tree
316
int depth1 = 0;
317         int depth2 = 0;
318         Node p1 = first;
319         Node p2 = second;
320         while (p1 != null) {
321             depth1++;
322             p1 = p1.getParent();
323             if (p1 == second) return +1;
324         }
325         while (p2 != null) {
326             depth2++;
327             p2 = p2.getParent();
328             if (p2 == first) return -1;
329         }
330
331         // move up one branch of the tree so we have two nodes on the same level
332
p1 = first;
333         while (depth1 > depth2) {
334             p1 = p1.getParent();
335             depth1--;
336         }
337         p2 = second;
338         while (depth2 > depth1) {
339             p2 = p2.getParent();
340             depth2--;
341         }
342
343         // now move up both branches in sync until we find a common parent
344
while (true) {
345             firstParent = p1.getParent();
346             secondParent = p2.getParent();
347             if (firstParent == null || secondParent == null) {
348                 // both nodes are documentless, use arbitrary but fixed order
349
// based on their root elements
350
return p1.hashCode() - p2.hashCode();
351                 // throw new NullPointerException("XOM tree compare - internal error");
352
}
353             if (firstParent == secondParent) {
354                 return firstParent.indexOf(p1) - firstParent.indexOf(p2);
355             }
356             p1 = firstParent;
357             p2 = secondParent;
358         }
359     }
360
361     /**
362      * Return the string value of the node. The interpretation of this depends
363      * on the type of node. For an element it is the accumulated character
364      * content of the element, including descendant elements.
365      *
366      * @return the string value of the node
367      */

368
369     public String JavaDoc getStringValue() {
370         return node.getValue();
371     }
372
373     /**
374      * Get the value of the item as a CharSequence. This is in some cases more efficient than
375      * the version of the method that returns a String.
376      */

377
378     public CharSequence JavaDoc getStringValueCS() {
379         return node.getValue();
380     }
381
382     /**
383      * Get name code. The name code is a coded form of the node name: two nodes
384      * with the same name code have the same namespace URI, the same local name,
385      * and the same prefix. By masking the name code with &0xfffff, you get a
386      * fingerprint: two nodes with the same fingerprint have the same local name
387      * and namespace URI.
388      *
389      * @see net.sf.saxon.om.NamePool#allocate allocate
390      */

391
392     public int getNameCode() {
393         switch (nodeKind) {
394             case Type.ELEMENT:
395             case Type.ATTRIBUTE:
396             case Type.PROCESSING_INSTRUCTION:
397                 return docWrapper.getNamePool().allocate(getPrefix(), getURI(),
398                         getLocalPart());
399             default:
400                 return -1;
401         }
402     }
403
404     /**
405      * Get fingerprint. The fingerprint is a coded form of the expanded name of
406      * the node: two nodes with the same name code have the same namespace URI
407      * and the same local name. A fingerprint of -1 should be returned for a
408      * node with no name.
409      */

410
411     public int getFingerprint() {
412         int nc = getNameCode();
413         if (nc == -1) {
414             return -1;
415         }
416         return nc & 0xfffff;
417     }
418
419     /**
420      * Get the local part of the name of this node. This is the name after the
421      * ":" if any.
422      *
423      * @return the local part of the name. For an unnamed node, returns "".
424      */

425
426     public String JavaDoc getLocalPart() {
427         switch (nodeKind) {
428             case Type.ELEMENT:
429                 return ((Element) node).getLocalName();
430             case Type.ATTRIBUTE:
431                 return ((Attribute) node).getLocalName();
432             case Type.PROCESSING_INSTRUCTION:
433                 return ((ProcessingInstruction) node).getTarget();
434             default:
435                 return "";
436         }
437     }
438
439     /**
440      * Get the prefix of the name of the node. This is defined only for elements and attributes.
441      * If the node has no prefix, or for other kinds of node, returns a zero-length string.
442      * @return The prefix of the name of the node.
443      */

444
445     public String JavaDoc getPrefix() {
446         switch (nodeKind) {
447             case Type.ELEMENT:
448                 return ((Element) node).getNamespacePrefix();
449             case Type.ATTRIBUTE:
450                 return ((Attribute) node).getNamespacePrefix();
451             default:
452                 return "";
453         }
454     }
455
456     /**
457      * Get the URI part of the name of this node. This is the URI corresponding
458      * to the prefix, or the URI of the default namespace if appropriate.
459      *
460      * @return The URI of the namespace of this node. For an unnamed node, or
461      * for a node with an empty prefix, return an empty string.
462      */

463
464     public String JavaDoc getURI() {
465         switch (nodeKind) {
466             case Type.ELEMENT:
467                 return ((Element) node).getNamespaceURI();
468             case Type.ATTRIBUTE:
469                 return ((Attribute) node).getNamespaceURI();
470             default:
471                 return "";
472         }
473     }
474
475     /**
476      * Get the display name of this node. For elements and attributes this is
477      * [prefix:]localname. For unnamed nodes, it is an empty string.
478      *
479      * @return The display name of this node. For a node with no name, return an
480      * empty string.
481      */

482
483     public String JavaDoc getDisplayName() {
484         switch (nodeKind) {
485             case Type.ELEMENT:
486                 return ((Element) node).getQualifiedName();
487             case Type.ATTRIBUTE:
488                 return ((Attribute) node).getQualifiedName();
489             case Type.PROCESSING_INSTRUCTION:
490                 return ((ProcessingInstruction) node).getTarget();
491             default:
492                 return "";
493         }
494     }
495
496     /**
497      * Get the NodeInfo object representing the parent of this node
498      */

499
500     public NodeInfo getParent() {
501         if (parent == null) {
502             ParentNode p = node.getParent();
503             if (p != null) parent = makeWrapper(p, docWrapper);
504         }
505         return parent;
506     }
507
508     /**
509      * Get the index position of this node among its siblings (starting from 0)
510      */

511
512     public int getSiblingPosition() {
513         if (index != -1) return index;
514         switch (nodeKind) {
515             case Type.ATTRIBUTE: {
516                 Attribute att = (Attribute) node;
517                 Element p = (Element) att.getParent();
518                 if (p == null) return 0;
519                 for (int i=p.getAttributeCount(); --i >= 0;) {
520                     if (p.getAttribute(i) == att) {
521                         index = i;
522                         return i;
523                     }
524                 }
525                 throw new IllegalStateException JavaDoc("XOM node not linked to parent node");
526             }
527
528             default: {
529                 ParentNode p = node.getParent();
530                 int i = (p == null ? 0 : p.indexOf(node));
531                 if (i == -1) throw new IllegalStateException JavaDoc("XOM node not linked to parent node");
532                 index = i;
533                 return index;
534             }
535         }
536     }
537
538     /**
539      * Return an iteration over the nodes reached by the given axis from this
540      * node
541      *
542      * @param axisNumber
543      * the axis to be used
544      * @return a SequenceIterator that scans the nodes reached by the axis in
545      * turn.
546      */

547
548     public AxisIterator iterateAxis(byte axisNumber) {
549         return iterateAxis(axisNumber, AnyNodeTest.getInstance());
550     }
551
552     /**
553      * Return an iteration over the nodes reached by the given axis from this
554      * node
555      *
556      * @param axisNumber
557      * the axis to be used
558      * @param nodeTest
559      * A pattern to be matched by the returned nodes
560      * @return a SequenceIterator that scans the nodes reached by the axis in
561      * turn.
562      */

563
564     public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
565         switch (axisNumber) {
566         case Axis.ANCESTOR:
567             return new AncestorAxisIterator(this, false, nodeTest);
568
569         case Axis.ANCESTOR_OR_SELF:
570             return new AncestorAxisIterator(this, true, nodeTest);
571
572         case Axis.ATTRIBUTE:
573             if (nodeKind != Type.ELEMENT || ((Element) node).getAttributeCount() == 0) {
574                 return EmptyIterator.getInstance();
575             } else {
576                 return new AttributeAxisIterator(this, nodeTest);
577             }
578
579         case Axis.CHILD:
580             if (hasChildNodes()) {
581                 return new ChildAxisIterator(this, true, true, nodeTest);
582             } else {
583                 return EmptyIterator.getInstance();
584             }
585
586         case Axis.DESCENDANT:
587             if (hasChildNodes()) {
588                 return new DescendantAxisIterator(this, false, false, nodeTest);
589             } else {
590                 return EmptyIterator.getInstance();
591             }
592
593         case Axis.DESCENDANT_OR_SELF:
594             if (hasChildNodes()) {
595                 return new DescendantAxisIterator(this, true, false, nodeTest);
596             } else {
597                 return makeSingleIterator(this, nodeTest);
598             }
599         case Axis.FOLLOWING:
600             if (getParent() == null) {
601                 return EmptyIterator.getInstance();
602             } else {
603                 return new DescendantAxisIterator(this, false, true, nodeTest);
604             }
605
606         case Axis.FOLLOWING_SIBLING:
607             if (nodeKind == Type.ATTRIBUTE || getParent() == null) {
608                 return EmptyIterator.getInstance();
609             } else {
610                 return new ChildAxisIterator(this, false, true, nodeTest);
611             }
612
613         case Axis.NAMESPACE:
614             if (nodeKind == Type.ELEMENT) {
615                 return new NamespaceIterator(this, nodeTest);
616             } else {
617                 return EmptyIterator.getInstance();
618             }
619
620         case Axis.PARENT:
621             if (getParent() == null) {
622                 return EmptyIterator.getInstance();
623             } else {
624                 return makeSingleIterator(parent, nodeTest);
625             }
626
627         case Axis.PRECEDING:
628             return new PrecedingAxisIterator(this, false, nodeTest);
629
630         case Axis.PRECEDING_SIBLING:
631             if (nodeKind == Type.ATTRIBUTE || getParent() == null) {
632                 return EmptyIterator.getInstance();
633             } else {
634                 return new ChildAxisIterator(this, false, false, nodeTest);
635             }
636
637         case Axis.SELF:
638             return makeSingleIterator(this, nodeTest);
639
640         case Axis.PRECEDING_OR_ANCESTOR:
641             // This axis is used internally by saxon for the xsl:number implementation,
642
// it returns the union of the preceding axis and the ancestor axis.
643
return new PrecedingAxisIterator(this, true, nodeTest);
644
645         default:
646             throw new IllegalArgumentException JavaDoc("Unknown axis number " + axisNumber);
647         }
648     }
649
650     private static AxisIterator makeSingleIterator(NodeWrapper wrapper, NodeTest nodeTest) {
651         if (nodeTest == AnyNodeTest.getInstance() || nodeTest.matches(wrapper))
652             return SingletonIterator.makeIterator(wrapper);
653         else
654             return EmptyIterator.getInstance();
655     }
656
657     /**
658      * Get the value of a given attribute of this node
659      *
660      * @param fingerprint
661      * The fingerprint of the attribute name
662      * @return the attribute value if it exists or null if not
663      */

664
665     public String JavaDoc getAttributeValue(int fingerprint) {
666         if (nodeKind == Type.ELEMENT) {
667             NamePool pool = docWrapper.getNamePool();
668             String JavaDoc localName = pool.getLocalName(fingerprint);
669             String JavaDoc uri = pool.getURI(fingerprint);
670             Attribute att = ((Element) node).getAttribute(localName, uri);
671             if (att != null) return att.getValue();
672         }
673         return null;
674     }
675
676     /**
677      * Get the root node - always a document node with this tree implementation
678      *
679      * @return the NodeInfo representing the containing document
680      */

681
682     public NodeInfo getRoot() {
683         return docWrapper;
684     }
685
686     /**
687      * Get the root (document) node
688      *
689      * @return the DocumentInfo representing the containing document
690      */

691
692     public DocumentInfo getDocumentRoot() {
693         return docWrapper;
694     }
695
696     /**
697      * Determine whether the node has any children. <br />
698      * Note: the result is equivalent to <br />
699      * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
700      */

701
702     public boolean hasChildNodes() {
703         return node.getChildCount() > 0;
704     }
705
706     /**
707      * Get a character string that uniquely identifies this node. Note:
708      * a.isSameNode(b) if and only if generateId(a)==generateId(b)
709      *
710      * @return a string that uniquely identifies this node, across all documents
711      */

712
713     public String JavaDoc generateId() {
714         return Navigator.getSequentialKey(this);
715     }
716
717     /**
718      * Get the document number of the document containing this node. For a
719      * free-standing orphan node, just return the hashcode.
720      */

721
722     public int getDocumentNumber() {
723         return getDocumentRoot().getDocumentNumber();
724     }
725
726     /**
727      * Copy this node to a given outputter (deep copy)
728      */

729
730     public void copy(Receiver out, int whichNamespaces,
731             boolean copyAnnotations, int locationId) throws XPathException {
732         Navigator.copy(this, out, docWrapper.getNamePool(), whichNamespaces,
733                 copyAnnotations, locationId);
734     }
735
736     /**
737      * Output all namespace nodes associated with this element. Does nothing if
738      * the node is not an element.
739      *
740      * @param out
741      * The relevant outputter
742      * @param includeAncestors
743      * True if namespaces declared on ancestor elements must be
744      */

745
746     public void sendNamespaceDeclarations(Receiver out, boolean includeAncestors)
747         throws XPathException {
748         Navigator.sendNamespaceDeclarations(this, out, includeAncestors);
749     }
750
751     /**
752      * Get all namespace undeclarations and undeclarations defined on this element.
753      *
754      * @param buffer If this is non-null, and the result array fits in this buffer, then the result
755      * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
756      * @return An array of integers representing the namespace declarations and undeclarations present on
757      * this element. For a node other than an element, return null. Otherwise, the returned array is a
758      * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
759      * top half word of each namespace code represents the prefix, the bottom half represents the URI.
760      * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
761      * The XML namespace is never included in the list. If the supplied array is larger than required,
762      * then the first unused entry will be set to -1.
763      * <p/>
764      * <p>For a node other than an element, the method returns null.</p>
765      */

766
767     public int[] getDeclaredNamespaces(int[] buffer) {
768         if (node instanceof Element) {
769             Element elem = (Element)node;
770             int size = elem.getNamespaceDeclarationCount();
771             if (size == 0) {
772                 return EMPTY_NAMESPACE_LIST;
773             }
774             int[] result = (size <= buffer.length ? buffer : new int[size]);
775             NamePool pool = getNamePool();
776             for (int i=0; i < size; i++) {
777                 String JavaDoc prefix = elem.getNamespacePrefix(i);
778                 String JavaDoc uri = elem.getNamespaceURI(prefix);
779                 result[i] = pool.allocateNamespaceCode(prefix, uri);
780             }
781             if (size < result.length) {
782                 result[size] = -1;
783             }
784             return result;
785         } else {
786             return null;
787         }
788     }
789
790     ///////////////////////////////////////////////////////////////////////////////
791
// Axis enumeration classes
792
///////////////////////////////////////////////////////////////////////////////
793

794     /**
795      * Handles the ancestor axis in a rather direct manner.
796      */

797     private final class AncestorAxisIterator implements AxisIterator {
798
799         private NodeWrapper start;
800         private boolean includeSelf;
801
802         private NodeInfo current;
803
804         private NodeTest nodeTest;
805         private int position;
806
807         public AncestorAxisIterator(NodeWrapper start, boolean includeSelf, NodeTest test) {
808             // use lazy instead of eager materialization (performance)
809
this.start = start;
810             if (test == AnyNodeTest.getInstance()) test = null;
811             this.nodeTest = test;
812             if (!includeSelf) this.current = start;
813             this.includeSelf = includeSelf;
814             this.position = 0;
815         }
816
817         public Item next() {
818             NodeInfo curr;
819             do { // until we find a match
820
curr = advance();
821             }
822             while (curr != null && nodeTest != null && (! nodeTest.matches(curr)));
823
824             if (curr != null) position++;
825             current = curr;
826             return curr;
827         }
828
829         private NodeInfo advance() {
830             if (current == null)
831                 current = start;
832             else
833                 current = current.getParent();
834
835             return current;
836         }
837
838         public Item current() {
839             return current;
840         }
841
842         public int position() {
843             return position;
844         }
845
846         public SequenceIterator getAnother() {
847             return new AncestorAxisIterator(start, includeSelf, nodeTest);
848         }
849
850         /**
851          * Get properties of this iterator, as a bit-significant integer.
852          *
853          * @return the properties of this iterator. This will be some combination of
854          * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
855          * and {@link LOOKAHEAD}. It is always
856          * acceptable to return the value zero, indicating that there are no known special properties.
857          * It is acceptable for the properties of the iterator to change depending on its state.
858          */

859
860         public int getProperties() {
861             return 0;
862         }
863
864     } // end of class AncestorAxisIterator
865

866     /**
867      * Handles the attribute axis in a rather direct manner.
868      */

869
870     private final class AttributeAxisIterator implements AxisIterator {
871
872         private NodeWrapper start;
873
874         private NodeInfo current;
875         private int cursor;
876
877         private NodeTest nodeTest;
878         private int position;
879
880         public AttributeAxisIterator(NodeWrapper start, NodeTest test) {
881             // use lazy instead of eager materialization (performance)
882
this.start = start;
883             if (test == AnyNodeTest.getInstance()) test = null;
884             this.nodeTest = test;
885             this.position = 0;
886             this.cursor = 0;
887         }
888
889         public Item next() {
890             NodeInfo curr;
891             do { // until we find a match
892
curr = advance();
893             }
894             while (curr != null && nodeTest != null && (! nodeTest.matches(curr)));
895
896             if (curr != null) position++;
897             current = curr;
898             return curr;
899         }
900
901         private NodeInfo advance() {
902             Element elem = (Element) start.node;
903             if (cursor == elem.getAttributeCount()) return null;
904             NodeInfo curr = makeWrapper(elem.getAttribute(cursor), docWrapper, start, cursor);
905             cursor++;
906             return curr;
907         }
908
909         public Item current() {
910             return current;
911         }
912
913         public int position() {
914             return position;
915         }
916
917         public SequenceIterator getAnother() {
918             return new AttributeAxisIterator(start, nodeTest);
919         }
920
921         /**
922          * Get properties of this iterator, as a bit-significant integer.
923          *
924          * @return the properties of this iterator. This will be some combination of
925          * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
926          * and {@link LOOKAHEAD}. It is always
927          * acceptable to return the value zero, indicating that there are no known special properties.
928          * It is acceptable for the properties of the iterator to change depending on its state.
929          */

930
931         public int getProperties() {
932             return 0;
933         }
934
935     } // end of class AttributeAxisIterator
936

937     /**
938      * The class ChildAxisIterator handles not only the child axis, but also the
939      * following-sibling and preceding-sibling axes. It can also iterate the
940      * children of the start node in reverse order, something that is needed to
941      * support the preceding and preceding-or-ancestor axes (the latter being
942      * used by xsl:number)
943      */

944     private final class ChildAxisIterator implements AxisIterator {
945
946         private NodeWrapper start;
947         private NodeWrapper commonParent;
948         private int ix;
949         private boolean downwards; // iterate children of start node (not siblings)
950
private boolean forwards; // iterate in document order (not reverse order)
951

952         private NodeInfo current;
953         private ParentNode par;
954         private int cursor;
955
956         private NodeTest nodeTest;
957         private int position;
958
959         private ChildAxisIterator(NodeWrapper start, boolean downwards, boolean forwards, NodeTest test) {
960             this.start = start;
961             this.downwards = downwards;
962             this.forwards = forwards;
963
964             if (test == AnyNodeTest.getInstance()) test = null;
965             this.nodeTest = test;
966             this.position = 0;
967
968             if (downwards)
969                 commonParent = start;
970             else
971                 commonParent = (NodeWrapper) start.getParent();
972
973             par = (ParentNode) commonParent.node;
974             if (downwards) {
975                 ix = (forwards ? 0 : par.getChildCount());
976             } else {
977                 // find the start node among the list of siblings
978
// ix = start.getSiblingPosition();
979
ix = par.indexOf(start.node);
980                 if (forwards) ix++;
981             }
982             cursor = ix;
983             if (!downwards && !forwards) ix--;
984         }
985
986         public Item next() {
987             NodeInfo curr;
988             do { // until we find a match
989
curr = advance();
990             }
991             while (curr != null && nodeTest != null && (! nodeTest.matches(curr)));
992
993             if (curr != null) position++;
994             current = curr;
995             return curr;
996         }
997
998         private NodeInfo advance() {
999             Node nextChild;
1000            do {
1001                if (forwards) {
1002                    if (cursor == par.getChildCount()) return null;
1003                    nextChild = par.getChild(cursor++);
1004                } else { // backwards
1005
if (cursor == 0) return null;
1006                    nextChild = par.getChild(--cursor);
1007                }
1008            } while (nextChild instanceof DocType);
1009            // DocType is not an XPath node; can occur for /child::node()
1010

1011            NodeInfo curr = makeWrapper(nextChild, docWrapper, commonParent, ix);
1012            ix += (forwards ? 1 : -1);
1013            return curr;
1014        }
1015
1016        public Item current() {
1017            return current;
1018        }
1019
1020        public int position() {
1021            return position;
1022        }
1023
1024        public SequenceIterator getAnother() {
1025            return new ChildAxisIterator(start, downwards, forwards, nodeTest);
1026        }
1027
1028        /**
1029         * Get properties of this iterator, as a bit-significant integer.
1030         *
1031         * @return the properties of this iterator. This will be some combination of
1032         * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
1033         * and {@link LOOKAHEAD}. It is always
1034         * acceptable to return the value zero, indicating that there are no known special properties.
1035         * It is acceptable for the properties of the iterator to change depending on its state.
1036         */

1037
1038        public int getProperties() {
1039            return 0;
1040        }
1041
1042    }
1043
1044    /**
1045     * A bit of a misnomer; efficiently takes care of descendants,
1046     * descentants-or-self as well as "following" axis.
1047     * "includeSelf" must be false for the following axis.
1048     * Uses simple and effective O(1) backtracking via indexOf().
1049     */

1050    private final class DescendantAxisIterator implements AxisIterator {
1051
1052        private NodeWrapper start;
1053        private boolean includeSelf;
1054        private boolean following;
1055
1056        private Node anchor; // so we know where to stop the scan
1057
private Node currNode;
1058        private boolean moveToNextSibling;
1059
1060        private NodeInfo current;
1061        private NodeTest nodeTest;
1062        private int position;
1063
1064        private String JavaDoc testLocalName;
1065        private String JavaDoc testURI;
1066
1067        public DescendantAxisIterator(NodeWrapper start, boolean includeSelf, boolean following, NodeTest test) {
1068            this.start = start;
1069            this.includeSelf = includeSelf;
1070            this.following = following;
1071            this.moveToNextSibling = following;
1072
1073            if (!following) anchor = start.node;
1074            if (!includeSelf) currNode = start.node;
1075
1076            if (test == AnyNodeTest.getInstance()) {
1077                test = null; // mark as AnyNodeTest
1078
}
1079            else if (test instanceof NameTest) {
1080                NameTest nt = (NameTest) test;
1081                if (nt.getPrimitiveType() == Type.ELEMENT) {
1082                    // mark as element name test
1083
NamePool pool = getNamePool();
1084                    this.testLocalName = pool.getLocalName(nt.getFingerprint());
1085                    this.testURI = pool.getURI(nt.getFingerprint());
1086                }
1087            }
1088            else if (test instanceof NodeKindTest) {
1089                if (test.getPrimitiveType() == Type.ELEMENT) {
1090                    // mark as element type test
1091
this.testLocalName = "";
1092                    this.testURI = null;
1093                }
1094            }
1095            this.nodeTest = test;
1096            this.position = 0;
1097        }
1098
1099        public Item next() {
1100            NodeInfo curr;
1101            do { // until we find a match
1102
curr = advance();
1103            }
1104            while (curr != null && nodeTest != null && (! nodeTest.matches(curr)));
1105
1106            if (curr != null) position++;
1107            current = curr;
1108            return curr;
1109        }
1110
1111        private NodeInfo advance() {
1112            if (currNode == null) { // if includeSelf
1113
currNode = start.node;
1114                return start;
1115            }
1116
1117            int i;
1118            do {
1119                i = 0;
1120                Node p = currNode;
1121
1122                if (p.getChildCount() == 0 || moveToNextSibling) { // move to next sibling
1123

1124                    moveToNextSibling = false; // do it just once
1125
while (true) {
1126                        // if we've the reached the root, we're done scanning
1127
p = currNode.getParent();
1128                        if (p == null) return null;
1129
1130                        // Note: correct even if currNode is an attribute.
1131
// Performance is particularly good with the O(1) patch
1132
// for XOM's ParentNode.indexOf()
1133
i = currNode.getParent().indexOf(currNode) + 1;
1134
1135                        if (i < p.getChildCount()) {
1136                            break; // break out of while(true) loop; move to next sibling
1137
}
1138                        else { // reached last sibling; move up
1139
currNode = p;
1140                            // if we've come all the way back to the start anchor we're done
1141
if (p == anchor) return null;
1142                        }
1143                    }
1144                }
1145                currNode = p.getChild(i);
1146            } while (!conforms(currNode));
1147
1148            // note the null here: makeNodeWrapper(parent, ...) is fast, so it
1149
// doesn't really matter that we don't keep a link to it.
1150
// In fact, it makes objects more short lived, easing pressure on
1151
// the VM allocator and collector for tenured heaps.
1152
return makeWrapper(currNode, docWrapper, null, i);
1153        }
1154
1155        // avoids NodeWrapper allocation when there's clearly a mismatch (common case)
1156
private boolean conforms(Node node) {
1157            if (this.testLocalName != null) { // element test?
1158
if (!(node instanceof Element)) return false;
1159                if (this.testURI == null) return true; // pure element type test
1160

1161                // element name test
1162
Element elem = (Element) node;
1163                return this.testLocalName.equals(elem.getLocalName()) &&
1164                    this.testURI.equals(elem.getNamespaceURI());
1165            }
1166            else { // DocType is not an XPath node; can occur for /descendants::node()
1167
return !(node instanceof DocType);
1168            }
1169        }
1170
1171        public Item current() {
1172            return current;
1173        }
1174
1175        public int position() {
1176            return position;
1177        }
1178
1179        public SequenceIterator getAnother() {
1180            return new DescendantAxisIterator(start, includeSelf, following, nodeTest);
1181        }
1182
1183        /**
1184         * Get properties of this iterator, as a bit-significant integer.
1185         *
1186         * @return the properties of this iterator. This will be some combination of
1187         * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
1188         * and {@link LOOKAHEAD}. It is always
1189         * acceptable to return the value zero, indicating that there are no known special properties.
1190         * It is acceptable for the properties of the iterator to change depending on its state.
1191         */

1192
1193        public int getProperties() {
1194            return 0;
1195        }
1196    }
1197    /**
1198     * Efficiently takes care of preceding axis and Saxon internal preceding-or-ancestor axis.
1199     * Uses simple and effective O(1) backtracking via indexOf().
1200     * Implemented along similar lines as DescendantAxisIterator.
1201     */

1202    private final class PrecedingAxisIterator implements AxisIterator {
1203
1204        private NodeWrapper start;
1205        private boolean includeAncestors;
1206
1207        private Node currNode;
1208        private ParentNode nextAncestor; // next ancestors to skip if !includeAncestors
1209

1210        private NodeInfo current;
1211        private NodeTest nodeTest;
1212        private int position;
1213
1214        private String JavaDoc testLocalName;
1215        private String JavaDoc testURI;
1216
1217        public PrecedingAxisIterator(NodeWrapper start, boolean includeAncestors, NodeTest test) {
1218            this.start = start;
1219            this.includeAncestors = includeAncestors;
1220            this.currNode = start.node;
1221            if (includeAncestors)
1222                nextAncestor = null;
1223            else
1224                nextAncestor = start.node.getParent();
1225
1226            if (test == AnyNodeTest.getInstance()) { // performance hack
1227
test = null; // mark as AnyNodeTest
1228
}
1229            else if (test instanceof NameTest) {
1230                NameTest nt = (NameTest) test;
1231                if (nt.getPrimitiveType() == Type.ELEMENT) { // performance hack
1232
// mark as element name test
1233
NamePool pool = getNamePool();
1234                    this.testLocalName = pool.getLocalName(nt.getFingerprint());
1235                    this.testURI = pool.getURI(nt.getFingerprint());
1236                }
1237            }
1238            else if (test instanceof NodeKindTest) {
1239                if (test.getPrimitiveType() == Type.ELEMENT) { // performance hack
1240
// mark as element type test
1241
this.testLocalName = "";
1242                    this.testURI = null;
1243                }
1244            }
1245            this.nodeTest = test;
1246            this.position = 0;
1247        }
1248
1249        public Item next() {
1250            NodeInfo curr;
1251            do { // until we find a match
1252
curr = advance();
1253            }
1254            while (curr != null && nodeTest != null && (! nodeTest.matches(curr)));
1255
1256            if (curr != null) position++;
1257            current = curr;
1258            return curr;
1259        }
1260
1261        // might look expensive at first glance - but it's not
1262
private NodeInfo advance() {
1263            int i;
1264            do {
1265                Node p;
1266
1267                while (true) {
1268                    // if we've reached the root we're done scanning
1269
// System.out.println("p="+p);
1270
p = currNode.getParent();
1271                    if (p == null) return null;
1272
1273                    // Note: correct even if currNode is an attribute.
1274
// Performance is particularly good with the O(1) patch
1275
// for XOM's ParentNode.indexOf()
1276
i = currNode.getParent().indexOf(currNode) - 1;
1277
1278                    if (i >= 0) { // move to next sibling's last descendant node
1279
p = p.getChild(i); // move to next sibling
1280
int j;
1281                        while ((j = p.getChildCount()-1) >= 0) { // move to last descendant node
1282
p = p.getChild(j);
1283                            i = j;
1284                        }
1285                        break; // break out of while(true) loop
1286
}
1287                    else { // there are no more siblings; move up
1288
// if !includeAncestors skip the ancestors of the start node
1289
// assert p != null
1290
if (p != nextAncestor) break; // break out of while(true) loop
1291

1292                        nextAncestor = nextAncestor.getParent();
1293                        currNode = p;
1294                    }
1295                }
1296                currNode = p;
1297
1298            } while (!conforms(currNode));
1299
1300            // note the null here: makeNodeWrapper(parent, ...) is fast, so it
1301
// doesn't really matter that we don't keep a link to it.
1302
// In fact, it makes objects more short lived, easing pressure on
1303
// the VM allocator and collector for tenured heaps.
1304
return makeWrapper(currNode, docWrapper, null, i);
1305        }
1306
1307        // avoids NodeWrapper allocation when there's clearly a mismatch (common case)
1308
// same as for DescendantAxisIterator
1309
private boolean conforms(Node node) {
1310            if (this.testLocalName != null) { // element test?
1311
if (!(node instanceof Element)) return false;
1312                if (this.testURI == null) return true; // pure element type test
1313

1314                // element name test
1315
Element elem = (Element) node;
1316                return this.testLocalName.equals(elem.getLocalName()) &&
1317                    this.testURI.equals(elem.getNamespaceURI());
1318            }
1319            else { // DocType is not an XPath node
1320
return !(node instanceof DocType);
1321            }
1322        }
1323
1324        public Item current() {
1325            return current;
1326        }
1327
1328        public int position() {
1329            return position;
1330        }
1331
1332        public SequenceIterator getAnother() {
1333            return new PrecedingAxisIterator(start, includeAncestors, nodeTest);
1334        }
1335
1336        /**
1337         * Get properties of this iterator, as a bit-significant integer.
1338         *
1339         * @return the properties of this iterator. This will be some combination of
1340         * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
1341         * and {@link LOOKAHEAD}. It is always
1342         * acceptable to return the value zero, indicating that there are no known special properties.
1343         * It is acceptable for the properties of the iterator to change depending on its state.
1344         */

1345
1346        public int getProperties() {
1347            return 0;
1348        }
1349    }
1350
1351}
1352
1353//
1354
// The contents of this file are subject to the Mozilla Public License Version
1355
// 1.0 (the "License");
1356
// you may not use this file except in compliance with the License. You may
1357
// obtain a copy of the
1358
// License at http://www.mozilla.org/MPL/
1359
//
1360
// Software distributed under the License is distributed on an "AS IS" basis,
1361
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1362
// See the License for the specific language governing rights and limitations
1363
// under the License.
1364
//
1365
// The Original Code is: all this file.
1366
//
1367
// The Initial Developer of the Original Code is Michael Kay, with extensive
1368
// rewriting by Wolfgang Hoschek
1369
//
1370
// Portions created by (your name) are Copyright (C) (your legal entity). All
1371
// Rights Reserved.
1372
//
1373
// Contributor(s): none.
1374
//
1375
Popular Tags