2 minidom.py -- a lightweight DOM implementation.
6 parseString("<foo><bar/></foo>")
10 * convenience methods for getting elements and text.
12 * bring some of the writer and linearizer code into conformance with this
19 from xml
.dom
import EMPTY_NAMESPACE
, EMPTY_PREFIX
, XMLNS_NAMESPACE
, domreg
20 from xml
.dom
.minicompat
import *
21 from xml
.dom
.xmlbuilder
import DOMImplementationLS
, DocumentLS
23 # This is used by the ID-cache invalidation checks; the list isn't
24 # actually complete, since the nodes being checked will never be the
25 # DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE. (The node being checked is
26 # the node being added or removed, not the node being modified.)
28 _nodeTypes_with_children
= (xml
.dom
.Node
.ELEMENT_NODE
,
29 xml
.dom
.Node
.ENTITY_REFERENCE_NODE
)
32 class Node(xml
.dom
.Node
):
33 namespaceURI
= None # this is non-null only for elements and attributes
37 previousSibling
= None
39 prefix
= EMPTY_PREFIX
# non-null only for NS elements and attributes
41 def __nonzero__(self
):
44 def toxml(self
, encoding
= None):
45 return self
.toprettyxml("", "", encoding
)
47 def toprettyxml(self
, indent
="\t", newl
="\n", encoding
= None):
48 # indent = the indentation string to prepend, per level
49 # newl = the newline string to append
50 writer
= _get_StringIO()
51 if encoding
is not None:
53 # Can't use codecs.getwriter to preserve 2.0 compatibility
54 writer
= codecs
.lookup(encoding
)[3](writer
)
55 if self
.nodeType
== Node
.DOCUMENT_NODE
:
56 # Can pass encoding only to document, to put it into XML header
57 self
.writexml(writer
, "", indent
, newl
, encoding
)
59 self
.writexml(writer
, "", indent
, newl
)
60 return writer
.getvalue()
62 def hasChildNodes(self
):
68 def _get_childNodes(self
):
69 return self
.childNodes
71 def _get_firstChild(self
):
73 return self
.childNodes
[0]
75 def _get_lastChild(self
):
77 return self
.childNodes
[-1]
79 def insertBefore(self
, newChild
, refChild
):
80 if newChild
.nodeType
== self
.DOCUMENT_FRAGMENT_NODE
:
81 for c
in tuple(newChild
.childNodes
):
82 self
.insertBefore(c
, refChild
)
83 ### The DOM does not clearly specify what to return in this case
85 if newChild
.nodeType
not in self
._child
_node
_types
:
86 raise xml
.dom
.HierarchyRequestErr(
87 "%s cannot be child of %s" % (repr(newChild
), repr(self
)))
88 if newChild
.parentNode
is not None:
89 newChild
.parentNode
.removeChild(newChild
)
91 self
.appendChild(newChild
)
94 index
= self
.childNodes
.index(refChild
)
96 raise xml
.dom
.NotFoundErr()
97 if newChild
.nodeType
in _nodeTypes_with_children
:
99 self
.childNodes
.insert(index
, newChild
)
100 newChild
.nextSibling
= refChild
101 refChild
.previousSibling
= newChild
103 node
= self
.childNodes
[index
-1]
104 node
.nextSibling
= newChild
105 newChild
.previousSibling
= node
107 newChild
.previousSibling
= None
108 newChild
.parentNode
= self
111 def appendChild(self
, node
):
112 if node
.nodeType
== self
.DOCUMENT_FRAGMENT_NODE
:
113 for c
in tuple(node
.childNodes
):
115 ### The DOM does not clearly specify what to return in this case
117 if node
.nodeType
not in self
._child
_node
_types
:
118 raise xml
.dom
.HierarchyRequestErr(
119 "%s cannot be child of %s" % (repr(node
), repr(self
)))
120 elif node
.nodeType
in _nodeTypes_with_children
:
121 _clear_id_cache(self
)
122 if node
.parentNode
is not None:
123 node
.parentNode
.removeChild(node
)
124 _append_child(self
, node
)
125 node
.nextSibling
= None
128 def replaceChild(self
, newChild
, oldChild
):
129 if newChild
.nodeType
== self
.DOCUMENT_FRAGMENT_NODE
:
130 refChild
= oldChild
.nextSibling
131 self
.removeChild(oldChild
)
132 return self
.insertBefore(newChild
, refChild
)
133 if newChild
.nodeType
not in self
._child
_node
_types
:
134 raise xml
.dom
.HierarchyRequestErr(
135 "%s cannot be child of %s" % (repr(newChild
), repr(self
)))
136 if newChild
is oldChild
:
138 if newChild
.parentNode
is not None:
139 newChild
.parentNode
.removeChild(newChild
)
141 index
= self
.childNodes
.index(oldChild
)
143 raise xml
.dom
.NotFoundErr()
144 self
.childNodes
[index
] = newChild
145 newChild
.parentNode
= self
146 oldChild
.parentNode
= None
147 if (newChild
.nodeType
in _nodeTypes_with_children
148 or oldChild
.nodeType
in _nodeTypes_with_children
):
149 _clear_id_cache(self
)
150 newChild
.nextSibling
= oldChild
.nextSibling
151 newChild
.previousSibling
= oldChild
.previousSibling
152 oldChild
.nextSibling
= None
153 oldChild
.previousSibling
= None
154 if newChild
.previousSibling
:
155 newChild
.previousSibling
.nextSibling
= newChild
156 if newChild
.nextSibling
:
157 newChild
.nextSibling
.previousSibling
= newChild
160 def removeChild(self
, oldChild
):
162 self
.childNodes
.remove(oldChild
)
164 raise xml
.dom
.NotFoundErr()
165 if oldChild
.nextSibling
is not None:
166 oldChild
.nextSibling
.previousSibling
= oldChild
.previousSibling
167 if oldChild
.previousSibling
is not None:
168 oldChild
.previousSibling
.nextSibling
= oldChild
.nextSibling
169 oldChild
.nextSibling
= oldChild
.previousSibling
= None
170 if oldChild
.nodeType
in _nodeTypes_with_children
:
171 _clear_id_cache(self
)
173 oldChild
.parentNode
= None
178 for child
in self
.childNodes
:
179 if child
.nodeType
== Node
.TEXT_NODE
:
181 # empty text node; discard
183 L
[-1].nextSibling
= child
.nextSibling
184 if child
.nextSibling
:
185 child
.nextSibling
.previousSibling
= child
.previousSibling
187 elif L
and L
[-1].nodeType
== child
.nodeType
:
190 node
.data
= node
.data
+ child
.data
191 node
.nextSibling
= child
.nextSibling
192 if child
.nextSibling
:
193 child
.nextSibling
.previousSibling
= node
199 if child
.nodeType
== Node
.ELEMENT_NODE
:
201 self
.childNodes
[:] = L
203 def cloneNode(self
, deep
):
204 return _clone_node(self
, deep
, self
.ownerDocument
or self
)
206 def isSupported(self
, feature
, version
):
207 return self
.ownerDocument
.implementation
.hasFeature(feature
, version
)
209 def _get_localName(self
):
210 # Overridden in Element and Attr where localName can be Non-Null
213 # Node interfaces from Level 3 (WD 9 April 2002)
215 def isSameNode(self
, other
):
218 def getInterface(self
, feature
):
219 if self
.isSupported(feature
, None):
224 # The "user data" functions use a dictionary that is only present
225 # if some user data has been set, so be careful not to assume it
228 def getUserData(self
, key
):
230 return self
._user
_data
[key
][0]
231 except (AttributeError, KeyError):
234 def setUserData(self
, key
, data
, handler
):
238 except AttributeError:
244 # ignore handlers passed for None
249 d
[key
] = (data
, handler
)
252 def _call_user_data_handler(self
, operation
, src
, dst
):
253 if hasattr(self
, "_user_data"):
254 for key
, (data
, handler
) in self
._user
_data
.items():
255 if handler
is not None:
256 handler
.handle(operation
, key
, data
, src
, dst
)
258 # minidom-specific API:
261 self
.parentNode
= self
.ownerDocument
= None
263 for child
in self
.childNodes
:
265 self
.childNodes
= NodeList()
266 self
.previousSibling
= None
267 self
.nextSibling
= None
269 defproperty(Node
, "firstChild", doc
="First child node, or None.")
270 defproperty(Node
, "lastChild", doc
="Last child node, or None.")
271 defproperty(Node
, "localName", doc
="Namespace-local name of this node.")
274 def _append_child(self
, node
):
275 # fast path with less checks; usable by DOM builders if careful
276 childNodes
= self
.childNodes
278 last
= childNodes
[-1]
279 node
.__dict
__["previousSibling"] = last
280 last
.__dict
__["nextSibling"] = node
281 childNodes
.append(node
)
282 node
.__dict
__["parentNode"] = self
284 def _in_document(node
):
285 # return True iff node is part of a document tree
286 while node
is not None:
287 if node
.nodeType
== Node
.DOCUMENT_NODE
:
289 node
= node
.parentNode
292 def _write_data(writer
, data
):
293 "Writes datachars to writer."
295 data
= data
.replace("&", "&").replace("<", "<"). \
296 replace("\"", """).replace(">", ">")
299 def _get_elements_by_tagName_helper(parent
, name
, rc
):
300 for node
in parent
.childNodes
:
301 if node
.nodeType
== Node
.ELEMENT_NODE
and \
302 (name
== "*" or node
.tagName
== name
):
304 _get_elements_by_tagName_helper(node
, name
, rc
)
307 def _get_elements_by_tagName_ns_helper(parent
, nsURI
, localName
, rc
):
308 for node
in parent
.childNodes
:
309 if node
.nodeType
== Node
.ELEMENT_NODE
:
310 if ((localName
== "*" or node
.localName
== localName
) and
311 (nsURI
== "*" or node
.namespaceURI
== nsURI
)):
313 _get_elements_by_tagName_ns_helper(node
, nsURI
, localName
, rc
)
316 class DocumentFragment(Node
):
317 nodeType
= Node
.DOCUMENT_FRAGMENT_NODE
318 nodeName
= "#document-fragment"
322 _child_node_types
= (Node
.ELEMENT_NODE
,
324 Node
.CDATA_SECTION_NODE
,
325 Node
.ENTITY_REFERENCE_NODE
,
326 Node
.PROCESSING_INSTRUCTION_NODE
,
331 self
.childNodes
= NodeList()
335 nodeType
= Node
.ATTRIBUTE_NODE
341 _child_node_types
= (Node
.TEXT_NODE
, Node
.ENTITY_REFERENCE_NODE
)
343 def __init__(self
, qName
, namespaceURI
=EMPTY_NAMESPACE
, localName
=None,
345 # skip setattr for performance
347 d
["nodeName"] = d
["name"] = qName
348 d
["namespaceURI"] = namespaceURI
350 d
['childNodes'] = NodeList()
352 # Add the single child node that represents the value of the attr
353 self
.childNodes
.append(Text())
355 # nodeValue and value are set elsewhere
357 def _get_localName(self
):
358 return self
.nodeName
.split(":", 1)[-1]
363 def _get_specified(self
):
364 return self
.specified
366 def __setattr__(self
, name
, value
):
368 if name
in ("value", "nodeValue"):
369 d
["value"] = d
["nodeValue"] = value
370 d2
= self
.childNodes
[0].__dict
__
371 d2
["data"] = d2
["nodeValue"] = value
372 if self
.ownerElement
is not None:
373 _clear_id_cache(self
.ownerElement
)
374 elif name
in ("name", "nodeName"):
375 d
["name"] = d
["nodeName"] = value
376 if self
.ownerElement
is not None:
377 _clear_id_cache(self
.ownerElement
)
381 def _set_prefix(self
, prefix
):
382 nsuri
= self
.namespaceURI
383 if prefix
== "xmlns":
384 if nsuri
and nsuri
!= XMLNS_NAMESPACE
:
385 raise xml
.dom
.NamespaceErr(
386 "illegal use of 'xmlns' prefix for the wrong namespace")
390 newName
= self
.localName
392 newName
= "%s:%s" % (prefix
, self
.localName
)
393 if self
.ownerElement
:
394 _clear_id_cache(self
.ownerElement
)
395 d
['nodeName'] = d
['name'] = newName
397 def _set_value(self
, value
):
399 d
['value'] = d
['nodeValue'] = value
400 if self
.ownerElement
:
401 _clear_id_cache(self
.ownerElement
)
402 self
.childNodes
[0].data
= value
405 # This implementation does not call the base implementation
406 # since most of that is not needed, and the expense of the
407 # method call is not warranted. We duplicate the removal of
408 # children, but that's all we needed from the base class.
409 elem
= self
.ownerElement
411 del elem
._attrs
[self
.nodeName
]
412 del elem
._attrsNS
[(self
.namespaceURI
, self
.localName
)]
415 elem
._magic
_id
_nodes
-= 1
416 self
.ownerDocument
._magic
_id
_count
-= 1
417 for child
in self
.childNodes
:
419 del self
.childNodes
[:]
424 doc
= self
.ownerDocument
425 elem
= self
.ownerElement
426 if doc
is None or elem
is None:
429 info
= doc
._get
_elem
_info
(elem
)
432 if self
.namespaceURI
:
433 return info
.isIdNS(self
.namespaceURI
, self
.localName
)
435 return info
.isId(self
.nodeName
)
437 def _get_schemaType(self
):
438 doc
= self
.ownerDocument
439 elem
= self
.ownerElement
440 if doc
is None or elem
is None:
443 info
= doc
._get
_elem
_info
(elem
)
446 if self
.namespaceURI
:
447 return info
.getAttributeTypeNS(self
.namespaceURI
, self
.localName
)
449 return info
.getAttributeType(self
.nodeName
)
451 defproperty(Attr
, "isId", doc
="True if this attribute is an ID.")
452 defproperty(Attr
, "localName", doc
="Namespace-local name of this attribute.")
453 defproperty(Attr
, "schemaType", doc
="Schema type for this attribute.")
456 class NamedNodeMap(object):
457 """The attribute list is a transient interface to the underlying
458 dictionaries. Mutations here will change the underlying element's
461 Ordering is imposed artificially and does not reflect the order of
462 attributes as found in an input document.
465 __slots__
= ('_attrs', '_attrsNS', '_ownerElement')
467 def __init__(self
, attrs
, attrsNS
, ownerElement
):
469 self
._attrsNS
= attrsNS
470 self
._ownerElement
= ownerElement
472 def _get_length(self
):
473 return len(self
._attrs
)
475 def item(self
, index
):
477 return self
[self
._attrs
.keys()[index
]]
483 for node
in self
._attrs
.values():
484 L
.append((node
.nodeName
, node
.value
))
489 for node
in self
._attrs
.values():
490 L
.append(((node
.namespaceURI
, node
.localName
), node
.value
))
493 def has_key(self
, key
):
494 if isinstance(key
, StringTypes
):
495 return key
in self
._attrs
497 return key
in self
._attrsNS
500 return self
._attrs
.keys()
503 return self
._attrsNS
.keys()
506 return self
._attrs
.values()
508 def get(self
, name
, value
=None):
509 return self
._attrs
.get(name
, value
)
511 __len__
= _get_length
513 __hash__
= None # Mutable type can't be correctly hashed
514 def __cmp__(self
, other
):
515 if self
._attrs
is getattr(other
, "_attrs", None):
518 return cmp(id(self
), id(other
))
520 def __getitem__(self
, attname_or_tuple
):
521 if isinstance(attname_or_tuple
, tuple):
522 return self
._attrsNS
[attname_or_tuple
]
524 return self
._attrs
[attname_or_tuple
]
527 def __setitem__(self
, attname
, value
):
528 if isinstance(value
, StringTypes
):
530 node
= self
._attrs
[attname
]
533 node
.ownerDocument
= self
._ownerElement
.ownerDocument
534 self
.setNamedItem(node
)
537 if not isinstance(value
, Attr
):
538 raise TypeError, "value must be a string or Attr object"
540 self
.setNamedItem(node
)
542 def getNamedItem(self
, name
):
544 return self
._attrs
[name
]
548 def getNamedItemNS(self
, namespaceURI
, localName
):
550 return self
._attrsNS
[(namespaceURI
, localName
)]
554 def removeNamedItem(self
, name
):
555 n
= self
.getNamedItem(name
)
557 _clear_id_cache(self
._ownerElement
)
558 del self
._attrs
[n
.nodeName
]
559 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
560 if 'ownerElement' in n
.__dict
__:
561 n
.__dict
__['ownerElement'] = None
564 raise xml
.dom
.NotFoundErr()
566 def removeNamedItemNS(self
, namespaceURI
, localName
):
567 n
= self
.getNamedItemNS(namespaceURI
, localName
)
569 _clear_id_cache(self
._ownerElement
)
570 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
571 del self
._attrs
[n
.nodeName
]
572 if 'ownerElement' in n
.__dict
__:
573 n
.__dict
__['ownerElement'] = None
576 raise xml
.dom
.NotFoundErr()
578 def setNamedItem(self
, node
):
579 if not isinstance(node
, Attr
):
580 raise xml
.dom
.HierarchyRequestErr(
581 "%s cannot be child of %s" % (repr(node
), repr(self
)))
582 old
= self
._attrs
.get(node
.name
)
585 self
._attrs
[node
.name
] = node
586 self
._attrsNS
[(node
.namespaceURI
, node
.localName
)] = node
587 node
.ownerElement
= self
._ownerElement
588 _clear_id_cache(node
.ownerElement
)
591 def setNamedItemNS(self
, node
):
592 return self
.setNamedItem(node
)
594 def __delitem__(self
, attname_or_tuple
):
595 node
= self
[attname_or_tuple
]
596 _clear_id_cache(node
.ownerElement
)
599 def __getstate__(self
):
600 return self
._attrs
, self
._attrsNS
, self
._ownerElement
602 def __setstate__(self
, state
):
603 self
._attrs
, self
._attrsNS
, self
._ownerElement
= state
605 defproperty(NamedNodeMap
, "length",
606 doc
="Number of nodes in the NamedNodeMap.")
608 AttributeList
= NamedNodeMap
611 class TypeInfo(object):
612 __slots__
= 'namespace', 'name'
614 def __init__(self
, namespace
, name
):
615 self
.namespace
= namespace
620 return "<TypeInfo %r (from %r)>" % (self
.name
, self
.namespace
)
622 return "<TypeInfo %r>" % self
.name
627 def _get_namespace(self
):
628 return self
.namespace
630 _no_type
= TypeInfo(None, None)
633 nodeType
= Node
.ELEMENT_NODE
635 schemaType
= _no_type
639 _child_node_types
= (Node
.ELEMENT_NODE
,
640 Node
.PROCESSING_INSTRUCTION_NODE
,
643 Node
.CDATA_SECTION_NODE
,
644 Node
.ENTITY_REFERENCE_NODE
)
646 def __init__(self
, tagName
, namespaceURI
=EMPTY_NAMESPACE
, prefix
=None,
648 self
.tagName
= self
.nodeName
= tagName
650 self
.namespaceURI
= namespaceURI
651 self
.childNodes
= NodeList()
653 self
._attrs
= {} # attributes are double-indexed:
654 self
._attrsNS
= {} # tagName -> Attribute
655 # URI,localName -> Attribute
656 # in the future: consider lazy generation
657 # of attribute objects this is too tricky
658 # for now because of headaches with
661 def _get_localName(self
):
662 return self
.tagName
.split(":", 1)[-1]
664 def _get_tagName(self
):
668 for attr
in self
._attrs
.values():
674 def getAttribute(self
, attname
):
676 return self
._attrs
[attname
].value
680 def getAttributeNS(self
, namespaceURI
, localName
):
682 return self
._attrsNS
[(namespaceURI
, localName
)].value
686 def setAttribute(self
, attname
, value
):
687 attr
= self
.getAttributeNode(attname
)
692 d
["value"] = d
["nodeValue"] = value
693 d
["ownerDocument"] = self
.ownerDocument
694 self
.setAttributeNode(attr
)
695 elif value
!= attr
.value
:
697 d
["value"] = d
["nodeValue"] = value
699 _clear_id_cache(self
)
701 def setAttributeNS(self
, namespaceURI
, qualifiedName
, value
):
702 prefix
, localname
= _nssplit(qualifiedName
)
703 attr
= self
.getAttributeNodeNS(namespaceURI
, localname
)
706 attr
= Attr(qualifiedName
, namespaceURI
, localname
, prefix
)
709 d
["nodeName"] = qualifiedName
710 d
["value"] = d
["nodeValue"] = value
711 d
["ownerDocument"] = self
.ownerDocument
712 self
.setAttributeNode(attr
)
715 if value
!= attr
.value
:
716 d
["value"] = d
["nodeValue"] = value
718 _clear_id_cache(self
)
719 if attr
.prefix
!= prefix
:
721 d
["nodeName"] = qualifiedName
723 def getAttributeNode(self
, attrname
):
724 return self
._attrs
.get(attrname
)
726 def getAttributeNodeNS(self
, namespaceURI
, localName
):
727 return self
._attrsNS
.get((namespaceURI
, localName
))
729 def setAttributeNode(self
, attr
):
730 if attr
.ownerElement
not in (None, self
):
731 raise xml
.dom
.InuseAttributeErr("attribute node already owned")
732 old1
= self
._attrs
.get(attr
.name
, None)
734 self
.removeAttributeNode(old1
)
735 old2
= self
._attrsNS
.get((attr
.namespaceURI
, attr
.localName
), None)
736 if old2
is not None and old2
is not old1
:
737 self
.removeAttributeNode(old2
)
738 _set_attribute_node(self
, attr
)
741 # It might have already been part of this node, in which case
742 # it doesn't represent a change, and should not be returned.
747 setAttributeNodeNS
= setAttributeNode
749 def removeAttribute(self
, name
):
751 attr
= self
._attrs
[name
]
753 raise xml
.dom
.NotFoundErr()
754 self
.removeAttributeNode(attr
)
756 def removeAttributeNS(self
, namespaceURI
, localName
):
758 attr
= self
._attrsNS
[(namespaceURI
, localName
)]
760 raise xml
.dom
.NotFoundErr()
761 self
.removeAttributeNode(attr
)
763 def removeAttributeNode(self
, node
):
765 raise xml
.dom
.NotFoundErr()
767 self
._attrs
[node
.name
]
769 raise xml
.dom
.NotFoundErr()
770 _clear_id_cache(self
)
772 # Restore this since the node is still useful and otherwise
774 node
.ownerDocument
= self
.ownerDocument
776 removeAttributeNodeNS
= removeAttributeNode
778 def hasAttribute(self
, name
):
779 return name
in self
._attrs
781 def hasAttributeNS(self
, namespaceURI
, localName
):
782 return (namespaceURI
, localName
) in self
._attrsNS
784 def getElementsByTagName(self
, name
):
785 return _get_elements_by_tagName_helper(self
, name
, NodeList())
787 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
788 return _get_elements_by_tagName_ns_helper(
789 self
, namespaceURI
, localName
, NodeList())
792 return "<DOM Element: %s at %#x>" % (self
.tagName
, id(self
))
794 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
795 # indent = current indentation
796 # addindent = indentation to add to higher levels
797 # newl = newline string
798 writer
.write(indent
+"<" + self
.tagName
)
800 attrs
= self
._get
_attributes
()
801 a_names
= attrs
.keys()
804 for a_name
in a_names
:
805 writer
.write(" %s=\"" % a_name
)
806 _write_data(writer
, attrs
[a_name
].value
)
809 writer
.write(">%s"%(newl))
810 for node
in self
.childNodes
:
811 node
.writexml(writer
,indent
+addindent
,addindent
,newl
)
812 writer
.write("%s</%s>%s" % (indent
,self
.tagName
,newl
))
814 writer
.write("/>%s"%(newl))
816 def _get_attributes(self
):
817 return NamedNodeMap(self
._attrs
, self
._attrsNS
, self
)
819 def hasAttributes(self
):
825 # DOM Level 3 attributes, based on the 22 Oct 2002 draft
827 def setIdAttribute(self
, name
):
828 idAttr
= self
.getAttributeNode(name
)
829 self
.setIdAttributeNode(idAttr
)
831 def setIdAttributeNS(self
, namespaceURI
, localName
):
832 idAttr
= self
.getAttributeNodeNS(namespaceURI
, localName
)
833 self
.setIdAttributeNode(idAttr
)
835 def setIdAttributeNode(self
, idAttr
):
836 if idAttr
is None or not self
.isSameNode(idAttr
.ownerElement
):
837 raise xml
.dom
.NotFoundErr()
838 if _get_containing_entref(self
) is not None:
839 raise xml
.dom
.NoModificationAllowedErr()
840 if not idAttr
._is
_id
:
841 idAttr
.__dict
__['_is_id'] = True
842 self
._magic
_id
_nodes
+= 1
843 self
.ownerDocument
._magic
_id
_count
+= 1
844 _clear_id_cache(self
)
846 defproperty(Element
, "attributes",
847 doc
="NamedNodeMap of attributes on the element.")
848 defproperty(Element
, "localName",
849 doc
="Namespace-local name of this element.")
852 def _set_attribute_node(element
, attr
):
853 _clear_id_cache(element
)
854 element
._attrs
[attr
.name
] = attr
855 element
._attrsNS
[(attr
.namespaceURI
, attr
.localName
)] = attr
857 # This creates a circular reference, but Element.unlink()
858 # breaks the cycle since the references to the attribute
859 # dictionaries are tossed.
860 attr
.__dict
__['ownerElement'] = element
864 """Mixin that makes childless-ness easy to implement and avoids
865 the complexity of the Node methods that deal with children.
869 childNodes
= EmptyNodeList()
873 def _get_firstChild(self
):
876 def _get_lastChild(self
):
879 def appendChild(self
, node
):
880 raise xml
.dom
.HierarchyRequestErr(
881 self
.nodeName
+ " nodes cannot have children")
883 def hasChildNodes(self
):
886 def insertBefore(self
, newChild
, refChild
):
887 raise xml
.dom
.HierarchyRequestErr(
888 self
.nodeName
+ " nodes do not have children")
890 def removeChild(self
, oldChild
):
891 raise xml
.dom
.NotFoundErr(
892 self
.nodeName
+ " nodes do not have children")
895 # For childless nodes, normalize() has nothing to do.
898 def replaceChild(self
, newChild
, oldChild
):
899 raise xml
.dom
.HierarchyRequestErr(
900 self
.nodeName
+ " nodes do not have children")
903 class ProcessingInstruction(Childless
, Node
):
904 nodeType
= Node
.PROCESSING_INSTRUCTION_NODE
906 def __init__(self
, target
, data
):
907 self
.target
= self
.nodeName
= target
908 self
.data
= self
.nodeValue
= data
912 def _set_data(self
, value
):
914 d
['data'] = d
['nodeValue'] = value
916 def _get_target(self
):
918 def _set_target(self
, value
):
920 d
['target'] = d
['nodeName'] = value
922 def __setattr__(self
, name
, value
):
923 if name
== "data" or name
== "nodeValue":
924 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
925 elif name
== "target" or name
== "nodeName":
926 self
.__dict
__['target'] = self
.__dict
__['nodeName'] = value
928 self
.__dict
__[name
] = value
930 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
931 writer
.write("%s<?%s %s?>%s" % (indent
,self
.target
, self
.data
, newl
))
934 class CharacterData(Childless
, Node
):
935 def _get_length(self
):
936 return len(self
.data
)
937 __len__
= _get_length
940 return self
.__dict
__['data']
941 def _set_data(self
, data
):
943 d
['data'] = d
['nodeValue'] = data
945 _get_nodeValue
= _get_data
946 _set_nodeValue
= _set_data
948 def __setattr__(self
, name
, value
):
949 if name
== "data" or name
== "nodeValue":
950 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
952 self
.__dict
__[name
] = value
960 return '<DOM %s node "%r%s">' % (
961 self
.__class
__.__name
__, data
[0:10], dotdotdot
)
963 def substringData(self
, offset
, count
):
965 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
966 if offset
>= len(self
.data
):
967 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
969 raise xml
.dom
.IndexSizeErr("count cannot be negative")
970 return self
.data
[offset
:offset
+count
]
972 def appendData(self
, arg
):
973 self
.data
= self
.data
+ arg
975 def insertData(self
, offset
, arg
):
977 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
978 if offset
>= len(self
.data
):
979 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
981 self
.data
= "%s%s%s" % (
982 self
.data
[:offset
], arg
, self
.data
[offset
:])
984 def deleteData(self
, offset
, count
):
986 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
987 if offset
>= len(self
.data
):
988 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
990 raise xml
.dom
.IndexSizeErr("count cannot be negative")
992 self
.data
= self
.data
[:offset
] + self
.data
[offset
+count
:]
994 def replaceData(self
, offset
, count
, arg
):
996 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
997 if offset
>= len(self
.data
):
998 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
1000 raise xml
.dom
.IndexSizeErr("count cannot be negative")
1002 self
.data
= "%s%s%s" % (
1003 self
.data
[:offset
], arg
, self
.data
[offset
+count
:])
1005 defproperty(CharacterData
, "length", doc
="Length of the string data.")
1008 class Text(CharacterData
):
1009 # Make sure we don't add an instance __dict__ if we don't already
1010 # have one, at least when that's possible:
1011 # XXX this does not work, CharacterData is an old-style class
1014 nodeType
= Node
.TEXT_NODE
1018 def splitText(self
, offset
):
1019 if offset
< 0 or offset
> len(self
.data
):
1020 raise xml
.dom
.IndexSizeErr("illegal offset value")
1021 newText
= self
.__class
__()
1022 newText
.data
= self
.data
[offset
:]
1023 newText
.ownerDocument
= self
.ownerDocument
1024 next
= self
.nextSibling
1025 if self
.parentNode
and self
in self
.parentNode
.childNodes
:
1027 self
.parentNode
.appendChild(newText
)
1029 self
.parentNode
.insertBefore(newText
, next
)
1030 self
.data
= self
.data
[:offset
]
1033 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1034 _write_data(writer
, "%s%s%s"%(indent
, self
.data
, newl
))
1036 # DOM Level 3 (WD 9 April 2002)
1038 def _get_wholeText(self
):
1040 n
= self
.previousSibling
1041 while n
is not None:
1042 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1044 n
= n
.previousSibling
1047 n
= self
.nextSibling
1048 while n
is not None:
1049 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1056 def replaceWholeText(self
, content
):
1057 # XXX This needs to be seriously changed if minidom ever
1058 # supports EntityReference nodes.
1059 parent
= self
.parentNode
1060 n
= self
.previousSibling
1061 while n
is not None:
1062 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1063 next
= n
.previousSibling
1064 parent
.removeChild(n
)
1068 n
= self
.nextSibling
1070 parent
.removeChild(self
)
1071 while n
is not None:
1072 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1073 next
= n
.nextSibling
1074 parent
.removeChild(n
)
1081 d
['nodeValue'] = content
1086 def _get_isWhitespaceInElementContent(self
):
1087 if self
.data
.strip():
1089 elem
= _get_containing_element(self
)
1092 info
= self
.ownerDocument
._get
_elem
_info
(elem
)
1096 return info
.isElementContent()
1098 defproperty(Text
, "isWhitespaceInElementContent",
1099 doc
="True iff this text node contains only whitespace"
1100 " and is in element content.")
1101 defproperty(Text
, "wholeText",
1102 doc
="The text of all logically-adjacent text nodes.")
1105 def _get_containing_element(node
):
1107 while c
is not None:
1108 if c
.nodeType
== Node
.ELEMENT_NODE
:
1113 def _get_containing_entref(node
):
1115 while c
is not None:
1116 if c
.nodeType
== Node
.ENTITY_REFERENCE_NODE
:
1122 class Comment(Childless
, CharacterData
):
1123 nodeType
= Node
.COMMENT_NODE
1124 nodeName
= "#comment"
1126 def __init__(self
, data
):
1127 self
.data
= self
.nodeValue
= data
1129 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1130 if "--" in self
.data
:
1131 raise ValueError("'--' is not allowed in a comment node")
1132 writer
.write("%s<!--%s-->%s" % (indent
, self
.data
, newl
))
1135 class CDATASection(Text
):
1136 # Make sure we don't add an instance __dict__ if we don't already
1137 # have one, at least when that's possible:
1138 # XXX this does not work, Text is an old-style class
1141 nodeType
= Node
.CDATA_SECTION_NODE
1142 nodeName
= "#cdata-section"
1144 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1145 if self
.data
.find("]]>") >= 0:
1146 raise ValueError("']]>' not allowed in a CDATA section")
1147 writer
.write("<![CDATA[%s]]>" % self
.data
)
1150 class ReadOnlySequentialNamedNodeMap(object):
1153 def __init__(self
, seq
=()):
1154 # seq should be a list or tuple
1158 return len(self
._seq
)
1160 def _get_length(self
):
1161 return len(self
._seq
)
1163 def getNamedItem(self
, name
):
1165 if n
.nodeName
== name
:
1168 def getNamedItemNS(self
, namespaceURI
, localName
):
1170 if n
.namespaceURI
== namespaceURI
and n
.localName
== localName
:
1173 def __getitem__(self
, name_or_tuple
):
1174 if isinstance(name_or_tuple
, tuple):
1175 node
= self
.getNamedItemNS(*name_or_tuple
)
1177 node
= self
.getNamedItem(name_or_tuple
)
1179 raise KeyError, name_or_tuple
1182 def item(self
, index
):
1186 return self
._seq
[index
]
1190 def removeNamedItem(self
, name
):
1191 raise xml
.dom
.NoModificationAllowedErr(
1192 "NamedNodeMap instance is read-only")
1194 def removeNamedItemNS(self
, namespaceURI
, localName
):
1195 raise xml
.dom
.NoModificationAllowedErr(
1196 "NamedNodeMap instance is read-only")
1198 def setNamedItem(self
, node
):
1199 raise xml
.dom
.NoModificationAllowedErr(
1200 "NamedNodeMap instance is read-only")
1202 def setNamedItemNS(self
, node
):
1203 raise xml
.dom
.NoModificationAllowedErr(
1204 "NamedNodeMap instance is read-only")
1206 def __getstate__(self
):
1209 def __setstate__(self
, state
):
1210 self
._seq
= state
[0]
1212 defproperty(ReadOnlySequentialNamedNodeMap
, "length",
1213 doc
="Number of entries in the NamedNodeMap.")
1217 """Mix-in class that supports the publicId and systemId attributes."""
1219 # XXX this does not work, this is an old-style class
1220 # __slots__ = 'publicId', 'systemId'
1222 def _identified_mixin_init(self
, publicId
, systemId
):
1223 self
.publicId
= publicId
1224 self
.systemId
= systemId
1226 def _get_publicId(self
):
1227 return self
.publicId
1229 def _get_systemId(self
):
1230 return self
.systemId
1232 class DocumentType(Identified
, Childless
, Node
):
1233 nodeType
= Node
.DOCUMENT_TYPE_NODE
1238 internalSubset
= None
1240 def __init__(self
, qualifiedName
):
1241 self
.entities
= ReadOnlySequentialNamedNodeMap()
1242 self
.notations
= ReadOnlySequentialNamedNodeMap()
1244 prefix
, localname
= _nssplit(qualifiedName
)
1245 self
.name
= localname
1246 self
.nodeName
= self
.name
1248 def _get_internalSubset(self
):
1249 return self
.internalSubset
1251 def cloneNode(self
, deep
):
1252 if self
.ownerDocument
is None:
1254 clone
= DocumentType(None)
1255 clone
.name
= self
.name
1256 clone
.nodeName
= self
.name
1257 operation
= xml
.dom
.UserDataHandler
.NODE_CLONED
1259 clone
.entities
._seq
= []
1260 clone
.notations
._seq
= []
1261 for n
in self
.notations
._seq
:
1262 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1263 clone
.notations
._seq
.append(notation
)
1264 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1265 for e
in self
.entities
._seq
:
1266 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1268 entity
.actualEncoding
= e
.actualEncoding
1269 entity
.encoding
= e
.encoding
1270 entity
.version
= e
.version
1271 clone
.entities
._seq
.append(entity
)
1272 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1273 self
._call
_user
_data
_handler
(operation
, self
, clone
)
1278 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1279 writer
.write("<!DOCTYPE ")
1280 writer
.write(self
.name
)
1282 writer
.write("%s PUBLIC '%s'%s '%s'"
1283 % (newl
, self
.publicId
, newl
, self
.systemId
))
1285 writer
.write("%s SYSTEM '%s'" % (newl
, self
.systemId
))
1286 if self
.internalSubset
is not None:
1288 writer
.write(self
.internalSubset
)
1290 writer
.write(">"+newl
)
1292 class Entity(Identified
, Node
):
1294 nodeType
= Node
.ENTITY_NODE
1297 actualEncoding
= None
1301 def __init__(self
, name
, publicId
, systemId
, notation
):
1302 self
.nodeName
= name
1303 self
.notationName
= notation
1304 self
.childNodes
= NodeList()
1305 self
._identified
_mixin
_init
(publicId
, systemId
)
1307 def _get_actualEncoding(self
):
1308 return self
.actualEncoding
1310 def _get_encoding(self
):
1311 return self
.encoding
1313 def _get_version(self
):
1316 def appendChild(self
, newChild
):
1317 raise xml
.dom
.HierarchyRequestErr(
1318 "cannot append children to an entity node")
1320 def insertBefore(self
, newChild
, refChild
):
1321 raise xml
.dom
.HierarchyRequestErr(
1322 "cannot insert children below an entity node")
1324 def removeChild(self
, oldChild
):
1325 raise xml
.dom
.HierarchyRequestErr(
1326 "cannot remove children from an entity node")
1328 def replaceChild(self
, newChild
, oldChild
):
1329 raise xml
.dom
.HierarchyRequestErr(
1330 "cannot replace children of an entity node")
1332 class Notation(Identified
, Childless
, Node
):
1333 nodeType
= Node
.NOTATION_NODE
1336 def __init__(self
, name
, publicId
, systemId
):
1337 self
.nodeName
= name
1338 self
._identified
_mixin
_init
(publicId
, systemId
)
1341 class DOMImplementation(DOMImplementationLS
):
1342 _features
= [("core", "1.0"),
1352 def hasFeature(self
, feature
, version
):
1355 return (feature
.lower(), version
) in self
._features
1357 def createDocument(self
, namespaceURI
, qualifiedName
, doctype
):
1358 if doctype
and doctype
.parentNode
is not None:
1359 raise xml
.dom
.WrongDocumentErr(
1360 "doctype object owned by another DOM tree")
1361 doc
= self
._create
_document
()
1363 add_root_element
= not (namespaceURI
is None
1364 and qualifiedName
is None
1365 and doctype
is None)
1367 if not qualifiedName
and add_root_element
:
1368 # The spec is unclear what to raise here; SyntaxErr
1369 # would be the other obvious candidate. Since Xerces raises
1370 # InvalidCharacterErr, and since SyntaxErr is not listed
1371 # for createDocument, that seems to be the better choice.
1372 # XXX: need to check for illegal characters here and in
1375 # DOM Level III clears this up when talking about the return value
1376 # of this function. If namespaceURI, qName and DocType are
1377 # Null the document is returned without a document element
1378 # Otherwise if doctype or namespaceURI are not None
1379 # Then we go back to the above problem
1380 raise xml
.dom
.InvalidCharacterErr("Element with no name")
1382 if add_root_element
:
1383 prefix
, localname
= _nssplit(qualifiedName
)
1384 if prefix
== "xml" \
1385 and namespaceURI
!= "http://www.w3.org/XML/1998/namespace":
1386 raise xml
.dom
.NamespaceErr("illegal use of 'xml' prefix")
1387 if prefix
and not namespaceURI
:
1388 raise xml
.dom
.NamespaceErr(
1389 "illegal use of prefix without namespaces")
1390 element
= doc
.createElementNS(namespaceURI
, qualifiedName
)
1392 doc
.appendChild(doctype
)
1393 doc
.appendChild(element
)
1396 doctype
.parentNode
= doctype
.ownerDocument
= doc
1398 doc
.doctype
= doctype
1399 doc
.implementation
= self
1402 def createDocumentType(self
, qualifiedName
, publicId
, systemId
):
1403 doctype
= DocumentType(qualifiedName
)
1404 doctype
.publicId
= publicId
1405 doctype
.systemId
= systemId
1408 # DOM Level 3 (WD 9 April 2002)
1410 def getInterface(self
, feature
):
1411 if self
.hasFeature(feature
, None):
1417 def _create_document(self
):
1420 class ElementInfo(object):
1421 """Object that represents content-model information for an element.
1423 This implementation is not expected to be used in practice; DOM
1424 builders should provide implementations which do the right thing
1425 using information available to it.
1429 __slots__
= 'tagName',
1431 def __init__(self
, name
):
1434 def getAttributeType(self
, aname
):
1437 def getAttributeTypeNS(self
, namespaceURI
, localName
):
1440 def isElementContent(self
):
1444 """Returns true iff this element is declared to have an EMPTY
1448 def isId(self
, aname
):
1449 """Returns true iff the named attribute is a DTD-style ID."""
1452 def isIdNS(self
, namespaceURI
, localName
):
1453 """Returns true iff the identified attribute is a DTD-style ID."""
1456 def __getstate__(self
):
1459 def __setstate__(self
, state
):
1460 self
.tagName
= state
1462 def _clear_id_cache(node
):
1463 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1464 node
._id
_cache
.clear()
1465 node
._id
_search
_stack
= None
1466 elif _in_document(node
):
1467 node
.ownerDocument
._id
_cache
.clear()
1468 node
.ownerDocument
._id
_search
_stack
= None
1470 class Document(Node
, DocumentLS
):
1471 _child_node_types
= (Node
.ELEMENT_NODE
, Node
.PROCESSING_INSTRUCTION_NODE
,
1472 Node
.COMMENT_NODE
, Node
.DOCUMENT_TYPE_NODE
)
1474 nodeType
= Node
.DOCUMENT_NODE
1475 nodeName
= "#document"
1480 previousSibling
= nextSibling
= None
1482 implementation
= DOMImplementation()
1484 # Document attributes from Level 3 (WD 9 April 2002)
1486 actualEncoding
= None
1490 strictErrorChecking
= False
1497 self
.childNodes
= NodeList()
1498 # mapping of (namespaceURI, localName) -> ElementInfo
1499 # and tagName -> ElementInfo
1500 self
._elem
_info
= {}
1502 self
._id
_search
_stack
= None
1504 def _get_elem_info(self
, element
):
1505 if element
.namespaceURI
:
1506 key
= element
.namespaceURI
, element
.localName
1508 key
= element
.tagName
1509 return self
._elem
_info
.get(key
)
1511 def _get_actualEncoding(self
):
1512 return self
.actualEncoding
1514 def _get_doctype(self
):
1517 def _get_documentURI(self
):
1518 return self
.documentURI
1520 def _get_encoding(self
):
1521 return self
.encoding
1523 def _get_errorHandler(self
):
1524 return self
.errorHandler
1526 def _get_standalone(self
):
1527 return self
.standalone
1529 def _get_strictErrorChecking(self
):
1530 return self
.strictErrorChecking
1532 def _get_version(self
):
1535 def appendChild(self
, node
):
1536 if node
.nodeType
not in self
._child
_node
_types
:
1537 raise xml
.dom
.HierarchyRequestErr(
1538 "%s cannot be child of %s" % (repr(node
), repr(self
)))
1539 if node
.parentNode
is not None:
1540 # This needs to be done before the next test since this
1541 # may *be* the document element, in which case it should
1542 # end up re-ordered to the end.
1543 node
.parentNode
.removeChild(node
)
1545 if node
.nodeType
== Node
.ELEMENT_NODE \
1546 and self
._get
_documentElement
():
1547 raise xml
.dom
.HierarchyRequestErr(
1548 "two document elements disallowed")
1549 return Node
.appendChild(self
, node
)
1551 def removeChild(self
, oldChild
):
1553 self
.childNodes
.remove(oldChild
)
1555 raise xml
.dom
.NotFoundErr()
1556 oldChild
.nextSibling
= oldChild
.previousSibling
= None
1557 oldChild
.parentNode
= None
1558 if self
.documentElement
is oldChild
:
1559 self
.documentElement
= None
1563 def _get_documentElement(self
):
1564 for node
in self
.childNodes
:
1565 if node
.nodeType
== Node
.ELEMENT_NODE
:
1569 if self
.doctype
is not None:
1570 self
.doctype
.unlink()
1574 def cloneNode(self
, deep
):
1577 clone
= self
.implementation
.createDocument(None, None, None)
1578 clone
.encoding
= self
.encoding
1579 clone
.standalone
= self
.standalone
1580 clone
.version
= self
.version
1581 for n
in self
.childNodes
:
1582 childclone
= _clone_node(n
, deep
, clone
)
1583 assert childclone
.ownerDocument
.isSameNode(clone
)
1584 clone
.childNodes
.append(childclone
)
1585 if childclone
.nodeType
== Node
.DOCUMENT_NODE
:
1586 assert clone
.documentElement
is None
1587 elif childclone
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1588 assert clone
.doctype
is None
1589 clone
.doctype
= childclone
1590 childclone
.parentNode
= clone
1591 self
._call
_user
_data
_handler
(xml
.dom
.UserDataHandler
.NODE_CLONED
,
1595 def createDocumentFragment(self
):
1596 d
= DocumentFragment()
1597 d
.ownerDocument
= self
1600 def createElement(self
, tagName
):
1601 e
= Element(tagName
)
1602 e
.ownerDocument
= self
1605 def createTextNode(self
, data
):
1606 if not isinstance(data
, StringTypes
):
1607 raise TypeError, "node contents must be a string"
1610 t
.ownerDocument
= self
1613 def createCDATASection(self
, data
):
1614 if not isinstance(data
, StringTypes
):
1615 raise TypeError, "node contents must be a string"
1618 c
.ownerDocument
= self
1621 def createComment(self
, data
):
1623 c
.ownerDocument
= self
1626 def createProcessingInstruction(self
, target
, data
):
1627 p
= ProcessingInstruction(target
, data
)
1628 p
.ownerDocument
= self
1631 def createAttribute(self
, qName
):
1633 a
.ownerDocument
= self
1637 def createElementNS(self
, namespaceURI
, qualifiedName
):
1638 prefix
, localName
= _nssplit(qualifiedName
)
1639 e
= Element(qualifiedName
, namespaceURI
, prefix
)
1640 e
.ownerDocument
= self
1643 def createAttributeNS(self
, namespaceURI
, qualifiedName
):
1644 prefix
, localName
= _nssplit(qualifiedName
)
1645 a
= Attr(qualifiedName
, namespaceURI
, localName
, prefix
)
1646 a
.ownerDocument
= self
1650 # A couple of implementation-specific helpers to create node types
1651 # not supported by the W3C DOM specs:
1653 def _create_entity(self
, name
, publicId
, systemId
, notationName
):
1654 e
= Entity(name
, publicId
, systemId
, notationName
)
1655 e
.ownerDocument
= self
1658 def _create_notation(self
, name
, publicId
, systemId
):
1659 n
= Notation(name
, publicId
, systemId
)
1660 n
.ownerDocument
= self
1663 def getElementById(self
, id):
1664 if id in self
._id
_cache
:
1665 return self
._id
_cache
[id]
1666 if not (self
._elem
_info
or self
._magic
_id
_count
):
1669 stack
= self
._id
_search
_stack
1671 # we never searched before, or the cache has been cleared
1672 stack
= [self
.documentElement
]
1673 self
._id
_search
_stack
= stack
1675 # Previous search was completed and cache is still valid;
1682 # add child elements to stack for continued searching
1683 stack
.extend([child
for child
in node
.childNodes
1684 if child
.nodeType
in _nodeTypes_with_children
])
1686 info
= self
._get
_elem
_info
(node
)
1688 # We have to process all ID attributes before
1689 # returning in order to get all the attributes set to
1690 # be IDs using Element.setIdAttribute*().
1691 for attr
in node
.attributes
.values():
1692 if attr
.namespaceURI
:
1693 if info
.isIdNS(attr
.namespaceURI
, attr
.localName
):
1694 self
._id
_cache
[attr
.value
] = node
1695 if attr
.value
== id:
1697 elif not node
._magic
_id
_nodes
:
1699 elif info
.isId(attr
.name
):
1700 self
._id
_cache
[attr
.value
] = node
1701 if attr
.value
== id:
1703 elif not node
._magic
_id
_nodes
:
1706 self
._id
_cache
[attr
.value
] = node
1707 if attr
.value
== id:
1709 elif node
._magic
_id
_nodes
== 1:
1711 elif node
._magic
_id
_nodes
:
1712 for attr
in node
.attributes
.values():
1714 self
._id
_cache
[attr
.value
] = node
1715 if attr
.value
== id:
1717 if result
is not None:
1721 def getElementsByTagName(self
, name
):
1722 return _get_elements_by_tagName_helper(self
, name
, NodeList())
1724 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
1725 return _get_elements_by_tagName_ns_helper(
1726 self
, namespaceURI
, localName
, NodeList())
1728 def isSupported(self
, feature
, version
):
1729 return self
.implementation
.hasFeature(feature
, version
)
1731 def importNode(self
, node
, deep
):
1732 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1733 raise xml
.dom
.NotSupportedErr("cannot import document nodes")
1734 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1735 raise xml
.dom
.NotSupportedErr("cannot import document type nodes")
1736 return _clone_node(node
, deep
, self
)
1738 def writexml(self
, writer
, indent
="", addindent
="", newl
="",
1740 if encoding
is None:
1741 writer
.write('<?xml version="1.0" ?>'+newl
)
1743 writer
.write('<?xml version="1.0" encoding="%s"?>%s' % (encoding
, newl
))
1744 for node
in self
.childNodes
:
1745 node
.writexml(writer
, indent
, addindent
, newl
)
1747 # DOM Level 3 (WD 9 April 2002)
1749 def renameNode(self
, n
, namespaceURI
, name
):
1750 if n
.ownerDocument
is not self
:
1751 raise xml
.dom
.WrongDocumentErr(
1752 "cannot rename nodes from other documents;\n"
1753 "expected %s,\nfound %s" % (self
, n
.ownerDocument
))
1754 if n
.nodeType
not in (Node
.ELEMENT_NODE
, Node
.ATTRIBUTE_NODE
):
1755 raise xml
.dom
.NotSupportedErr(
1756 "renameNode() only applies to element and attribute nodes")
1757 if namespaceURI
!= EMPTY_NAMESPACE
:
1759 prefix
, localName
= name
.split(':', 1)
1760 if ( prefix
== "xmlns"
1761 and namespaceURI
!= xml
.dom
.XMLNS_NAMESPACE
):
1762 raise xml
.dom
.NamespaceErr(
1763 "illegal use of 'xmlns' prefix")
1765 if ( name
== "xmlns"
1766 and namespaceURI
!= xml
.dom
.XMLNS_NAMESPACE
1767 and n
.nodeType
== Node
.ATTRIBUTE_NODE
):
1768 raise xml
.dom
.NamespaceErr(
1769 "illegal use of the 'xmlns' attribute")
1775 if n
.nodeType
== Node
.ATTRIBUTE_NODE
:
1776 element
= n
.ownerElement
1777 if element
is not None:
1779 element
.removeAttributeNode(n
)
1784 d
['prefix'] = prefix
1785 d
['localName'] = localName
1786 d
['namespaceURI'] = namespaceURI
1787 d
['nodeName'] = name
1788 if n
.nodeType
== Node
.ELEMENT_NODE
:
1793 if element
is not None:
1794 element
.setAttributeNode(n
)
1796 element
.setIdAttributeNode(n
)
1797 # It's not clear from a semantic perspective whether we should
1798 # call the user data handlers for the NODE_RENAMED event since
1799 # we're re-using the existing node. The draft spec has been
1800 # interpreted as meaning "no, don't call the handler unless a
1801 # new node is created."
1804 defproperty(Document
, "documentElement",
1805 doc
="Top-level element of this document.")
1808 def _clone_node(node
, deep
, newOwnerDocument
):
1810 Clone a node and give it the new owner document.
1811 Called by Node.cloneNode and Document.importNode
1813 if node
.ownerDocument
.isSameNode(newOwnerDocument
):
1814 operation
= xml
.dom
.UserDataHandler
.NODE_CLONED
1816 operation
= xml
.dom
.UserDataHandler
.NODE_IMPORTED
1817 if node
.nodeType
== Node
.ELEMENT_NODE
:
1818 clone
= newOwnerDocument
.createElementNS(node
.namespaceURI
,
1820 for attr
in node
.attributes
.values():
1821 clone
.setAttributeNS(attr
.namespaceURI
, attr
.nodeName
, attr
.value
)
1822 a
= clone
.getAttributeNodeNS(attr
.namespaceURI
, attr
.localName
)
1823 a
.specified
= attr
.specified
1826 for child
in node
.childNodes
:
1827 c
= _clone_node(child
, deep
, newOwnerDocument
)
1828 clone
.appendChild(c
)
1830 elif node
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1831 clone
= newOwnerDocument
.createDocumentFragment()
1833 for child
in node
.childNodes
:
1834 c
= _clone_node(child
, deep
, newOwnerDocument
)
1835 clone
.appendChild(c
)
1837 elif node
.nodeType
== Node
.TEXT_NODE
:
1838 clone
= newOwnerDocument
.createTextNode(node
.data
)
1839 elif node
.nodeType
== Node
.CDATA_SECTION_NODE
:
1840 clone
= newOwnerDocument
.createCDATASection(node
.data
)
1841 elif node
.nodeType
== Node
.PROCESSING_INSTRUCTION_NODE
:
1842 clone
= newOwnerDocument
.createProcessingInstruction(node
.target
,
1844 elif node
.nodeType
== Node
.COMMENT_NODE
:
1845 clone
= newOwnerDocument
.createComment(node
.data
)
1846 elif node
.nodeType
== Node
.ATTRIBUTE_NODE
:
1847 clone
= newOwnerDocument
.createAttributeNS(node
.namespaceURI
,
1849 clone
.specified
= True
1850 clone
.value
= node
.value
1851 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1852 assert node
.ownerDocument
is not newOwnerDocument
1853 operation
= xml
.dom
.UserDataHandler
.NODE_IMPORTED
1854 clone
= newOwnerDocument
.implementation
.createDocumentType(
1855 node
.name
, node
.publicId
, node
.systemId
)
1856 clone
.ownerDocument
= newOwnerDocument
1858 clone
.entities
._seq
= []
1859 clone
.notations
._seq
= []
1860 for n
in node
.notations
._seq
:
1861 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1862 notation
.ownerDocument
= newOwnerDocument
1863 clone
.notations
._seq
.append(notation
)
1864 if hasattr(n
, '_call_user_data_handler'):
1865 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1866 for e
in node
.entities
._seq
:
1867 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1869 entity
.actualEncoding
= e
.actualEncoding
1870 entity
.encoding
= e
.encoding
1871 entity
.version
= e
.version
1872 entity
.ownerDocument
= newOwnerDocument
1873 clone
.entities
._seq
.append(entity
)
1874 if hasattr(e
, '_call_user_data_handler'):
1875 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1877 # Note the cloning of Document and DocumentType nodes is
1878 # implementation specific. minidom handles those cases
1879 # directly in the cloneNode() methods.
1880 raise xml
.dom
.NotSupportedErr("Cannot clone node %s" % repr(node
))
1882 # Check for _call_user_data_handler() since this could conceivably
1883 # used with other DOM implementations (one of the FourThought
1885 if hasattr(node
, '_call_user_data_handler'):
1886 node
._call
_user
_data
_handler
(operation
, node
, clone
)
1890 def _nssplit(qualifiedName
):
1891 fields
= qualifiedName
.split(':', 1)
1892 if len(fields
) == 2:
1895 return (None, fields
[0])
1898 def _get_StringIO():
1899 # we can't use cStringIO since it doesn't support Unicode strings
1900 from StringIO
import StringIO
1903 def _do_pulldom_parse(func
, args
, kwargs
):
1904 events
= func(*args
, **kwargs
)
1905 toktype
, rootNode
= events
.getEvent()
1906 events
.expandNode(rootNode
)
1910 def parse(file, parser
=None, bufsize
=None):
1911 """Parse a file into a DOM by filename or file object."""
1912 if parser
is None and not bufsize
:
1913 from xml
.dom
import expatbuilder
1914 return expatbuilder
.parse(file)
1916 from xml
.dom
import pulldom
1917 return _do_pulldom_parse(pulldom
.parse
, (file,),
1918 {'parser': parser
, 'bufsize': bufsize
})
1920 def parseString(string
, parser
=None):
1921 """Parse a file into a DOM from a string."""
1923 from xml
.dom
import expatbuilder
1924 return expatbuilder
.parseString(string
)
1926 from xml
.dom
import pulldom
1927 return _do_pulldom_parse(pulldom
.parseString
, (string
,),
1930 def getDOMImplementation(features
=None):
1932 if isinstance(features
, StringTypes
):
1933 features
= domreg
._parse
_feature
_string
(features
)
1934 for f
, v
in features
:
1935 if not Document
.implementation
.hasFeature(f
, v
):
1937 return Document
.implementation