libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / xml / dom / DomDocument.java
blobb32c6b82dfc8cb1fbe7ec5b2b9a576637a9cda82
1 /* DomDocument.java --
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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. */
38 package gnu.xml.dom;
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;
70 /**
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
83 extends DomNode
84 implements Document, DocumentTraversal, XPathEvaluator
87 private final DOMImplementation implementation;
88 private boolean checkingCharacters = true;
89 boolean checkingWellformedness = true;
90 private boolean defaultAttributes = true;
92 boolean building; // if true, skip mutation events in the tree
94 DomDocumentConfiguration config;
96 String inputEncoding;
97 String encoding;
98 String version = "1.0";
99 boolean standalone;
100 String systemId;
103 * Constructs a Document node, associating it with an instance
104 * of the DomImpl class.
106 * <p> Note that this constructor disables character checking.
107 * It is normally used when connecting a DOM to an XML parser,
108 * and duplicating such checks is undesirable. When used for
109 * purposes other than connecting to a parser, you should
110 * re-enable that checking.
112 * @see #setCheckingCharacters
114 public DomDocument()
116 this(new DomImpl());
120 * Constructs a Document node, associating it with the specified
121 * implementation. This should only be used in conjunction with
122 * a specialized implementation; it will normally be called by
123 * that implementation.
125 * @see DomImpl
126 * @see #setCheckingCharacters
128 protected DomDocument(DOMImplementation impl)
130 super(DOCUMENT_NODE, null);
131 implementation = impl;
135 * Sets the <code>building</code> flag.
136 * Mutation events in the document are not reported.
138 public void setBuilding(boolean flag)
140 building = flag;
144 * Sets whether to check for document well-formedness.
145 * If true, an exception will be raised if a second doctype or root
146 * element node is added to the document.
148 public void setCheckWellformedness(boolean flag)
150 checkingWellformedness = flag;
154 * Sets whether to check for document characters.
156 public void setCheckingCharacters(boolean flag)
158 checkingCharacters = flag;
162 * Sets whether to default attributes for new elements.
164 public void setDefaultAttributes(boolean flag)
166 defaultAttributes = flag;
170 * <b>DOM L1</b>
171 * Returns the constant "#document".
173 final public String getNodeName()
175 return "#document";
179 * <b>DOM L1</b>
180 * Returns the document's root element, or null.
182 final public Element getDocumentElement()
184 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
186 if (ctx.nodeType == ELEMENT_NODE)
188 return (Element) ctx;
191 return null;
195 * <b>DOM L1</b>
196 * Returns the document's DocumentType, or null.
198 final public DocumentType getDoctype()
200 for (DomNode ctx = first; ctx != null; ctx = ctx.next)
202 if (ctx.nodeType == DOCUMENT_TYPE_NODE)
204 return (DocumentType) ctx;
207 return null;
211 * <b>DOM L1</b>
212 * Returns the document's DOMImplementation.
214 final public DOMImplementation getImplementation()
216 return implementation;
220 * <b>DOM L1 (relocated in DOM L2)</b>
221 * Returns the element with the specified "ID" attribute, or null.
223 * <p>Returns null unless {@link Consumer} was used to populate internal
224 * DTD declaration information, using package-private APIs. If that
225 * internal DTD information is available, the document may be searched for
226 * the element with that ID.
228 public Element getElementById(String id)
230 if (id == null || id.length() == 0)
232 return null;
234 DomDoctype doctype = (DomDoctype) getDoctype();
235 if (doctype != null && !doctype.hasIds())
237 doctype = null;
240 // yes, this is linear in size of document.
241 // it'd be easy enough to maintain a hashtable.
242 Node current = getDocumentElement();
243 Node temp;
245 if (current == null)
247 return null;
249 while (current != this)
251 // done?
252 if (current.getNodeType() == ELEMENT_NODE)
254 DomElement element = (DomElement) current;
255 if (element.userIdAttrs != null)
257 for (Iterator i = element.userIdAttrs.iterator();
258 i.hasNext(); )
260 Node idAttr = (Node) i.next();
261 if (id.equals(idAttr.getNodeValue()))
263 return element;
267 if (doctype != null)
269 DTDElementTypeInfo info =
270 doctype.getElementTypeInfo(current.getNodeName());
271 if (info != null &&
272 id.equals(element.getAttribute(info.idAttrName)))
274 return element;
277 // xml:id
278 String xmlId = element.getAttribute("xml:id");
279 if (xmlId == null)
281 xmlId = element.getAttributeNS(XMLConstants.XML_NS_URI,
282 "id");
284 if (id.equals(xmlId))
286 return element;
290 // descend?
291 if (current.hasChildNodes())
293 current = current.getFirstChild();
294 continue;
297 // lateral?
298 temp = current.getNextSibling();
299 if (temp != null)
301 current = temp;
302 continue;
305 // back up ...
308 temp = current.getParentNode();
309 if (temp == null)
311 return null;
313 current = temp;
314 temp = current.getNextSibling();
316 while (temp == null);
317 current = temp;
319 return null;
322 private void checkNewChild(Node newChild)
324 if (newChild.getNodeType() == ELEMENT_NODE
325 && getDocumentElement() != null)
327 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
328 "document element already present: " +
329 getDocumentElement(), newChild, 0);
331 if (newChild.getNodeType() == DOCUMENT_TYPE_NODE
332 && getDoctype() != null)
334 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
335 "document type already present: " +
336 getDoctype(), newChild, 0);
341 * <b>DOM L1</b>
342 * Appends the specified node to this node's list of children,
343 * enforcing the constraints that there be only one root element
344 * and one document type child.
346 public Node appendChild(Node newChild)
348 if (checkingWellformedness)
350 checkNewChild(newChild);
352 return super.appendChild(newChild);
356 * <b>DOM L1</b>
357 * Inserts the specified node in this node's list of children,
358 * enforcing the constraints that there be only one root element
359 * and one document type child.
361 public Node insertBefore(Node newChild, Node refChild)
363 if (checkingWellformedness)
365 checkNewChild(newChild);
367 return super.insertBefore(newChild, refChild);
371 * <b>DOM L1</b>
372 * Replaces the specified node in this node's list of children,
373 * enforcing the constraints that there be only one root element
374 * and one document type child.
376 public Node replaceChild(Node newChild, Node refChild)
378 if (checkingWellformedness &&
379 ((newChild.getNodeType() == ELEMENT_NODE &&
380 refChild.getNodeType() != ELEMENT_NODE) ||
381 (newChild.getNodeType() == DOCUMENT_TYPE_NODE &&
382 refChild.getNodeType() != DOCUMENT_TYPE_NODE)))
384 checkNewChild(newChild);
386 return super.replaceChild(newChild, refChild);
389 // NOTE: DOM can't really tell when the name of an entity,
390 // notation, or PI must follow the namespace rules (excluding
391 // colons) instead of the XML rules (which allow them without
392 // much restriction). That's an API issue. verifyXmlName
393 // aims to enforce the XML rules, not the namespace rules.
396 * Throws a DOM exception if the specified name is not a legal XML 1.0
397 * Name.
398 * @deprecated This method is deprecated and may be removed in future
399 * versions of GNU JAXP
401 public static void verifyXmlName(String name)
403 // XXX why is this public?
404 checkName(name, false);
407 static void checkName(String name, boolean xml11)
409 if (name == null)
411 throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
413 int len = name.length();
414 if (len == 0)
416 throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
419 // dog: rewritten to use the rules for XML 1.0 and 1.1
421 // Name start character
422 char c = name.charAt(0);
423 if (xml11)
425 // XML 1.1
426 if ((c < 0x0041 || c > 0x005a) &&
427 (c < 0x0061 || c > 0x007a) &&
428 c != ':' && c != '_' &&
429 (c < 0x00c0 || c > 0x00d6) &&
430 (c < 0x00d8 || c > 0x00f6) &&
431 (c < 0x00f8 || c > 0x02ff) &&
432 (c < 0x0370 || c > 0x037d) &&
433 (c < 0x037f || c > 0x1fff) &&
434 (c < 0x200c || c > 0x200d) &&
435 (c < 0x2070 || c > 0x218f) &&
436 (c < 0x2c00 || c > 0x2fef) &&
437 (c < 0x3001 || c > 0xd7ff) &&
438 (c < 0xf900 || c > 0xfdcf) &&
439 (c < 0xfdf0 || c > 0xfffd) &&
440 (c < 0x10000 || c > 0xeffff))
442 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
443 name, null, c);
446 else
448 // XML 1.0
449 int type = Character.getType(c);
450 switch (type)
452 case Character.LOWERCASE_LETTER: // Ll
453 case Character.UPPERCASE_LETTER: // Lu
454 case Character.OTHER_LETTER: // Lo
455 case Character.TITLECASE_LETTER: // Lt
456 case Character.LETTER_NUMBER: // Nl
457 if ((c > 0xf900 && c < 0xfffe) ||
458 (c >= 0x20dd && c <= 0x20e0))
460 // Compatibility area and Unicode 2.0 exclusions
461 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
462 name, null, c);
464 break;
465 default:
466 if (c != ':' && c != '_' && (c < 0x02bb || c > 0x02c1) &&
467 c != 0x0559 && c != 0x06e5 && c != 0x06e6)
469 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
470 name, null, c);
475 // Subsequent characters
476 for (int i = 1; i < len; i++)
478 c = name.charAt(i);
479 if (xml11)
481 // XML 1.1
482 if ((c < 0x0041 || c > 0x005a) &&
483 (c < 0x0061 || c > 0x007a) &&
484 (c < 0x0030 || c > 0x0039) &&
485 c != ':' && c != '_' && c != '-' && c != '.' &&
486 (c < 0x00c0 || c > 0x00d6) &&
487 (c < 0x00d8 || c > 0x00f6) &&
488 (c < 0x00f8 || c > 0x02ff) &&
489 (c < 0x0370 || c > 0x037d) &&
490 (c < 0x037f || c > 0x1fff) &&
491 (c < 0x200c || c > 0x200d) &&
492 (c < 0x2070 || c > 0x218f) &&
493 (c < 0x2c00 || c > 0x2fef) &&
494 (c < 0x3001 || c > 0xd7ff) &&
495 (c < 0xf900 || c > 0xfdcf) &&
496 (c < 0xfdf0 || c > 0xfffd) &&
497 (c < 0x10000 || c > 0xeffff) &&
498 c != 0x00b7 &&
499 (c < 0x0300 || c > 0x036f) &&
500 (c < 0x203f || c > 0x2040))
502 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR, name,
503 null, c);
506 else
508 // XML 1.0
509 int type = Character.getType(c);
510 switch (type)
512 case Character.LOWERCASE_LETTER: // Ll
513 case Character.UPPERCASE_LETTER: // Lu
514 case Character.DECIMAL_DIGIT_NUMBER: // Nd
515 case Character.OTHER_LETTER: // Lo
516 case Character.TITLECASE_LETTER: // Lt
517 case Character.LETTER_NUMBER: // Nl
518 case Character.COMBINING_SPACING_MARK: // Mc
519 case Character.ENCLOSING_MARK: // Me
520 case Character.NON_SPACING_MARK: // Mn
521 case Character.MODIFIER_LETTER: // Lm
522 if ((c > 0xf900 && c < 0xfffe) ||
523 (c >= 0x20dd && c <= 0x20e0))
525 // Compatibility area and Unicode 2.0 exclusions
526 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
527 name, null, c);
529 break;
530 default:
531 if (c != '-' && c != '.' && c != ':' && c != '_' &&
532 c != 0x0387 && (c < 0x02bb || c > 0x02c1) &&
533 c != 0x0559 && c != 0x06e5 && c != 0x06e6 && c != 0x00b7)
535 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
536 name, null, c);
542 // FIXME characters with a font or compatibility decomposition (i.e.
543 // those with a "compatibility formatting tag" in field 5 of the
544 // database -- marked by field 5 beginning with a "<") are not allowed.
547 // package private
548 static void checkNCName(String name, boolean xml11)
550 checkName(name, xml11);
551 int len = name.length();
552 int index = name.indexOf(':');
553 if (index != -1)
555 if (index == 0 || index == (len - 1) || name.lastIndexOf(':') != index)
557 throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
562 // package private
563 static void checkChar(String value, boolean xml11)
565 char[] chars = value.toCharArray();
566 checkChar(chars, 0, chars.length, xml11);
569 static void checkChar(char[] buf, int off, int len, boolean xml11)
571 for (int i = 0; i < len; i++)
573 char c = buf[i];
575 // assume surrogate pairing checks out OK, for simplicity
576 if ((c >= 0x0020 && c <= 0xd7ff) ||
577 (c == 0x000a || c == 0x000d || c == 0x0009) ||
578 (c >= 0xe000 && c <= 0xfffd) ||
579 (c >= 0x10000 && c <= 0x10ffff))
581 continue;
583 if (xml11)
585 if ((c >= 0x0001 && c <= 0x001f) ||
586 (c >= 0x007f && c <= 0x0084) ||
587 (c >= 0x0086 && c <= 0x009f))
589 continue;
592 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
593 new String(buf, off, len), null, c);
598 * <b>DOM L1</b>
599 * Returns a newly created element with the specified name.
600 * The node name of the created element will be equal to {@code name}.
601 * The namespace, prefix and local name will all be {@code null}.
603 public Element createElement(String name)
605 Element element;
607 if (checkingCharacters)
609 checkName(name, "1.1".equals(version));
611 if (name.startsWith("xml:"))
613 element = createElementNS(null, name);
615 else
617 DomElement domElement = new DomElement(this, null, name, null, null);
618 element = domElement;
620 if (defaultAttributes)
621 setDefaultAttributes(element, name);
622 return element;
626 * <b>DOM L2</b>
627 * Returns a newly created element with the specified name
628 * and namespace information.
630 public Element createElementNS(String namespaceURI, String name)
632 if (checkingCharacters)
634 checkNCName(name, "1.1".equals(version));
637 if ("".equals(namespaceURI))
639 namespaceURI = null;
641 if (name.startsWith("xml:"))
643 if (namespaceURI != null
644 && !XMLConstants.XML_NS_URI.equals(namespaceURI))
646 throw new DomDOMException(DOMException.NAMESPACE_ERR,
647 "xml namespace is always " +
648 XMLConstants.XML_NS_URI, this, 0);
650 namespaceURI = XMLConstants.XML_NS_URI;
652 else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
653 name.startsWith("xmlns:"))
655 throw new DomDOMException(DOMException.NAMESPACE_ERR,
656 "xmlns is reserved", this, 0);
658 else if (namespaceURI == null && name.indexOf(':') != -1)
660 throw new DomDOMException(DOMException.NAMESPACE_ERR,
661 "prefixed name '" + name +
662 "' needs a URI", this, 0);
665 Element element = new DomElement(this, namespaceURI, name);
666 if (defaultAttributes)
667 setDefaultAttributes(element, name);
668 return element;
671 private void setDefaultAttributes(Element element, String name)
673 DomDoctype doctype = (DomDoctype) getDoctype();
674 if (doctype == null)
676 return;
679 // default any attributes that need it
680 DTDElementTypeInfo info = doctype.getElementTypeInfo(name);
681 if (info != null)
683 for (Iterator i = info.attributes(); i != null && i.hasNext(); )
685 DTDAttributeTypeInfo attr = (DTDAttributeTypeInfo) i.next();
686 String value = attr.value;
687 if ("#IMPLIED".equals(attr.mode) && value == null)
688 continue;
689 DomAttr node = (DomAttr) createAttribute(attr.name);
691 if (value == null)
693 value = "";
695 node.setValue(value);
696 node.setSpecified(false);
697 element.setAttributeNode(node);
703 * <b>DOM L1</b>
704 * Returns a newly created document fragment.
706 public DocumentFragment createDocumentFragment()
708 return new DomDocumentFragment(this);
712 * <b>DOM L1</b>
713 * Returns a newly created text node with the specified value.
715 public Text createTextNode(String value)
717 if (checkingCharacters)
719 checkChar(value, "1.1".equals(version));
721 return new DomText(this, value);
725 * Returns a newly created text node with the specified value.
727 public Text createTextNode(char[] buf, int off, int len)
729 if (checkingCharacters)
731 checkChar(buf, off, len, "1.1".equals(version));
733 return new DomText(this, buf, off, len);
737 * <b>DOM L1</b>
738 * Returns a newly created comment node with the specified value.
740 public Comment createComment(String value)
742 if (checkingCharacters)
744 checkChar(value, "1.1".equals(version));
746 return new DomComment(this, value);
750 * <b>DOM L1</b>
751 * Returns a newly created CDATA section node with the specified value.
753 public CDATASection createCDATASection(String value)
755 if (checkingCharacters)
757 checkChar(value, "1.1".equals(version));
759 return new DomCDATASection(this, value);
763 * Returns a newly created CDATA section node with the specified value.
765 public CDATASection createCDATASection(char[] buf, int off, int len)
767 if (checkingCharacters)
769 checkChar(buf, off, len, "1.1".equals(version));
771 return new DomCDATASection(this, buf, off, len);
775 * <b>DOM L1</b>
776 * Returns a newly created processing instruction.
778 public ProcessingInstruction createProcessingInstruction(String target,
779 String data)
781 if (checkingCharacters)
783 boolean xml11 = "1.1".equals(version);
784 checkName(target, xml11);
785 if ("xml".equalsIgnoreCase(target))
787 throw new DomDOMException(DOMException.SYNTAX_ERR,
788 "illegal PI target name",
789 this, 0);
791 checkChar(data, xml11);
793 return new DomProcessingInstruction(this, target, data);
797 * <b>DOM L1</b>
798 * Returns a newly created attribute with the specified name.
800 public Attr createAttribute(String name)
802 if (checkingCharacters)
804 checkName(name, "1.1".equals(version));
806 if (name.startsWith("xml:"))
808 return createAttributeNS(XMLConstants.XML_NS_URI, name);
810 else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
811 name.startsWith("xmlns:"))
813 return createAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, name);
815 else
817 DomAttr ret = new DomAttr(this, null, name, null, null);
818 return ret;
823 * <b>DOM L2</b>
824 * Returns a newly created attribute with the specified name
825 * and namespace information.
827 public Attr createAttributeNS(String namespaceURI, String name)
829 if (checkingCharacters)
831 checkNCName(name, "1.1".equals(version));
834 if ("".equals(namespaceURI))
836 namespaceURI = null;
838 if (name.startsWith ("xml:"))
840 if (namespaceURI == null)
842 namespaceURI = XMLConstants.XML_NS_URI;
844 else if (!XMLConstants.XML_NS_URI.equals(namespaceURI))
846 throw new DomDOMException(DOMException.NAMESPACE_ERR,
847 "xml namespace is always " +
848 XMLConstants.XML_NS_URI,
849 this, 0);
852 else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
853 name.startsWith("xmlns:"))
855 if (namespaceURI == null)
857 namespaceURI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
859 else if (!XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
861 throw new DomDOMException(DOMException.NAMESPACE_ERR,
862 "xmlns namespace must be " +
863 XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
864 this, 0);
867 else if (namespaceURI == null && name.indexOf(':') != -1)
869 throw new DomDOMException(DOMException.NAMESPACE_ERR,
870 "prefixed name needs a URI: " + name, this, 0);
872 return new DomAttr(this, namespaceURI, name);
876 * <b>DOM L1</b>
877 * Returns a newly created reference to the specified entity.
878 * The caller should populate this with the appropriate children
879 * and then mark it as readonly.
881 * @see DomNode#makeReadonly
883 public EntityReference createEntityReference(String name)
885 DomEntityReference ret = new DomEntityReference(this, name);
886 DocumentType doctype = getDoctype();
887 if (doctype != null)
889 DomEntity ent = (DomEntity) doctype.getEntities().getNamedItem(name);
890 if (ent != null)
892 for (DomNode ctx = ent.first; ctx != null; ctx = ctx.next)
894 ret.appendChild(ctx.cloneNode(true));
898 ret.makeReadonly();
899 return ret;
903 * <b>DOM L2</b>
904 * Makes a copy of the specified node, with all nodes "owned" by
905 * this document and with children optionally copied. This type
906 * of standard utility has become, well, a standard utility.
908 * <p> Note that EntityReference nodes created through this method (either
909 * directly, or recursively) never have children, and that there is no
910 * portable way to associate them with such children.
912 * <p> Note also that there is no requirement that the specified node
913 * be associated with a different document. This differs from the
914 * <em>cloneNode</em> operation in that the node itself is not given
915 * an opportunity to participate, so that any information managed
916 * by node subclasses will be lost.
918 public Node importNode(Node src, boolean deep)
920 Node dst = null;
921 switch (src.getNodeType())
923 case TEXT_NODE:
924 dst = createTextNode(src.getNodeValue());
925 break;
926 case CDATA_SECTION_NODE:
927 dst = createCDATASection(src.getNodeValue());
928 break;
929 case COMMENT_NODE:
930 dst = createComment(src.getNodeValue());
931 break;
932 case PROCESSING_INSTRUCTION_NODE:
933 dst = createProcessingInstruction(src.getNodeName(),
934 src.getNodeValue());
935 break;
936 case NOTATION_NODE:
937 // NOTE: There's no standard way to create
938 // these, or add them to a doctype. Useless.
939 Notation notation = (Notation) src;
940 dst = new DomNotation(this, notation.getNodeName(),
941 notation.getPublicId(),
942 notation.getSystemId());
943 break;
944 case ENTITY_NODE:
945 // NOTE: There's no standard way to create
946 // these, or add them to a doctype. Useless.
947 Entity entity = (Entity) src;
948 dst = new DomEntity(this, entity.getNodeName(),
949 entity.getPublicId(),
950 entity.getSystemId(),
951 entity.getNotationName());
952 if (deep)
954 for (Node ctx = src.getFirstChild(); ctx != null;
955 ctx = ctx.getNextSibling())
957 dst.appendChild(importNode(ctx, deep));
960 break;
961 case ENTITY_REFERENCE_NODE:
962 dst = createEntityReference(src.getNodeName());
963 break;
964 case DOCUMENT_FRAGMENT_NODE:
965 dst = new DomDocumentFragment(this);
966 if (deep)
968 for (Node ctx = src.getFirstChild(); ctx != null;
969 ctx = ctx.getNextSibling())
971 dst.appendChild(importNode(ctx, deep));
974 break;
975 case ATTRIBUTE_NODE:
976 String attr_nsuri = src.getNamespaceURI();
977 if (attr_nsuri != null)
979 dst = createAttributeNS(attr_nsuri, src.getNodeName());
981 else
983 dst = createAttribute(src.getNodeName());
985 // this is _always_ done regardless of "deep" setting
986 for (Node ctx = src.getFirstChild(); ctx != null;
987 ctx = ctx.getNextSibling())
989 dst.appendChild(importNode(ctx, false));
991 break;
992 case ELEMENT_NODE:
993 String elem_nsuri = src.getNamespaceURI();
994 if (elem_nsuri != null)
996 dst = createElementNS(elem_nsuri, src.getNodeName());
998 else
1000 dst = createElement(src.getNodeName());
1002 NamedNodeMap srcAttrs = src.getAttributes();
1003 NamedNodeMap dstAttrs = dst.getAttributes();
1004 int len = srcAttrs.getLength();
1005 for (int i = 0; i < len; i++)
1007 Attr a = (Attr) srcAttrs.item(i);
1008 Attr dflt;
1010 // maybe update defaulted attributes
1011 dflt = (Attr) dstAttrs.getNamedItem(a.getNodeName());
1012 if (dflt != null)
1014 String newval = a.getNodeValue();
1015 if (!dflt.getNodeValue().equals(newval)
1016 || a.getSpecified () == true)
1018 dflt.setNodeValue (newval);
1020 continue;
1023 dstAttrs.setNamedItem((Attr) importNode(a, false));
1025 if (deep)
1027 for (Node ctx = src.getFirstChild(); ctx != null;
1028 ctx = ctx.getNextSibling())
1030 dst.appendChild(importNode(ctx, true));
1033 break;
1034 // can't import document or doctype nodes
1035 case DOCUMENT_NODE:
1036 case DOCUMENT_TYPE_NODE:
1037 // FALLTHROUGH
1038 // can't import unrecognized or nonstandard nodes
1039 default:
1040 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR, null, src, 0);
1043 // FIXME cleanup a bit -- for deep copies, copy those
1044 // children in one place, here (code sharing is healthy)
1046 if (src instanceof DomNode)
1048 ((DomNode) src).notifyUserDataHandlers(UserDataHandler.NODE_IMPORTED,
1049 src, dst);
1051 return dst;
1055 * <b>DOM L2 (Traversal)</b>
1056 * Returns a newly created node iterator. Don't forget to detach
1057 * this iterator when you're done using it!
1059 * @see DomIterator
1061 public NodeIterator createNodeIterator(Node root,
1062 int whatToShow,
1063 NodeFilter filter,
1064 boolean expandEntities)
1066 return new DomNodeIterator(root, whatToShow, filter, expandEntities,
1067 false);
1070 public TreeWalker createTreeWalker(Node root,
1071 int whatToShow,
1072 NodeFilter filter,
1073 boolean expandEntities)
1075 return new DomNodeIterator(root, whatToShow, filter, expandEntities,
1076 true);
1079 // DOM Level 3 methods
1082 * DOM L3
1084 public String getInputEncoding()
1086 return inputEncoding;
1089 public void setInputEncoding(String inputEncoding)
1091 this.inputEncoding = inputEncoding;
1095 * DOM L3
1097 public String getXmlEncoding()
1099 return encoding;
1102 public void setXmlEncoding(String encoding)
1104 this.encoding = encoding;
1107 public boolean getXmlStandalone()
1109 return standalone;
1112 public void setXmlStandalone(boolean xmlStandalone)
1114 standalone = xmlStandalone;
1117 public String getXmlVersion()
1119 return version;
1122 public void setXmlVersion(String xmlVersion)
1124 if (xmlVersion == null)
1126 xmlVersion = "1.0";
1128 if ("1.0".equals(xmlVersion) ||
1129 "1.1".equals(xmlVersion))
1131 version = xmlVersion;
1133 else
1135 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
1139 public boolean getStrictErrorChecking()
1141 return checkingCharacters;
1144 public void setStrictErrorChecking(boolean strictErrorChecking)
1146 checkingCharacters = strictErrorChecking;
1149 public String lookupPrefix(String namespaceURI)
1151 Node root = getDocumentElement();
1152 return (root == null) ? null : root.lookupPrefix(namespaceURI);
1155 public boolean isDefaultNamespace(String namespaceURI)
1157 Node root = getDocumentElement();
1158 return (root == null) ? false : root.isDefaultNamespace(namespaceURI);
1161 public String lookupNamespaceURI(String prefix)
1163 Node root = getDocumentElement();
1164 return (root == null) ? null : root.lookupNamespaceURI(prefix);
1167 public String getBaseURI()
1169 return getDocumentURI();
1171 Node root = getDocumentElement();
1172 if (root != null)
1174 NamedNodeMap attrs = root.getAttributes();
1175 Node xmlBase = attrs.getNamedItemNS(XMLConstants.XML_NS_URI, "base");
1176 if (xmlBase != null)
1178 return xmlBase.getNodeValue();
1181 return systemId;
1185 public String getDocumentURI()
1187 return systemId;
1190 public void setDocumentURI(String documentURI)
1192 systemId = documentURI;
1195 public Node adoptNode(Node source)
1197 int sourceNodeType = source.getNodeType();
1198 switch (sourceNodeType)
1200 case DOCUMENT_NODE:
1201 case DOCUMENT_TYPE_NODE:
1202 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
1203 case ENTITY_NODE:
1204 case NOTATION_NODE:
1205 throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
1207 if (source instanceof DomNode)
1209 // GNU native
1210 DomNode src = (DomNode) source;
1211 DomNode dst = src;
1212 if (dst.parent != null)
1214 dst = (DomNode) dst.cloneNode(true);
1216 dst.setOwner(this);
1217 src.notifyUserDataHandlers(UserDataHandler.NODE_ADOPTED, src, dst);
1218 return dst;
1220 else
1222 // Some other implementation
1223 Node dst = null;
1224 switch (sourceNodeType)
1226 case Node.ATTRIBUTE_NODE:
1228 Attr src = (Attr) source;
1229 String nodeName = src.getNodeName();
1230 String localName = src.getLocalName();
1231 String namespaceUri = src.getNamespaceURI();
1232 dst = (localName == null) ?
1233 createAttribute(nodeName) :
1234 createAttributeNS(namespaceUri, nodeName);
1235 adoptChildren(src, dst);
1236 break;
1238 case Node.CDATA_SECTION_NODE:
1240 CDATASection src = (CDATASection) source;
1241 dst = createCDATASection(src.getData());
1242 break;
1244 case Node.COMMENT_NODE:
1246 Comment src = (Comment) source;
1247 dst = createComment(src.getData());
1248 break;
1250 case Node.DOCUMENT_FRAGMENT_NODE:
1252 DocumentFragment src = (DocumentFragment) source;
1253 dst = createDocumentFragment();
1254 adoptChildren(src, dst);
1255 break;
1257 case Node.ELEMENT_NODE:
1259 Element src = (Element) source;
1260 String nodeName = src.getNodeName();
1261 String localName = src.getLocalName();
1262 String namespaceUri = src.getNamespaceURI();
1263 dst = (localName == null) ?
1264 createElement(nodeName) :
1265 createElementNS(namespaceUri, nodeName);
1266 adoptAttributes(src, dst);
1267 adoptChildren(src, dst);
1268 break;
1270 case Node.ENTITY_REFERENCE_NODE:
1272 EntityReference src = (EntityReference) source;
1273 dst = createEntityReference(src.getNodeName());
1274 adoptChildren(src, dst);
1275 break;
1277 case Node.PROCESSING_INSTRUCTION_NODE:
1279 ProcessingInstruction src = (ProcessingInstruction) source;
1280 dst = createProcessingInstruction(src.getTarget(),
1281 src.getData());
1282 break;
1284 case Node.TEXT_NODE:
1286 Text src = (Text) source;
1287 dst = createTextNode(src.getData());
1288 break;
1291 return dst;
1295 void adoptChildren(Node src, Node dst)
1297 Node node = src.getFirstChild();
1298 while (node != null)
1300 Node next = node.getNextSibling();
1301 dst.appendChild(adoptNode(node));
1302 node = next;
1306 void adoptAttributes(Node src, Node dst)
1308 NamedNodeMap srcAttrs = src.getAttributes();
1309 NamedNodeMap dstAttrs = dst.getAttributes();
1310 int len = srcAttrs.getLength();
1311 for (int i = 0; i < len; i++)
1313 Node node = srcAttrs.item(i);
1314 String localName = node.getLocalName();
1315 if (localName == null)
1317 dstAttrs.setNamedItem(adoptNode(node));
1319 else
1321 dstAttrs.setNamedItemNS(adoptNode(node));
1326 public DOMConfiguration getDomConfig()
1328 if (config == null)
1330 config = new DomDocumentConfiguration();
1332 return config;
1335 public boolean isEqualNode(Node arg)
1337 if (!super.isEqualNode(arg))
1338 return false;
1339 Document d = (Document) arg;
1340 String dversion = d.getXmlVersion();
1341 if (dversion == null || !dversion.equals(version))
1342 return false;
1343 boolean dstandalone = d.getXmlStandalone();
1344 if (dstandalone != standalone)
1345 return false;
1346 String dencoding = d.getXmlEncoding();
1347 if (dencoding == null || dencoding.equalsIgnoreCase("UTF-8"))
1349 if (encoding != null && !encoding.equalsIgnoreCase("UTF-8"))
1350 return false;
1352 else
1354 if (!dencoding.equals(encoding))
1355 return false;
1357 return true;
1360 public void normalizeDocument()
1362 boolean save = building;
1363 building = true;
1364 normalizeNode(this);
1365 building = save;
1368 void normalizeNode(DomNode node)
1370 node.normalize();
1371 if (config != null)
1373 switch (node.nodeType)
1375 case CDATA_SECTION_NODE:
1376 if (!config.cdataSections)
1378 // replace CDATA section with text node
1379 Text text = createTextNode(node.getNodeValue());
1380 node.parent.insertBefore(text, node);
1381 node.parent.removeChild(node);
1382 // merge adjacent text nodes
1383 String data = text.getWholeText();
1384 node = (DomNode) text.replaceWholeText(data);
1386 else if (config.splitCdataSections)
1388 String value = node.getNodeValue();
1389 int i = value.indexOf("]]>");
1390 while (i != -1)
1392 Node node2 = createCDATASection(value.substring(0, i));
1393 node.parent.insertBefore(node2, node);
1394 value = value.substring(i + 3);
1395 node.setNodeValue(value);
1396 i = value.indexOf("]]>");
1399 break;
1400 case COMMENT_NODE:
1401 if (!config.comments)
1403 node.parent.removeChild(node);
1405 break;
1406 case TEXT_NODE:
1407 if (!config.elementContentWhitespace &&
1408 ((Text) node).isElementContentWhitespace())
1410 node.parent.removeChild(node);
1412 break;
1413 case ENTITY_REFERENCE_NODE:
1414 if (!config.entities)
1416 for (DomNode ctx = node.first; ctx != null; )
1418 DomNode ctxNext = ctx.next;
1419 node.parent.insertBefore(ctx, node);
1420 ctx = ctxNext;
1422 node.parent.removeChild(node);
1424 break;
1425 case ELEMENT_NODE:
1426 if (!config.namespaceDeclarations)
1428 DomNamedNodeMap attrs =
1429 (DomNamedNodeMap) node.getAttributes();
1430 boolean aro = attrs.readonly;
1431 attrs.readonly = false; // Ensure we can delete if necessary
1432 int len = attrs.getLength();
1433 for (int i = 0; i < len; i++)
1435 Node attr = attrs.item(i);
1436 String namespace = attr.getNamespaceURI();
1437 if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespace))
1439 attrs.removeNamedItemNS(namespace,
1440 attr.getLocalName());
1441 i--;
1442 len--;
1445 attrs.readonly = aro;
1447 break;
1450 for (DomNode ctx = node.first; ctx != null; )
1452 DomNode ctxNext = ctx.next;
1453 normalizeNode(ctx);
1454 ctx = ctxNext;
1458 public Node renameNode(Node n, String namespaceURI, String qualifiedName)
1459 throws DOMException
1461 if (n instanceof DomNsNode)
1463 DomNsNode src = (DomNsNode) n;
1464 if (src == null)
1466 throw new DomDOMException(DOMException.NOT_FOUND_ERR);
1468 if (src.owner != this)
1470 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
1471 null, src, 0);
1473 boolean xml11 = "1.1".equals(version);
1474 checkName(qualifiedName, xml11);
1475 int ci = qualifiedName.indexOf(':');
1476 if ("".equals(namespaceURI))
1478 namespaceURI = null;
1480 if (namespaceURI != null)
1482 checkNCName(qualifiedName, xml11);
1483 String prefix = (ci == -1) ? "" :
1484 qualifiedName.substring(0, ci);
1485 if (XMLConstants.XML_NS_PREFIX.equals(prefix) &&
1486 !XMLConstants.XML_NS_URI.equals(namespaceURI))
1488 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1489 "xml namespace must be " +
1490 XMLConstants.XML_NS_URI, src, 0);
1492 else if (src.nodeType == ATTRIBUTE_NODE &&
1493 (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
1494 XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)) &&
1495 !XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
1497 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1498 "xmlns namespace must be " +
1499 XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
1501 if (XMLConstants.XML_NS_URI.equals(namespaceURI) &&
1502 !XMLConstants.XML_NS_PREFIX.equals(prefix))
1504 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1505 "xml namespace must be " +
1506 XMLConstants.XML_NS_URI, src, 0);
1508 else if (src.nodeType == ATTRIBUTE_NODE &&
1509 XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI) &&
1510 !(XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
1511 XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)))
1513 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1514 "xmlns namespace must be " +
1515 XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
1519 src.setNodeName(qualifiedName);
1520 src.setNamespaceURI(namespaceURI);
1521 src.notifyUserDataHandlers(UserDataHandler.NODE_RENAMED, src, src);
1522 // TODO MutationNameEvents
1523 // DOMElementNameChanged or DOMAttributeNameChanged
1524 return src;
1526 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR, null, n, 0);
1529 // -- XPathEvaluator --
1531 public XPathExpression createExpression(String expression,
1532 XPathNSResolver resolver)
1533 throws XPathException, DOMException
1535 return new DomXPathExpression(this, expression, resolver);
1538 public XPathNSResolver createNSResolver(Node nodeResolver)
1540 return new DomXPathNSResolver(nodeResolver);
1543 public Object evaluate(String expression,
1544 Node contextNode,
1545 XPathNSResolver resolver,
1546 short type,
1547 Object result)
1548 throws XPathException, DOMException
1550 XPathExpression xpe =
1551 new DomXPathExpression(this, expression, resolver);
1552 return xpe.evaluate(contextNode, type, result);