Merge from the pain train
[official-gcc.git] / libjava / gnu / xml / dom / DomDocument.java
blobd8ccc9d821cbd07d1359e8730e8f4705c423e5b0
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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;
91 boolean building; // if true, skip mutation events in the tree
93 DomDocumentConfiguration config;
95 String inputEncoding;
96 String encoding;
97 String version = "1.0";
98 boolean standalone;
99 String systemId;
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
113 public DomDocument()
115 this(new DomImpl());
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.
124 * @see DomImpl
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)
139 building = 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;
153 * <b>DOM L1</b>
154 * Returns the constant "#document".
156 final public String getNodeName()
158 return "#document";
162 * <b>DOM L1</b>
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;
174 return null;
178 * <b>DOM L1</b>
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;
190 return null;
194 * <b>DOM L1</b>
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 DomDoctype doctype = (DomDoctype) getDoctype();
215 if (doctype == null || !doctype.hasIds()
216 || id == null || id.length() == 0)
218 return null;
221 // yes, this is linear in size of document.
222 // it'd be easy enough to maintain a hashtable.
223 Node current = getDocumentElement();
224 Node temp;
226 if (current == null)
228 return null;
230 while (current != this)
232 // done?
233 if (current.getNodeType() == ELEMENT_NODE)
235 DomElement element = (DomElement) current;
236 DTDElementTypeInfo info =
237 doctype.getElementTypeInfo(current.getNodeName());
238 if (info != null &&
239 id.equals(element.getAttribute(info.idAttrName)))
241 return element;
243 else if (element.userIdAttrs != null)
245 for (Iterator i = element.userIdAttrs.iterator();
246 i.hasNext(); )
248 Node idAttr = (Node) i.next();
249 if (id.equals(idAttr.getNodeValue()))
251 return element;
257 // descend?
258 if (current.hasChildNodes())
260 current = current.getFirstChild();
261 continue;
264 // lateral?
265 temp = current.getNextSibling();
266 if (temp != null)
268 current = temp;
269 continue;
272 // back up ...
275 temp = current.getParentNode();
276 if (temp == null)
278 return null;
280 current = temp;
281 temp = current.getNextSibling();
283 while (temp == null);
284 current = temp;
286 return null;
289 private void checkNewChild(Node newChild)
291 if (newChild.getNodeType() == ELEMENT_NODE
292 && getDocumentElement() != null)
294 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
295 "document element already present: " +
296 getDocumentElement(), newChild, 0);
298 if (newChild.getNodeType() == DOCUMENT_TYPE_NODE
299 && getDoctype() != null)
301 throw new DomDOMException(DOMException.HIERARCHY_REQUEST_ERR,
302 "document type already present: " +
303 getDoctype(), newChild, 0);
308 * <b>DOM L1</b>
309 * Appends the specified node to this node's list of children,
310 * enforcing the constraints that there be only one root element
311 * and one document type child.
313 public Node appendChild(Node newChild)
315 if (checkingWellformedness)
317 checkNewChild(newChild);
319 return super.appendChild(newChild);
323 * <b>DOM L1</b>
324 * Inserts the specified node in this node's list of children,
325 * enforcing the constraints that there be only one root element
326 * and one document type child.
328 public Node insertBefore(Node newChild, Node refChild)
330 if (checkingWellformedness)
332 checkNewChild(newChild);
334 return super.insertBefore(newChild, refChild);
338 * <b>DOM L1</b>
339 * Replaces the specified node in this node's list of children,
340 * enforcing the constraints that there be only one root element
341 * and one document type child.
343 public Node replaceChild(Node newChild, Node refChild)
345 if (checkingWellformedness &&
346 ((newChild.getNodeType() == ELEMENT_NODE &&
347 refChild.getNodeType() != ELEMENT_NODE) ||
348 (newChild.getNodeType() == DOCUMENT_TYPE_NODE &&
349 refChild.getNodeType() != DOCUMENT_TYPE_NODE)))
351 checkNewChild(newChild);
353 return super.replaceChild(newChild, refChild);
356 // NOTE: DOM can't really tell when the name of an entity,
357 // notation, or PI must follow the namespace rules (excluding
358 // colons) instead of the XML rules (which allow them without
359 // much restriction). That's an API issue. verifyXmlName
360 // aims to enforce the XML rules, not the namespace rules.
363 * Throws a DOM exception if the specified name is not a legal XML 1.0
364 * Name.
365 * @deprecated This method is deprecated and may be removed in future
366 * versions of GNU JAXP
368 public static void verifyXmlName(String name)
370 // XXX why is this public?
371 checkName(name, false);
374 static void checkName(String name, boolean xml11)
376 if (name == null)
378 throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
380 int len = name.length();
381 if (len == 0)
383 throw new DomDOMException(DOMException.NAMESPACE_ERR, name, null, 0);
386 // dog: rewritten to use the rules for XML 1.0 and 1.1
388 // Name start character
389 char c = name.charAt(0);
390 if (xml11)
392 // XML 1.1
393 if ((c < 0x0041 || c > 0x005a) &&
394 (c < 0x0061 || c > 0x007a) &&
395 c != ':' && c != '_' &&
396 (c < 0x00c0 || c > 0x00d6) &&
397 (c < 0x00d8 || c > 0x00f6) &&
398 (c < 0x00f8 || c > 0x02ff) &&
399 (c < 0x0370 || c > 0x037d) &&
400 (c < 0x037f || c > 0x1fff) &&
401 (c < 0x200c || c > 0x200d) &&
402 (c < 0x2070 || c > 0x218f) &&
403 (c < 0x2c00 || c > 0x2fef) &&
404 (c < 0x3001 || c > 0xd7ff) &&
405 (c < 0xf900 || c > 0xfdcf) &&
406 (c < 0xfdf0 || c > 0xfffd) &&
407 (c < 0x10000 || c > 0xeffff))
409 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
410 name, null, c);
413 else
415 // XML 1.0
416 int type = Character.getType(c);
417 switch (type)
419 case Character.LOWERCASE_LETTER: // Ll
420 case Character.UPPERCASE_LETTER: // Lu
421 case Character.OTHER_LETTER: // Lo
422 case Character.TITLECASE_LETTER: // Lt
423 case Character.LETTER_NUMBER: // Nl
424 if ((c > 0xf900 && c < 0xfffe) ||
425 (c >= 0x20dd && c <= 0x20e0))
427 // Compatibility area and Unicode 2.0 exclusions
428 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
429 name, null, c);
431 break;
432 default:
433 if (c != ':' && c != '_' && (c < 0x02bb || c > 0x02c1) &&
434 c != 0x0559 && c != 0x06e5 && c != 0x06e6)
436 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
437 name, null, c);
442 // Subsequent characters
443 for (int i = 1; i < len; i++)
445 c = name.charAt(i);
446 if (xml11)
448 // XML 1.1
449 if ((c < 0x0041 || c > 0x005a) &&
450 (c < 0x0061 || c > 0x007a) &&
451 (c < 0x0030 || c > 0x0039) &&
452 c != ':' && c != '_' && c != '-' && c != '.' &&
453 (c < 0x00c0 || c > 0x00d6) &&
454 (c < 0x00d8 || c > 0x00f6) &&
455 (c < 0x00f8 || c > 0x02ff) &&
456 (c < 0x0370 || c > 0x037d) &&
457 (c < 0x037f || c > 0x1fff) &&
458 (c < 0x200c || c > 0x200d) &&
459 (c < 0x2070 || c > 0x218f) &&
460 (c < 0x2c00 || c > 0x2fef) &&
461 (c < 0x3001 || c > 0xd7ff) &&
462 (c < 0xf900 || c > 0xfdcf) &&
463 (c < 0xfdf0 || c > 0xfffd) &&
464 (c < 0x10000 || c > 0xeffff) &&
465 c != 0x00b7 &&
466 (c < 0x0300 || c > 0x036f) &&
467 (c < 0x203f || c > 0x2040))
469 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR, name,
470 null, c);
473 else
475 // XML 1.0
476 int type = Character.getType(c);
477 switch (type)
479 case Character.LOWERCASE_LETTER: // Ll
480 case Character.UPPERCASE_LETTER: // Lu
481 case Character.DECIMAL_DIGIT_NUMBER: // Nd
482 case Character.OTHER_LETTER: // Lo
483 case Character.TITLECASE_LETTER: // Lt
484 case Character.LETTER_NUMBER: // Nl
485 case Character.COMBINING_SPACING_MARK: // Mc
486 case Character.ENCLOSING_MARK: // Me
487 case Character.NON_SPACING_MARK: // Mn
488 case Character.MODIFIER_LETTER: // Lm
489 if ((c > 0xf900 && c < 0xfffe) ||
490 (c >= 0x20dd && c <= 0x20e0))
492 // Compatibility area and Unicode 2.0 exclusions
493 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
494 name, null, c);
496 break;
497 default:
498 if (c != '-' && c != '.' && c != ':' && c != '_' &&
499 c != 0x0387 && (c < 0x02bb || c > 0x02c1) &&
500 c != 0x0559 && c != 0x06e5 && c != 0x06e6 && c != 0x00b7)
502 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
503 name, null, c);
509 // FIXME characters with a font or compatibility decomposition (i.e.
510 // those with a "compatibility formatting tag" in field 5 of the
511 // database -- marked by field 5 beginning with a "<") are not allowed.
514 // package private
515 static void checkNCName(String name, boolean xml11)
517 checkName(name, xml11);
518 int len = name.length();
519 int index = name.indexOf(':');
520 if (index != -1)
522 if (index == 0 || index == (len - 1) ||
523 name.lastIndexOf(':') != index)
525 throw new DomDOMException(DOMException.NAMESPACE_ERR,
526 name, null, 0);
531 // package private
532 static void checkChar(String value, boolean xml11)
534 char[] chars = value.toCharArray();
535 checkChar(chars, 0, chars.length, xml11);
538 static void checkChar(char[] buf, int off, int len, boolean xml11)
540 for (int i = 0; i < len; i++)
542 char c = buf[i];
544 // assume surrogate pairing checks out OK, for simplicity
545 if ((c >= 0x0020 && c <= 0xd7ff) ||
546 (c == 0x000a || c == 0x000d || c == 0x0009) ||
547 (c >= 0xe000 && c <= 0xfffd) ||
548 (c >= 0x10000 && c <= 0x10ffff))
550 continue;
552 if (xml11)
554 if ((c >= 0x0001 && c <= 0x001f) ||
555 (c >= 0x007f && c <= 0x0084) ||
556 (c >= 0x0086 && c <= 0x009f))
558 continue;
561 throw new DomDOMException(DOMException.INVALID_CHARACTER_ERR,
562 new String(buf, off, len), null, c);
567 * <b>DOM L1</b>
568 * Returns a newly created element with the specified name.
570 public Element createElement(String name)
572 Element element;
574 if (checkingCharacters)
576 checkName(name, "1.1".equals(version));
578 if (name.startsWith("xml:"))
580 element = createElementNS(null, name);
582 else
584 element = new DomElement(this, null, name);
586 defaultAttributes(element, name);
587 return element;
591 * <b>DOM L2</b>
592 * Returns a newly created element with the specified name
593 * and namespace information.
595 public Element createElementNS(String namespaceURI, String name)
597 if (checkingCharacters)
599 checkNCName(name, "1.1".equals(version));
602 if ("".equals(namespaceURI))
604 namespaceURI = null;
606 if (name.startsWith("xml:"))
608 if (namespaceURI != null
609 && !XMLConstants.XML_NS_URI.equals(namespaceURI))
611 throw new DomDOMException(DOMException.NAMESPACE_ERR,
612 "xml namespace is always " +
613 XMLConstants.XML_NS_URI, this, 0);
615 namespaceURI = XMLConstants.XML_NS_URI;
617 else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
618 name.startsWith("xmlns:"))
620 throw new DomDOMException(DOMException.NAMESPACE_ERR,
621 "xmlns is reserved", this, 0);
623 else if (namespaceURI == null && name.indexOf(':') != -1)
625 throw new DomDOMException(DOMException.NAMESPACE_ERR,
626 "prefixed name '" + name +
627 "' needs a URI", this, 0);
630 Element element = new DomElement(this, namespaceURI, name);
631 defaultAttributes(element, name);
632 return element;
635 private void defaultAttributes(Element element, String name)
637 DomDoctype doctype = (DomDoctype) getDoctype();
638 if (doctype == null)
640 return;
643 // default any attributes that need it
644 DTDElementTypeInfo info = doctype.getElementTypeInfo(name);
645 if (info != null)
647 for (Iterator i = info.attributes(); i != null && i.hasNext(); )
649 DTDAttributeTypeInfo attr = (DTDAttributeTypeInfo) i.next();
650 DomAttr node = (DomAttr) createAttribute(attr.name);
652 String value = attr.value;
653 if (value == null)
655 value = "";
657 node.setValue(value);
658 node.setSpecified(false);
659 element.setAttributeNode(node);
665 * <b>DOM L1</b>
666 * Returns a newly created document fragment.
668 public DocumentFragment createDocumentFragment()
670 return new DomDocumentFragment(this);
674 * <b>DOM L1</b>
675 * Returns a newly created text node with the specified value.
677 public Text createTextNode(String value)
679 if (checkingCharacters)
681 checkChar(value, "1.1".equals(version));
683 return new DomText(this, value);
687 * Returns a newly created text node with the specified value.
689 public Text createTextNode(char[] buf, int off, int len)
691 if (checkingCharacters)
693 checkChar(buf, off, len, "1.1".equals(version));
695 return new DomText(this, buf, off, len);
699 * <b>DOM L1</b>
700 * Returns a newly created comment node with the specified value.
702 public Comment createComment(String value)
704 if (checkingCharacters)
706 checkChar(value, "1.1".equals(version));
708 return new DomComment(this, value);
712 * <b>DOM L1</b>
713 * Returns a newly created CDATA section node with the specified value.
715 public CDATASection createCDATASection(String value)
717 if (checkingCharacters)
719 checkChar(value, "1.1".equals(version));
721 return new DomCDATASection(this, value);
725 * Returns a newly created CDATA section node with the specified value.
727 public CDATASection createCDATASection(char[] buf, int off, int len)
729 if (checkingCharacters)
731 checkChar(buf, off, len, "1.1".equals(version));
733 return new DomCDATASection(this, buf, off, len);
737 * <b>DOM L1</b>
738 * Returns a newly created processing instruction.
740 public ProcessingInstruction createProcessingInstruction(String target,
741 String data)
743 if (checkingCharacters)
745 boolean xml11 = "1.1".equals(version);
746 checkName(target, xml11);
747 if ("xml".equalsIgnoreCase(target))
749 throw new DomDOMException(DOMException.SYNTAX_ERR,
750 "illegal PI target name",
751 this, 0);
753 checkChar(data, xml11);
755 return new DomProcessingInstruction(this, target, data);
759 * <b>DOM L1</b>
760 * Returns a newly created attribute with the specified name.
762 public Attr createAttribute(String name)
764 if (checkingCharacters)
766 checkName(name, "1.1".equals(version));
768 if (name.startsWith("xml:"))
770 return createAttributeNS(XMLConstants.XML_NS_URI, name);
772 else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
773 name.startsWith("xmlns:"))
775 return createAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, name);
777 else
779 return new DomAttr(this, null, name);
784 * <b>DOM L2</b>
785 * Returns a newly created attribute with the specified name
786 * and namespace information.
788 public Attr createAttributeNS(String namespaceURI, String name)
790 if (checkingCharacters)
792 checkNCName(name, "1.1".equals(version));
795 if ("".equals(namespaceURI))
797 namespaceURI = null;
799 if (name.startsWith ("xml:"))
801 if (namespaceURI == null)
803 namespaceURI = XMLConstants.XML_NS_URI;
805 else if (!XMLConstants.XML_NS_URI.equals(namespaceURI))
807 throw new DomDOMException(DOMException.NAMESPACE_ERR,
808 "xml namespace is always " +
809 XMLConstants.XML_NS_URI,
810 this, 0);
813 else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
814 name.startsWith("xmlns:"))
816 if (namespaceURI == null)
818 namespaceURI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
820 else if (!XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
822 throw new DomDOMException(DOMException.NAMESPACE_ERR,
823 "xmlns namespace must be " +
824 XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
825 this, 0);
828 else if (namespaceURI == null && name.indexOf(':') != -1)
830 throw new DomDOMException(DOMException.NAMESPACE_ERR,
831 "prefixed name needs a URI: " + name, this, 0);
833 return new DomAttr(this, namespaceURI, name);
837 * <b>DOM L1</b>
838 * Returns a newly created reference to the specified entity.
839 * The caller should populate this with the appropriate children
840 * and then mark it as readonly.
842 * @see DomNode#makeReadonly
844 public EntityReference createEntityReference(String name)
846 DomEntityReference ret = new DomEntityReference(this, name);
847 DocumentType doctype = getDoctype();
848 if (doctype != null)
850 DomEntity ent = (DomEntity) doctype.getEntities().getNamedItem(name);
851 if (ent != null)
853 for (DomNode ctx = ent.first; ctx != null; ctx = ctx.next)
855 ret.appendChild(ctx.cloneNode(true));
859 ret.makeReadonly();
860 return ret;
864 * <b>DOM L2</b>
865 * Makes a copy of the specified node, with all nodes "owned" by
866 * this document and with children optionally copied. This type
867 * of standard utility has become, well, a standard utility.
869 * <p> Note that EntityReference nodes created through this method (either
870 * directly, or recursively) never have children, and that there is no
871 * portable way to associate them with such children.
873 * <p> Note also that there is no requirement that the specified node
874 * be associated with a different document. This differs from the
875 * <em>cloneNode</em> operation in that the node itself is not given
876 * an opportunity to participate, so that any information managed
877 * by node subclasses will be lost.
879 public Node importNode(Node src, boolean deep)
881 Node dst = null;
882 switch (src.getNodeType())
884 case TEXT_NODE:
885 dst = createTextNode(src.getNodeValue());
886 break;
887 case CDATA_SECTION_NODE:
888 dst = createCDATASection(src.getNodeValue());
889 break;
890 case COMMENT_NODE:
891 dst = createComment(src.getNodeValue());
892 break;
893 case PROCESSING_INSTRUCTION_NODE:
894 dst = createProcessingInstruction(src.getNodeName(),
895 src.getNodeValue());
896 break;
897 case NOTATION_NODE:
898 // NOTE: There's no standard way to create
899 // these, or add them to a doctype. Useless.
900 Notation notation = (Notation) src;
901 dst = new DomNotation(this, notation.getNodeName(),
902 notation.getPublicId(),
903 notation.getSystemId());
904 break;
905 case ENTITY_NODE:
906 // NOTE: There's no standard way to create
907 // these, or add them to a doctype. Useless.
908 Entity entity = (Entity) src;
909 dst = new DomEntity(this, entity.getNodeName(),
910 entity.getPublicId(),
911 entity.getSystemId(),
912 entity.getNotationName());
913 if (deep)
915 for (Node ctx = src.getFirstChild(); ctx != null;
916 ctx = ctx.getNextSibling())
918 dst.appendChild(importNode(ctx, deep));
921 break;
922 case ENTITY_REFERENCE_NODE:
923 dst = createEntityReference(src.getNodeName());
924 break;
925 case DOCUMENT_FRAGMENT_NODE:
926 dst = new DomDocumentFragment(this);
927 if (deep)
929 for (Node ctx = src.getFirstChild(); ctx != null;
930 ctx = ctx.getNextSibling())
932 dst.appendChild(importNode(ctx, deep));
935 break;
936 case ATTRIBUTE_NODE:
937 String attr_nsuri = src.getNamespaceURI();
938 if (attr_nsuri != null)
940 dst = createAttributeNS(attr_nsuri, src.getNodeName());
942 else
944 dst = createAttribute(src.getNodeName());
946 // this is _always_ done regardless of "deep" setting
947 for (Node ctx = src.getFirstChild(); ctx != null;
948 ctx = ctx.getNextSibling())
950 dst.appendChild(importNode(ctx, false));
952 break;
953 case ELEMENT_NODE:
954 String elem_nsuri = src.getNamespaceURI();
955 if (elem_nsuri != null)
957 dst = createElementNS(elem_nsuri, src.getNodeName());
959 else
961 dst = createElement(src.getNodeName());
963 NamedNodeMap srcAttrs = src.getAttributes();
964 NamedNodeMap dstAttrs = dst.getAttributes();
965 int len = srcAttrs.getLength();
966 for (int i = 0; i < len; i++)
968 Attr a = (Attr) srcAttrs.item(i);
969 Attr dflt;
971 // maybe update defaulted attributes
972 dflt = (Attr) dstAttrs.getNamedItem(a.getNodeName());
973 if (dflt != null)
975 String newval = a.getNodeValue();
976 if (!dflt.getNodeValue().equals(newval)
977 || a.getSpecified () == true)
979 dflt.setNodeValue (newval);
981 continue;
984 dstAttrs.setNamedItem((Attr) importNode(a, false));
986 if (deep)
988 for (Node ctx = src.getFirstChild(); ctx != null;
989 ctx = ctx.getNextSibling())
991 dst.appendChild(importNode(ctx, true));
994 break;
995 // can't import document or doctype nodes
996 case DOCUMENT_NODE:
997 case DOCUMENT_TYPE_NODE:
998 // FALLTHROUGH
999 // can't import unrecognized or nonstandard nodes
1000 default:
1001 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR, null, src, 0);
1004 // FIXME cleanup a bit -- for deep copies, copy those
1005 // children in one place, here (code sharing is healthy)
1007 if (src instanceof DomNode)
1009 ((DomNode) src).notifyUserDataHandlers(UserDataHandler.NODE_IMPORTED,
1010 src, dst);
1012 return dst;
1016 * <b>DOM L2 (Traversal)</b>
1017 * Returns a newly created node iterator. Don't forget to detach
1018 * this iterator when you're done using it!
1020 * @see DomIterator
1022 public NodeIterator createNodeIterator(Node root,
1023 int whatToShow,
1024 NodeFilter filter,
1025 boolean expandEntities)
1027 return new DomNodeIterator(root, whatToShow, filter, expandEntities,
1028 false);
1031 public TreeWalker createTreeWalker(Node root,
1032 int whatToShow,
1033 NodeFilter filter,
1034 boolean expandEntities)
1036 return new DomNodeIterator(root, whatToShow, filter, expandEntities,
1037 true);
1040 // DOM Level 3 methods
1043 * DOM L3
1045 public String getInputEncoding()
1047 return inputEncoding;
1050 public void setInputEncoding(String inputEncoding)
1052 this.inputEncoding = inputEncoding;
1056 * DOM L3
1058 public String getXmlEncoding()
1060 return encoding;
1063 public void setXmlEncoding(String encoding)
1065 this.encoding = encoding;
1068 public boolean getXmlStandalone()
1070 return standalone;
1073 public void setXmlStandalone(boolean xmlStandalone)
1075 standalone = xmlStandalone;
1078 public String getXmlVersion()
1080 return version;
1083 public void setXmlVersion(String xmlVersion)
1085 if (xmlVersion == null)
1087 xmlVersion = "1.0";
1089 if ("1.0".equals(xmlVersion) ||
1090 "1.1".equals(xmlVersion))
1092 version = xmlVersion;
1094 else
1096 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
1100 public boolean getStrictErrorChecking()
1102 return checkingCharacters;
1105 public void setStrictErrorChecking(boolean strictErrorChecking)
1107 checkingCharacters = strictErrorChecking;
1110 public String lookupPrefix(String namespaceURI)
1112 Node root = getDocumentElement();
1113 return (root == null) ? null : root.lookupPrefix(namespaceURI);
1116 public boolean isDefaultNamespace(String namespaceURI)
1118 Node root = getDocumentElement();
1119 return (root == null) ? false : root.isDefaultNamespace(namespaceURI);
1122 public String lookupNamespaceURI(String prefix)
1124 Node root = getDocumentElement();
1125 return (root == null) ? null : root.lookupNamespaceURI(prefix);
1128 public String getBaseURI()
1130 return getDocumentURI();
1132 Node root = getDocumentElement();
1133 if (root != null)
1135 NamedNodeMap attrs = root.getAttributes();
1136 Node xmlBase = attrs.getNamedItemNS(XMLConstants.XML_NS_URI, "base");
1137 if (xmlBase != null)
1139 return xmlBase.getNodeValue();
1142 return systemId;
1146 public String getDocumentURI()
1148 return systemId;
1151 public void setDocumentURI(String documentURI)
1153 systemId = documentURI;
1156 public Node adoptNode(Node source)
1158 switch (source.getNodeType())
1160 case DOCUMENT_NODE:
1161 case DOCUMENT_TYPE_NODE:
1162 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR);
1163 case ENTITY_NODE:
1164 case NOTATION_NODE:
1165 throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
1167 if (source instanceof DomNode)
1169 DomNode src = (DomNode) source;
1170 DomNode dst = src;
1171 if (dst.parent != null)
1173 dst = (DomNode) dst.cloneNode(true);
1175 dst.setOwner(this);
1176 src.notifyUserDataHandlers(UserDataHandler.NODE_ADOPTED, src, dst);
1177 return dst;
1179 return null;
1182 public DOMConfiguration getDomConfig()
1184 if (config == null)
1186 config = new DomDocumentConfiguration();
1188 return config;
1191 public void normalizeDocument()
1193 boolean save = building;
1194 building = true;
1195 normalizeNode(this);
1196 building = save;
1199 void normalizeNode(DomNode node)
1201 node.normalize();
1202 if (config != null)
1204 switch (node.nodeType)
1206 case CDATA_SECTION_NODE:
1207 if (!config.cdataSections)
1209 // replace CDATA section with text node
1210 Text text = createTextNode(node.getNodeValue());
1211 node.parent.insertBefore(text, node);
1212 node.parent.removeChild(node);
1213 // merge adjacent text nodes
1214 String data = text.getWholeText();
1215 node = (DomNode) text.replaceWholeText(data);
1217 else if (config.splitCdataSections)
1219 String value = node.getNodeValue();
1220 int i = value.indexOf("]]>");
1221 while (i != -1)
1223 Node node2 = createCDATASection(value.substring(0, i));
1224 node.parent.insertBefore(node2, node);
1225 value = value.substring(i + 3);
1226 node.setNodeValue(value);
1227 i = value.indexOf("]]>");
1230 break;
1231 case COMMENT_NODE:
1232 if (!config.comments)
1234 node.parent.removeChild(node);
1236 break;
1237 case TEXT_NODE:
1238 if (!config.elementContentWhitespace &&
1239 ((Text) node).isElementContentWhitespace())
1241 node.parent.removeChild(node);
1243 break;
1244 case ENTITY_REFERENCE_NODE:
1245 if (!config.entities)
1247 for (DomNode ctx = node.first; ctx != null; )
1249 DomNode ctxNext = ctx.next;
1250 node.parent.insertBefore(ctx, node);
1251 ctx = ctxNext;
1253 node.parent.removeChild(node);
1255 break;
1256 case ELEMENT_NODE:
1257 if (!config.namespaceDeclarations)
1259 DomNamedNodeMap attrs =
1260 (DomNamedNodeMap) node.getAttributes();
1261 boolean aro = attrs.readonly;
1262 attrs.readonly = false; // Ensure we can delete if necessary
1263 int len = attrs.getLength();
1264 for (int i = 0; i < len; i++)
1266 Node attr = attrs.item(i);
1267 String namespace = attr.getNamespaceURI();
1268 if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespace))
1270 attrs.removeNamedItemNS(namespace,
1271 attr.getLocalName());
1272 i--;
1273 len--;
1276 attrs.readonly = aro;
1278 break;
1281 for (DomNode ctx = node.first; ctx != null; )
1283 DomNode ctxNext = ctx.next;
1284 normalizeNode(ctx);
1285 ctx = ctxNext;
1289 public Node renameNode(Node n, String namespaceURI, String qualifiedName)
1290 throws DOMException
1292 if (n instanceof DomNsNode)
1294 DomNsNode src = (DomNsNode) n;
1295 if (src == null)
1297 throw new DomDOMException(DOMException.NOT_FOUND_ERR);
1299 if (src.owner != this)
1301 throw new DomDOMException(DOMException.WRONG_DOCUMENT_ERR,
1302 null, src, 0);
1304 boolean xml11 = "1.1".equals(version);
1305 checkName(qualifiedName, xml11);
1306 int ci = qualifiedName.indexOf(':');
1307 if ("".equals(namespaceURI))
1309 namespaceURI = null;
1311 if (namespaceURI != null)
1313 checkNCName(qualifiedName, xml11);
1314 String prefix = (ci == -1) ? "" :
1315 qualifiedName.substring(0, ci);
1316 if (XMLConstants.XML_NS_PREFIX.equals(prefix) &&
1317 !XMLConstants.XML_NS_URI.equals(namespaceURI))
1319 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1320 "xml namespace must be " +
1321 XMLConstants.XML_NS_URI, src, 0);
1323 else if (src.nodeType == ATTRIBUTE_NODE &&
1324 (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
1325 XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)) &&
1326 !XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
1328 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1329 "xmlns namespace must be " +
1330 XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
1332 if (XMLConstants.XML_NS_URI.equals(namespaceURI) &&
1333 !XMLConstants.XML_NS_PREFIX.equals(prefix))
1335 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1336 "xml namespace must be " +
1337 XMLConstants.XML_NS_URI, src, 0);
1339 else if (src.nodeType == ATTRIBUTE_NODE &&
1340 XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI) &&
1341 !(XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
1342 XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)))
1344 throw new DomDOMException(DOMException.NAMESPACE_ERR,
1345 "xmlns namespace must be " +
1346 XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
1350 src.setNodeName(qualifiedName);
1351 src.setNamespaceURI(namespaceURI);
1352 src.notifyUserDataHandlers(UserDataHandler.NODE_RENAMED, src, src);
1353 // TODO MutationNameEvents
1354 // DOMElementNameChanged or DOMAttributeNameChanged
1355 return src;
1357 throw new DomDOMException(DOMException.NOT_SUPPORTED_ERR, null, n, 0);
1360 // -- XPathEvaluator --
1362 public XPathExpression createExpression(String expression,
1363 XPathNSResolver resolver)
1364 throws XPathException, DOMException
1366 return new DomXPathExpression(this, expression, resolver);
1369 public XPathNSResolver createNSResolver(Node nodeResolver)
1371 return new DomXPathNSResolver(nodeResolver);
1374 public Object evaluate(String expression,
1375 Node contextNode,
1376 XPathNSResolver resolver,
1377 short type,
1378 Object result)
1379 throws XPathException, DOMException
1381 XPathExpression xpe =
1382 new DomXPathExpression(this, expression, resolver);
1383 return xpe.evaluate(contextNode, type, result);