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."
294 data
= data
.replace("&", "&").replace("<", "<")
295 data
= data
.replace("\"", """).replace(">", ">")
298 def _get_elements_by_tagName_helper(parent
, name
, rc
):
299 for node
in parent
.childNodes
:
300 if node
.nodeType
== Node
.ELEMENT_NODE
and \
301 (name
== "*" or node
.tagName
== name
):
303 _get_elements_by_tagName_helper(node
, name
, rc
)
306 def _get_elements_by_tagName_ns_helper(parent
, nsURI
, localName
, rc
):
307 for node
in parent
.childNodes
:
308 if node
.nodeType
== Node
.ELEMENT_NODE
:
309 if ((localName
== "*" or node
.localName
== localName
) and
310 (nsURI
== "*" or node
.namespaceURI
== nsURI
)):
312 _get_elements_by_tagName_ns_helper(node
, nsURI
, localName
, rc
)
315 class DocumentFragment(Node
):
316 nodeType
= Node
.DOCUMENT_FRAGMENT_NODE
317 nodeName
= "#document-fragment"
321 _child_node_types
= (Node
.ELEMENT_NODE
,
323 Node
.CDATA_SECTION_NODE
,
324 Node
.ENTITY_REFERENCE_NODE
,
325 Node
.PROCESSING_INSTRUCTION_NODE
,
330 self
.childNodes
= NodeList()
334 nodeType
= Node
.ATTRIBUTE_NODE
340 _child_node_types
= (Node
.TEXT_NODE
, Node
.ENTITY_REFERENCE_NODE
)
342 def __init__(self
, qName
, namespaceURI
=EMPTY_NAMESPACE
, localName
=None,
344 # skip setattr for performance
346 d
["nodeName"] = d
["name"] = qName
347 d
["namespaceURI"] = namespaceURI
349 d
['childNodes'] = NodeList()
351 # Add the single child node that represents the value of the attr
352 self
.childNodes
.append(Text())
354 # nodeValue and value are set elsewhere
356 def _get_localName(self
):
357 return self
.nodeName
.split(":", 1)[-1]
362 def _get_specified(self
):
363 return self
.specified
365 def __setattr__(self
, name
, value
):
367 if name
in ("value", "nodeValue"):
368 d
["value"] = d
["nodeValue"] = value
369 d2
= self
.childNodes
[0].__dict
__
370 d2
["data"] = d2
["nodeValue"] = value
371 if self
.ownerElement
is not None:
372 _clear_id_cache(self
.ownerElement
)
373 elif name
in ("name", "nodeName"):
374 d
["name"] = d
["nodeName"] = value
375 if self
.ownerElement
is not None:
376 _clear_id_cache(self
.ownerElement
)
380 def _set_prefix(self
, prefix
):
381 nsuri
= self
.namespaceURI
382 if prefix
== "xmlns":
383 if nsuri
and nsuri
!= XMLNS_NAMESPACE
:
384 raise xml
.dom
.NamespaceErr(
385 "illegal use of 'xmlns' prefix for the wrong namespace")
389 newName
= self
.localName
391 newName
= "%s:%s" % (prefix
, self
.localName
)
392 if self
.ownerElement
:
393 _clear_id_cache(self
.ownerElement
)
394 d
['nodeName'] = d
['name'] = newName
396 def _set_value(self
, value
):
398 d
['value'] = d
['nodeValue'] = value
399 if self
.ownerElement
:
400 _clear_id_cache(self
.ownerElement
)
401 self
.childNodes
[0].data
= value
404 # This implementation does not call the base implementation
405 # since most of that is not needed, and the expense of the
406 # method call is not warranted. We duplicate the removal of
407 # children, but that's all we needed from the base class.
408 elem
= self
.ownerElement
410 del elem
._attrs
[self
.nodeName
]
411 del elem
._attrsNS
[(self
.namespaceURI
, self
.localName
)]
414 elem
._magic
_id
_nodes
-= 1
415 self
.ownerDocument
._magic
_id
_count
-= 1
416 for child
in self
.childNodes
:
418 del self
.childNodes
[:]
423 doc
= self
.ownerDocument
424 elem
= self
.ownerElement
425 if doc
is None or elem
is None:
428 info
= doc
._get
_elem
_info
(elem
)
431 if self
.namespaceURI
:
432 return info
.isIdNS(self
.namespaceURI
, self
.localName
)
434 return info
.isId(self
.nodeName
)
436 def _get_schemaType(self
):
437 doc
= self
.ownerDocument
438 elem
= self
.ownerElement
439 if doc
is None or elem
is None:
442 info
= doc
._get
_elem
_info
(elem
)
445 if self
.namespaceURI
:
446 return info
.getAttributeTypeNS(self
.namespaceURI
, self
.localName
)
448 return info
.getAttributeType(self
.nodeName
)
450 defproperty(Attr
, "isId", doc
="True if this attribute is an ID.")
451 defproperty(Attr
, "localName", doc
="Namespace-local name of this attribute.")
452 defproperty(Attr
, "schemaType", doc
="Schema type for this attribute.")
455 class NamedNodeMap(object):
456 """The attribute list is a transient interface to the underlying
457 dictionaries. Mutations here will change the underlying element's
460 Ordering is imposed artificially and does not reflect the order of
461 attributes as found in an input document.
464 __slots__
= ('_attrs', '_attrsNS', '_ownerElement')
466 def __init__(self
, attrs
, attrsNS
, ownerElement
):
468 self
._attrsNS
= attrsNS
469 self
._ownerElement
= ownerElement
471 def _get_length(self
):
472 return len(self
._attrs
)
474 def item(self
, index
):
476 return self
[self
._attrs
.keys()[index
]]
482 for node
in self
._attrs
.values():
483 L
.append((node
.nodeName
, node
.value
))
488 for node
in self
._attrs
.values():
489 L
.append(((node
.namespaceURI
, node
.localName
), node
.value
))
492 def has_key(self
, key
):
493 if isinstance(key
, StringTypes
):
494 return self
._attrs
.has_key(key
)
496 return self
._attrsNS
.has_key(key
)
499 return self
._attrs
.keys()
502 return self
._attrsNS
.keys()
505 return self
._attrs
.values()
507 def get(self
, name
, value
=None):
508 return self
._attrs
.get(name
, value
)
510 __len__
= _get_length
512 __hash__
= None # Mutable type can't be correctly hashed
513 def __cmp__(self
, other
):
514 if self
._attrs
is getattr(other
, "_attrs", None):
517 return cmp(id(self
), id(other
))
519 def __getitem__(self
, attname_or_tuple
):
520 if isinstance(attname_or_tuple
, tuple):
521 return self
._attrsNS
[attname_or_tuple
]
523 return self
._attrs
[attname_or_tuple
]
526 def __setitem__(self
, attname
, value
):
527 if isinstance(value
, StringTypes
):
529 node
= self
._attrs
[attname
]
532 node
.ownerDocument
= self
._ownerElement
.ownerDocument
533 self
.setNamedItem(node
)
536 if not isinstance(value
, Attr
):
537 raise TypeError, "value must be a string or Attr object"
539 self
.setNamedItem(node
)
541 def getNamedItem(self
, name
):
543 return self
._attrs
[name
]
547 def getNamedItemNS(self
, namespaceURI
, localName
):
549 return self
._attrsNS
[(namespaceURI
, localName
)]
553 def removeNamedItem(self
, name
):
554 n
= self
.getNamedItem(name
)
556 _clear_id_cache(self
._ownerElement
)
557 del self
._attrs
[n
.nodeName
]
558 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
559 if 'ownerElement' in n
.__dict
__:
560 n
.__dict
__['ownerElement'] = None
563 raise xml
.dom
.NotFoundErr()
565 def removeNamedItemNS(self
, namespaceURI
, localName
):
566 n
= self
.getNamedItemNS(namespaceURI
, localName
)
568 _clear_id_cache(self
._ownerElement
)
569 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
570 del self
._attrs
[n
.nodeName
]
571 if 'ownerElement' in n
.__dict
__:
572 n
.__dict
__['ownerElement'] = None
575 raise xml
.dom
.NotFoundErr()
577 def setNamedItem(self
, node
):
578 if not isinstance(node
, Attr
):
579 raise xml
.dom
.HierarchyRequestErr(
580 "%s cannot be child of %s" % (repr(node
), repr(self
)))
581 old
= self
._attrs
.get(node
.name
)
584 self
._attrs
[node
.name
] = node
585 self
._attrsNS
[(node
.namespaceURI
, node
.localName
)] = node
586 node
.ownerElement
= self
._ownerElement
587 _clear_id_cache(node
.ownerElement
)
590 def setNamedItemNS(self
, node
):
591 return self
.setNamedItem(node
)
593 def __delitem__(self
, attname_or_tuple
):
594 node
= self
[attname_or_tuple
]
595 _clear_id_cache(node
.ownerElement
)
598 def __getstate__(self
):
599 return self
._attrs
, self
._attrsNS
, self
._ownerElement
601 def __setstate__(self
, state
):
602 self
._attrs
, self
._attrsNS
, self
._ownerElement
= state
604 defproperty(NamedNodeMap
, "length",
605 doc
="Number of nodes in the NamedNodeMap.")
607 AttributeList
= NamedNodeMap
610 class TypeInfo(object):
611 __slots__
= 'namespace', 'name'
613 def __init__(self
, namespace
, name
):
614 self
.namespace
= namespace
619 return "<TypeInfo %r (from %r)>" % (self
.name
, self
.namespace
)
621 return "<TypeInfo %r>" % self
.name
626 def _get_namespace(self
):
627 return self
.namespace
629 _no_type
= TypeInfo(None, None)
632 nodeType
= Node
.ELEMENT_NODE
634 schemaType
= _no_type
638 _child_node_types
= (Node
.ELEMENT_NODE
,
639 Node
.PROCESSING_INSTRUCTION_NODE
,
642 Node
.CDATA_SECTION_NODE
,
643 Node
.ENTITY_REFERENCE_NODE
)
645 def __init__(self
, tagName
, namespaceURI
=EMPTY_NAMESPACE
, prefix
=None,
647 self
.tagName
= self
.nodeName
= tagName
649 self
.namespaceURI
= namespaceURI
650 self
.childNodes
= NodeList()
652 self
._attrs
= {} # attributes are double-indexed:
653 self
._attrsNS
= {} # tagName -> Attribute
654 # URI,localName -> Attribute
655 # in the future: consider lazy generation
656 # of attribute objects this is too tricky
657 # for now because of headaches with
660 def _get_localName(self
):
661 return self
.tagName
.split(":", 1)[-1]
663 def _get_tagName(self
):
667 for attr
in self
._attrs
.values():
673 def getAttribute(self
, attname
):
675 return self
._attrs
[attname
].value
679 def getAttributeNS(self
, namespaceURI
, localName
):
681 return self
._attrsNS
[(namespaceURI
, localName
)].value
685 def setAttribute(self
, attname
, value
):
686 attr
= self
.getAttributeNode(attname
)
691 d
["value"] = d
["nodeValue"] = value
692 d
["ownerDocument"] = self
.ownerDocument
693 self
.setAttributeNode(attr
)
694 elif value
!= attr
.value
:
696 d
["value"] = d
["nodeValue"] = value
698 _clear_id_cache(self
)
700 def setAttributeNS(self
, namespaceURI
, qualifiedName
, value
):
701 prefix
, localname
= _nssplit(qualifiedName
)
702 attr
= self
.getAttributeNodeNS(namespaceURI
, localname
)
705 attr
= Attr(qualifiedName
, namespaceURI
, localname
, prefix
)
708 d
["nodeName"] = qualifiedName
709 d
["value"] = d
["nodeValue"] = value
710 d
["ownerDocument"] = self
.ownerDocument
711 self
.setAttributeNode(attr
)
714 if value
!= attr
.value
:
715 d
["value"] = d
["nodeValue"] = value
717 _clear_id_cache(self
)
718 if attr
.prefix
!= prefix
:
720 d
["nodeName"] = qualifiedName
722 def getAttributeNode(self
, attrname
):
723 return self
._attrs
.get(attrname
)
725 def getAttributeNodeNS(self
, namespaceURI
, localName
):
726 return self
._attrsNS
.get((namespaceURI
, localName
))
728 def setAttributeNode(self
, attr
):
729 if attr
.ownerElement
not in (None, self
):
730 raise xml
.dom
.InuseAttributeErr("attribute node already owned")
731 old1
= self
._attrs
.get(attr
.name
, None)
733 self
.removeAttributeNode(old1
)
734 old2
= self
._attrsNS
.get((attr
.namespaceURI
, attr
.localName
), None)
735 if old2
is not None and old2
is not old1
:
736 self
.removeAttributeNode(old2
)
737 _set_attribute_node(self
, attr
)
740 # It might have already been part of this node, in which case
741 # it doesn't represent a change, and should not be returned.
746 setAttributeNodeNS
= setAttributeNode
748 def removeAttribute(self
, name
):
750 attr
= self
._attrs
[name
]
752 raise xml
.dom
.NotFoundErr()
753 self
.removeAttributeNode(attr
)
755 def removeAttributeNS(self
, namespaceURI
, localName
):
757 attr
= self
._attrsNS
[(namespaceURI
, localName
)]
759 raise xml
.dom
.NotFoundErr()
760 self
.removeAttributeNode(attr
)
762 def removeAttributeNode(self
, node
):
764 raise xml
.dom
.NotFoundErr()
766 self
._attrs
[node
.name
]
768 raise xml
.dom
.NotFoundErr()
769 _clear_id_cache(self
)
771 # Restore this since the node is still useful and otherwise
773 node
.ownerDocument
= self
.ownerDocument
775 removeAttributeNodeNS
= removeAttributeNode
777 def hasAttribute(self
, name
):
778 return self
._attrs
.has_key(name
)
780 def hasAttributeNS(self
, namespaceURI
, localName
):
781 return self
._attrsNS
.has_key((namespaceURI
, localName
))
783 def getElementsByTagName(self
, name
):
784 return _get_elements_by_tagName_helper(self
, name
, NodeList())
786 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
787 return _get_elements_by_tagName_ns_helper(
788 self
, namespaceURI
, localName
, NodeList())
791 return "<DOM Element: %s at %#x>" % (self
.tagName
, id(self
))
793 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
794 # indent = current indentation
795 # addindent = indentation to add to higher levels
796 # newl = newline string
797 writer
.write(indent
+"<" + self
.tagName
)
799 attrs
= self
._get
_attributes
()
800 a_names
= attrs
.keys()
803 for a_name
in a_names
:
804 writer
.write(" %s=\"" % a_name
)
805 _write_data(writer
, attrs
[a_name
].value
)
808 writer
.write(">%s"%(newl))
809 for node
in self
.childNodes
:
810 node
.writexml(writer
,indent
+addindent
,addindent
,newl
)
811 writer
.write("%s</%s>%s" % (indent
,self
.tagName
,newl
))
813 writer
.write("/>%s"%(newl))
815 def _get_attributes(self
):
816 return NamedNodeMap(self
._attrs
, self
._attrsNS
, self
)
818 def hasAttributes(self
):
824 # DOM Level 3 attributes, based on the 22 Oct 2002 draft
826 def setIdAttribute(self
, name
):
827 idAttr
= self
.getAttributeNode(name
)
828 self
.setIdAttributeNode(idAttr
)
830 def setIdAttributeNS(self
, namespaceURI
, localName
):
831 idAttr
= self
.getAttributeNodeNS(namespaceURI
, localName
)
832 self
.setIdAttributeNode(idAttr
)
834 def setIdAttributeNode(self
, idAttr
):
835 if idAttr
is None or not self
.isSameNode(idAttr
.ownerElement
):
836 raise xml
.dom
.NotFoundErr()
837 if _get_containing_entref(self
) is not None:
838 raise xml
.dom
.NoModificationAllowedErr()
839 if not idAttr
._is
_id
:
840 idAttr
.__dict
__['_is_id'] = True
841 self
._magic
_id
_nodes
+= 1
842 self
.ownerDocument
._magic
_id
_count
+= 1
843 _clear_id_cache(self
)
845 defproperty(Element
, "attributes",
846 doc
="NamedNodeMap of attributes on the element.")
847 defproperty(Element
, "localName",
848 doc
="Namespace-local name of this element.")
851 def _set_attribute_node(element
, attr
):
852 _clear_id_cache(element
)
853 element
._attrs
[attr
.name
] = attr
854 element
._attrsNS
[(attr
.namespaceURI
, attr
.localName
)] = attr
856 # This creates a circular reference, but Element.unlink()
857 # breaks the cycle since the references to the attribute
858 # dictionaries are tossed.
859 attr
.__dict
__['ownerElement'] = element
863 """Mixin that makes childless-ness easy to implement and avoids
864 the complexity of the Node methods that deal with children.
868 childNodes
= EmptyNodeList()
872 def _get_firstChild(self
):
875 def _get_lastChild(self
):
878 def appendChild(self
, node
):
879 raise xml
.dom
.HierarchyRequestErr(
880 self
.nodeName
+ " nodes cannot have children")
882 def hasChildNodes(self
):
885 def insertBefore(self
, newChild
, refChild
):
886 raise xml
.dom
.HierarchyRequestErr(
887 self
.nodeName
+ " nodes do not have children")
889 def removeChild(self
, oldChild
):
890 raise xml
.dom
.NotFoundErr(
891 self
.nodeName
+ " nodes do not have children")
893 def replaceChild(self
, newChild
, oldChild
):
894 raise xml
.dom
.HierarchyRequestErr(
895 self
.nodeName
+ " nodes do not have children")
898 class ProcessingInstruction(Childless
, Node
):
899 nodeType
= Node
.PROCESSING_INSTRUCTION_NODE
901 def __init__(self
, target
, data
):
902 self
.target
= self
.nodeName
= target
903 self
.data
= self
.nodeValue
= data
907 def _set_data(self
, value
):
909 d
['data'] = d
['nodeValue'] = value
911 def _get_target(self
):
913 def _set_target(self
, value
):
915 d
['target'] = d
['nodeName'] = value
917 def __setattr__(self
, name
, value
):
918 if name
== "data" or name
== "nodeValue":
919 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
920 elif name
== "target" or name
== "nodeName":
921 self
.__dict
__['target'] = self
.__dict
__['nodeName'] = value
923 self
.__dict
__[name
] = value
925 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
926 writer
.write("%s<?%s %s?>%s" % (indent
,self
.target
, self
.data
, newl
))
929 class CharacterData(Childless
, Node
):
930 def _get_length(self
):
931 return len(self
.data
)
932 __len__
= _get_length
935 return self
.__dict
__['data']
936 def _set_data(self
, data
):
938 d
['data'] = d
['nodeValue'] = data
940 _get_nodeValue
= _get_data
941 _set_nodeValue
= _set_data
943 def __setattr__(self
, name
, value
):
944 if name
== "data" or name
== "nodeValue":
945 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
947 self
.__dict
__[name
] = value
955 return '<DOM %s node "%r%s">' % (
956 self
.__class
__.__name
__, data
[0:10], dotdotdot
)
958 def substringData(self
, offset
, count
):
960 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
961 if offset
>= len(self
.data
):
962 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
964 raise xml
.dom
.IndexSizeErr("count cannot be negative")
965 return self
.data
[offset
:offset
+count
]
967 def appendData(self
, arg
):
968 self
.data
= self
.data
+ arg
970 def insertData(self
, offset
, arg
):
972 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
973 if offset
>= len(self
.data
):
974 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
976 self
.data
= "%s%s%s" % (
977 self
.data
[:offset
], arg
, self
.data
[offset
:])
979 def deleteData(self
, offset
, count
):
981 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
982 if offset
>= len(self
.data
):
983 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
985 raise xml
.dom
.IndexSizeErr("count cannot be negative")
987 self
.data
= self
.data
[:offset
] + self
.data
[offset
+count
:]
989 def replaceData(self
, offset
, count
, arg
):
991 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
992 if offset
>= len(self
.data
):
993 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
995 raise xml
.dom
.IndexSizeErr("count cannot be negative")
997 self
.data
= "%s%s%s" % (
998 self
.data
[:offset
], arg
, self
.data
[offset
+count
:])
1000 defproperty(CharacterData
, "length", doc
="Length of the string data.")
1003 class Text(CharacterData
):
1004 # Make sure we don't add an instance __dict__ if we don't already
1005 # have one, at least when that's possible:
1006 # XXX this does not work, CharacterData is an old-style class
1009 nodeType
= Node
.TEXT_NODE
1013 def splitText(self
, offset
):
1014 if offset
< 0 or offset
> len(self
.data
):
1015 raise xml
.dom
.IndexSizeErr("illegal offset value")
1016 newText
= self
.__class
__()
1017 newText
.data
= self
.data
[offset
:]
1018 newText
.ownerDocument
= self
.ownerDocument
1019 next
= self
.nextSibling
1020 if self
.parentNode
and self
in self
.parentNode
.childNodes
:
1022 self
.parentNode
.appendChild(newText
)
1024 self
.parentNode
.insertBefore(newText
, next
)
1025 self
.data
= self
.data
[:offset
]
1028 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1029 _write_data(writer
, "%s%s%s"%(indent
, self
.data
, newl
))
1031 # DOM Level 3 (WD 9 April 2002)
1033 def _get_wholeText(self
):
1035 n
= self
.previousSibling
1036 while n
is not None:
1037 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1039 n
= n
.previousSibling
1042 n
= self
.nextSibling
1043 while n
is not None:
1044 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1051 def replaceWholeText(self
, content
):
1052 # XXX This needs to be seriously changed if minidom ever
1053 # supports EntityReference nodes.
1054 parent
= self
.parentNode
1055 n
= self
.previousSibling
1056 while n
is not None:
1057 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1058 next
= n
.previousSibling
1059 parent
.removeChild(n
)
1063 n
= self
.nextSibling
1065 parent
.removeChild(self
)
1066 while n
is not None:
1067 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1068 next
= n
.nextSibling
1069 parent
.removeChild(n
)
1076 d
['nodeValue'] = content
1081 def _get_isWhitespaceInElementContent(self
):
1082 if self
.data
.strip():
1084 elem
= _get_containing_element(self
)
1087 info
= self
.ownerDocument
._get
_elem
_info
(elem
)
1091 return info
.isElementContent()
1093 defproperty(Text
, "isWhitespaceInElementContent",
1094 doc
="True iff this text node contains only whitespace"
1095 " and is in element content.")
1096 defproperty(Text
, "wholeText",
1097 doc
="The text of all logically-adjacent text nodes.")
1100 def _get_containing_element(node
):
1102 while c
is not None:
1103 if c
.nodeType
== Node
.ELEMENT_NODE
:
1108 def _get_containing_entref(node
):
1110 while c
is not None:
1111 if c
.nodeType
== Node
.ENTITY_REFERENCE_NODE
:
1117 class Comment(Childless
, CharacterData
):
1118 nodeType
= Node
.COMMENT_NODE
1119 nodeName
= "#comment"
1121 def __init__(self
, data
):
1122 self
.data
= self
.nodeValue
= data
1124 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1125 if "--" in self
.data
:
1126 raise ValueError("'--' is not allowed in a comment node")
1127 writer
.write("%s<!--%s-->%s" % (indent
, self
.data
, newl
))
1130 class CDATASection(Text
):
1131 # Make sure we don't add an instance __dict__ if we don't already
1132 # have one, at least when that's possible:
1133 # XXX this does not work, Text is an old-style class
1136 nodeType
= Node
.CDATA_SECTION_NODE
1137 nodeName
= "#cdata-section"
1139 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1140 if self
.data
.find("]]>") >= 0:
1141 raise ValueError("']]>' not allowed in a CDATA section")
1142 writer
.write("<![CDATA[%s]]>" % self
.data
)
1145 class ReadOnlySequentialNamedNodeMap(object):
1148 def __init__(self
, seq
=()):
1149 # seq should be a list or tuple
1153 return len(self
._seq
)
1155 def _get_length(self
):
1156 return len(self
._seq
)
1158 def getNamedItem(self
, name
):
1160 if n
.nodeName
== name
:
1163 def getNamedItemNS(self
, namespaceURI
, localName
):
1165 if n
.namespaceURI
== namespaceURI
and n
.localName
== localName
:
1168 def __getitem__(self
, name_or_tuple
):
1169 if isinstance(name_or_tuple
, tuple):
1170 node
= self
.getNamedItemNS(*name_or_tuple
)
1172 node
= self
.getNamedItem(name_or_tuple
)
1174 raise KeyError, name_or_tuple
1177 def item(self
, index
):
1181 return self
._seq
[index
]
1185 def removeNamedItem(self
, name
):
1186 raise xml
.dom
.NoModificationAllowedErr(
1187 "NamedNodeMap instance is read-only")
1189 def removeNamedItemNS(self
, namespaceURI
, localName
):
1190 raise xml
.dom
.NoModificationAllowedErr(
1191 "NamedNodeMap instance is read-only")
1193 def setNamedItem(self
, node
):
1194 raise xml
.dom
.NoModificationAllowedErr(
1195 "NamedNodeMap instance is read-only")
1197 def setNamedItemNS(self
, node
):
1198 raise xml
.dom
.NoModificationAllowedErr(
1199 "NamedNodeMap instance is read-only")
1201 def __getstate__(self
):
1204 def __setstate__(self
, state
):
1205 self
._seq
= state
[0]
1207 defproperty(ReadOnlySequentialNamedNodeMap
, "length",
1208 doc
="Number of entries in the NamedNodeMap.")
1212 """Mix-in class that supports the publicId and systemId attributes."""
1214 # XXX this does not work, this is an old-style class
1215 # __slots__ = 'publicId', 'systemId'
1217 def _identified_mixin_init(self
, publicId
, systemId
):
1218 self
.publicId
= publicId
1219 self
.systemId
= systemId
1221 def _get_publicId(self
):
1222 return self
.publicId
1224 def _get_systemId(self
):
1225 return self
.systemId
1227 class DocumentType(Identified
, Childless
, Node
):
1228 nodeType
= Node
.DOCUMENT_TYPE_NODE
1233 internalSubset
= None
1235 def __init__(self
, qualifiedName
):
1236 self
.entities
= ReadOnlySequentialNamedNodeMap()
1237 self
.notations
= ReadOnlySequentialNamedNodeMap()
1239 prefix
, localname
= _nssplit(qualifiedName
)
1240 self
.name
= localname
1241 self
.nodeName
= self
.name
1243 def _get_internalSubset(self
):
1244 return self
.internalSubset
1246 def cloneNode(self
, deep
):
1247 if self
.ownerDocument
is None:
1249 clone
= DocumentType(None)
1250 clone
.name
= self
.name
1251 clone
.nodeName
= self
.name
1252 operation
= xml
.dom
.UserDataHandler
.NODE_CLONED
1254 clone
.entities
._seq
= []
1255 clone
.notations
._seq
= []
1256 for n
in self
.notations
._seq
:
1257 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1258 clone
.notations
._seq
.append(notation
)
1259 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1260 for e
in self
.entities
._seq
:
1261 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1263 entity
.actualEncoding
= e
.actualEncoding
1264 entity
.encoding
= e
.encoding
1265 entity
.version
= e
.version
1266 clone
.entities
._seq
.append(entity
)
1267 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1268 self
._call
_user
_data
_handler
(operation
, self
, clone
)
1273 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1274 writer
.write("<!DOCTYPE ")
1275 writer
.write(self
.name
)
1277 writer
.write("%s PUBLIC '%s'%s '%s'"
1278 % (newl
, self
.publicId
, newl
, self
.systemId
))
1280 writer
.write("%s SYSTEM '%s'" % (newl
, self
.systemId
))
1281 if self
.internalSubset
is not None:
1283 writer
.write(self
.internalSubset
)
1285 writer
.write(">"+newl
)
1287 class Entity(Identified
, Node
):
1289 nodeType
= Node
.ENTITY_NODE
1292 actualEncoding
= None
1296 def __init__(self
, name
, publicId
, systemId
, notation
):
1297 self
.nodeName
= name
1298 self
.notationName
= notation
1299 self
.childNodes
= NodeList()
1300 self
._identified
_mixin
_init
(publicId
, systemId
)
1302 def _get_actualEncoding(self
):
1303 return self
.actualEncoding
1305 def _get_encoding(self
):
1306 return self
.encoding
1308 def _get_version(self
):
1311 def appendChild(self
, newChild
):
1312 raise xml
.dom
.HierarchyRequestErr(
1313 "cannot append children to an entity node")
1315 def insertBefore(self
, newChild
, refChild
):
1316 raise xml
.dom
.HierarchyRequestErr(
1317 "cannot insert children below an entity node")
1319 def removeChild(self
, oldChild
):
1320 raise xml
.dom
.HierarchyRequestErr(
1321 "cannot remove children from an entity node")
1323 def replaceChild(self
, newChild
, oldChild
):
1324 raise xml
.dom
.HierarchyRequestErr(
1325 "cannot replace children of an entity node")
1327 class Notation(Identified
, Childless
, Node
):
1328 nodeType
= Node
.NOTATION_NODE
1331 def __init__(self
, name
, publicId
, systemId
):
1332 self
.nodeName
= name
1333 self
._identified
_mixin
_init
(publicId
, systemId
)
1336 class DOMImplementation(DOMImplementationLS
):
1337 _features
= [("core", "1.0"),
1349 def hasFeature(self
, feature
, version
):
1352 return (feature
.lower(), version
) in self
._features
1354 def createDocument(self
, namespaceURI
, qualifiedName
, doctype
):
1355 if doctype
and doctype
.parentNode
is not None:
1356 raise xml
.dom
.WrongDocumentErr(
1357 "doctype object owned by another DOM tree")
1358 doc
= self
._create
_document
()
1360 add_root_element
= not (namespaceURI
is None
1361 and qualifiedName
is None
1362 and doctype
is None)
1364 if not qualifiedName
and add_root_element
:
1365 # The spec is unclear what to raise here; SyntaxErr
1366 # would be the other obvious candidate. Since Xerces raises
1367 # InvalidCharacterErr, and since SyntaxErr is not listed
1368 # for createDocument, that seems to be the better choice.
1369 # XXX: need to check for illegal characters here and in
1372 # DOM Level III clears this up when talking about the return value
1373 # of this function. If namespaceURI, qName and DocType are
1374 # Null the document is returned without a document element
1375 # Otherwise if doctype or namespaceURI are not None
1376 # Then we go back to the above problem
1377 raise xml
.dom
.InvalidCharacterErr("Element with no name")
1379 if add_root_element
:
1380 prefix
, localname
= _nssplit(qualifiedName
)
1381 if prefix
== "xml" \
1382 and namespaceURI
!= "http://www.w3.org/XML/1998/namespace":
1383 raise xml
.dom
.NamespaceErr("illegal use of 'xml' prefix")
1384 if prefix
and not namespaceURI
:
1385 raise xml
.dom
.NamespaceErr(
1386 "illegal use of prefix without namespaces")
1387 element
= doc
.createElementNS(namespaceURI
, qualifiedName
)
1389 doc
.appendChild(doctype
)
1390 doc
.appendChild(element
)
1393 doctype
.parentNode
= doctype
.ownerDocument
= doc
1395 doc
.doctype
= doctype
1396 doc
.implementation
= self
1399 def createDocumentType(self
, qualifiedName
, publicId
, systemId
):
1400 doctype
= DocumentType(qualifiedName
)
1401 doctype
.publicId
= publicId
1402 doctype
.systemId
= systemId
1405 # DOM Level 3 (WD 9 April 2002)
1407 def getInterface(self
, feature
):
1408 if self
.hasFeature(feature
, None):
1414 def _create_document(self
):
1417 class ElementInfo(object):
1418 """Object that represents content-model information for an element.
1420 This implementation is not expected to be used in practice; DOM
1421 builders should provide implementations which do the right thing
1422 using information available to it.
1426 __slots__
= 'tagName',
1428 def __init__(self
, name
):
1431 def getAttributeType(self
, aname
):
1434 def getAttributeTypeNS(self
, namespaceURI
, localName
):
1437 def isElementContent(self
):
1441 """Returns true iff this element is declared to have an EMPTY
1445 def isId(self
, aname
):
1446 """Returns true iff the named attribte is a DTD-style ID."""
1449 def isIdNS(self
, namespaceURI
, localName
):
1450 """Returns true iff the identified attribute is a DTD-style ID."""
1453 def __getstate__(self
):
1456 def __setstate__(self
, state
):
1457 self
.tagName
= state
1459 def _clear_id_cache(node
):
1460 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1461 node
._id
_cache
.clear()
1462 node
._id
_search
_stack
= None
1463 elif _in_document(node
):
1464 node
.ownerDocument
._id
_cache
.clear()
1465 node
.ownerDocument
._id
_search
_stack
= None
1467 class Document(Node
, DocumentLS
):
1468 _child_node_types
= (Node
.ELEMENT_NODE
, Node
.PROCESSING_INSTRUCTION_NODE
,
1469 Node
.COMMENT_NODE
, Node
.DOCUMENT_TYPE_NODE
)
1471 nodeType
= Node
.DOCUMENT_NODE
1472 nodeName
= "#document"
1477 previousSibling
= nextSibling
= None
1479 implementation
= DOMImplementation()
1481 # Document attributes from Level 3 (WD 9 April 2002)
1483 actualEncoding
= None
1487 strictErrorChecking
= False
1494 self
.childNodes
= NodeList()
1495 # mapping of (namespaceURI, localName) -> ElementInfo
1496 # and tagName -> ElementInfo
1497 self
._elem
_info
= {}
1499 self
._id
_search
_stack
= None
1501 def _get_elem_info(self
, element
):
1502 if element
.namespaceURI
:
1503 key
= element
.namespaceURI
, element
.localName
1505 key
= element
.tagName
1506 return self
._elem
_info
.get(key
)
1508 def _get_actualEncoding(self
):
1509 return self
.actualEncoding
1511 def _get_doctype(self
):
1514 def _get_documentURI(self
):
1515 return self
.documentURI
1517 def _get_encoding(self
):
1518 return self
.encoding
1520 def _get_errorHandler(self
):
1521 return self
.errorHandler
1523 def _get_standalone(self
):
1524 return self
.standalone
1526 def _get_strictErrorChecking(self
):
1527 return self
.strictErrorChecking
1529 def _get_version(self
):
1532 def appendChild(self
, node
):
1533 if node
.nodeType
not in self
._child
_node
_types
:
1534 raise xml
.dom
.HierarchyRequestErr(
1535 "%s cannot be child of %s" % (repr(node
), repr(self
)))
1536 if node
.parentNode
is not None:
1537 # This needs to be done before the next test since this
1538 # may *be* the document element, in which case it should
1539 # end up re-ordered to the end.
1540 node
.parentNode
.removeChild(node
)
1542 if node
.nodeType
== Node
.ELEMENT_NODE \
1543 and self
._get
_documentElement
():
1544 raise xml
.dom
.HierarchyRequestErr(
1545 "two document elements disallowed")
1546 return Node
.appendChild(self
, node
)
1548 def removeChild(self
, oldChild
):
1550 self
.childNodes
.remove(oldChild
)
1552 raise xml
.dom
.NotFoundErr()
1553 oldChild
.nextSibling
= oldChild
.previousSibling
= None
1554 oldChild
.parentNode
= None
1555 if self
.documentElement
is oldChild
:
1556 self
.documentElement
= None
1560 def _get_documentElement(self
):
1561 for node
in self
.childNodes
:
1562 if node
.nodeType
== Node
.ELEMENT_NODE
:
1566 if self
.doctype
is not None:
1567 self
.doctype
.unlink()
1571 def cloneNode(self
, deep
):
1574 clone
= self
.implementation
.createDocument(None, None, None)
1575 clone
.encoding
= self
.encoding
1576 clone
.standalone
= self
.standalone
1577 clone
.version
= self
.version
1578 for n
in self
.childNodes
:
1579 childclone
= _clone_node(n
, deep
, clone
)
1580 assert childclone
.ownerDocument
.isSameNode(clone
)
1581 clone
.childNodes
.append(childclone
)
1582 if childclone
.nodeType
== Node
.DOCUMENT_NODE
:
1583 assert clone
.documentElement
is None
1584 elif childclone
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1585 assert clone
.doctype
is None
1586 clone
.doctype
= childclone
1587 childclone
.parentNode
= clone
1588 self
._call
_user
_data
_handler
(xml
.dom
.UserDataHandler
.NODE_CLONED
,
1592 def createDocumentFragment(self
):
1593 d
= DocumentFragment()
1594 d
.ownerDocument
= self
1597 def createElement(self
, tagName
):
1598 e
= Element(tagName
)
1599 e
.ownerDocument
= self
1602 def createTextNode(self
, data
):
1603 if not isinstance(data
, StringTypes
):
1604 raise TypeError, "node contents must be a string"
1607 t
.ownerDocument
= self
1610 def createCDATASection(self
, data
):
1611 if not isinstance(data
, StringTypes
):
1612 raise TypeError, "node contents must be a string"
1615 c
.ownerDocument
= self
1618 def createComment(self
, data
):
1620 c
.ownerDocument
= self
1623 def createProcessingInstruction(self
, target
, data
):
1624 p
= ProcessingInstruction(target
, data
)
1625 p
.ownerDocument
= self
1628 def createAttribute(self
, qName
):
1630 a
.ownerDocument
= self
1634 def createElementNS(self
, namespaceURI
, qualifiedName
):
1635 prefix
, localName
= _nssplit(qualifiedName
)
1636 e
= Element(qualifiedName
, namespaceURI
, prefix
)
1637 e
.ownerDocument
= self
1640 def createAttributeNS(self
, namespaceURI
, qualifiedName
):
1641 prefix
, localName
= _nssplit(qualifiedName
)
1642 a
= Attr(qualifiedName
, namespaceURI
, localName
, prefix
)
1643 a
.ownerDocument
= self
1647 # A couple of implementation-specific helpers to create node types
1648 # not supported by the W3C DOM specs:
1650 def _create_entity(self
, name
, publicId
, systemId
, notationName
):
1651 e
= Entity(name
, publicId
, systemId
, notationName
)
1652 e
.ownerDocument
= self
1655 def _create_notation(self
, name
, publicId
, systemId
):
1656 n
= Notation(name
, publicId
, systemId
)
1657 n
.ownerDocument
= self
1660 def getElementById(self
, id):
1661 if id in self
._id
_cache
:
1662 return self
._id
_cache
[id]
1663 if not (self
._elem
_info
or self
._magic
_id
_count
):
1666 stack
= self
._id
_search
_stack
1668 # we never searched before, or the cache has been cleared
1669 stack
= [self
.documentElement
]
1670 self
._id
_search
_stack
= stack
1672 # Previous search was completed and cache is still valid;
1679 # add child elements to stack for continued searching
1680 stack
.extend([child
for child
in node
.childNodes
1681 if child
.nodeType
in _nodeTypes_with_children
])
1683 info
= self
._get
_elem
_info
(node
)
1685 # We have to process all ID attributes before
1686 # returning in order to get all the attributes set to
1687 # be IDs using Element.setIdAttribute*().
1688 for attr
in node
.attributes
.values():
1689 if attr
.namespaceURI
:
1690 if info
.isIdNS(attr
.namespaceURI
, attr
.localName
):
1691 self
._id
_cache
[attr
.value
] = node
1692 if attr
.value
== id:
1694 elif not node
._magic
_id
_nodes
:
1696 elif info
.isId(attr
.name
):
1697 self
._id
_cache
[attr
.value
] = node
1698 if attr
.value
== id:
1700 elif not node
._magic
_id
_nodes
:
1703 self
._id
_cache
[attr
.value
] = node
1704 if attr
.value
== id:
1706 elif node
._magic
_id
_nodes
== 1:
1708 elif node
._magic
_id
_nodes
:
1709 for attr
in node
.attributes
.values():
1711 self
._id
_cache
[attr
.value
] = node
1712 if attr
.value
== id:
1714 if result
is not None:
1718 def getElementsByTagName(self
, name
):
1719 return _get_elements_by_tagName_helper(self
, name
, NodeList())
1721 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
1722 return _get_elements_by_tagName_ns_helper(
1723 self
, namespaceURI
, localName
, NodeList())
1725 def isSupported(self
, feature
, version
):
1726 return self
.implementation
.hasFeature(feature
, version
)
1728 def importNode(self
, node
, deep
):
1729 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1730 raise xml
.dom
.NotSupportedErr("cannot import document nodes")
1731 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1732 raise xml
.dom
.NotSupportedErr("cannot import document type nodes")
1733 return _clone_node(node
, deep
, self
)
1735 def writexml(self
, writer
, indent
="", addindent
="", newl
="",
1737 if encoding
is None:
1738 writer
.write('<?xml version="1.0" ?>'+newl
)
1740 writer
.write('<?xml version="1.0" encoding="%s"?>%s' % (encoding
, newl
))
1741 for node
in self
.childNodes
:
1742 node
.writexml(writer
, indent
, addindent
, newl
)
1744 # DOM Level 3 (WD 9 April 2002)
1746 def renameNode(self
, n
, namespaceURI
, name
):
1747 if n
.ownerDocument
is not self
:
1748 raise xml
.dom
.WrongDocumentErr(
1749 "cannot rename nodes from other documents;\n"
1750 "expected %s,\nfound %s" % (self
, n
.ownerDocument
))
1751 if n
.nodeType
not in (Node
.ELEMENT_NODE
, Node
.ATTRIBUTE_NODE
):
1752 raise xml
.dom
.NotSupportedErr(
1753 "renameNode() only applies to element and attribute nodes")
1754 if namespaceURI
!= EMPTY_NAMESPACE
:
1756 prefix
, localName
= name
.split(':', 1)
1757 if ( prefix
== "xmlns"
1758 and namespaceURI
!= xml
.dom
.XMLNS_NAMESPACE
):
1759 raise xml
.dom
.NamespaceErr(
1760 "illegal use of 'xmlns' prefix")
1762 if ( name
== "xmlns"
1763 and namespaceURI
!= xml
.dom
.XMLNS_NAMESPACE
1764 and n
.nodeType
== Node
.ATTRIBUTE_NODE
):
1765 raise xml
.dom
.NamespaceErr(
1766 "illegal use of the 'xmlns' attribute")
1772 if n
.nodeType
== Node
.ATTRIBUTE_NODE
:
1773 element
= n
.ownerElement
1774 if element
is not None:
1776 element
.removeAttributeNode(n
)
1781 d
['prefix'] = prefix
1782 d
['localName'] = localName
1783 d
['namespaceURI'] = namespaceURI
1784 d
['nodeName'] = name
1785 if n
.nodeType
== Node
.ELEMENT_NODE
:
1790 if element
is not None:
1791 element
.setAttributeNode(n
)
1793 element
.setIdAttributeNode(n
)
1794 # It's not clear from a semantic perspective whether we should
1795 # call the user data handlers for the NODE_RENAMED event since
1796 # we're re-using the existing node. The draft spec has been
1797 # interpreted as meaning "no, don't call the handler unless a
1798 # new node is created."
1801 defproperty(Document
, "documentElement",
1802 doc
="Top-level element of this document.")
1805 def _clone_node(node
, deep
, newOwnerDocument
):
1807 Clone a node and give it the new owner document.
1808 Called by Node.cloneNode and Document.importNode
1810 if node
.ownerDocument
.isSameNode(newOwnerDocument
):
1811 operation
= xml
.dom
.UserDataHandler
.NODE_CLONED
1813 operation
= xml
.dom
.UserDataHandler
.NODE_IMPORTED
1814 if node
.nodeType
== Node
.ELEMENT_NODE
:
1815 clone
= newOwnerDocument
.createElementNS(node
.namespaceURI
,
1817 for attr
in node
.attributes
.values():
1818 clone
.setAttributeNS(attr
.namespaceURI
, attr
.nodeName
, attr
.value
)
1819 a
= clone
.getAttributeNodeNS(attr
.namespaceURI
, attr
.localName
)
1820 a
.specified
= attr
.specified
1823 for child
in node
.childNodes
:
1824 c
= _clone_node(child
, deep
, newOwnerDocument
)
1825 clone
.appendChild(c
)
1827 elif node
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1828 clone
= newOwnerDocument
.createDocumentFragment()
1830 for child
in node
.childNodes
:
1831 c
= _clone_node(child
, deep
, newOwnerDocument
)
1832 clone
.appendChild(c
)
1834 elif node
.nodeType
== Node
.TEXT_NODE
:
1835 clone
= newOwnerDocument
.createTextNode(node
.data
)
1836 elif node
.nodeType
== Node
.CDATA_SECTION_NODE
:
1837 clone
= newOwnerDocument
.createCDATASection(node
.data
)
1838 elif node
.nodeType
== Node
.PROCESSING_INSTRUCTION_NODE
:
1839 clone
= newOwnerDocument
.createProcessingInstruction(node
.target
,
1841 elif node
.nodeType
== Node
.COMMENT_NODE
:
1842 clone
= newOwnerDocument
.createComment(node
.data
)
1843 elif node
.nodeType
== Node
.ATTRIBUTE_NODE
:
1844 clone
= newOwnerDocument
.createAttributeNS(node
.namespaceURI
,
1846 clone
.specified
= True
1847 clone
.value
= node
.value
1848 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1849 assert node
.ownerDocument
is not newOwnerDocument
1850 operation
= xml
.dom
.UserDataHandler
.NODE_IMPORTED
1851 clone
= newOwnerDocument
.implementation
.createDocumentType(
1852 node
.name
, node
.publicId
, node
.systemId
)
1853 clone
.ownerDocument
= newOwnerDocument
1855 clone
.entities
._seq
= []
1856 clone
.notations
._seq
= []
1857 for n
in node
.notations
._seq
:
1858 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1859 notation
.ownerDocument
= newOwnerDocument
1860 clone
.notations
._seq
.append(notation
)
1861 if hasattr(n
, '_call_user_data_handler'):
1862 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1863 for e
in node
.entities
._seq
:
1864 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1866 entity
.actualEncoding
= e
.actualEncoding
1867 entity
.encoding
= e
.encoding
1868 entity
.version
= e
.version
1869 entity
.ownerDocument
= newOwnerDocument
1870 clone
.entities
._seq
.append(entity
)
1871 if hasattr(e
, '_call_user_data_handler'):
1872 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1874 # Note the cloning of Document and DocumentType nodes is
1875 # implemenetation specific. minidom handles those cases
1876 # directly in the cloneNode() methods.
1877 raise xml
.dom
.NotSupportedErr("Cannot clone node %s" % repr(node
))
1879 # Check for _call_user_data_handler() since this could conceivably
1880 # used with other DOM implementations (one of the FourThought
1882 if hasattr(node
, '_call_user_data_handler'):
1883 node
._call
_user
_data
_handler
(operation
, node
, clone
)
1887 def _nssplit(qualifiedName
):
1888 fields
= qualifiedName
.split(':', 1)
1889 if len(fields
) == 2:
1892 return (None, fields
[0])
1895 def _get_StringIO():
1896 # we can't use cStringIO since it doesn't support Unicode strings
1897 from StringIO
import StringIO
1900 def _do_pulldom_parse(func
, args
, kwargs
):
1901 events
= func(*args
, **kwargs
)
1902 toktype
, rootNode
= events
.getEvent()
1903 events
.expandNode(rootNode
)
1907 def parse(file, parser
=None, bufsize
=None):
1908 """Parse a file into a DOM by filename or file object."""
1909 if parser
is None and not bufsize
:
1910 from xml
.dom
import expatbuilder
1911 return expatbuilder
.parse(file)
1913 from xml
.dom
import pulldom
1914 return _do_pulldom_parse(pulldom
.parse
, (file,),
1915 {'parser': parser
, 'bufsize': bufsize
})
1917 def parseString(string
, parser
=None):
1918 """Parse a file into a DOM from a string."""
1920 from xml
.dom
import expatbuilder
1921 return expatbuilder
.parseString(string
)
1923 from xml
.dom
import pulldom
1924 return _do_pulldom_parse(pulldom
.parseString
, (string
,),
1927 def getDOMImplementation(features
=None):
1929 if isinstance(features
, StringTypes
):
1930 features
= domreg
._parse
_feature
_string
(features
)
1931 for f
, v
in features
:
1932 if not Document
.implementation
.hasFeature(f
, v
):
1934 return Document
.implementation