2 Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
40 import java
.util
.Iterator
;
41 import javax
.xml
.XMLConstants
;
43 import org
.w3c
.dom
.Attr
;
44 import org
.w3c
.dom
.CDATASection
;
45 import org
.w3c
.dom
.Comment
;
46 import org
.w3c
.dom
.Document
;
47 import org
.w3c
.dom
.DocumentFragment
;
48 import org
.w3c
.dom
.DocumentType
;
49 import org
.w3c
.dom
.DOMConfiguration
;
50 import org
.w3c
.dom
.DOMImplementation
;
51 import org
.w3c
.dom
.DOMException
;
52 import org
.w3c
.dom
.Element
;
53 import org
.w3c
.dom
.Entity
;
54 import org
.w3c
.dom
.EntityReference
;
55 import org
.w3c
.dom
.NamedNodeMap
;
56 import org
.w3c
.dom
.Node
;
57 import org
.w3c
.dom
.Notation
;
58 import org
.w3c
.dom
.ProcessingInstruction
;
59 import org
.w3c
.dom
.Text
;
60 import org
.w3c
.dom
.UserDataHandler
;
61 import org
.w3c
.dom
.traversal
.DocumentTraversal
;
62 import org
.w3c
.dom
.traversal
.NodeFilter
;
63 import org
.w3c
.dom
.traversal
.NodeIterator
;
64 import org
.w3c
.dom
.traversal
.TreeWalker
;
65 import org
.w3c
.dom
.xpath
.XPathEvaluator
;
66 import org
.w3c
.dom
.xpath
.XPathException
;
67 import org
.w3c
.dom
.xpath
.XPathExpression
;
68 import org
.w3c
.dom
.xpath
.XPathNSResolver
;
71 * <p> "Document" and "DocumentTraversal" implementation.
73 * <p> Note that when this checks names for legality, it uses an
74 * approximation of the XML rules, not the real ones. Specifically,
75 * it uses Unicode rules, with sufficient tweaks to pass a majority
76 * of basic XML conformance tests. (The huge XML character tables are
77 * hairy to implement.)
79 * @author David Brownell
80 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
82 public class DomDocument
84 implements Document
, DocumentTraversal
, XPathEvaluator
87 private final DOMImplementation implementation
;
88 private boolean checkingCharacters
= true;
89 boolean checkingWellformedness
= true;
91 boolean building
; // if true, skip mutation events in the tree
93 DomDocumentConfiguration config
;
97 String version
= "1.0";
102 * Constructs a Document node, associating it with an instance
103 * of the DomImpl class.
105 * <p> Note that this constructor disables character checking.
106 * It is normally used when connecting a DOM to an XML parser,
107 * and duplicating such checks is undesirable. When used for
108 * purposes other than connecting to a parser, you should
109 * re-enable that checking.
111 * @see #setCheckingCharacters
119 * Constructs a Document node, associating it with the specified
120 * implementation. This should only be used in conjunction with
121 * a specialized implementation; it will normally be called by
122 * that implementation.
125 * @see #setCheckingCharacters
127 protected DomDocument(DOMImplementation impl
)
129 super(DOCUMENT_NODE
, null);
130 implementation
= impl
;
134 * Sets the <code>building</code> flag.
135 * Mutation events in the document are not reported.
137 public void setBuilding(boolean flag
)
143 * Sets whether to check for document well-formedness.
144 * If true, an exception will be raised if a second doctype or root
145 * element node is added to the document.
147 public void setCheckWellformedness(boolean flag
)
149 checkingWellformedness
= flag
;
154 * Returns the constant "#document".
156 final public String
getNodeName()
163 * Returns the document's root element, or null.
165 final public Element
getDocumentElement()
167 for (DomNode ctx
= first
; ctx
!= null; ctx
= ctx
.next
)
169 if (ctx
.nodeType
== ELEMENT_NODE
)
171 return (Element
) ctx
;
179 * Returns the document's DocumentType, or null.
181 final public DocumentType
getDoctype()
183 for (DomNode ctx
= first
; ctx
!= null; ctx
= ctx
.next
)
185 if (ctx
.nodeType
== DOCUMENT_TYPE_NODE
)
187 return (DocumentType
) ctx
;
195 * Returns the document's DOMImplementation.
197 final public DOMImplementation
getImplementation()
199 return implementation
;
203 * <b>DOM L1 (relocated in DOM L2)</b>
204 * Returns the element with the specified "ID" attribute, or null.
206 * <p>Returns null unless {@link Consumer} was used to populate internal
207 * DTD declaration information, using package-private APIs. If that
208 * internal DTD information is available, the document may be searched for
209 * the element with that ID.
211 public Element
getElementById(String id
)
213 if (id
== null || id
.length() == 0)
217 DomDoctype doctype
= (DomDoctype
) getDoctype();
218 if (doctype
!= null && !doctype
.hasIds())
223 // yes, this is linear in size of document.
224 // it'd be easy enough to maintain a hashtable.
225 Node current
= getDocumentElement();
232 while (current
!= this)
235 if (current
.getNodeType() == ELEMENT_NODE
)
237 DomElement element
= (DomElement
) current
;
240 DTDElementTypeInfo info
=
241 doctype
.getElementTypeInfo(current
.getNodeName());
243 id
.equals(element
.getAttribute(info
.idAttrName
)))
247 else if (element
.userIdAttrs
!= null)
249 for (Iterator i
= element
.userIdAttrs
.iterator();
252 Node idAttr
= (Node
) i
.next();
253 if (id
.equals(idAttr
.getNodeValue()))
261 String xmlId
= element
.getAttribute("xml:id");
264 xmlId
= element
.getAttributeNS(XMLConstants
.XML_NS_URI
,
267 if (id
.equals(xmlId
))
274 if (current
.hasChildNodes())
276 current
= current
.getFirstChild();
281 temp
= current
.getNextSibling();
291 temp
= current
.getParentNode();
297 temp
= current
.getNextSibling();
299 while (temp
== null);
305 private void checkNewChild(Node newChild
)
307 if (newChild
.getNodeType() == ELEMENT_NODE
308 && getDocumentElement() != null)
310 throw new DomDOMException(DOMException
.HIERARCHY_REQUEST_ERR
,
311 "document element already present: " +
312 getDocumentElement(), newChild
, 0);
314 if (newChild
.getNodeType() == DOCUMENT_TYPE_NODE
315 && getDoctype() != null)
317 throw new DomDOMException(DOMException
.HIERARCHY_REQUEST_ERR
,
318 "document type already present: " +
319 getDoctype(), newChild
, 0);
325 * Appends the specified node to this node's list of children,
326 * enforcing the constraints that there be only one root element
327 * and one document type child.
329 public Node
appendChild(Node newChild
)
331 if (checkingWellformedness
)
333 checkNewChild(newChild
);
335 return super.appendChild(newChild
);
340 * Inserts the specified node in this node's list of children,
341 * enforcing the constraints that there be only one root element
342 * and one document type child.
344 public Node
insertBefore(Node newChild
, Node refChild
)
346 if (checkingWellformedness
)
348 checkNewChild(newChild
);
350 return super.insertBefore(newChild
, refChild
);
355 * Replaces the specified node in this node's list of children,
356 * enforcing the constraints that there be only one root element
357 * and one document type child.
359 public Node
replaceChild(Node newChild
, Node refChild
)
361 if (checkingWellformedness
&&
362 ((newChild
.getNodeType() == ELEMENT_NODE
&&
363 refChild
.getNodeType() != ELEMENT_NODE
) ||
364 (newChild
.getNodeType() == DOCUMENT_TYPE_NODE
&&
365 refChild
.getNodeType() != DOCUMENT_TYPE_NODE
)))
367 checkNewChild(newChild
);
369 return super.replaceChild(newChild
, refChild
);
372 // NOTE: DOM can't really tell when the name of an entity,
373 // notation, or PI must follow the namespace rules (excluding
374 // colons) instead of the XML rules (which allow them without
375 // much restriction). That's an API issue. verifyXmlName
376 // aims to enforce the XML rules, not the namespace rules.
379 * Throws a DOM exception if the specified name is not a legal XML 1.0
381 * @deprecated This method is deprecated and may be removed in future
382 * versions of GNU JAXP
384 public static void verifyXmlName(String name
)
386 // XXX why is this public?
387 checkName(name
, false);
390 static void checkName(String name
, boolean xml11
)
394 throw new DomDOMException(DOMException
.NAMESPACE_ERR
, name
, null, 0);
396 int len
= name
.length();
399 throw new DomDOMException(DOMException
.NAMESPACE_ERR
, name
, null, 0);
402 // dog: rewritten to use the rules for XML 1.0 and 1.1
404 // Name start character
405 char c
= name
.charAt(0);
409 if ((c
< 0x0041 || c
> 0x005a) &&
410 (c
< 0x0061 || c
> 0x007a) &&
411 c
!= ':' && c
!= '_' &&
412 (c
< 0x00c0 || c
> 0x00d6) &&
413 (c
< 0x00d8 || c
> 0x00f6) &&
414 (c
< 0x00f8 || c
> 0x02ff) &&
415 (c
< 0x0370 || c
> 0x037d) &&
416 (c
< 0x037f || c
> 0x1fff) &&
417 (c
< 0x200c || c
> 0x200d) &&
418 (c
< 0x2070 || c
> 0x218f) &&
419 (c
< 0x2c00 || c
> 0x2fef) &&
420 (c
< 0x3001 || c
> 0xd7ff) &&
421 (c
< 0xf900 || c
> 0xfdcf) &&
422 (c
< 0xfdf0 || c
> 0xfffd) &&
423 (c
< 0x10000 || c
> 0xeffff))
425 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
,
432 int type
= Character
.getType(c
);
435 case Character
.LOWERCASE_LETTER
: // Ll
436 case Character
.UPPERCASE_LETTER
: // Lu
437 case Character
.OTHER_LETTER
: // Lo
438 case Character
.TITLECASE_LETTER
: // Lt
439 case Character
.LETTER_NUMBER
: // Nl
440 if ((c
> 0xf900 && c
< 0xfffe) ||
441 (c
>= 0x20dd && c
<= 0x20e0))
443 // Compatibility area and Unicode 2.0 exclusions
444 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
,
449 if (c
!= ':' && c
!= '_' && (c
< 0x02bb || c
> 0x02c1) &&
450 c
!= 0x0559 && c
!= 0x06e5 && c
!= 0x06e6)
452 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
,
458 // Subsequent characters
459 for (int i
= 1; i
< len
; i
++)
465 if ((c
< 0x0041 || c
> 0x005a) &&
466 (c
< 0x0061 || c
> 0x007a) &&
467 (c
< 0x0030 || c
> 0x0039) &&
468 c
!= ':' && c
!= '_' && c
!= '-' && c
!= '.' &&
469 (c
< 0x00c0 || c
> 0x00d6) &&
470 (c
< 0x00d8 || c
> 0x00f6) &&
471 (c
< 0x00f8 || c
> 0x02ff) &&
472 (c
< 0x0370 || c
> 0x037d) &&
473 (c
< 0x037f || c
> 0x1fff) &&
474 (c
< 0x200c || c
> 0x200d) &&
475 (c
< 0x2070 || c
> 0x218f) &&
476 (c
< 0x2c00 || c
> 0x2fef) &&
477 (c
< 0x3001 || c
> 0xd7ff) &&
478 (c
< 0xf900 || c
> 0xfdcf) &&
479 (c
< 0xfdf0 || c
> 0xfffd) &&
480 (c
< 0x10000 || c
> 0xeffff) &&
482 (c
< 0x0300 || c
> 0x036f) &&
483 (c
< 0x203f || c
> 0x2040))
485 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
, name
,
492 int type
= Character
.getType(c
);
495 case Character
.LOWERCASE_LETTER
: // Ll
496 case Character
.UPPERCASE_LETTER
: // Lu
497 case Character
.DECIMAL_DIGIT_NUMBER
: // Nd
498 case Character
.OTHER_LETTER
: // Lo
499 case Character
.TITLECASE_LETTER
: // Lt
500 case Character
.LETTER_NUMBER
: // Nl
501 case Character
.COMBINING_SPACING_MARK
: // Mc
502 case Character
.ENCLOSING_MARK
: // Me
503 case Character
.NON_SPACING_MARK
: // Mn
504 case Character
.MODIFIER_LETTER
: // Lm
505 if ((c
> 0xf900 && c
< 0xfffe) ||
506 (c
>= 0x20dd && c
<= 0x20e0))
508 // Compatibility area and Unicode 2.0 exclusions
509 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
,
514 if (c
!= '-' && c
!= '.' && c
!= ':' && c
!= '_' &&
515 c
!= 0x0387 && (c
< 0x02bb || c
> 0x02c1) &&
516 c
!= 0x0559 && c
!= 0x06e5 && c
!= 0x06e6 && c
!= 0x00b7)
518 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
,
525 // FIXME characters with a font or compatibility decomposition (i.e.
526 // those with a "compatibility formatting tag" in field 5 of the
527 // database -- marked by field 5 beginning with a "<") are not allowed.
531 static void checkNCName(String name
, boolean xml11
)
533 checkName(name
, xml11
);
534 int len
= name
.length();
535 int index
= name
.indexOf(':');
538 if (index
== 0 || index
== (len
- 1) ||
539 name
.lastIndexOf(':') != index
)
541 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
548 static void checkChar(String value
, boolean xml11
)
550 char[] chars
= value
.toCharArray();
551 checkChar(chars
, 0, chars
.length
, xml11
);
554 static void checkChar(char[] buf
, int off
, int len
, boolean xml11
)
556 for (int i
= 0; i
< len
; i
++)
560 // assume surrogate pairing checks out OK, for simplicity
561 if ((c
>= 0x0020 && c
<= 0xd7ff) ||
562 (c
== 0x000a || c
== 0x000d || c
== 0x0009) ||
563 (c
>= 0xe000 && c
<= 0xfffd) ||
564 (c
>= 0x10000 && c
<= 0x10ffff))
570 if ((c
>= 0x0001 && c
<= 0x001f) ||
571 (c
>= 0x007f && c
<= 0x0084) ||
572 (c
>= 0x0086 && c
<= 0x009f))
577 throw new DomDOMException(DOMException
.INVALID_CHARACTER_ERR
,
578 new String(buf
, off
, len
), null, c
);
584 * Returns a newly created element with the specified name.
586 public Element
createElement(String name
)
590 if (checkingCharacters
)
592 checkName(name
, "1.1".equals(version
));
594 if (name
.startsWith("xml:"))
596 element
= createElementNS(null, name
);
600 DomElement domElement
= new DomElement(this, null, name
);
601 domElement
.localName
= null;
602 element
= domElement
;
604 defaultAttributes(element
, name
);
610 * Returns a newly created element with the specified name
611 * and namespace information.
613 public Element
createElementNS(String namespaceURI
, String name
)
615 if (checkingCharacters
)
617 checkNCName(name
, "1.1".equals(version
));
620 if ("".equals(namespaceURI
))
624 if (name
.startsWith("xml:"))
626 if (namespaceURI
!= null
627 && !XMLConstants
.XML_NS_URI
.equals(namespaceURI
))
629 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
630 "xml namespace is always " +
631 XMLConstants
.XML_NS_URI
, this, 0);
633 namespaceURI
= XMLConstants
.XML_NS_URI
;
635 else if (XMLConstants
.XMLNS_ATTRIBUTE
.equals(name
) ||
636 name
.startsWith("xmlns:"))
638 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
639 "xmlns is reserved", this, 0);
641 else if (namespaceURI
== null && name
.indexOf(':') != -1)
643 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
644 "prefixed name '" + name
+
645 "' needs a URI", this, 0);
648 Element element
= new DomElement(this, namespaceURI
, name
);
649 defaultAttributes(element
, name
);
653 private void defaultAttributes(Element element
, String name
)
655 DomDoctype doctype
= (DomDoctype
) getDoctype();
661 // default any attributes that need it
662 DTDElementTypeInfo info
= doctype
.getElementTypeInfo(name
);
665 for (Iterator i
= info
.attributes(); i
!= null && i
.hasNext(); )
667 DTDAttributeTypeInfo attr
= (DTDAttributeTypeInfo
) i
.next();
668 DomAttr node
= (DomAttr
) createAttribute(attr
.name
);
670 String value
= attr
.value
;
675 node
.setValue(value
);
676 node
.setSpecified(false);
677 element
.setAttributeNode(node
);
684 * Returns a newly created document fragment.
686 public DocumentFragment
createDocumentFragment()
688 return new DomDocumentFragment(this);
693 * Returns a newly created text node with the specified value.
695 public Text
createTextNode(String value
)
697 if (checkingCharacters
)
699 checkChar(value
, "1.1".equals(version
));
701 return new DomText(this, value
);
705 * Returns a newly created text node with the specified value.
707 public Text
createTextNode(char[] buf
, int off
, int len
)
709 if (checkingCharacters
)
711 checkChar(buf
, off
, len
, "1.1".equals(version
));
713 return new DomText(this, buf
, off
, len
);
718 * Returns a newly created comment node with the specified value.
720 public Comment
createComment(String value
)
722 if (checkingCharacters
)
724 checkChar(value
, "1.1".equals(version
));
726 return new DomComment(this, value
);
731 * Returns a newly created CDATA section node with the specified value.
733 public CDATASection
createCDATASection(String value
)
735 if (checkingCharacters
)
737 checkChar(value
, "1.1".equals(version
));
739 return new DomCDATASection(this, value
);
743 * Returns a newly created CDATA section node with the specified value.
745 public CDATASection
createCDATASection(char[] buf
, int off
, int len
)
747 if (checkingCharacters
)
749 checkChar(buf
, off
, len
, "1.1".equals(version
));
751 return new DomCDATASection(this, buf
, off
, len
);
756 * Returns a newly created processing instruction.
758 public ProcessingInstruction
createProcessingInstruction(String target
,
761 if (checkingCharacters
)
763 boolean xml11
= "1.1".equals(version
);
764 checkName(target
, xml11
);
765 if ("xml".equalsIgnoreCase(target
))
767 throw new DomDOMException(DOMException
.SYNTAX_ERR
,
768 "illegal PI target name",
771 checkChar(data
, xml11
);
773 return new DomProcessingInstruction(this, target
, data
);
778 * Returns a newly created attribute with the specified name.
780 public Attr
createAttribute(String name
)
782 if (checkingCharacters
)
784 checkName(name
, "1.1".equals(version
));
786 if (name
.startsWith("xml:"))
788 return createAttributeNS(XMLConstants
.XML_NS_URI
, name
);
790 else if (XMLConstants
.XMLNS_ATTRIBUTE
.equals(name
) ||
791 name
.startsWith("xmlns:"))
793 return createAttributeNS(XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
, name
);
797 DomAttr ret
= new DomAttr(this, null, name
);
798 ret
.localName
= null;
805 * Returns a newly created attribute with the specified name
806 * and namespace information.
808 public Attr
createAttributeNS(String namespaceURI
, String name
)
810 if (checkingCharacters
)
812 checkNCName(name
, "1.1".equals(version
));
815 if ("".equals(namespaceURI
))
819 if (name
.startsWith ("xml:"))
821 if (namespaceURI
== null)
823 namespaceURI
= XMLConstants
.XML_NS_URI
;
825 else if (!XMLConstants
.XML_NS_URI
.equals(namespaceURI
))
827 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
828 "xml namespace is always " +
829 XMLConstants
.XML_NS_URI
,
833 else if (XMLConstants
.XMLNS_ATTRIBUTE
.equals(name
) ||
834 name
.startsWith("xmlns:"))
836 if (namespaceURI
== null)
838 namespaceURI
= XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
;
840 else if (!XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
.equals(namespaceURI
))
842 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
843 "xmlns namespace must be " +
844 XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
,
848 else if (namespaceURI
== null && name
.indexOf(':') != -1)
850 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
851 "prefixed name needs a URI: " + name
, this, 0);
853 return new DomAttr(this, namespaceURI
, name
);
858 * Returns a newly created reference to the specified entity.
859 * The caller should populate this with the appropriate children
860 * and then mark it as readonly.
862 * @see DomNode#makeReadonly
864 public EntityReference
createEntityReference(String name
)
866 DomEntityReference ret
= new DomEntityReference(this, name
);
867 DocumentType doctype
= getDoctype();
870 DomEntity ent
= (DomEntity
) doctype
.getEntities().getNamedItem(name
);
873 for (DomNode ctx
= ent
.first
; ctx
!= null; ctx
= ctx
.next
)
875 ret
.appendChild(ctx
.cloneNode(true));
885 * Makes a copy of the specified node, with all nodes "owned" by
886 * this document and with children optionally copied. This type
887 * of standard utility has become, well, a standard utility.
889 * <p> Note that EntityReference nodes created through this method (either
890 * directly, or recursively) never have children, and that there is no
891 * portable way to associate them with such children.
893 * <p> Note also that there is no requirement that the specified node
894 * be associated with a different document. This differs from the
895 * <em>cloneNode</em> operation in that the node itself is not given
896 * an opportunity to participate, so that any information managed
897 * by node subclasses will be lost.
899 public Node
importNode(Node src
, boolean deep
)
902 switch (src
.getNodeType())
905 dst
= createTextNode(src
.getNodeValue());
907 case CDATA_SECTION_NODE
:
908 dst
= createCDATASection(src
.getNodeValue());
911 dst
= createComment(src
.getNodeValue());
913 case PROCESSING_INSTRUCTION_NODE
:
914 dst
= createProcessingInstruction(src
.getNodeName(),
918 // NOTE: There's no standard way to create
919 // these, or add them to a doctype. Useless.
920 Notation notation
= (Notation
) src
;
921 dst
= new DomNotation(this, notation
.getNodeName(),
922 notation
.getPublicId(),
923 notation
.getSystemId());
926 // NOTE: There's no standard way to create
927 // these, or add them to a doctype. Useless.
928 Entity entity
= (Entity
) src
;
929 dst
= new DomEntity(this, entity
.getNodeName(),
930 entity
.getPublicId(),
931 entity
.getSystemId(),
932 entity
.getNotationName());
935 for (Node ctx
= src
.getFirstChild(); ctx
!= null;
936 ctx
= ctx
.getNextSibling())
938 dst
.appendChild(importNode(ctx
, deep
));
942 case ENTITY_REFERENCE_NODE
:
943 dst
= createEntityReference(src
.getNodeName());
945 case DOCUMENT_FRAGMENT_NODE
:
946 dst
= new DomDocumentFragment(this);
949 for (Node ctx
= src
.getFirstChild(); ctx
!= null;
950 ctx
= ctx
.getNextSibling())
952 dst
.appendChild(importNode(ctx
, deep
));
957 String attr_nsuri
= src
.getNamespaceURI();
958 if (attr_nsuri
!= null)
960 dst
= createAttributeNS(attr_nsuri
, src
.getNodeName());
964 dst
= createAttribute(src
.getNodeName());
966 // this is _always_ done regardless of "deep" setting
967 for (Node ctx
= src
.getFirstChild(); ctx
!= null;
968 ctx
= ctx
.getNextSibling())
970 dst
.appendChild(importNode(ctx
, false));
974 String elem_nsuri
= src
.getNamespaceURI();
975 if (elem_nsuri
!= null)
977 dst
= createElementNS(elem_nsuri
, src
.getNodeName());
981 dst
= createElement(src
.getNodeName());
983 NamedNodeMap srcAttrs
= src
.getAttributes();
984 NamedNodeMap dstAttrs
= dst
.getAttributes();
985 int len
= srcAttrs
.getLength();
986 for (int i
= 0; i
< len
; i
++)
988 Attr a
= (Attr
) srcAttrs
.item(i
);
991 // maybe update defaulted attributes
992 dflt
= (Attr
) dstAttrs
.getNamedItem(a
.getNodeName());
995 String newval
= a
.getNodeValue();
996 if (!dflt
.getNodeValue().equals(newval
)
997 || a
.getSpecified () == true)
999 dflt
.setNodeValue (newval
);
1004 dstAttrs
.setNamedItem((Attr
) importNode(a
, false));
1008 for (Node ctx
= src
.getFirstChild(); ctx
!= null;
1009 ctx
= ctx
.getNextSibling())
1011 dst
.appendChild(importNode(ctx
, true));
1015 // can't import document or doctype nodes
1017 case DOCUMENT_TYPE_NODE
:
1019 // can't import unrecognized or nonstandard nodes
1021 throw new DomDOMException(DOMException
.NOT_SUPPORTED_ERR
, null, src
, 0);
1024 // FIXME cleanup a bit -- for deep copies, copy those
1025 // children in one place, here (code sharing is healthy)
1027 if (src
instanceof DomNode
)
1029 ((DomNode
) src
).notifyUserDataHandlers(UserDataHandler
.NODE_IMPORTED
,
1036 * <b>DOM L2 (Traversal)</b>
1037 * Returns a newly created node iterator. Don't forget to detach
1038 * this iterator when you're done using it!
1042 public NodeIterator
createNodeIterator(Node root
,
1045 boolean expandEntities
)
1047 return new DomNodeIterator(root
, whatToShow
, filter
, expandEntities
,
1051 public TreeWalker
createTreeWalker(Node root
,
1054 boolean expandEntities
)
1056 return new DomNodeIterator(root
, whatToShow
, filter
, expandEntities
,
1060 // DOM Level 3 methods
1065 public String
getInputEncoding()
1067 return inputEncoding
;
1070 public void setInputEncoding(String inputEncoding
)
1072 this.inputEncoding
= inputEncoding
;
1078 public String
getXmlEncoding()
1083 public void setXmlEncoding(String encoding
)
1085 this.encoding
= encoding
;
1088 public boolean getXmlStandalone()
1093 public void setXmlStandalone(boolean xmlStandalone
)
1095 standalone
= xmlStandalone
;
1098 public String
getXmlVersion()
1103 public void setXmlVersion(String xmlVersion
)
1105 if (xmlVersion
== null)
1109 if ("1.0".equals(xmlVersion
) ||
1110 "1.1".equals(xmlVersion
))
1112 version
= xmlVersion
;
1116 throw new DomDOMException(DOMException
.NOT_SUPPORTED_ERR
);
1120 public boolean getStrictErrorChecking()
1122 return checkingCharacters
;
1125 public void setStrictErrorChecking(boolean strictErrorChecking
)
1127 checkingCharacters
= strictErrorChecking
;
1130 public String
lookupPrefix(String namespaceURI
)
1132 Node root
= getDocumentElement();
1133 return (root
== null) ?
null : root
.lookupPrefix(namespaceURI
);
1136 public boolean isDefaultNamespace(String namespaceURI
)
1138 Node root
= getDocumentElement();
1139 return (root
== null) ?
false : root
.isDefaultNamespace(namespaceURI
);
1142 public String
lookupNamespaceURI(String prefix
)
1144 Node root
= getDocumentElement();
1145 return (root
== null) ?
null : root
.lookupNamespaceURI(prefix
);
1148 public String
getBaseURI()
1150 return getDocumentURI();
1152 Node root = getDocumentElement();
1155 NamedNodeMap attrs = root.getAttributes();
1156 Node xmlBase = attrs.getNamedItemNS(XMLConstants.XML_NS_URI, "base");
1157 if (xmlBase != null)
1159 return xmlBase.getNodeValue();
1166 public String
getDocumentURI()
1171 public void setDocumentURI(String documentURI
)
1173 systemId
= documentURI
;
1176 public Node
adoptNode(Node source
)
1178 int sourceNodeType
= source
.getNodeType();
1179 switch (sourceNodeType
)
1182 case DOCUMENT_TYPE_NODE
:
1183 throw new DomDOMException(DOMException
.NOT_SUPPORTED_ERR
);
1186 throw new DomDOMException(DOMException
.NO_MODIFICATION_ALLOWED_ERR
);
1188 if (source
instanceof DomNode
)
1191 DomNode src
= (DomNode
) source
;
1193 if (dst
.parent
!= null)
1195 dst
= (DomNode
) dst
.cloneNode(true);
1198 src
.notifyUserDataHandlers(UserDataHandler
.NODE_ADOPTED
, src
, dst
);
1203 // Some other implementation
1205 switch (sourceNodeType
)
1207 case Node
.ATTRIBUTE_NODE
:
1209 Attr src
= (Attr
) source
;
1210 String nodeName
= src
.getNodeName();
1211 String localName
= src
.getLocalName();
1212 String namespaceUri
= src
.getNamespaceURI();
1213 dst
= (localName
== null) ?
1214 createAttribute(nodeName
) :
1215 createAttributeNS(namespaceUri
, nodeName
);
1216 adoptChildren(src
, dst
);
1219 case Node
.CDATA_SECTION_NODE
:
1221 CDATASection src
= (CDATASection
) source
;
1222 dst
= createCDATASection(src
.getData());
1225 case Node
.COMMENT_NODE
:
1227 Comment src
= (Comment
) source
;
1228 dst
= createComment(src
.getData());
1231 case Node
.DOCUMENT_FRAGMENT_NODE
:
1233 DocumentFragment src
= (DocumentFragment
) source
;
1234 dst
= createDocumentFragment();
1235 adoptChildren(src
, dst
);
1238 case Node
.ELEMENT_NODE
:
1240 Element src
= (Element
) source
;
1241 String nodeName
= src
.getNodeName();
1242 String localName
= src
.getLocalName();
1243 String namespaceUri
= src
.getNamespaceURI();
1244 dst
= (localName
== null) ?
1245 createElement(nodeName
) :
1246 createElementNS(namespaceUri
, nodeName
);
1247 adoptAttributes(src
, dst
);
1248 adoptChildren(src
, dst
);
1251 case Node
.ENTITY_REFERENCE_NODE
:
1253 EntityReference src
= (EntityReference
) source
;
1254 dst
= createEntityReference(src
.getNodeName());
1255 adoptChildren(src
, dst
);
1258 case Node
.PROCESSING_INSTRUCTION_NODE
:
1260 ProcessingInstruction src
= (ProcessingInstruction
) source
;
1261 dst
= createProcessingInstruction(src
.getTarget(),
1265 case Node
.TEXT_NODE
:
1267 Text src
= (Text
) source
;
1268 dst
= createTextNode(src
.getData());
1276 void adoptChildren(Node src
, Node dst
)
1278 Node node
= src
.getFirstChild();
1279 while (node
!= null)
1281 Node next
= node
.getNextSibling();
1282 dst
.appendChild(adoptNode(node
));
1287 void adoptAttributes(Node src
, Node dst
)
1289 NamedNodeMap srcAttrs
= src
.getAttributes();
1290 NamedNodeMap dstAttrs
= dst
.getAttributes();
1291 int len
= srcAttrs
.getLength();
1292 for (int i
= 0; i
< len
; i
++)
1294 Node node
= srcAttrs
.item(i
);
1295 String localName
= node
.getLocalName();
1296 if (localName
== null)
1298 dstAttrs
.setNamedItem(adoptNode(node
));
1302 dstAttrs
.setNamedItemNS(adoptNode(node
));
1307 public DOMConfiguration
getDomConfig()
1311 config
= new DomDocumentConfiguration();
1316 public boolean isEqualNode(Node arg
)
1318 if (!super.isEqualNode(arg
))
1320 Document d
= (Document
) arg
;
1321 String dversion
= d
.getXmlVersion();
1322 if (dversion
== null || !dversion
.equals(version
))
1324 boolean dstandalone
= d
.getXmlStandalone();
1325 if (dstandalone
!= standalone
)
1327 String dencoding
= d
.getXmlEncoding();
1328 if (dencoding
== null || dencoding
.equalsIgnoreCase("UTF-8"))
1330 if (encoding
!= null && !encoding
.equalsIgnoreCase("UTF-8"))
1335 if (!dencoding
.equals(encoding
))
1341 public void normalizeDocument()
1343 boolean save
= building
;
1345 normalizeNode(this);
1349 void normalizeNode(DomNode node
)
1354 switch (node
.nodeType
)
1356 case CDATA_SECTION_NODE
:
1357 if (!config
.cdataSections
)
1359 // replace CDATA section with text node
1360 Text text
= createTextNode(node
.getNodeValue());
1361 node
.parent
.insertBefore(text
, node
);
1362 node
.parent
.removeChild(node
);
1363 // merge adjacent text nodes
1364 String data
= text
.getWholeText();
1365 node
= (DomNode
) text
.replaceWholeText(data
);
1367 else if (config
.splitCdataSections
)
1369 String value
= node
.getNodeValue();
1370 int i
= value
.indexOf("]]>");
1373 Node node2
= createCDATASection(value
.substring(0, i
));
1374 node
.parent
.insertBefore(node2
, node
);
1375 value
= value
.substring(i
+ 3);
1376 node
.setNodeValue(value
);
1377 i
= value
.indexOf("]]>");
1382 if (!config
.comments
)
1384 node
.parent
.removeChild(node
);
1388 if (!config
.elementContentWhitespace
&&
1389 ((Text
) node
).isElementContentWhitespace())
1391 node
.parent
.removeChild(node
);
1394 case ENTITY_REFERENCE_NODE
:
1395 if (!config
.entities
)
1397 for (DomNode ctx
= node
.first
; ctx
!= null; )
1399 DomNode ctxNext
= ctx
.next
;
1400 node
.parent
.insertBefore(ctx
, node
);
1403 node
.parent
.removeChild(node
);
1407 if (!config
.namespaceDeclarations
)
1409 DomNamedNodeMap attrs
=
1410 (DomNamedNodeMap
) node
.getAttributes();
1411 boolean aro
= attrs
.readonly
;
1412 attrs
.readonly
= false; // Ensure we can delete if necessary
1413 int len
= attrs
.getLength();
1414 for (int i
= 0; i
< len
; i
++)
1416 Node attr
= attrs
.item(i
);
1417 String namespace
= attr
.getNamespaceURI();
1418 if (XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
.equals(namespace
))
1420 attrs
.removeNamedItemNS(namespace
,
1421 attr
.getLocalName());
1426 attrs
.readonly
= aro
;
1431 for (DomNode ctx
= node
.first
; ctx
!= null; )
1433 DomNode ctxNext
= ctx
.next
;
1439 public Node
renameNode(Node n
, String namespaceURI
, String qualifiedName
)
1442 if (n
instanceof DomNsNode
)
1444 DomNsNode src
= (DomNsNode
) n
;
1447 throw new DomDOMException(DOMException
.NOT_FOUND_ERR
);
1449 if (src
.owner
!= this)
1451 throw new DomDOMException(DOMException
.WRONG_DOCUMENT_ERR
,
1454 boolean xml11
= "1.1".equals(version
);
1455 checkName(qualifiedName
, xml11
);
1456 int ci
= qualifiedName
.indexOf(':');
1457 if ("".equals(namespaceURI
))
1459 namespaceURI
= null;
1461 if (namespaceURI
!= null)
1463 checkNCName(qualifiedName
, xml11
);
1464 String prefix
= (ci
== -1) ?
"" :
1465 qualifiedName
.substring(0, ci
);
1466 if (XMLConstants
.XML_NS_PREFIX
.equals(prefix
) &&
1467 !XMLConstants
.XML_NS_URI
.equals(namespaceURI
))
1469 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
1470 "xml namespace must be " +
1471 XMLConstants
.XML_NS_URI
, src
, 0);
1473 else if (src
.nodeType
== ATTRIBUTE_NODE
&&
1474 (XMLConstants
.XMLNS_ATTRIBUTE
.equals(prefix
) ||
1475 XMLConstants
.XMLNS_ATTRIBUTE
.equals(qualifiedName
)) &&
1476 !XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
.equals(namespaceURI
))
1478 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
1479 "xmlns namespace must be " +
1480 XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
, src
, 0);
1482 if (XMLConstants
.XML_NS_URI
.equals(namespaceURI
) &&
1483 !XMLConstants
.XML_NS_PREFIX
.equals(prefix
))
1485 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
1486 "xml namespace must be " +
1487 XMLConstants
.XML_NS_URI
, src
, 0);
1489 else if (src
.nodeType
== ATTRIBUTE_NODE
&&
1490 XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
.equals(namespaceURI
) &&
1491 !(XMLConstants
.XMLNS_ATTRIBUTE
.equals(prefix
) ||
1492 XMLConstants
.XMLNS_ATTRIBUTE
.equals(qualifiedName
)))
1494 throw new DomDOMException(DOMException
.NAMESPACE_ERR
,
1495 "xmlns namespace must be " +
1496 XMLConstants
.XMLNS_ATTRIBUTE_NS_URI
, src
, 0);
1500 src
.setNodeName(qualifiedName
);
1501 src
.setNamespaceURI(namespaceURI
);
1502 src
.notifyUserDataHandlers(UserDataHandler
.NODE_RENAMED
, src
, src
);
1503 // TODO MutationNameEvents
1504 // DOMElementNameChanged or DOMAttributeNameChanged
1507 throw new DomDOMException(DOMException
.NOT_SUPPORTED_ERR
, null, n
, 0);
1510 // -- XPathEvaluator --
1512 public XPathExpression
createExpression(String expression
,
1513 XPathNSResolver resolver
)
1514 throws XPathException
, DOMException
1516 return new DomXPathExpression(this, expression
, resolver
);
1519 public XPathNSResolver
createNSResolver(Node nodeResolver
)
1521 return new DomXPathNSResolver(nodeResolver
);
1524 public Object
evaluate(String expression
,
1526 XPathNSResolver resolver
,
1529 throws XPathException
, DOMException
1531 XPathExpression xpe
=
1532 new DomXPathExpression(this, expression
, resolver
);
1533 return xpe
.evaluate(contextNode
, type
, result
);