1 package org.jaxen.dom;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 import javax.xml.parsers.DocumentBuilder;
52 import javax.xml.parsers.DocumentBuilderFactory;
53 import javax.xml.parsers.ParserConfigurationException;
54
55 import java.io.IOException;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.NoSuchElementException;
59
60 import org.jaxen.DefaultNavigator;
61 import org.jaxen.FunctionCallException;
62 import org.jaxen.Navigator;
63 import org.jaxen.XPath;
64 import org.jaxen.JaxenConstants;
65 import org.w3c.dom.Attr;
66 import org.w3c.dom.Document;
67 import org.w3c.dom.NamedNodeMap;
68 import org.w3c.dom.Node;
69 import org.w3c.dom.NodeList;
70 import org.w3c.dom.ProcessingInstruction;
71 import org.xml.sax.SAXException;
72
73 /*** Interface for navigating around the W3C DOM Level 2 object model.
74 *
75 * <p>
76 * This class is not intended for direct usage, but is
77 * used by the Jaxen engine during evaluation.
78 * </p>
79 *
80 * <p>This class implements the {@link org.jaxen.DefaultNavigator} interface
81 * for the Jaxen XPath library. This adapter allows the Jaxen
82 * library to be used to execute XPath queries against any object tree
83 * that implements the DOM level 2 interfaces.</p>
84 *
85 * <p>Note: DOM level 2 does not include a node representing an XPath
86 * namespace node. This navigator will return namespace nodes
87 * as instances of the custom {@link NamespaceNode} class, and
88 * users will have to check result sets to locate and isolate
89 * these.</p>
90 *
91 * @author David Megginson
92 * @author James Strachan
93 *
94 * @see XPath
95 * @see NamespaceNode
96 */
97 public class DocumentNavigator extends DefaultNavigator
98 {
99
100
101
102
103
104
105 /***
106 *
107 */
108 private static final long serialVersionUID = 8460943068889528115L;
109
110 private final static DocumentNavigator SINGLETON = new DocumentNavigator();
111
112
113
114
115
116
117
118
119 /***
120 * Default constructor.
121 */
122 public DocumentNavigator ()
123 {
124 }
125
126
127 /***
128 * Get a constant DocumentNavigator for efficiency.
129 *
130 * @return a constant instance of a DocumentNavigator.
131 */
132 public static Navigator getInstance ()
133 {
134 return SINGLETON;
135 }
136
137
138
139
140
141
142
143
144 /***
145 * Get an iterator over all of this node's children.
146 *
147 * @param contextNode the context node for the child axis.
148 * @return a possibly-empty iterator (not null)
149 */
150 public Iterator getChildAxisIterator (Object contextNode)
151 {
152 return new NodeIterator ((Node)contextNode) {
153 protected Node getFirstNode (Node node)
154 {
155 return node.getFirstChild();
156 }
157 protected Node getNextNode (Node node)
158 {
159 return node.getNextSibling();
160 }
161 };
162 }
163
164
165 /***
166 * Get a (single-member) iterator over this node's parent.
167 *
168 * @param contextNode the context node for the parent axis
169 * @return a possibly-empty iterator (not null)
170 */
171 public Iterator getParentAxisIterator (Object contextNode)
172 {
173 Node node = (Node)contextNode;
174
175 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
176 return new NodeIterator (node) {
177 protected Node getFirstNode (Node n)
178 {
179
180
181 return ((Attr)n).getOwnerElement();
182 }
183 protected Node getNextNode (Node n) {
184 return null;
185 }
186 };
187 } else {
188 return new NodeIterator (node) {
189 protected Node getFirstNode (Node n)
190 {
191 return n.getParentNode();
192 }
193 protected Node getNextNode (Node n) {
194 return null;
195 }
196 };
197 }
198 }
199
200
201 /***
202 * Return the XPath parent of the supplied DOM node.
203 * XPath has slightly different definition of parent than DOM does.
204 * In particular, the parent of an attribute is not null.
205 *
206 * @param child the child node
207 *
208 * @return the parent of the specified node; or null if
209 * the node does not have a parent
210 */
211 public Object getParentNode(Object child) {
212 Node node = (Node) child;
213 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
214 return ((Attr) node).getOwnerElement();
215 }
216 return node.getParentNode();
217 }
218
219
220 /***
221 * Get an iterator over all following siblings.
222 *
223 * @param contextNode the context node for the sibling iterator
224 * @return a possibly-empty iterator (not null)
225 */
226 public Iterator getFollowingSiblingAxisIterator (Object contextNode)
227 {
228 return new NodeIterator ((Node)contextNode) {
229 protected Node getFirstNode (Node node)
230 {
231 return getNextNode(node);
232 }
233 protected Node getNextNode (Node node) {
234 return node.getNextSibling();
235 }
236 };
237 }
238
239
240 /***
241 * Get an iterator over all preceding siblings.
242 *
243 * @param contextNode the context node for the preceding sibling axis
244 * @return a possibly-empty iterator (not null)
245 */
246 public Iterator getPrecedingSiblingAxisIterator (Object contextNode)
247 {
248 return new NodeIterator ((Node)contextNode) {
249 protected Node getFirstNode (Node node)
250 {
251 return getNextNode(node);
252 }
253 protected Node getNextNode (Node node) {
254 return node.getPreviousSibling();
255 }
256 };
257 }
258
259
260 /***
261 * Get an iterator over all following nodes, depth-first.
262 *
263 * @param contextNode the context node for the following axis
264 * @return a possibly-empty iterator (not null)
265 */
266 public Iterator getFollowingAxisIterator (Object contextNode)
267 {
268 return new NodeIterator ((Node)contextNode) {
269 protected Node getFirstNode (Node node)
270 {
271 if (node == null) {
272 return null;
273 }
274 else {
275 Node sibling = node.getNextSibling();
276 if (sibling == null) {
277 return getFirstNode(node.getParentNode());
278 }
279 else {
280 return sibling;
281 }
282 }
283 }
284 protected Node getNextNode (Node node) {
285 if (node == null) {
286 return null;
287 }
288 else {
289 Node n = node.getFirstChild();
290 if (n == null) n = node.getNextSibling();
291 if (n == null) return getFirstNode(node.getParentNode());
292 else return n;
293 }
294 }
295 };
296 }
297
298
299 /***
300 * Get an iterator over all attributes.
301 *
302 * @param contextNode the context node for the attribute axis
303 * @return a possibly-empty iterator (not null)
304 */
305 public Iterator getAttributeAxisIterator (Object contextNode)
306 {
307 if (isElement(contextNode)) {
308 return new AttributeIterator((Node)contextNode);
309 }
310 else {
311 return JaxenConstants.EMPTY_ITERATOR;
312 }
313 }
314
315
316 /***
317 * Get an iterator over all declared namespaces.
318 *
319 * <p>Note: this iterator is not live: it takes a snapshot
320 * and that snapshot remains static during the life of
321 * the iterator (i.e. it won't reflect subsequent changes
322 * to the DOM).</p>
323 *
324 * <p>
325 * In the event that the DOM is inconsistent; for instance a
326 * <code>pre:foo</code> element is declared by DOM to be in the
327 * http://www.a.com/ namespace but also has an
328 * <code>xmlns:pre="http://www.b.com"</code> attribute; then only
329 * one of the namespaces will be counted. This will be the intrinsic
330 * namespace of the <code>Element</code> or <code>Attr</code> object
331 * rather than the one provide by the contradictory namespace
332 * declaration attribute. In the event of a contradiction between two
333 * attributes on the same element--e.g. <code>pre:foo</code> in the
334 * http://www.a.com/ namespace and <code>pre:bar</code> in the
335 * http://www.b.com/ namespace--it is undefined which namespace
336 * will be returned.
337 * </p>
338 *
339 * @param contextNode the context node for the namespace axis
340 * @return a possibly-empty iterator (not null)
341 */
342 public Iterator getNamespaceAxisIterator (Object contextNode)
343 {
344
345 if (isElement(contextNode)) {
346
347 HashMap nsMap = new HashMap();
348
349
350
351
352 for (Node n = (Node) contextNode;
353 n != null;
354 n = n.getParentNode()) {
355
356
357 String myNamespace = n.getNamespaceURI();
358 if (myNamespace != null && ! "".equals(myNamespace)) {
359 String myPrefix = n.getPrefix();
360 if (!nsMap.containsKey(myPrefix)) {
361 NamespaceNode ns = new NamespaceNode((Node) contextNode, myPrefix, myNamespace);
362 nsMap.put(myPrefix, ns);
363 }
364 }
365
366 if (n.hasAttributes()) {
367 NamedNodeMap atts = n.getAttributes();
368 int length = atts.getLength();
369
370 for (int i = 0; i < length; i++) {
371 Attr att = (Attr) atts.item(i);
372
373 String attributeNamespace = att.getNamespaceURI();
374 if ("http://www.w3.org/2000/xmlns/".equals(attributeNamespace)) {
375 }
376 else if (attributeNamespace != null) {
377 String prefix = att.getPrefix();
378 NamespaceNode ns =
379 new NamespaceNode((Node)contextNode, prefix, attributeNamespace);
380
381 if (!nsMap.containsKey(prefix)) nsMap.put(prefix, ns);
382
383 }
384 }
385
386
387 for (int i = 0; i < length; i++) {
388 Attr att = (Attr) atts.item(i);
389
390 String attributeNamespace = att.getNamespaceURI();
391 if ("http://www.w3.org/2000/xmlns/".equals(attributeNamespace)) {
392 NamespaceNode ns =
393 new NamespaceNode( (Node)contextNode, att);
394
395 String name = ns.getNodeName();
396 if (!nsMap.containsKey(name)) nsMap.put(name, ns);
397 }
398 }
399
400 }
401
402 }
403
404
405 nsMap.put("xml",
406 new
407 NamespaceNode((Node)contextNode,
408 "xml",
409 "http://www.w3.org/XML/1998/namespace"));
410
411
412
413 NamespaceNode defaultNS = (NamespaceNode)nsMap.get("");
414 if (defaultNS != null && defaultNS.getNodeValue().length() == 0) {
415 nsMap.remove("");
416 }
417 return nsMap.values().iterator();
418 }
419 else {
420 return JaxenConstants.EMPTY_ITERATOR;
421 }
422 }
423
424 /*** Returns a parsed form of the given XPath string, which will be suitable
425 * for queries on DOM documents.
426 *
427 * @param xpath the XPath expression
428 * @return a parsed form of the given XPath string
429 * @throws org.jaxen.saxpath.SAXPathException if the string is syntactically incorrect
430 */
431 public XPath parseXPath (String xpath) throws org.jaxen.saxpath.SAXPathException
432 {
433 return new DOMXPath(xpath);
434 }
435
436 /***
437 * Get the top-level document node.
438 *
439 * @param contextNode any node in the document
440 * @return the root node
441 */
442 public Object getDocumentNode (Object contextNode)
443 {
444 if (isDocument(contextNode)) return contextNode;
445 else return ((Node)contextNode).getOwnerDocument();
446 }
447
448
449
450
451 /***
452 * Get the namespace URI of an element.
453 *
454 * @param element the target node
455 * @return a string (possibly empty) if the node is an element,
456 * and null otherwise
457 */
458 public String getElementNamespaceUri (Object element)
459 {
460 try {
461 Node node = (Node) element;
462 if (node.getNodeType() == Node.ELEMENT_NODE) {
463 return node.getNamespaceURI();
464 }
465 }
466 catch (ClassCastException ex) {
467 }
468 return null;
469 }
470
471
472 /***
473 * Get the local name of an element.
474 *
475 * @param element the target node
476 * @return a string representing the unqualified local name
477 * if the node is an element, or null otherwise
478 */
479 public String getElementName (Object element)
480 {
481 if (isElement(element)) {
482 String name = ((Node)element).getLocalName();
483 if (name == null) name = ((Node)element).getNodeName();
484 return name;
485 }
486 return null;
487 }
488
489
490 /***
491 * Get the qualified name of an element.
492 *
493 * @param element the target node
494 * @return a string representing the qualified (i.e. possibly
495 * prefixed) name if the argument is an element, or null otherwise
496 */
497 public String getElementQName (Object element)
498 {
499 try {
500 Node node = (Node) element;
501 if (node.getNodeType() == Node.ELEMENT_NODE) {
502 return node.getNodeName();
503 }
504 }
505 catch (ClassCastException ex) {
506 }
507 return null;
508 }
509
510
511 /***
512 * Get the namespace URI of an attribute.
513 *
514 * @param attribute the target node
515 *
516 * @return the namespace name of the specified node
517 *
518 */
519 public String getAttributeNamespaceUri (Object attribute)
520 {
521 try {
522 Node node = (Node) attribute;
523 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
524 return node.getNamespaceURI();
525 }
526 }
527 catch (ClassCastException ex) {
528 }
529 return null;
530 }
531
532
533 /***
534 * Get the local name of an attribute.
535 *
536 * @param attribute the target node
537 * @return a string representing the unqualified local name
538 * if the node is an attribute, or null otherwise
539 */
540 public String getAttributeName (Object attribute)
541 {
542 if (isAttribute(attribute)) {
543 String name = ((Node)attribute).getLocalName();
544 if (name == null) name = ((Node)attribute).getNodeName();
545 return name;
546 }
547 return null;
548 }
549
550
551 /***
552 * Get the qualified name of an attribute.
553 *
554 * @param attribute the target node
555 *
556 * @return a string representing the qualified (i.e. possibly
557 * prefixed) name if the argument is an attribute, or null otherwise
558 */
559 public String getAttributeQName (Object attribute)
560 {
561 try {
562 Node node = (Node) attribute;
563 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
564 return node.getNodeName();
565 }
566 }
567 catch (ClassCastException ex) {
568 }
569 return null;
570 }
571
572
573 /***
574 * Test if a node is a top-level document.
575 *
576 * @param object the target node
577 * @return true if the node is the document root, false otherwise
578 */
579 public boolean isDocument (Object object)
580 {
581 return (object instanceof Node) &&
582 (((Node)object).getNodeType() == Node.DOCUMENT_NODE);
583 }
584
585
586 /***
587 * Test if a node is a namespace.
588 *
589 * @param object the target node
590 * @return true if the node is a namespace, false otherwise
591 */
592 public boolean isNamespace (Object object)
593 {
594 return (object instanceof NamespaceNode);
595 }
596
597
598 /***
599 * Test if a node is an element.
600 *
601 * @param object the target node
602 * @return true if the node is an element, false otherwise
603 */
604 public boolean isElement (Object object)
605 {
606 return (object instanceof Node) &&
607 (((Node)object).getNodeType() == Node.ELEMENT_NODE);
608 }
609
610
611 /***
612 * Test if a node is an attribute. <code>xmlns</code> and
613 * <code>xmlns:pre</code> attributes do not count as attributes
614 * for the purposes of XPath.
615 *
616 * @param object the target node
617 * @return true if the node is an attribute, false otherwise
618 */
619 public boolean isAttribute (Object object)
620 {
621 return (object instanceof Node) &&
622 (((Node)object).getNodeType() == Node.ATTRIBUTE_NODE)
623 && ! "http://www.w3.org/2000/xmlns/".equals(((Node) object).getNamespaceURI());
624 }
625
626
627 /***
628 * Test if a node is a comment.
629 *
630 * @param object the target node
631 * @return true if the node is a comment, false otherwise
632 */
633 public boolean isComment (Object object)
634 {
635 return (object instanceof Node) &&
636 (((Node)object).getNodeType() == Node.COMMENT_NODE);
637 }
638
639
640 /***
641 * Test if a node is plain text.
642 *
643 * @param object the target node
644 * @return true if the node is a text node, false otherwise
645 */
646 public boolean isText (Object object)
647 {
648 if (object instanceof Node) {
649 switch (((Node)object).getNodeType()) {
650 case Node.TEXT_NODE:
651 case Node.CDATA_SECTION_NODE:
652 return true;
653 default:
654 return false;
655 }
656 } else {
657 return false;
658 }
659 }
660
661
662 /***
663 * Test if a node is a processing instruction.
664 *
665 * @param object the target node
666 * @return true if the node is a processing instruction, false otherwise
667 */
668 public boolean isProcessingInstruction (Object object)
669 {
670 return (object instanceof Node) &&
671 (((Node)object).getNodeType() == Node.PROCESSING_INSTRUCTION_NODE);
672 }
673
674
675 /***
676 * Get the string value of an element node.
677 *
678 * @param object the target node
679 * @return the text inside the node and its descendants if the node
680 * is an element, null otherwise
681 */
682 public String getElementStringValue (Object object)
683 {
684 if (isElement(object)) {
685 return getStringValue((Node)object, new StringBuffer()).toString();
686 }
687 else {
688 return null;
689 }
690 }
691
692
693 /***
694 * Construct a node's string value recursively.
695 *
696 * @param node the current node
697 * @param buffer the buffer for building the text
698 * @return the buffer passed as a parameter (for convenience)
699 */
700 private StringBuffer getStringValue (Node node, StringBuffer buffer)
701 {
702 if (isText(node)) {
703 buffer.append(node.getNodeValue());
704 } else {
705 NodeList children = node.getChildNodes();
706 int length = children.getLength();
707 for (int i = 0; i < length; i++) {
708 getStringValue(children.item(i), buffer);
709 }
710 }
711 return buffer;
712 }
713
714
715 /***
716 * Get the string value of an attribute node.
717 *
718 * @param object the target node
719 * @return the text of the attribute value if the node is an
720 * attribute, null otherwise
721 */
722 public String getAttributeStringValue (Object object)
723 {
724 if (isAttribute(object)) return ((Node)object).getNodeValue();
725 else return null;
726 }
727
728
729 /***
730 * Get the string value of text.
731 *
732 * @param object the target node
733 * @return the string of text if the node is text, null otherwise
734 */
735 public String getTextStringValue (Object object)
736 {
737 if (isText(object)) return ((Node)object).getNodeValue();
738 else return null;
739 }
740
741
742 /***
743 * Get the string value of a comment node.
744 *
745 * @param object the target node
746 * @return the text of the comment if the node is a comment, null otherwise
747 */
748 public String getCommentStringValue (Object object)
749 {
750 if (isComment(object)) return ((Node)object).getNodeValue();
751 else return null;
752 }
753
754
755 /***
756 * Get the string value of a namespace node.
757 *
758 * @param object the target node
759 * @return the namespace URI as a (possibly empty) string if the
760 * node is a namespace node, null otherwise
761 */
762 public String getNamespaceStringValue (Object object)
763 {
764 if (isNamespace(object)) return ((NamespaceNode)object).getNodeValue();
765 else return null;
766 }
767
768 /***
769 * Get the prefix value of a namespace node.
770 *
771 * @param object the target node
772 * @return the namespace prefix a (possibly empty) string if the
773 * node is a namespace node, null otherwise
774 */
775 public String getNamespacePrefix (Object object)
776 {
777 if (isNamespace(object)) return ((NamespaceNode)object).getLocalName();
778 else return null;
779 }
780
781 /***
782 * Translate a namespace prefix to a URI.
783 *
784 * @param prefix the namespace prefix
785 * @param element the namespace context
786 * @return the namespace URI bound to the prefix in the scope of <code>element</code>;
787 * null if the prefix is not bound
788 */
789 public String translateNamespacePrefixToUri (String prefix, Object element)
790 {
791 Iterator it = getNamespaceAxisIterator(element);
792 while (it.hasNext()) {
793 NamespaceNode ns = (NamespaceNode)it.next();
794 if (prefix.equals(ns.getNodeName())) return ns.getNodeValue();
795 }
796 return null;
797 }
798
799 /***
800 * Use JAXP to load a namespace aware document from a given URI.
801 *
802 * @param uri the URI of the document to load
803 * @return the new W3C DOM Level 2 Document instance
804 * @throws FunctionCallException containing a nested exception
805 * if a problem occurs trying to parse the given document
806 *
807 * @todo Possibly we could make the factory a thread local.
808 */
809 public Object getDocument(String uri) throws FunctionCallException
810 {
811 try
812 {
813
814
815 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
816 factory.setNamespaceAware(true);
817 DocumentBuilder builder = factory.newDocumentBuilder();
818 return builder.parse( uri );
819 }
820 catch (ParserConfigurationException e) {
821 throw new FunctionCallException("JAXP setup error in document() function: " + e.getMessage(), e);
822 }
823 catch (SAXException e) {
824 throw new FunctionCallException("XML error in document() function: " + e.getMessage(), e);
825 }
826 catch (IOException e) {
827 throw new FunctionCallException("I/O error in document() function: " + e.getMessage(), e);
828 }
829
830 }
831
832 /***
833 * Get the target of a processing instruction node.
834 *
835 * @param obj the processing instruction
836 * @return the target of the processing instruction
837 * @throws ClassCastException if obj is not a processing instruxtion
838 *
839 */
840 public String getProcessingInstructionTarget(Object obj)
841 {
842 if (isProcessingInstruction(obj)) {
843 ProcessingInstruction pi = (ProcessingInstruction) obj;
844 return pi.getTarget();
845 }
846 throw new ClassCastException(obj + " is not a processing instruction");
847 }
848
849 /***
850 * Get the data of a processing instruction node.
851 *
852 * @param obj the processing instruction
853 * @return the target of the processing instruction
854 * @throws ClassCastException if obj is not a processing instruxtion
855 *
856 */
857 public String getProcessingInstructionData(Object obj)
858 {
859 if (isProcessingInstruction(obj)) {
860 ProcessingInstruction pi = (ProcessingInstruction) obj;
861 return pi.getData();
862 }
863 throw new ClassCastException(obj + " is not a processing instruction");
864 }
865
866
867
868
869
870
871
872
873
874
875
876 /***
877 * A generic iterator over DOM nodes.
878 *
879 * <p>Concrete subclasses must implement the {@link #getFirstNode}
880 * and {@link #getNextNode} methods for a specific iteration
881 * strategy.</p>
882 */
883 abstract class NodeIterator
884 implements Iterator
885 {
886
887
888 /***
889 * Constructor.
890 *
891 * @param contextNode the starting node
892 */
893 public NodeIterator (Node contextNode)
894 {
895 node = getFirstNode(contextNode);
896 while (!isXPathNode(node)) {
897 node = getNextNode(node);
898 }
899 }
900
901 public boolean hasNext ()
902 {
903 return (node != null);
904 }
905
906 public Object next ()
907 {
908 if (node == null) throw new NoSuchElementException();
909 Node ret = node;
910 node = getNextNode(node);
911 while (!isXPathNode(node)) {
912 node = getNextNode(node);
913 }
914 return ret;
915 }
916
917 public void remove ()
918 {
919 throw new UnsupportedOperationException();
920 }
921
922
923 /***
924 * Get the first node for iteration.
925 *
926 * <p>This method must derive an initial node for iteration
927 * from a context node.</p>
928 *
929 * @param contextNode the starting node
930 * @return the first node in the iteration
931 * @see #getNextNode
932 */
933 protected abstract Node getFirstNode (Node contextNode);
934
935
936 /***
937 * Get the next node for iteration.
938 *
939 * <p>This method must locate a following node from the
940 * current context node.</p>
941 *
942 * @param contextNode the current node in the iteration
943 * @return the following node in the iteration, or null
944 * if there is none
945 * @see #getFirstNode
946 */
947 protected abstract Node getNextNode (Node contextNode);
948
949
950 /***
951 * Test whether a DOM node is usable by XPath.
952 *
953 * @param node the DOM node to test
954 * @return true if the node is usable, false if it should be skipped
955 */
956 private boolean isXPathNode (Node node)
957 {
958
959 if (node == null) return true;
960
961 switch (node.getNodeType()) {
962 case Node.DOCUMENT_FRAGMENT_NODE:
963 case Node.DOCUMENT_TYPE_NODE:
964 case Node.ENTITY_NODE:
965 case Node.ENTITY_REFERENCE_NODE:
966 case Node.NOTATION_NODE:
967 return false;
968 default:
969 return true;
970 }
971 }
972
973 private Node node;
974 }
975
976
977
978
979
980
981
982
983 /***
984 * An iterator over an attribute list.
985 */
986 private static class AttributeIterator implements Iterator
987 {
988
989 /***
990 * Constructor.
991 *
992 * @param parent the parent DOM element for the attributes.
993 */
994 AttributeIterator (Node parent)
995 {
996 this.map = parent.getAttributes();
997 this.pos = 0;
998 for (int i = this.map.getLength()-1; i >= 0; i--) {
999 Node node = map.item(i);
1000 if (! "http://www.w3.org/2000/xmlns/".equals(node.getNamespaceURI())) {
1001 this.lastAttribute = i;
1002 break;
1003 }
1004 }
1005 }
1006
1007 public boolean hasNext ()
1008 {
1009 return pos <= lastAttribute;
1010 }
1011
1012 public Object next ()
1013 {
1014 Node attr = map.item(pos++);
1015 if (attr == null) throw new NoSuchElementException();
1016 else if ("http://www.w3.org/2000/xmlns/".equals(attr.getNamespaceURI())) {
1017
1018
1019 return next();
1020 }
1021 else return attr;
1022 }
1023
1024 public void remove ()
1025 {
1026 throw new UnsupportedOperationException();
1027 }
1028
1029
1030 private NamedNodeMap map;
1031 private int pos;
1032 private int lastAttribute = -1;
1033
1034 }
1035
1036 /***
1037 * Returns the element whose ID is given by elementId.
1038 * If no such element exists, returns null.
1039 * Attributes with the name "ID" are not of type ID unless so defined.
1040 * Attribute types are only known if when the parser understands DTD's or
1041 * schemas that declare attributes of type ID. When JAXP is used, you
1042 * must call <code>setValidating(true)</code> on the
1043 * DocumentBuilderFactory.
1044 *
1045 * @param object a node from the document in which to look for the id
1046 * @param elementId id to look for
1047 *
1048 * @return element whose ID is given by elementId, or null if no such
1049 * element exists in the document or if the implementation
1050 * does not know about attribute types
1051 * @see javax.xml.parsers.DocumentBuilderFactory
1052 *
1053 * @throws ClassCastException if object is not an <code>org.w3c.dom.Node</code> object
1054 *
1055 */
1056 public Object getElementById(Object object, String elementId)
1057 {
1058 Document doc = (Document)getDocumentNode(object);
1059 if (doc != null) return doc.getElementById(elementId);
1060 else return null;
1061 }
1062
1063 }
1064
1065