KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.dom;
2 import net.sf.saxon.Configuration;
3 import net.sf.saxon.event.Receiver;
4 import net.sf.saxon.om.*;
5 import net.sf.saxon.pattern.NameTest;
6 import net.sf.saxon.pattern.NodeTest;
7 import net.sf.saxon.style.StandardNames;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.Type;
10 import net.sf.saxon.value.UntypedAtomicValue;
11 import net.sf.saxon.value.Value;
12 import org.w3c.dom.*;
13
14 import java.lang.reflect.InvocationTargetException JavaDoc;
15 import java.lang.reflect.Method JavaDoc;
16 import java.util.ArrayList JavaDoc;
17
18
19 /**
20   * A node in the XML parse tree representing an XML element, character content, or attribute.<P>
21   * This is the implementation of the NodeInfo interface used as a wrapper for DOM nodes.
22   */

23
24 public class NodeWrapper implements NodeInfo, VirtualNode, SiblingCountingNode {
25
26     protected Node node;
27     private int namecode = -1;
28     protected short nodeKind;
29     private NodeWrapper parent; // null means unknown
30
protected DocumentWrapper docWrapper;
31     protected int index; // -1 means unknown
32
protected int span = 1; // the number of adjacent text nodes wrapped by this NodeWrapper.
33
// If span>1, node will always be the first of a sequence of adjacent text nodes
34

35     /**
36      * This constructor is protected: nodes should be created using the makeWrapper
37      * factory method
38      * @param node The DOM node to be wrapped
39      * @param parent The NodeWrapper that wraps the parent of this node
40      * @param index Position of this node among its siblings
41      */

42     protected NodeWrapper(Node node, NodeWrapper parent, int index) {
43         this.node = node;
44         this.parent = parent;
45         this.index = index;
46     }
47
48     /**
49      * Factory method to wrap a DOM node with a wrapper that implements the Saxon
50      * NodeInfo interface.
51      * @param node The DOM node
52      * @param docWrapper The wrapper for the containing Document node
53      * @return The new wrapper for the supplied node
54      * @throws NullPointerException if the node or the document wrapper are null
55      */

56     protected NodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper) {
57         if (node == null) {
58             throw new NullPointerException JavaDoc("NodeWrapper#makeWrapper: Node must not be null");
59         }
60         if (docWrapper == null) {
61             throw new NullPointerException JavaDoc("NodeWrapper#makeWrapper: DocumentWrapper must not be null");
62         }
63         return makeWrapper(node, docWrapper, null, -1);
64     }
65
66     /**
67      * Factory method to wrap a DOM node with a wrapper that implements the Saxon
68      * NodeInfo interface.
69      * @param node The DOM node
70      * @param docWrapper The wrapper for the containing Document node *
71      * @param parent The wrapper for the parent of the JDOM node
72      * @param index The position of this node relative to its siblings
73      * @return The new wrapper for the supplied node
74      */

75
76     protected NodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper,
77                                    NodeWrapper parent, int index) {
78         NodeWrapper wrapper;
79         switch (node.getNodeType()) {
80         case Node.DOCUMENT_NODE:
81             return docWrapper;
82         case Node.ELEMENT_NODE:
83             wrapper = new NodeWrapper(node, parent, index);
84             wrapper.nodeKind = Type.ELEMENT;
85             break;
86         case Node.ATTRIBUTE_NODE:
87             wrapper = new NodeWrapper(node, parent, index);
88             wrapper.nodeKind = Type.ATTRIBUTE;
89             break;
90         case Node.TEXT_NODE:
91             wrapper = new NodeWrapper(node, parent, index);
92             wrapper.nodeKind = Type.TEXT;
93             break;
94         case Node.CDATA_SECTION_NODE:
95             wrapper = new NodeWrapper(node, parent, index);
96             wrapper.nodeKind = Type.TEXT;
97             break;
98         case Node.COMMENT_NODE:
99             wrapper = new NodeWrapper(node, parent, index);
100             wrapper.nodeKind = Type.COMMENT;
101             break;
102         case Node.PROCESSING_INSTRUCTION_NODE:
103             wrapper = new NodeWrapper(node, parent, index);
104             wrapper.nodeKind = Type.PROCESSING_INSTRUCTION;
105             break;
106         default:
107             throw new IllegalArgumentException JavaDoc("Unsupported node type in DOM! " + node.getNodeType() + " instance " + node.toString());
108         }
109         wrapper.docWrapper = docWrapper;
110         return wrapper;
111     }
112
113     /**
114     * Get the underlying DOM node, to implement the VirtualNode interface
115     */

116
117     public Object JavaDoc getUnderlyingNode() {
118         return node;
119     }
120
121     /**
122      * Get the configuration
123      */

124
125     public Configuration getConfiguration() {
126         return docWrapper.getConfiguration();
127     }
128
129     /**
130      * Get the name pool for this node
131      * @return the NamePool
132      */

133
134     public NamePool getNamePool() {
135         return docWrapper.getNamePool();
136     }
137
138     /**
139     * Return the type of node.
140     * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
141     */

142
143     public int getNodeKind() {
144         return nodeKind;
145     }
146
147     /**
148     * Get the typed value of the item
149     */

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

165
166     public Value atomize() throws XPathException {
167         return new UntypedAtomicValue(getStringValueCS());
168     }
169
170     /**
171     * Get the type annotation
172     * @return -1 (there is no type annotation)
173     */

174
175     public int getTypeAnnotation() {
176         return -1;
177     }
178
179     /**
180     * Determine whether this is the same node as another node. <br />
181     * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b)
182     * @return true if this Node object and the supplied Node object represent the
183     * same node in the tree.
184     */

185
186     public boolean isSameNodeInfo(NodeInfo other) {
187         // DOM does not offer any guarantees that the same node is always represented
188
// by the same object
189

190         if (!(other instanceof NodeWrapper)) {
191             return false;
192         }
193
194         // For a level-3 DOM, use the DOM isSameNode() method
195
if (docWrapper.level3) {
196             try {
197                 Class JavaDoc[] argClasses = {Node.class};
198                 Method JavaDoc isSameNode = Node.class.getMethod("isSameNode", argClasses);
199                 Object JavaDoc[] args = {((NodeWrapper)other).node};
200                 Boolean JavaDoc b = (Boolean JavaDoc)(isSameNode.invoke(node, args));
201                 return b.booleanValue();
202             } catch (NoSuchMethodException JavaDoc e) {
203                 // use fallback implementation
204
} catch (IllegalAccessException JavaDoc e) {
205                 // use fallback implementation
206
} catch (InvocationTargetException JavaDoc e) {
207                 // use fallback implementation
208
}
209         }
210         NodeWrapper ow = (NodeWrapper)other;
211         return getNodeKind()==ow.getNodeKind() &&
212                 getNameCode()==ow.getNameCode() && // redundant, but gives a quick exit
213
getSiblingPosition()==ow.getSiblingPosition() &&
214                 getParent().isSameNodeInfo(ow.getParent());
215         // Note: this has been known to fail because getParent() returns null. Extra checks have been added to the
216
// methods for constructing DOM document wrappers to ensure that wrapped nodes always belong to a wrapped
217
// Document, which should prevent this happening again.
218
}
219
220     /**
221     * Get the System ID for the node.
222     * @return the System Identifier of the entity in the source document containing the node,
223     * or null if not known. Note this is not the same as the base URI: the base URI can be
224     * modified by xml:base, but the system ID cannot.
225     */

226
227     public String JavaDoc getSystemId() {
228         return docWrapper.baseURI;
229     }
230
231     public void setSystemId(String JavaDoc uri) {
232         docWrapper.baseURI = uri;
233     }
234
235     /**
236      * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
237      * in the node. In the DOM model, base URIs are held only an the document level.
238     */

239
240     public String JavaDoc getBaseURI() {
241         NodeInfo n = this;
242         if (getNodeKind() != Type.ELEMENT) {
243             n = getParent();
244         }
245         // Look for an xml:base attribute
246
while (n != null) {
247             String JavaDoc xmlbase = n.getAttributeValue(StandardNames.XML_BASE);
248             if (xmlbase != null) {
249                 return xmlbase;
250             }
251             n = n.getParent();
252         }
253         // if not found, return the base URI of the document node
254
return docWrapper.baseURI;
255     }
256
257     /**
258     * Get line number
259     * @return the line number of the node in its original source document; or -1 if not available
260     */

261
262     public int getLineNumber() {
263         return -1;
264     }
265
266     /**
267     * Determine the relative position of this node and another node, in document order.
268     * The other node will always be in the same document.
269     * @param other The other node, whose position is to be compared with this node
270     * @return -1 if this node precedes the other node, +1 if it follows the other
271     * node, or 0 if they are the same node. (In this case, isSameNode() will always
272     * return true, and the two nodes will produce the same result for generateId())
273     */

274
275     public int compareOrder(NodeInfo other) {
276         // Use the DOM Level-3 compareDocumentPosition() method if available
277
if (docWrapper.level3 && other instanceof NodeWrapper) {
278             if (isSameNodeInfo(other)) {
279                 return 0;
280             }
281             try {
282                 Class JavaDoc[] argClasses = {Node.class};
283                 Method JavaDoc compareDocumentPosition = Node.class.getMethod("compareDocumentPosition", argClasses);
284                 Object JavaDoc[] args = {((NodeWrapper)other).node};
285                 Short JavaDoc i = (Short JavaDoc)(compareDocumentPosition.invoke(node, args));
286                 switch (i.shortValue()) {
287                                 // the symbolic constants require JDK 1.5
288
case 2: //Node.DOCUMENT_POSITION_PRECEDING:
289
case 8: //Node.DOCUMENT_POSITION_CONTAINS:
290
return +1;
291                     case 4: //Node.DOCUMENT_POSITION_FOLLOWING:
292
case 16: //Node.DOCUMENT_POSITION_CONTAINED_BY:
293
return -1;
294                     default:
295                         // use fallback implementation
296
}
297             } catch (NoSuchMethodException JavaDoc e) {
298                 // use fallback implementation
299
} catch (IllegalAccessException JavaDoc e) {
300                 // use fallback implementation
301
} catch (InvocationTargetException JavaDoc e) {
302                 // use fallback implementation
303
} catch (DOMException e) {
304                 // use fallback implementation
305
}
306         }
307
308         if (other instanceof SiblingCountingNode) {
309             return Navigator.compareOrder(this, (SiblingCountingNode)other);
310         } else {
311             // it's presumably a Namespace Node
312
return -other.compareOrder(this);
313         }
314     }
315
316     /**
317     * Return the string value of the node. The interpretation of this depends on the type
318     * of node. For an element it is the accumulated character content of the element,
319     * including descendant elements.
320     * @return the string value of the node
321     */

322
323     public String JavaDoc getStringValue() {
324         return getStringValueCS().toString();
325
326     }
327
328     /**
329      * Get the value of the item as a CharSequence. This is in some cases more efficient than
330      * the version of the method that returns a String.
331      */

332
333     public CharSequence JavaDoc getStringValueCS() {
334         //return getStringValue(node, nodeKind);
335
switch (nodeKind) {
336             case Type.DOCUMENT:
337             case Type.ELEMENT:
338                 NodeList children1 = node.getChildNodes();
339                 StringBuffer JavaDoc sb1 = new StringBuffer JavaDoc(16);
340                 expandStringValue(children1, sb1);
341                 return sb1;
342
343             case Type.ATTRIBUTE:
344                 return ((Attr)node).getValue();
345
346             case Type.TEXT:
347                 if (span == 1) {
348                     return node.getNodeValue();
349                 } else {
350                     FastStringBuffer fsb = new FastStringBuffer(100);
351                     Node textNode = node;
352                     for (int i=0; i<span; i++) {
353                         fsb.append(textNode.getNodeValue());
354                         textNode = textNode.getNextSibling();
355                     }
356                     return fsb.condense();
357                 }
358
359             case Type.COMMENT:
360             case Type.PROCESSING_INSTRUCTION:
361                 return node.getNodeValue();
362
363             default:
364                 return "";
365         }
366     }
367
368     /**
369      * Get the string value of a DOM node
370      * @param node
371      * @param nodeKind
372      * @return
373      */

374
375     private static CharSequence JavaDoc getStringValue(Node node, int nodeKind) {
376         switch (nodeKind) {
377             case Type.DOCUMENT:
378             case Type.ELEMENT:
379                 NodeList children1 = node.getChildNodes();
380                 StringBuffer JavaDoc sb1 = new StringBuffer JavaDoc(16);
381                 expandStringValue(children1, sb1);
382                 return sb1;
383
384             case Type.ATTRIBUTE:
385                 return ((Attr)node).getValue();
386
387             case Type.TEXT:
388             case Node.CDATA_SECTION_NODE:
389                 return node.getNodeValue();
390
391             case Type.COMMENT:
392             case Type.PROCESSING_INSTRUCTION:
393                 return node.getNodeValue();
394
395             default:
396                 return "";
397         }
398     }
399
400     private static void expandStringValue(NodeList list, StringBuffer JavaDoc sb) {
401         for (int i = 0; i < list.getLength(); i++) {
402             Node child = list.item(i);
403             switch (child.getNodeType()) {
404                 case Node.ELEMENT_NODE:
405                     expandStringValue(child.getChildNodes(), sb);
406                     break;
407                 default:
408                     sb.append(child.getNodeValue());
409             }
410         }
411     }
412
413     /**
414     * Get name code. The name code is a coded form of the node name: two nodes
415     * with the same name code have the same namespace URI, the same local name,
416     * and the same prefix. By masking the name code with &0xfffff, you get a
417     * fingerprint: two nodes with the same fingerprint have the same local name
418     * and namespace URI.
419     * @see NamePool#allocate allocate
420     */

421
422     public int getNameCode() {
423         if (namecode != -1) {
424             // this is a memo function
425
return namecode;
426         }
427         int nodeKind = getNodeKind();
428         if (nodeKind == Type.ELEMENT || nodeKind == Type.ATTRIBUTE) {
429             String JavaDoc prefix = node.getPrefix();
430             if (prefix==null) {
431                 prefix = "";
432             }
433             namecode = docWrapper.getNamePool().allocate(prefix, getURI(), getLocalPart());
434             return namecode;
435         } else if (nodeKind == Type.PROCESSING_INSTRUCTION ) {
436             namecode = docWrapper.getNamePool().allocate("", "", getLocalPart());
437             return namecode;
438         } else {
439             return -1;
440         }
441     }
442
443     /**
444     * Get fingerprint. The fingerprint is a coded form of the expanded name
445     * of the node: two nodes
446     * with the same name code have the same namespace URI and the same local name.
447     * A fingerprint of -1 should be returned for a node with no name.
448     */

449
450     public int getFingerprint() {
451         int nc = getNameCode();
452         if (nc == -1) {
453             return -1;
454         }
455         return nc&0xfffff;
456     }
457
458     /**
459     * Get the local part of the name of this node. This is the name after the ":" if any.
460     * @return the local part of the name. For an unnamed node, returns null, except for
461      * un unnamed namespace node, which returns "".
462     */

463
464     public String JavaDoc getLocalPart() {
465         String JavaDoc s = node.getLocalName();
466         if (s == null) {
467             // Crimson returns null for the attribute "xml:space": test axes-dom132
468
String JavaDoc n = getDisplayName();
469             int colon = n.indexOf(':');
470             if (colon >= 0) {
471                 return n.substring(colon+1);
472             }
473             return n;
474         } else {
475             return s;
476         }
477     }
478
479     /**
480     * Get the URI part of the name of this node. This is the URI corresponding to the
481     * prefix, or the URI of the default namespace if appropriate.
482      * @return The URI of the namespace of this node. For an unnamed node,
483      * or for a node with an empty prefix, return an empty
484      * string.
485     */

486
487     public String JavaDoc getURI() {
488         NodeInfo element;
489         if (nodeKind == Type.ELEMENT) {
490             element = this;
491         } else if (nodeKind == Type.ATTRIBUTE) {
492             element = parent;
493         } else {
494             return "";
495         }
496
497         // The DOM methods getPrefix() and getNamespaceURI() do not always
498
// return the prefix and the URI; they both return null, unless the
499
// prefix and URI have been explicitly set in the node by using DOM
500
// level 2 interfaces. There's no obvious way of deciding whether
501
// an element whose name has no prefix is in the default namespace,
502
// other than searching for a default namespace declaration. So we have to
503
// be prepared to search.
504

505         // If getPrefix() and getNamespaceURI() are non-null, however,
506
// we can use the values.
507

508         String JavaDoc uri = node.getNamespaceURI();
509         if (uri != null) {
510             return uri;
511         }
512
513         // Otherwise we have to work it out the hard way...
514

515         if (node.getNodeName().startsWith("xml:")) {
516             return NamespaceConstant.XML;
517         }
518
519         String JavaDoc[] parts;
520         try {
521             parts = Name.getQNameParts(node.getNodeName());
522         } catch (QNameException e) {
523             throw new IllegalStateException JavaDoc("Invalid QName in DOM node. " + e);
524         }
525
526         if (nodeKind == Type.ATTRIBUTE && parts[0].equals("")) {
527             // for an attribute, no prefix means no namespace
528
uri = "";
529         } else {
530             AxisIterator nsiter = element.iterateAxis(Axis.NAMESPACE);
531             while (true) {
532                 NodeInfo ns = (NodeInfo)nsiter.next();
533                 if (ns == null) break;
534                 if (ns.getLocalPart().equals(parts[0])) {
535                     uri = ns.getStringValue();
536                     break;
537                 }
538             }
539             if (uri == null) {
540                 if (parts[0].equals("")) {
541                     uri = "";
542                 } else {
543                     throw new IllegalStateException JavaDoc("Undeclared namespace prefix in DOM input: " + parts[0]);
544                 }
545             }
546         }
547         return uri;
548     }
549
550     /**
551      * Get the prefix of the name of the node. This is defined only for elements and attributes.
552      * If the node has no prefix, or for other kinds of node, return a zero-length string.
553      * This implementation simply returns the prefix defined in the DOM model; this is nto strictly
554      * accurate in all cases, but is good enough for the purpose.
555      * @return The prefix of the name of the node.
556      */

557
558     public String JavaDoc getPrefix() {
559         return node.getPrefix();
560     }
561
562     /**
563     * Get the display name of this node. For elements and attributes this is [prefix:]localname.
564     * For unnamed nodes, it is an empty string.
565     * @return The display name of this node.
566     * For a node with no name, return an empty string.
567     */

568
569     public String JavaDoc getDisplayName() {
570         switch (nodeKind) {
571             case Type.ELEMENT:
572             case Type.ATTRIBUTE:
573             case Type.PROCESSING_INSTRUCTION:
574                 return node.getNodeName();
575             default:
576                 return "";
577
578         }
579     }
580
581     /**
582     * Get the NodeInfo object representing the parent of this node
583     */

584
585     public NodeInfo getParent() {
586         if (parent==null) {
587             switch (getNodeKind()) {
588             case Type.ATTRIBUTE:
589                 parent = makeWrapper(((Attr)node).getOwnerElement(), docWrapper);
590                 break;
591             default:
592                 Node p = node.getParentNode();
593                 if (p==null) {
594                     return null;
595                 } else {
596                     parent = makeWrapper(p, docWrapper);
597                 }
598             }
599         }
600         return parent;
601     }
602
603     /**
604      * Get the index position of this node among its siblings (starting from 0).
605      * In the case of a text node that maps to several adjacent siblings in the DOM,
606      * the numbering actually refers to the position of the underlying DOM nodes;
607      * thus the sibling position for the text node is that of the first DOM node
608      * to which it relates, and the numbering of subsequent XPath nodes is not necessarily
609      * consecutive.
610      */

611
612     public int getSiblingPosition() {
613         if (index == -1) {
614             switch (nodeKind) {
615                 case Type.ELEMENT:
616                 case Type.TEXT:
617                 case Type.COMMENT:
618                 case Type.PROCESSING_INSTRUCTION:
619                     int ix = 0;
620                     Node start = node;
621                     while (true) {
622                         start = start.getPreviousSibling();
623                         if (start == null) {
624                             index = ix;
625                             return ix;
626                         }
627                         ix++;
628                     }
629                 case Type.ATTRIBUTE:
630                     ix = 0;
631                     int fp = getFingerprint();
632                     AxisIterator iter = parent.iterateAxis(Axis.ATTRIBUTE);
633                     while (true) {
634                         NodeInfo n = (NodeInfo)iter.next();
635                         if (n==null || n.getFingerprint()==fp) {
636                             index = ix;
637                             return ix;
638                         }
639                         ix++;
640                     }
641
642                 case Type.NAMESPACE:
643                     ix = 0;
644                     fp = getFingerprint();
645                     iter = parent.iterateAxis(Axis.NAMESPACE);
646                     while (true) {
647                         NodeInfo n = (NodeInfo)iter.next();
648                         if (n==null || n.getFingerprint()==fp) {
649                             index = ix;
650                             return ix;
651                         }
652                         ix++;
653                     }
654                 default:
655                     index = 0;
656                     return index;
657             }
658         }
659         return index;
660     }
661
662     /**
663     * Return an iteration over the nodes reached by the given axis from this node
664     * @param axisNumber the axis to be used
665     * @return a SequenceIterator that scans the nodes reached by the axis in turn.
666     */

667
668     public AxisIterator iterateAxis(byte axisNumber) {
669         switch (axisNumber) {
670             case Axis.ANCESTOR:
671                 if (nodeKind==Type.DOCUMENT) return EmptyIterator.getInstance();
672                 return new Navigator.AncestorEnumeration(this, false);
673
674             case Axis.ANCESTOR_OR_SELF:
675                 if (nodeKind==Type.DOCUMENT) return EmptyIterator.getInstance();
676                 return new Navigator.AncestorEnumeration(this, true);
677
678             case Axis.ATTRIBUTE:
679                 if (nodeKind!=Type.ELEMENT) return EmptyIterator.getInstance();
680                 return new AttributeEnumeration(this);
681
682             case Axis.CHILD:
683                 if (hasChildNodes()) {
684                     return new ChildEnumeration(this, true, true);
685                 } else {
686                     return EmptyIterator.getInstance();
687                 }
688
689             case Axis.DESCENDANT:
690                 if (hasChildNodes()) {
691                     return new Navigator.DescendantEnumeration(this, false, true);
692                 } else {
693                     return EmptyIterator.getInstance();
694                 }
695
696             case Axis.DESCENDANT_OR_SELF:
697                  return new Navigator.DescendantEnumeration(this, true, true);
698
699             case Axis.FOLLOWING:
700                  return new Navigator.FollowingEnumeration(this);
701
702             case Axis.FOLLOWING_SIBLING:
703                  switch (nodeKind) {
704                     case Type.DOCUMENT:
705                     case Type.ATTRIBUTE:
706                     case Type.NAMESPACE:
707                         return EmptyIterator.getInstance();
708                     default:
709                         return new ChildEnumeration(this, false, true);
710                  }
711
712             case Axis.NAMESPACE:
713                  if (nodeKind!=Type.ELEMENT) {
714                      return EmptyIterator.getInstance();
715                  }
716                  return new NamespaceIterator(this, null);
717                  //return new NamespaceEnumeration(this);
718

719             case Axis.PARENT:
720                  getParent();
721                  return SingletonIterator.makeIterator(parent);
722
723             case Axis.PRECEDING:
724                  return new Navigator.PrecedingEnumeration(this, false);
725
726             case Axis.PRECEDING_SIBLING:
727                  switch (nodeKind) {
728                     case Type.DOCUMENT:
729                     case Type.ATTRIBUTE:
730                     case Type.NAMESPACE:
731                         return EmptyIterator.getInstance();
732                     default:
733                         return new ChildEnumeration(this, false, false);
734                  }
735
736             case Axis.SELF:
737                  return SingletonIterator.makeIterator(this);
738
739             case Axis.PRECEDING_OR_ANCESTOR:
740                  return new Navigator.PrecedingEnumeration(this, true);
741
742             default:
743                  throw new IllegalArgumentException JavaDoc("Unknown axis number " + axisNumber);
744         }
745     }
746
747     /**
748     * Return an iteration over the nodes reached by the given axis from this node
749     * @param axisNumber the axis to be used
750     * @param nodeTest A pattern to be matched by the returned nodes
751     * @return a SequenceIterator that scans the nodes reached by the axis in turn.
752     */

753
754     public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
755         return new Navigator.AxisFilter(iterateAxis(axisNumber), nodeTest);
756     }
757
758     /**
759     * Get the value of a given attribute of this node
760     * @param fingerprint The fingerprint of the attribute name
761     * @return the attribute value if it exists or null if not
762     */

763
764     public String JavaDoc getAttributeValue(int fingerprint) {
765         NameTest test = new NameTest(Type.ATTRIBUTE, fingerprint, getNamePool());
766         AxisIterator iterator = iterateAxis(Axis.ATTRIBUTE, test);
767         NodeInfo attribute = (NodeInfo)iterator.next();
768         if (attribute == null) {
769             return null;
770         } else {
771             return attribute.getStringValue();
772         }
773     }
774
775     /**
776     * Get the root node - always a document node with this tree implementation
777     * @return the NodeInfo representing the containing document
778     */

779
780     public NodeInfo getRoot() {
781         return docWrapper;
782     }
783
784     /**
785     * Get the root (document) node
786     * @return the DocumentInfo representing the containing document
787     */

788
789     public DocumentInfo getDocumentRoot() {
790         return docWrapper;
791     }
792
793     /**
794     * Determine whether the node has any children. <br />
795     * Note: the result is equivalent to <br />
796     * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
797     */

798
799     public boolean hasChildNodes() {
800         // In Xerces, an attribute node has child text nodes
801
if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
802             return false;
803         }
804         return node.hasChildNodes();
805     }
806
807     /**
808     * Get a character string that uniquely identifies this node.
809     * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
810     * @return a string that uniquely identifies this node, across all
811     * documents
812     */

813
814     public String JavaDoc generateId() {
815         return Navigator.getSequentialKey(this);
816     }
817
818     /**
819      * Get the document number of the document containing this node. For a free-standing
820      * orphan node, just return the hashcode.
821      */

822
823     public int getDocumentNumber() {
824         return getDocumentRoot().getDocumentNumber();
825     }
826
827     /**
828     * Copy this node to a given outputter (deep copy)
829     */

830
831     public void copy(Receiver out, int whichNamespaces, boolean copyAnnotations, int locationId) throws XPathException {
832         Navigator.copy(this, out, docWrapper.getNamePool(), whichNamespaces, copyAnnotations, locationId);
833     }
834
835     /**
836     * Output all namespace nodes associated with this element. Does nothing if
837     * the node is not an element.
838     * @param out The relevant outputter
839      * @param includeAncestors True if namespaces declared on ancestor elements must
840      */

841
842     public void sendNamespaceDeclarations(Receiver out, boolean includeAncestors)
843         throws XPathException {
844         Navigator.sendNamespaceDeclarations(this, out, includeAncestors);
845     }
846
847     /**
848      * Get all namespace undeclarations and undeclarations defined on this element.
849      *
850      * @param buffer If this is non-null, and the result array fits in this buffer, then the result
851      * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
852      * @return An array of integers representing the namespace declarations and undeclarations present on
853      * this element. For a node other than an element, return null. Otherwise, the returned array is a
854      * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
855      * top half word of each namespace code represents the prefix, the bottom half represents the URI.
856      * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
857      * The XML namespace is never included in the list. If the supplied array is larger than required,
858      * then the first unused entry will be set to -1.
859      * <p/>
860      * <p>For a node other than an element, the method returns null.</p>
861      */

862
863     public int[] getDeclaredNamespaces(int[] buffer) {
864         if (node.getNodeType() == Node.ELEMENT_NODE) {
865             Element elem = (Element)node;
866             NamedNodeMap atts = elem.getAttributes();
867
868             if (atts == null) {
869                 return EMPTY_NAMESPACE_LIST;
870             }
871             int count = 0;
872             for (int i=0; i<atts.getLength(); i++) {
873                 Attr att = (Attr)atts.item(i);
874                 String JavaDoc attName = att.getName();
875                 if (attName.equals("xmlns")) {
876                     count++;
877                 } else if (attName.startsWith("xmlns:")) {
878                     count++;
879                 }
880             }
881             if (count == 0) {
882                 return EMPTY_NAMESPACE_LIST;
883             } else {
884                 int[] result = (count > buffer.length ? new int[count] : buffer);
885                 NamePool pool = getNamePool();
886                 int n = 0;
887                 for (int i=0; i<atts.getLength(); i++) {
888                     Attr att = (Attr)atts.item(i);
889                     String JavaDoc attName = att.getName();
890                     if (attName.equals("xmlns")) {
891                         String JavaDoc prefix = "";
892                         String JavaDoc uri = att.getValue();
893                         result[n++] = pool.allocateNamespaceCode(prefix, uri);
894                     } else if (attName.startsWith("xmlns:")) {
895                         String JavaDoc prefix = attName.substring(6);
896                         String JavaDoc uri = att.getValue();
897                         result[n++] = pool.allocateNamespaceCode(prefix, uri);
898                     }
899                 }
900                 if (count < result.length) {
901                     result[count] = -1;
902                 }
903                 return result;
904             }
905         } else {
906             return null;
907         }
908     }
909
910
911     private final class AttributeEnumeration implements AxisIterator, LookaheadIterator {
912
913         private ArrayList JavaDoc attList = new ArrayList JavaDoc(10);
914         private int ix = 0;
915         private NodeWrapper start;
916         private NodeWrapper current;
917
918         public AttributeEnumeration(NodeWrapper start) {
919             this.start = start;
920             NamedNodeMap atts = start.node.getAttributes();
921             if (atts != null) {
922                 for (int i=0; i<atts.getLength(); i++) {
923                     String JavaDoc name = atts.item(i).getNodeName();
924                     if (!(name.startsWith("xmlns") &&
925                             (name.length() == 5 || name.charAt(5) == ':'))) {
926                         attList.add(atts.item(i));
927                     }
928                 }
929             }
930             ix = 0;
931         }
932
933         public boolean hasNext() {
934             return ix < attList.size();
935         }
936
937         public Item next() {
938             if (ix >= attList.size()) {
939                 return null;
940             }
941             current = start.makeWrapper(
942                     (Attr)attList.get(ix), docWrapper, start, ix);
943             ix++;
944             return current;
945         }
946
947         public Item current() {
948             return current;
949         }
950
951         public int position() {
952             return ix+1;
953         }
954
955         public SequenceIterator getAnother() {
956             return new AttributeEnumeration(start);
957         }
958
959         /**
960          * Get properties of this iterator, as a bit-significant integer.
961          *
962          * @return the properties of this iterator. This will be some combination of
963          * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
964          * and {@link LOOKAHEAD}. It is always
965          * acceptable to return the value zero, indicating that there are no known special properties.
966          * It is acceptable for the properties of the iterator to change depending on its state.
967          */

968
969         public int getProperties() {
970             return LOOKAHEAD;
971         }
972     }
973
974
975     /**
976     * The class ChildEnumeration handles not only the child axis, but also the
977     * following-sibling and preceding-sibling axes. It can also iterate the children
978     * of the start node in reverse order, something that is needed to support the
979     * preceding and preceding-or-ancestor axes (the latter being used by xsl:number)
980     */

981
982     private final class ChildEnumeration extends AxisIteratorImpl implements LookaheadIterator{
983
984         private NodeWrapper start;
985         private NodeWrapper commonParent;
986         private ArrayList JavaDoc items = new ArrayList JavaDoc(20);
987         private int ix = 0;
988         private boolean downwards; // iterate children of start node (not siblings)
989
private boolean forwards; // iterate in document order (not reverse order)
990

991         public ChildEnumeration(NodeWrapper start,
992                                 boolean downwards, boolean forwards) {
993             this.start = start;
994             this.downwards = downwards;
995             this.forwards = forwards;
996             position = 0;
997
998             if (downwards) {
999                 commonParent = start;
1000            } else {
1001                commonParent = (NodeWrapper)start.getParent();
1002            }
1003
1004            NodeList childNodes = commonParent.node.getChildNodes();
1005            if (downwards) {
1006                if (!forwards) {
1007                    // backwards enumeration: go to the end
1008
ix = childNodes.getLength() - 1;
1009                }
1010            } else {
1011                ix = start.getSiblingPosition() + (forwards ? span : -1);
1012            }
1013
1014            if (forwards) {
1015                boolean previousText = false;
1016                for (int i=ix; i<childNodes.getLength(); i++) {
1017                    boolean thisText = false;
1018                    Node node = childNodes.item(i);
1019                    switch (node.getNodeType()) {
1020                        case Node.DOCUMENT_TYPE_NODE:
1021                            break;
1022                        case Node.TEXT_NODE:
1023                        case Node.CDATA_SECTION_NODE:
1024                            thisText = true;
1025                            if (previousText) {
1026                                if (isAtomizing()) {
1027                                    UntypedAtomicValue old = (UntypedAtomicValue)(items.get(items.size()-1));
1028                                    String JavaDoc newval = old.getStringValue() + getStringValue(node, node.getNodeType());
1029                                    items.set(items.size()-1, new UntypedAtomicValue(newval));
1030                                } else {
1031                                    NodeWrapper old = ((NodeWrapper)items.get(items.size()-1));
1032                                    old.span++;
1033                                }
1034                                break;
1035                            }
1036                            // otherwise fall through to default case
1037
default:
1038                            previousText = thisText;
1039                            if (isAtomizing()) {
1040                                items.add(new UntypedAtomicValue(
1041                                        getStringValue(node, node.getNodeType())));
1042                            } else {
1043                                items.add(makeWrapper(node, docWrapper, commonParent, i));
1044                            }
1045                    }
1046                }
1047            } else {
1048                boolean previousText = false;
1049                for (int i=ix; i>=0; i--) {
1050                    boolean thisText = false;
1051                    Node node = childNodes.item(i);
1052                    switch (node.getNodeType()) {
1053                        case Node.DOCUMENT_TYPE_NODE:
1054                            break;
1055                        case Node.TEXT_NODE:
1056                        case Node.CDATA_SECTION_NODE:
1057                            thisText = true;
1058                            if (previousText) {
1059                                if (isAtomizing()) {
1060                                    UntypedAtomicValue old = (UntypedAtomicValue)(items.get(items.size()-1));
1061                                    String JavaDoc newval = old.getStringValue() + getStringValue(node, node.getNodeType());
1062                                    items.set(items.size()-1, new UntypedAtomicValue(newval));
1063                                } else {
1064                                    NodeWrapper old = ((NodeWrapper)items.get(items.size()-1));
1065                                    old.node = node;
1066                                    old.span++;
1067                                }
1068                                break;
1069                            }
1070                            // otherwise fall through to default case
1071
default:
1072                            previousText = thisText;
1073                            if (isAtomizing()) {
1074                                items.add(new UntypedAtomicValue(
1075                                        getStringValue(node, node.getNodeType())));
1076                            } else {
1077                                items.add(makeWrapper(node, docWrapper, commonParent, i));
1078                            }
1079                    }
1080                }
1081            }
1082        }
1083
1084        public boolean hasNext() {
1085            return position < items.size();
1086        }
1087
1088        public Item next() {
1089            if (position < items.size()) {
1090                current = (Item)items.get(position++);
1091                return current;
1092            } else {
1093                return null;
1094            }
1095        }
1096
1097        public SequenceIterator getAnother() {
1098            return new ChildEnumeration(start, downwards, forwards);
1099        }
1100
1101        /**
1102         * Get properties of this iterator, as a bit-significant integer.
1103         *
1104         * @return the properties of this iterator. This will be some combination of
1105         * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
1106         * and {@link LOOKAHEAD}. It is always
1107         * acceptable to return the value zero, indicating that there are no known special properties.
1108         * It is acceptable for the properties of the iterator to change depending on its state.
1109         */

1110
1111        public int getProperties() {
1112            return LOOKAHEAD;
1113        }
1114
1115    } // end of class ChildEnumeration
1116

1117
1118}
1119
1120//
1121
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1122
// you may not use this file except in compliance with the License. You may obtain a copy of the
1123
// License at http://www.mozilla.org/MPL/
1124
//
1125
// Software distributed under the License is distributed on an "AS IS" basis,
1126
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1127
// See the License for the specific language governing rights and limitations under the License.
1128
//
1129
// The Original Code is: all this file.
1130
//
1131
// The Initial Developer of the Original Code is Michael Kay
1132
//
1133
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1134
//
1135
// Contributor(s): none.
1136
//
1137
Popular Tags