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
25 # This is used by the ID-cache invalidation checks; the list isn't
26 # actually complete, since the nodes being checked will never be the
27 # DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE. (The node being checked is
28 # the node being added or removed, not the node being modified.)
30 _nodeTypes_with_children
= (xml
.dom
.Node
.ELEMENT_NODE
,
31 xml
.dom
.Node
.ENTITY_REFERENCE_NODE
)
34 class Node(xml
.dom
.Node
, GetattrMagic
):
35 namespaceURI
= None # this is non-null only for elements and attributes
39 previousSibling
= None
41 prefix
= EMPTY_PREFIX
# non-null only for NS elements and attributes
43 def __nonzero__(self
):
46 def toxml(self
, encoding
= None):
47 return self
.toprettyxml("", "", encoding
)
49 def toprettyxml(self
, indent
="\t", newl
="\n", encoding
= None):
50 # indent = the indentation string to prepend, per level
51 # newl = the newline string to append
52 writer
= _get_StringIO()
53 if encoding
is not None:
55 # Can't use codecs.getwriter to preserve 2.0 compatibility
56 writer
= codecs
.lookup(encoding
)[3](writer
)
57 if self
.nodeType
== Node
.DOCUMENT_NODE
:
58 # Can pass encoding only to document, to put it into XML header
59 self
.writexml(writer
, "", indent
, newl
, encoding
)
61 self
.writexml(writer
, "", indent
, newl
)
62 return writer
.getvalue()
64 def hasChildNodes(self
):
70 def _get_childNodes(self
):
71 return self
.childNodes
73 def _get_firstChild(self
):
75 return self
.childNodes
[0]
77 def _get_lastChild(self
):
79 return self
.childNodes
[-1]
81 def insertBefore(self
, newChild
, refChild
):
82 if newChild
.nodeType
== self
.DOCUMENT_FRAGMENT_NODE
:
83 for c
in tuple(newChild
.childNodes
):
84 self
.insertBefore(c
, refChild
)
85 ### The DOM does not clearly specify what to return in this case
87 if newChild
.nodeType
not in self
._child
_node
_types
:
88 raise xml
.dom
.HierarchyRequestErr(
89 "%s cannot be child of %s" % (repr(newChild
), repr(self
)))
90 if newChild
.parentNode
is not None:
91 newChild
.parentNode
.removeChild(newChild
)
93 self
.appendChild(newChild
)
96 index
= self
.childNodes
.index(refChild
)
98 raise xml
.dom
.NotFoundErr()
99 if newChild
.nodeType
in _nodeTypes_with_children
:
100 _clear_id_cache(self
)
101 self
.childNodes
.insert(index
, newChild
)
102 newChild
.nextSibling
= refChild
103 refChild
.previousSibling
= newChild
105 node
= self
.childNodes
[index
-1]
106 node
.nextSibling
= newChild
107 newChild
.previousSibling
= node
109 newChild
.previousSibling
= None
110 newChild
.parentNode
= self
113 def appendChild(self
, node
):
114 if node
.nodeType
== self
.DOCUMENT_FRAGMENT_NODE
:
115 for c
in tuple(node
.childNodes
):
117 ### The DOM does not clearly specify what to return in this case
119 if node
.nodeType
not in self
._child
_node
_types
:
120 raise xml
.dom
.HierarchyRequestErr(
121 "%s cannot be child of %s" % (repr(node
), repr(self
)))
122 elif node
.nodeType
in _nodeTypes_with_children
:
123 _clear_id_cache(self
)
124 if node
.parentNode
is not None:
125 node
.parentNode
.removeChild(node
)
126 _append_child(self
, node
)
127 node
.nextSibling
= None
130 def replaceChild(self
, newChild
, oldChild
):
131 if newChild
.nodeType
== self
.DOCUMENT_FRAGMENT_NODE
:
132 refChild
= oldChild
.nextSibling
133 self
.removeChild(oldChild
)
134 return self
.insertBefore(newChild
, refChild
)
135 if newChild
.nodeType
not in self
._child
_node
_types
:
136 raise xml
.dom
.HierarchyRequestErr(
137 "%s cannot be child of %s" % (repr(newChild
), repr(self
)))
138 if newChild
.parentNode
is not None:
139 newChild
.parentNode
.removeChild(newChild
)
140 if newChild
is oldChild
:
143 index
= self
.childNodes
.index(oldChild
)
145 raise xml
.dom
.NotFoundErr()
146 self
.childNodes
[index
] = newChild
147 newChild
.parentNode
= self
148 oldChild
.parentNode
= None
149 if (newChild
.nodeType
in _nodeTypes_with_children
150 or oldChild
.nodeType
in _nodeTypes_with_children
):
151 _clear_id_cache(self
)
152 newChild
.nextSibling
= oldChild
.nextSibling
153 newChild
.previousSibling
= oldChild
.previousSibling
154 oldChild
.nextSibling
= None
155 oldChild
.previousSibling
= None
156 if newChild
.previousSibling
:
157 newChild
.previousSibling
.nextSibling
= newChild
158 if newChild
.nextSibling
:
159 newChild
.nextSibling
.previousSibling
= newChild
162 def removeChild(self
, oldChild
):
164 self
.childNodes
.remove(oldChild
)
166 raise xml
.dom
.NotFoundErr()
167 if oldChild
.nextSibling
is not None:
168 oldChild
.nextSibling
.previousSibling
= oldChild
.previousSibling
169 if oldChild
.previousSibling
is not None:
170 oldChild
.previousSibling
.nextSibling
= oldChild
.nextSibling
171 oldChild
.nextSibling
= oldChild
.previousSibling
= None
172 if oldChild
.nodeType
in _nodeTypes_with_children
:
173 _clear_id_cache(self
)
175 oldChild
.parentNode
= None
180 for child
in self
.childNodes
:
181 if child
.nodeType
== Node
.TEXT_NODE
:
183 if data
and L
and L
[-1].nodeType
== child
.nodeType
:
186 node
.data
= node
.data
+ child
.data
187 node
.nextSibling
= child
.nextSibling
191 L
[-1].nextSibling
= child
192 child
.previousSibling
= L
[-1]
194 child
.previousSibling
= None
197 # empty text node; discard
201 L
[-1].nextSibling
= child
202 child
.previousSibling
= L
[-1]
204 child
.previousSibling
= None
206 if child
.nodeType
== Node
.ELEMENT_NODE
:
208 self
.childNodes
[:] = L
210 def cloneNode(self
, deep
):
211 return _clone_node(self
, deep
, self
.ownerDocument
or self
)
213 def isSupported(self
, feature
, version
):
214 return self
.ownerDocument
.implementation
.hasFeature(feature
, version
)
216 def _get_localName(self
):
217 # Overridden in Element and Attr where localName can be Non-Null
220 # Node interfaces from Level 3 (WD 9 April 2002)
222 def isSameNode(self
, other
):
225 def getInterface(self
, feature
):
226 if self
.isSupported(feature
, None):
231 # The "user data" functions use a dictionary that is only present
232 # if some user data has been set, so be careful not to assume it
235 def getUserData(self
, key
):
237 return self
._user
_data
[key
][0]
238 except (AttributeError, KeyError):
241 def setUserData(self
, key
, data
, handler
):
245 except AttributeError:
251 # ignore handlers passed for None
256 d
[key
] = (data
, handler
)
259 def _call_user_data_handler(self
, operation
, src
, dst
):
260 if hasattr(self
, "_user_data"):
261 for key
, (data
, handler
) in self
._user
_data
.items():
262 if handler
is not None:
263 handler
.handle(operation
, key
, data
, src
, dst
)
265 # minidom-specific API:
268 self
.parentNode
= self
.ownerDocument
= None
270 for child
in self
.childNodes
:
272 self
.childNodes
= NodeList()
273 self
.previousSibling
= None
274 self
.nextSibling
= None
276 defproperty(Node
, "firstChild", doc
="First child node, or None.")
277 defproperty(Node
, "lastChild", doc
="Last child node, or None.")
278 defproperty(Node
, "localName", doc
="Namespace-local name of this node.")
281 def _append_child(self
, node
):
282 # fast path with less checks; usable by DOM builders if careful
283 childNodes
= self
.childNodes
285 last
= childNodes
[-1]
286 node
.__dict
__["previousSibling"] = last
287 last
.__dict
__["nextSibling"] = node
288 childNodes
.append(node
)
289 node
.__dict
__["parentNode"] = self
291 def _in_document(node
):
292 # return True iff node is part of a document tree
293 while node
is not None:
294 if node
.nodeType
== Node
.DOCUMENT_NODE
:
296 node
= node
.parentNode
299 def _write_data(writer
, data
):
300 "Writes datachars to writer."
301 data
= data
.replace("&", "&").replace("<", "<")
302 data
= data
.replace("\"", """).replace(">", ">")
305 def _get_elements_by_tagName_helper(parent
, name
, rc
):
306 for node
in parent
.childNodes
:
307 if node
.nodeType
== Node
.ELEMENT_NODE
and \
308 (name
== "*" or node
.tagName
== name
):
310 _get_elements_by_tagName_helper(node
, name
, rc
)
313 def _get_elements_by_tagName_ns_helper(parent
, nsURI
, localName
, rc
):
314 for node
in parent
.childNodes
:
315 if node
.nodeType
== Node
.ELEMENT_NODE
:
316 if ((localName
== "*" or node
.localName
== localName
) and
317 (nsURI
== "*" or node
.namespaceURI
== nsURI
)):
319 _get_elements_by_tagName_ns_helper(node
, nsURI
, localName
, rc
)
322 class DocumentFragment(Node
):
323 nodeType
= Node
.DOCUMENT_FRAGMENT_NODE
324 nodeName
= "#document-fragment"
328 _child_node_types
= (Node
.ELEMENT_NODE
,
330 Node
.CDATA_SECTION_NODE
,
331 Node
.ENTITY_REFERENCE_NODE
,
332 Node
.PROCESSING_INSTRUCTION_NODE
,
337 self
.childNodes
= NodeList()
341 nodeType
= Node
.ATTRIBUTE_NODE
347 _child_node_types
= (Node
.TEXT_NODE
, Node
.ENTITY_REFERENCE_NODE
)
349 def __init__(self
, qName
, namespaceURI
=EMPTY_NAMESPACE
, localName
=None,
351 # skip setattr for performance
353 d
["nodeName"] = d
["name"] = qName
354 d
["namespaceURI"] = namespaceURI
356 d
['childNodes'] = NodeList()
358 # Add the single child node that represents the value of the attr
359 self
.childNodes
.append(Text())
361 # nodeValue and value are set elsewhere
363 def _get_localName(self
):
364 return self
.nodeName
.split(":", 1)[-1]
369 def _get_specified(self
):
370 return self
.specified
372 def __setattr__(self
, name
, value
):
374 if name
in ("value", "nodeValue"):
375 d
["value"] = d
["nodeValue"] = value
376 d2
= self
.childNodes
[0].__dict
__
377 d2
["data"] = d2
["nodeValue"] = value
378 if self
.ownerElement
is not None:
379 _clear_id_cache(self
.ownerElement
)
380 elif name
in ("name", "nodeName"):
381 d
["name"] = d
["nodeName"] = value
382 if self
.ownerElement
is not None:
383 _clear_id_cache(self
.ownerElement
)
387 def _set_prefix(self
, prefix
):
388 nsuri
= self
.namespaceURI
389 if prefix
== "xmlns":
390 if nsuri
and nsuri
!= XMLNS_NAMESPACE
:
391 raise xml
.dom
.NamespaceErr(
392 "illegal use of 'xmlns' prefix for the wrong namespace")
396 newName
= self
.localName
398 newName
= "%s:%s" % (prefix
, self
.localName
)
399 if self
.ownerElement
:
400 _clear_id_cache(self
.ownerElement
)
401 d
['nodeName'] = d
['name'] = newName
403 def _set_value(self
, value
):
405 d
['value'] = d
['nodeValue'] = value
406 if self
.ownerElement
:
407 _clear_id_cache(self
.ownerElement
)
408 self
.childNodes
[0].data
= value
411 # This implementation does not call the base implementation
412 # since most of that is not needed, and the expense of the
413 # method call is not warranted. We duplicate the removal of
414 # children, but that's all we needed from the base class.
415 elem
= self
.ownerElement
417 del elem
._attrs
[self
.nodeName
]
418 del elem
._attrsNS
[(self
.namespaceURI
, self
.localName
)]
421 elem
._magic
_id
_nodes
-= 1
422 self
.ownerDocument
._magic
_id
_count
-= 1
423 for child
in self
.childNodes
:
425 del self
.childNodes
[:]
430 doc
= self
.ownerDocument
431 elem
= self
.ownerElement
432 if doc
is None or elem
is None:
435 info
= doc
._get
_elem
_info
(elem
)
438 if self
.namespaceURI
:
439 return info
.isIdNS(self
.namespaceURI
, self
.localName
)
441 return info
.isId(self
.nodeName
)
443 def _get_schemaType(self
):
444 doc
= self
.ownerDocument
445 elem
= self
.ownerElement
446 if doc
is None or elem
is None:
449 info
= doc
._get
_elem
_info
(elem
)
452 if self
.namespaceURI
:
453 return info
.getAttributeTypeNS(self
.namespaceURI
, self
.localName
)
455 return info
.getAttributeType(self
.nodeName
)
457 defproperty(Attr
, "isId", doc
="True if this attribute is an ID.")
458 defproperty(Attr
, "localName", doc
="Namespace-local name of this attribute.")
459 defproperty(Attr
, "schemaType", doc
="Schema type for this attribute.")
462 class NamedNodeMap(NewStyle
, GetattrMagic
):
463 """The attribute list is a transient interface to the underlying
464 dictionaries. Mutations here will change the underlying element's
467 Ordering is imposed artificially and does not reflect the order of
468 attributes as found in an input document.
471 __slots__
= ('_attrs', '_attrsNS', '_ownerElement')
473 def __init__(self
, attrs
, attrsNS
, ownerElement
):
475 self
._attrsNS
= attrsNS
476 self
._ownerElement
= ownerElement
478 def _get_length(self
):
479 return len(self
._attrs
)
481 def item(self
, index
):
483 return self
[self
._attrs
.keys()[index
]]
489 for node
in self
._attrs
.values():
490 L
.append((node
.nodeName
, node
.value
))
495 for node
in self
._attrs
.values():
496 L
.append(((node
.namespaceURI
, node
.localName
), node
.value
))
499 def has_key(self
, key
):
500 if isinstance(key
, StringTypes
):
501 return self
._attrs
.has_key(key
)
503 return self
._attrsNS
.has_key(key
)
506 return self
._attrs
.keys()
509 return self
._attrsNS
.keys()
512 return self
._attrs
.values()
514 def get(self
, name
, value
=None):
515 return self
._attrs
.get(name
, value
)
517 __len__
= _get_length
519 def __cmp__(self
, other
):
520 if self
._attrs
is getattr(other
, "_attrs", None):
523 return cmp(id(self
), id(other
))
525 def __getitem__(self
, attname_or_tuple
):
526 if isinstance(attname_or_tuple
, _TupleType
):
527 return self
._attrsNS
[attname_or_tuple
]
529 return self
._attrs
[attname_or_tuple
]
532 def __setitem__(self
, attname
, value
):
533 if isinstance(value
, StringTypes
):
535 node
= self
._attrs
[attname
]
538 node
.ownerDocument
= self
._ownerElement
.ownerDocument
539 self
.setNamedItem(node
)
542 if not isinstance(value
, Attr
):
543 raise TypeError, "value must be a string or Attr object"
545 self
.setNamedItem(node
)
547 def getNamedItem(self
, name
):
549 return self
._attrs
[name
]
553 def getNamedItemNS(self
, namespaceURI
, localName
):
555 return self
._attrsNS
[(namespaceURI
, localName
)]
559 def removeNamedItem(self
, name
):
560 n
= self
.getNamedItem(name
)
562 _clear_id_cache(self
._ownerElement
)
563 del self
._attrs
[n
.nodeName
]
564 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
565 if n
.__dict
__.has_key('ownerElement'):
566 n
.__dict
__['ownerElement'] = None
569 raise xml
.dom
.NotFoundErr()
571 def removeNamedItemNS(self
, namespaceURI
, localName
):
572 n
= self
.getNamedItemNS(namespaceURI
, localName
)
574 _clear_id_cache(self
._ownerElement
)
575 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
576 del self
._attrs
[n
.nodeName
]
577 if n
.__dict
__.has_key('ownerElement'):
578 n
.__dict
__['ownerElement'] = None
581 raise xml
.dom
.NotFoundErr()
583 def setNamedItem(self
, node
):
584 if not isinstance(node
, Attr
):
585 raise xml
.dom
.HierarchyRequestErr(
586 "%s cannot be child of %s" % (repr(node
), repr(self
)))
587 old
= self
._attrs
.get(node
.name
)
590 self
._attrs
[node
.name
] = node
591 self
._attrsNS
[(node
.namespaceURI
, node
.localName
)] = node
592 node
.ownerElement
= self
._ownerElement
593 _clear_id_cache(node
.ownerElement
)
596 def setNamedItemNS(self
, node
):
597 return self
.setNamedItem(node
)
599 def __delitem__(self
, attname_or_tuple
):
600 node
= self
[attname_or_tuple
]
601 _clear_id_cache(node
.ownerElement
)
604 def __getstate__(self
):
605 return self
._attrs
, self
._attrsNS
, self
._ownerElement
607 def __setstate__(self
, state
):
608 self
._attrs
, self
._attrsNS
, self
._ownerElement
= state
610 defproperty(NamedNodeMap
, "length",
611 doc
="Number of nodes in the NamedNodeMap.")
613 AttributeList
= NamedNodeMap
616 class TypeInfo(NewStyle
):
617 __slots__
= 'namespace', 'name'
619 def __init__(self
, namespace
, name
):
620 self
.namespace
= namespace
625 return "<TypeInfo %s (from %s)>" % (`self
.name`
, `self
.namespace`
)
627 return "<TypeInfo %s>" % `self
.name`
632 def _get_namespace(self
):
633 return self
.namespace
635 _no_type
= TypeInfo(None, None)
638 nodeType
= Node
.ELEMENT_NODE
640 schemaType
= _no_type
644 _child_node_types
= (Node
.ELEMENT_NODE
,
645 Node
.PROCESSING_INSTRUCTION_NODE
,
648 Node
.CDATA_SECTION_NODE
,
649 Node
.ENTITY_REFERENCE_NODE
)
651 def __init__(self
, tagName
, namespaceURI
=EMPTY_NAMESPACE
, prefix
=None,
653 self
.tagName
= self
.nodeName
= tagName
655 self
.namespaceURI
= namespaceURI
656 self
.childNodes
= NodeList()
658 self
._attrs
= {} # attributes are double-indexed:
659 self
._attrsNS
= {} # tagName -> Attribute
660 # URI,localName -> Attribute
661 # in the future: consider lazy generation
662 # of attribute objects this is too tricky
663 # for now because of headaches with
666 def _get_localName(self
):
667 return self
.tagName
.split(":", 1)[-1]
669 def _get_tagName(self
):
673 for attr
in self
._attrs
.values():
679 def getAttribute(self
, attname
):
681 return self
._attrs
[attname
].value
685 def getAttributeNS(self
, namespaceURI
, localName
):
687 return self
._attrsNS
[(namespaceURI
, localName
)].value
691 def setAttribute(self
, attname
, value
):
692 attr
= self
.getAttributeNode(attname
)
697 d
["value"] = d
["nodeValue"] = value
698 d
["ownerDocument"] = self
.ownerDocument
699 self
.setAttributeNode(attr
)
700 elif value
!= attr
.value
:
702 d
["value"] = d
["nodeValue"] = value
704 _clear_id_cache(self
)
706 def setAttributeNS(self
, namespaceURI
, qualifiedName
, value
):
707 prefix
, localname
= _nssplit(qualifiedName
)
708 attr
= self
.getAttributeNodeNS(namespaceURI
, localname
)
711 attr
= Attr(qualifiedName
, namespaceURI
, localname
, prefix
)
714 d
["nodeName"] = qualifiedName
715 d
["value"] = d
["nodeValue"] = value
716 d
["ownerDocument"] = self
.ownerDocument
717 self
.setAttributeNode(attr
)
720 if value
!= attr
.value
:
721 d
["value"] = d
["nodeValue"] = value
723 _clear_id_cache(self
)
724 if attr
.prefix
!= prefix
:
726 d
["nodeName"] = qualifiedName
728 def getAttributeNode(self
, attrname
):
729 return self
._attrs
.get(attrname
)
731 def getAttributeNodeNS(self
, namespaceURI
, localName
):
732 return self
._attrsNS
.get((namespaceURI
, localName
))
734 def setAttributeNode(self
, attr
):
735 if attr
.ownerElement
not in (None, self
):
736 raise xml
.dom
.InuseAttributeErr("attribute node already owned")
737 old1
= self
._attrs
.get(attr
.name
, None)
739 self
.removeAttributeNode(old1
)
740 old2
= self
._attrsNS
.get((attr
.namespaceURI
, attr
.localName
), None)
741 if old2
is not None and old2
is not old1
:
742 self
.removeAttributeNode(old2
)
743 _set_attribute_node(self
, attr
)
746 # It might have already been part of this node, in which case
747 # it doesn't represent a change, and should not be returned.
752 setAttributeNodeNS
= setAttributeNode
754 def removeAttribute(self
, name
):
756 attr
= self
._attrs
[name
]
758 raise xml
.dom
.NotFoundErr()
759 self
.removeAttributeNode(attr
)
761 def removeAttributeNS(self
, namespaceURI
, localName
):
763 attr
= self
._attrsNS
[(namespaceURI
, localName
)]
765 raise xml
.dom
.NotFoundErr()
766 self
.removeAttributeNode(attr
)
768 def removeAttributeNode(self
, node
):
770 raise xml
.dom
.NotFoundErr()
772 self
._attrs
[node
.name
]
774 raise xml
.dom
.NotFoundErr()
775 _clear_id_cache(self
)
777 # Restore this since the node is still useful and otherwise
779 node
.ownerDocument
= self
.ownerDocument
781 removeAttributeNodeNS
= removeAttributeNode
783 def hasAttribute(self
, name
):
784 return self
._attrs
.has_key(name
)
786 def hasAttributeNS(self
, namespaceURI
, localName
):
787 return self
._attrsNS
.has_key((namespaceURI
, localName
))
789 def getElementsByTagName(self
, name
):
790 return _get_elements_by_tagName_helper(self
, name
, NodeList())
792 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
793 return _get_elements_by_tagName_ns_helper(
794 self
, namespaceURI
, localName
, NodeList())
797 return "<DOM Element: %s at %#x>" % (self
.tagName
, id(self
))
799 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
800 # indent = current indentation
801 # addindent = indentation to add to higher levels
802 # newl = newline string
803 writer
.write(indent
+"<" + self
.tagName
)
805 attrs
= self
._get
_attributes
()
806 a_names
= attrs
.keys()
809 for a_name
in a_names
:
810 writer
.write(" %s=\"" % a_name
)
811 _write_data(writer
, attrs
[a_name
].value
)
814 writer
.write(">%s"%(newl))
815 for node
in self
.childNodes
:
816 node
.writexml(writer
,indent
+addindent
,addindent
,newl
)
817 writer
.write("%s</%s>%s" % (indent
,self
.tagName
,newl
))
819 writer
.write("/>%s"%(newl))
821 def _get_attributes(self
):
822 return NamedNodeMap(self
._attrs
, self
._attrsNS
, self
)
824 def hasAttributes(self
):
830 # DOM Level 3 attributes, based on the 22 Oct 2002 draft
832 def setIdAttribute(self
, name
):
833 idAttr
= self
.getAttributeNode(name
)
834 self
.setIdAttributeNode(idAttr
)
836 def setIdAttributeNS(self
, namespaceURI
, localName
):
837 idAttr
= self
.getAttributeNodeNS(namespaceURI
, localName
)
838 self
.setIdAttributeNode(idAttr
)
840 def setIdAttributeNode(self
, idAttr
):
841 if idAttr
is None or not self
.isSameNode(idAttr
.ownerElement
):
842 raise xml
.dom
.NotFoundErr()
843 if _get_containing_entref(self
) is not None:
844 raise xml
.dom
.NoModificationAllowedErr()
845 if not idAttr
._is
_id
:
846 idAttr
.__dict
__['_is_id'] = True
847 self
._magic
_id
_nodes
+= 1
848 self
.ownerDocument
._magic
_id
_count
+= 1
849 _clear_id_cache(self
)
851 defproperty(Element
, "attributes",
852 doc
="NamedNodeMap of attributes on the element.")
853 defproperty(Element
, "localName",
854 doc
="Namespace-local name of this element.")
857 def _set_attribute_node(element
, attr
):
858 _clear_id_cache(element
)
859 element
._attrs
[attr
.name
] = attr
860 element
._attrsNS
[(attr
.namespaceURI
, attr
.localName
)] = attr
862 # This creates a circular reference, but Element.unlink()
863 # breaks the cycle since the references to the attribute
864 # dictionaries are tossed.
865 attr
.__dict
__['ownerElement'] = element
869 """Mixin that makes childless-ness easy to implement and avoids
870 the complexity of the Node methods that deal with children.
874 childNodes
= EmptyNodeList()
878 def _get_firstChild(self
):
881 def _get_lastChild(self
):
884 def appendChild(self
, node
):
885 raise xml
.dom
.HierarchyRequestErr(
886 self
.nodeName
+ " nodes cannot have children")
888 def hasChildNodes(self
):
891 def insertBefore(self
, newChild
, refChild
):
892 raise xml
.dom
.HierarchyRequestErr(
893 self
.nodeName
+ " nodes do not have children")
895 def removeChild(self
, oldChild
):
896 raise xml
.dom
.NotFoundErr(
897 self
.nodeName
+ " nodes do not have children")
899 def replaceChild(self
, newChild
, oldChild
):
900 raise xml
.dom
.HierarchyRequestErr(
901 self
.nodeName
+ " nodes do not have children")
904 class ProcessingInstruction(Childless
, Node
):
905 nodeType
= Node
.PROCESSING_INSTRUCTION_NODE
907 def __init__(self
, target
, data
):
908 self
.target
= self
.nodeName
= target
909 self
.data
= self
.nodeValue
= data
913 def _set_data(self
, value
):
915 d
['data'] = d
['nodeValue'] = value
917 def _get_target(self
):
919 def _set_target(self
, value
):
921 d
['target'] = d
['nodeName'] = value
923 def __setattr__(self
, name
, value
):
924 if name
== "data" or name
== "nodeValue":
925 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
926 elif name
== "target" or name
== "nodeName":
927 self
.__dict
__['target'] = self
.__dict
__['nodeName'] = value
929 self
.__dict
__[name
] = value
931 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
932 writer
.write("%s<?%s %s?>%s" % (indent
,self
.target
, self
.data
, newl
))
935 class CharacterData(Childless
, Node
):
936 def _get_length(self
):
937 return len(self
.data
)
938 __len__
= _get_length
941 return self
.__dict
__['data']
942 def _set_data(self
, data
):
944 d
['data'] = d
['nodeValue'] = data
946 _get_nodeValue
= _get_data
947 _set_nodeValue
= _set_data
949 def __setattr__(self
, name
, value
):
950 if name
== "data" or name
== "nodeValue":
951 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
953 self
.__dict
__[name
] = value
961 return "<DOM %s node \"%s%s\">" % (
962 self
.__class
__.__name
__, data
[0:10], dotdotdot
)
964 def substringData(self
, offset
, count
):
966 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
967 if offset
>= len(self
.data
):
968 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
970 raise xml
.dom
.IndexSizeErr("count cannot be negative")
971 return self
.data
[offset
:offset
+count
]
973 def appendData(self
, arg
):
974 self
.data
= self
.data
+ arg
976 def insertData(self
, offset
, arg
):
978 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
979 if offset
>= len(self
.data
):
980 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
982 self
.data
= "%s%s%s" % (
983 self
.data
[:offset
], arg
, self
.data
[offset
:])
985 def deleteData(self
, offset
, count
):
987 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
988 if offset
>= len(self
.data
):
989 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
991 raise xml
.dom
.IndexSizeErr("count cannot be negative")
993 self
.data
= self
.data
[:offset
] + self
.data
[offset
+count
:]
995 def replaceData(self
, offset
, count
, arg
):
997 raise xml
.dom
.IndexSizeErr("offset cannot be negative")
998 if offset
>= len(self
.data
):
999 raise xml
.dom
.IndexSizeErr("offset cannot be beyond end of data")
1001 raise xml
.dom
.IndexSizeErr("count cannot be negative")
1003 self
.data
= "%s%s%s" % (
1004 self
.data
[:offset
], arg
, self
.data
[offset
+count
:])
1006 defproperty(CharacterData
, "length", doc
="Length of the string data.")
1009 class Text(CharacterData
):
1010 # Make sure we don't add an instance __dict__ if we don't already
1011 # have one, at least when that's possible:
1012 # XXX this does not work, CharacterData is an old-style class
1015 nodeType
= Node
.TEXT_NODE
1019 def splitText(self
, offset
):
1020 if offset
< 0 or offset
> len(self
.data
):
1021 raise xml
.dom
.IndexSizeErr("illegal offset value")
1022 newText
= self
.__class
__()
1023 newText
.data
= self
.data
[offset
:]
1024 newText
.ownerDocument
= self
.ownerDocument
1025 next
= self
.nextSibling
1026 if self
.parentNode
and self
in self
.parentNode
.childNodes
:
1028 self
.parentNode
.appendChild(newText
)
1030 self
.parentNode
.insertBefore(newText
, next
)
1031 self
.data
= self
.data
[:offset
]
1034 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1035 _write_data(writer
, "%s%s%s"%(indent
, self
.data
, newl
))
1037 # DOM Level 3 (WD 9 April 2002)
1039 def _get_wholeText(self
):
1041 n
= self
.previousSibling
1042 while n
is not None:
1043 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1045 n
= n
.previousSibling
1048 n
= self
.nextSibling
1049 while n
is not None:
1050 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1057 def replaceWholeText(self
, content
):
1058 # XXX This needs to be seriously changed if minidom ever
1059 # supports EntityReference nodes.
1060 parent
= self
.parentNode
1061 n
= self
.previousSibling
1062 while n
is not None:
1063 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1064 next
= n
.previousSibling
1065 parent
.removeChild(n
)
1069 n
= self
.nextSibling
1071 parent
.removeChild(self
)
1072 while n
is not None:
1073 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1074 next
= n
.nextSibling
1075 parent
.removeChild(n
)
1082 d
['nodeValue'] = content
1087 def _get_isWhitespaceInElementContent(self
):
1088 if self
.data
.strip():
1090 elem
= _get_containing_element(self
)
1093 info
= self
.ownerDocument
._get
_elem
_info
(elem
)
1097 return info
.isElementContent()
1099 defproperty(Text
, "isWhitespaceInElementContent",
1100 doc
="True iff this text node contains only whitespace"
1101 " and is in element content.")
1102 defproperty(Text
, "wholeText",
1103 doc
="The text of all logically-adjacent text nodes.")
1106 def _get_containing_element(node
):
1108 while c
is not None:
1109 if c
.nodeType
== Node
.ELEMENT_NODE
:
1114 def _get_containing_entref(node
):
1116 while c
is not None:
1117 if c
.nodeType
== Node
.ENTITY_REFERENCE_NODE
:
1123 class Comment(Childless
, CharacterData
):
1124 nodeType
= Node
.COMMENT_NODE
1125 nodeName
= "#comment"
1127 def __init__(self
, data
):
1128 self
.data
= self
.nodeValue
= data
1130 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1131 writer
.write("%s<!--%s-->%s" % (indent
, self
.data
, newl
))
1134 class CDATASection(Text
):
1135 # Make sure we don't add an instance __dict__ if we don't already
1136 # have one, at least when that's possible:
1137 # XXX this does not work, Text is an old-style class
1140 nodeType
= Node
.CDATA_SECTION_NODE
1141 nodeName
= "#cdata-section"
1143 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1144 if self
.data
.find("]]>") >= 0:
1145 raise ValueError("']]>' not allowed in a CDATA section")
1146 writer
.write("<![CDATA[%s]]>" % self
.data
)
1149 class ReadOnlySequentialNamedNodeMap(NewStyle
, GetattrMagic
):
1152 def __init__(self
, seq
=()):
1153 # seq should be a list or tuple
1157 return len(self
._seq
)
1159 def _get_length(self
):
1160 return len(self
._seq
)
1162 def getNamedItem(self
, name
):
1164 if n
.nodeName
== name
:
1167 def getNamedItemNS(self
, namespaceURI
, localName
):
1169 if n
.namespaceURI
== namespaceURI
and n
.localName
== localName
:
1172 def __getitem__(self
, name_or_tuple
):
1173 if isinstance(name_or_tuple
, _TupleType
):
1174 node
= self
.getNamedItemNS(*name_or_tuple
)
1176 node
= self
.getNamedItem(name_or_tuple
)
1178 raise KeyError, name_or_tuple
1181 def item(self
, index
):
1185 return self
._seq
[index
]
1189 def removeNamedItem(self
, name
):
1190 raise xml
.dom
.NoModificationAllowedErr(
1191 "NamedNodeMap instance is read-only")
1193 def removeNamedItemNS(self
, namespaceURI
, localName
):
1194 raise xml
.dom
.NoModificationAllowedErr(
1195 "NamedNodeMap instance is read-only")
1197 def setNamedItem(self
, node
):
1198 raise xml
.dom
.NoModificationAllowedErr(
1199 "NamedNodeMap instance is read-only")
1201 def setNamedItemNS(self
, node
):
1202 raise xml
.dom
.NoModificationAllowedErr(
1203 "NamedNodeMap instance is read-only")
1205 def __getstate__(self
):
1208 def __setstate__(self
, state
):
1209 self
._seq
= state
[0]
1211 defproperty(ReadOnlySequentialNamedNodeMap
, "length",
1212 doc
="Number of entries in the NamedNodeMap.")
1216 """Mix-in class that supports the publicId and systemId attributes."""
1218 # XXX this does not work, this is an old-style class
1219 # __slots__ = 'publicId', 'systemId'
1221 def _identified_mixin_init(self
, publicId
, systemId
):
1222 self
.publicId
= publicId
1223 self
.systemId
= systemId
1225 def _get_publicId(self
):
1226 return self
.publicId
1228 def _get_systemId(self
):
1229 return self
.systemId
1231 class DocumentType(Identified
, Childless
, Node
):
1232 nodeType
= Node
.DOCUMENT_TYPE_NODE
1237 internalSubset
= None
1239 def __init__(self
, qualifiedName
):
1240 self
.entities
= ReadOnlySequentialNamedNodeMap()
1241 self
.notations
= ReadOnlySequentialNamedNodeMap()
1243 prefix
, localname
= _nssplit(qualifiedName
)
1244 self
.name
= localname
1245 self
.nodeName
= self
.name
1247 def _get_internalSubset(self
):
1248 return self
.internalSubset
1250 def cloneNode(self
, deep
):
1251 if self
.ownerDocument
is None:
1253 clone
= DocumentType(None)
1254 clone
.name
= self
.name
1255 clone
.nodeName
= self
.name
1256 operation
= xml
.dom
.UserDataHandler
.NODE_CLONED
1258 clone
.entities
._seq
= []
1259 clone
.notations
._seq
= []
1260 for n
in self
.notations
._seq
:
1261 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1262 clone
.notations
._seq
.append(notation
)
1263 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1264 for e
in self
.entities
._seq
:
1265 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1267 entity
.actualEncoding
= e
.actualEncoding
1268 entity
.encoding
= e
.encoding
1269 entity
.version
= e
.version
1270 clone
.entities
._seq
.append(entity
)
1271 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1272 self
._call
_user
_data
_handler
(operation
, self
, clone
)
1277 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1278 writer
.write("<!DOCTYPE ")
1279 writer
.write(self
.name
)
1281 writer
.write("\n PUBLIC '%s'\n '%s'"
1282 % (self
.publicId
, self
.systemId
))
1284 writer
.write("\n SYSTEM '%s'" % self
.systemId
)
1285 if self
.internalSubset
is not None:
1287 writer
.write(self
.internalSubset
)
1291 class Entity(Identified
, Node
):
1293 nodeType
= Node
.ENTITY_NODE
1296 actualEncoding
= None
1300 def __init__(self
, name
, publicId
, systemId
, notation
):
1301 self
.nodeName
= name
1302 self
.notationName
= notation
1303 self
.childNodes
= NodeList()
1304 self
._identified
_mixin
_init
(publicId
, systemId
)
1306 def _get_actualEncoding(self
):
1307 return self
.actualEncoding
1309 def _get_encoding(self
):
1310 return self
.encoding
1312 def _get_version(self
):
1315 def appendChild(self
, newChild
):
1316 raise xml
.dom
.HierarchyRequestErr(
1317 "cannot append children to an entity node")
1319 def insertBefore(self
, newChild
, refChild
):
1320 raise xml
.dom
.HierarchyRequestErr(
1321 "cannot insert children below an entity node")
1323 def removeChild(self
, oldChild
):
1324 raise xml
.dom
.HierarchyRequestErr(
1325 "cannot remove children from an entity node")
1327 def replaceChild(self
, newChild
, oldChild
):
1328 raise xml
.dom
.HierarchyRequestErr(
1329 "cannot replace children of an entity node")
1331 class Notation(Identified
, Childless
, Node
):
1332 nodeType
= Node
.NOTATION_NODE
1335 def __init__(self
, name
, publicId
, systemId
):
1336 self
.nodeName
= name
1337 self
._identified
_mixin
_init
(publicId
, systemId
)
1340 class DOMImplementation(DOMImplementationLS
):
1341 _features
= [("core", "1.0"),
1353 def hasFeature(self
, feature
, version
):
1356 return (feature
.lower(), version
) in self
._features
1358 def createDocument(self
, namespaceURI
, qualifiedName
, doctype
):
1359 if doctype
and doctype
.parentNode
is not None:
1360 raise xml
.dom
.WrongDocumentErr(
1361 "doctype object owned by another DOM tree")
1362 doc
= self
._create
_document
()
1364 add_root_element
= not (namespaceURI
is None
1365 and qualifiedName
is None
1366 and doctype
is None)
1368 if not qualifiedName
and add_root_element
:
1369 # The spec is unclear what to raise here; SyntaxErr
1370 # would be the other obvious candidate. Since Xerces raises
1371 # InvalidCharacterErr, and since SyntaxErr is not listed
1372 # for createDocument, that seems to be the better choice.
1373 # XXX: need to check for illegal characters here and in
1376 # DOM Level III clears this up when talking about the return value
1377 # of this function. If namespaceURI, qName and DocType are
1378 # Null the document is returned without a document element
1379 # Otherwise if doctype or namespaceURI are not None
1380 # Then we go back to the above problem
1381 raise xml
.dom
.InvalidCharacterErr("Element with no name")
1383 if add_root_element
:
1384 prefix
, localname
= _nssplit(qualifiedName
)
1385 if prefix
== "xml" \
1386 and namespaceURI
!= "http://www.w3.org/XML/1998/namespace":
1387 raise xml
.dom
.NamespaceErr("illegal use of 'xml' prefix")
1388 if prefix
and not namespaceURI
:
1389 raise xml
.dom
.NamespaceErr(
1390 "illegal use of prefix without namespaces")
1391 element
= doc
.createElementNS(namespaceURI
, qualifiedName
)
1393 doc
.appendChild(doctype
)
1394 doc
.appendChild(element
)
1397 doctype
.parentNode
= doctype
.ownerDocument
= doc
1399 doc
.doctype
= doctype
1400 doc
.implementation
= self
1403 def createDocumentType(self
, qualifiedName
, publicId
, systemId
):
1404 doctype
= DocumentType(qualifiedName
)
1405 doctype
.publicId
= publicId
1406 doctype
.systemId
= systemId
1409 # DOM Level 3 (WD 9 April 2002)
1411 def getInterface(self
, feature
):
1412 if self
.hasFeature(feature
, None):
1418 def _create_document(self
):
1421 class ElementInfo(NewStyle
):
1422 """Object that represents content-model information for an element.
1424 This implementation is not expected to be used in practice; DOM
1425 builders should provide implementations which do the right thing
1426 using information available to it.
1430 __slots__
= 'tagName',
1432 def __init__(self
, name
):
1435 def getAttributeType(self
, aname
):
1438 def getAttributeTypeNS(self
, namespaceURI
, localName
):
1441 def isElementContent(self
):
1445 """Returns true iff this element is declared to have an EMPTY
1449 def isId(self
, aname
):
1450 """Returns true iff the named attribte is a DTD-style ID."""
1453 def isIdNS(self
, namespaceURI
, localName
):
1454 """Returns true iff the identified attribute is a DTD-style ID."""
1457 def __getstate__(self
):
1460 def __setstate__(self
, state
):
1461 self
.tagName
= state
1463 def _clear_id_cache(node
):
1464 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1465 node
._id
_cache
.clear()
1466 node
._id
_search
_stack
= None
1467 elif _in_document(node
):
1468 node
.ownerDocument
._id
_cache
.clear()
1469 node
.ownerDocument
._id
_search
_stack
= None
1471 class Document(Node
, DocumentLS
):
1472 _child_node_types
= (Node
.ELEMENT_NODE
, Node
.PROCESSING_INSTRUCTION_NODE
,
1473 Node
.COMMENT_NODE
, Node
.DOCUMENT_TYPE_NODE
)
1475 nodeType
= Node
.DOCUMENT_NODE
1476 nodeName
= "#document"
1481 previousSibling
= nextSibling
= None
1483 implementation
= DOMImplementation()
1485 # Document attributes from Level 3 (WD 9 April 2002)
1487 actualEncoding
= None
1491 strictErrorChecking
= False
1498 self
.childNodes
= NodeList()
1499 # mapping of (namespaceURI, localName) -> ElementInfo
1500 # and tagName -> ElementInfo
1501 self
._elem
_info
= {}
1503 self
._id
_search
_stack
= None
1505 def _get_elem_info(self
, element
):
1506 if element
.namespaceURI
:
1507 key
= element
.namespaceURI
, element
.localName
1509 key
= element
.tagName
1510 return self
._elem
_info
.get(key
)
1512 def _get_actualEncoding(self
):
1513 return self
.actualEncoding
1515 def _get_doctype(self
):
1518 def _get_documentURI(self
):
1519 return self
.documentURI
1521 def _get_encoding(self
):
1522 return self
.encoding
1524 def _get_errorHandler(self
):
1525 return self
.errorHandler
1527 def _get_standalone(self
):
1528 return self
.standalone
1530 def _get_strictErrorChecking(self
):
1531 return self
.strictErrorChecking
1533 def _get_version(self
):
1536 def appendChild(self
, node
):
1537 if node
.nodeType
not in self
._child
_node
_types
:
1538 raise xml
.dom
.HierarchyRequestErr(
1539 "%s cannot be child of %s" % (repr(node
), repr(self
)))
1540 if node
.parentNode
is not None:
1541 # This needs to be done before the next test since this
1542 # may *be* the document element, in which case it should
1543 # end up re-ordered to the end.
1544 node
.parentNode
.removeChild(node
)
1546 if node
.nodeType
== Node
.ELEMENT_NODE \
1547 and self
._get
_documentElement
():
1548 raise xml
.dom
.HierarchyRequestErr(
1549 "two document elements disallowed")
1550 return Node
.appendChild(self
, node
)
1552 def removeChild(self
, oldChild
):
1554 self
.childNodes
.remove(oldChild
)
1556 raise xml
.dom
.NotFoundErr()
1557 oldChild
.nextSibling
= oldChild
.previousSibling
= None
1558 oldChild
.parentNode
= None
1559 if self
.documentElement
is oldChild
:
1560 self
.documentElement
= None
1564 def _get_documentElement(self
):
1565 for node
in self
.childNodes
:
1566 if node
.nodeType
== Node
.ELEMENT_NODE
:
1570 if self
.doctype
is not None:
1571 self
.doctype
.unlink()
1575 def cloneNode(self
, deep
):
1578 clone
= self
.implementation
.createDocument(None, None, None)
1579 clone
.encoding
= self
.encoding
1580 clone
.standalone
= self
.standalone
1581 clone
.version
= self
.version
1582 for n
in self
.childNodes
:
1583 childclone
= _clone_node(n
, deep
, clone
)
1584 assert childclone
.ownerDocument
.isSameNode(clone
)
1585 clone
.childNodes
.append(childclone
)
1586 if childclone
.nodeType
== Node
.DOCUMENT_NODE
:
1587 assert clone
.documentElement
is None
1588 elif childclone
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1589 assert clone
.doctype
is None
1590 clone
.doctype
= childclone
1591 childclone
.parentNode
= clone
1592 self
._call
_user
_data
_handler
(xml
.dom
.UserDataHandler
.NODE_CLONED
,
1596 def createDocumentFragment(self
):
1597 d
= DocumentFragment()
1598 d
.ownerDocument
= self
1601 def createElement(self
, tagName
):
1602 e
= Element(tagName
)
1603 e
.ownerDocument
= self
1606 def createTextNode(self
, data
):
1607 if not isinstance(data
, StringTypes
):
1608 raise TypeError, "node contents must be a string"
1611 t
.ownerDocument
= self
1614 def createCDATASection(self
, data
):
1615 if not isinstance(data
, StringTypes
):
1616 raise TypeError, "node contents must be a string"
1619 c
.ownerDocument
= self
1622 def createComment(self
, data
):
1624 c
.ownerDocument
= self
1627 def createProcessingInstruction(self
, target
, data
):
1628 p
= ProcessingInstruction(target
, data
)
1629 p
.ownerDocument
= self
1632 def createAttribute(self
, qName
):
1634 a
.ownerDocument
= self
1638 def createElementNS(self
, namespaceURI
, qualifiedName
):
1639 prefix
, localName
= _nssplit(qualifiedName
)
1640 e
= Element(qualifiedName
, namespaceURI
, prefix
)
1641 e
.ownerDocument
= self
1644 def createAttributeNS(self
, namespaceURI
, qualifiedName
):
1645 prefix
, localName
= _nssplit(qualifiedName
)
1646 a
= Attr(qualifiedName
, namespaceURI
, localName
, prefix
)
1647 a
.ownerDocument
= self
1651 # A couple of implementation-specific helpers to create node types
1652 # not supported by the W3C DOM specs:
1654 def _create_entity(self
, name
, publicId
, systemId
, notationName
):
1655 e
= Entity(name
, publicId
, systemId
, notationName
)
1656 e
.ownerDocument
= self
1659 def _create_notation(self
, name
, publicId
, systemId
):
1660 n
= Notation(name
, publicId
, systemId
)
1661 n
.ownerDocument
= self
1664 def getElementById(self
, id):
1665 if self
._id
_cache
.has_key(id):
1666 return self
._id
_cache
[id]
1667 if not (self
._elem
_info
or self
._magic
_id
_count
):
1670 stack
= self
._id
_search
_stack
1672 # we never searched before, or the cache has been cleared
1673 stack
= [self
.documentElement
]
1674 self
._id
_search
_stack
= stack
1676 # Previous search was completed and cache is still valid;
1683 # add child elements to stack for continued searching
1684 stack
.extend([child
for child
in node
.childNodes
1685 if child
.nodeType
in _nodeTypes_with_children
])
1687 info
= self
._get
_elem
_info
(node
)
1689 # We have to process all ID attributes before
1690 # returning in order to get all the attributes set to
1691 # be IDs using Element.setIdAttribute*().
1692 for attr
in node
.attributes
.values():
1693 if attr
.namespaceURI
:
1694 if info
.isIdNS(attr
.namespaceURI
, attr
.localName
):
1695 self
._id
_cache
[attr
.value
] = node
1696 if attr
.value
== id:
1698 elif not node
._magic
_id
_nodes
:
1700 elif info
.isId(attr
.name
):
1701 self
._id
_cache
[attr
.value
] = node
1702 if attr
.value
== id:
1704 elif not node
._magic
_id
_nodes
:
1707 self
._id
_cache
[attr
.value
] = node
1708 if attr
.value
== id:
1710 elif node
._magic
_id
_nodes
== 1:
1712 elif node
._magic
_id
_nodes
:
1713 for attr
in node
.attributes
.values():
1715 self
._id
_cache
[attr
.value
] = node
1716 if attr
.value
== id:
1718 if result
is not None:
1722 def getElementsByTagName(self
, name
):
1723 return _get_elements_by_tagName_helper(self
, name
, NodeList())
1725 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
1726 return _get_elements_by_tagName_ns_helper(
1727 self
, namespaceURI
, localName
, NodeList())
1729 def isSupported(self
, feature
, version
):
1730 return self
.implementation
.hasFeature(feature
, version
)
1732 def importNode(self
, node
, deep
):
1733 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1734 raise xml
.dom
.NotSupportedErr("cannot import document nodes")
1735 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1736 raise xml
.dom
.NotSupportedErr("cannot import document type nodes")
1737 return _clone_node(node
, deep
, self
)
1739 def writexml(self
, writer
, indent
="", addindent
="", newl
="",
1741 if encoding
is None:
1742 writer
.write('<?xml version="1.0" ?>\n')
1744 writer
.write('<?xml version="1.0" encoding="%s"?>\n' % encoding
)
1745 for node
in self
.childNodes
:
1746 node
.writexml(writer
, indent
, addindent
, newl
)
1748 # DOM Level 3 (WD 9 April 2002)
1750 def renameNode(self
, n
, namespaceURI
, name
):
1751 if n
.ownerDocument
is not self
:
1752 raise xml
.dom
.WrongDocumentErr(
1753 "cannot rename nodes from other documents;\n"
1754 "expected %s,\nfound %s" % (self
, n
.ownerDocument
))
1755 if n
.nodeType
not in (Node
.ELEMENT_NODE
, Node
.ATTRIBUTE_NODE
):
1756 raise xml
.dom
.NotSupportedErr(
1757 "renameNode() only applies to element and attribute nodes")
1758 if namespaceURI
!= EMPTY_NAMESPACE
:
1760 prefix
, localName
= name
.split(':', 1)
1761 if ( prefix
== "xmlns"
1762 and namespaceURI
!= xml
.dom
.XMLNS_NAMESPACE
):
1763 raise xml
.dom
.NamespaceErr(
1764 "illegal use of 'xmlns' prefix")
1766 if ( name
== "xmlns"
1767 and namespaceURI
!= xml
.dom
.XMLNS_NAMESPACE
1768 and n
.nodeType
== Node
.ATTRIBUTE_NODE
):
1769 raise xml
.dom
.NamespaceErr(
1770 "illegal use of the 'xmlns' attribute")
1776 if n
.nodeType
== Node
.ATTRIBUTE_NODE
:
1777 element
= n
.ownerElement
1778 if element
is not None:
1780 element
.removeAttributeNode(n
)
1785 d
['prefix'] = prefix
1786 d
['localName'] = localName
1787 d
['namespaceURI'] = namespaceURI
1788 d
['nodeName'] = name
1789 if n
.nodeType
== Node
.ELEMENT_NODE
:
1794 if element
is not None:
1795 element
.setAttributeNode(n
)
1797 element
.setIdAttributeNode(n
)
1798 # It's not clear from a semantic perspective whether we should
1799 # call the user data handlers for the NODE_RENAMED event since
1800 # we're re-using the existing node. The draft spec has been
1801 # interpreted as meaning "no, don't call the handler unless a
1802 # new node is created."
1805 defproperty(Document
, "documentElement",
1806 doc
="Top-level element of this document.")
1809 def _clone_node(node
, deep
, newOwnerDocument
):
1811 Clone a node and give it the new owner document.
1812 Called by Node.cloneNode and Document.importNode
1814 if node
.ownerDocument
.isSameNode(newOwnerDocument
):
1815 operation
= xml
.dom
.UserDataHandler
.NODE_CLONED
1817 operation
= xml
.dom
.UserDataHandler
.NODE_IMPORTED
1818 if node
.nodeType
== Node
.ELEMENT_NODE
:
1819 clone
= newOwnerDocument
.createElementNS(node
.namespaceURI
,
1821 for attr
in node
.attributes
.values():
1822 clone
.setAttributeNS(attr
.namespaceURI
, attr
.nodeName
, attr
.value
)
1823 a
= clone
.getAttributeNodeNS(attr
.namespaceURI
, attr
.localName
)
1824 a
.specified
= attr
.specified
1827 for child
in node
.childNodes
:
1828 c
= _clone_node(child
, deep
, newOwnerDocument
)
1829 clone
.appendChild(c
)
1831 elif node
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1832 clone
= newOwnerDocument
.createDocumentFragment()
1834 for child
in node
.childNodes
:
1835 c
= _clone_node(child
, deep
, newOwnerDocument
)
1836 clone
.appendChild(c
)
1838 elif node
.nodeType
== Node
.TEXT_NODE
:
1839 clone
= newOwnerDocument
.createTextNode(node
.data
)
1840 elif node
.nodeType
== Node
.CDATA_SECTION_NODE
:
1841 clone
= newOwnerDocument
.createCDATASection(node
.data
)
1842 elif node
.nodeType
== Node
.PROCESSING_INSTRUCTION_NODE
:
1843 clone
= newOwnerDocument
.createProcessingInstruction(node
.target
,
1845 elif node
.nodeType
== Node
.COMMENT_NODE
:
1846 clone
= newOwnerDocument
.createComment(node
.data
)
1847 elif node
.nodeType
== Node
.ATTRIBUTE_NODE
:
1848 clone
= newOwnerDocument
.createAttributeNS(node
.namespaceURI
,
1850 clone
.specified
= True
1851 clone
.value
= node
.value
1852 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1853 assert node
.ownerDocument
is not newOwnerDocument
1854 operation
= xml
.dom
.UserDataHandler
.NODE_IMPORTED
1855 clone
= newOwnerDocument
.implementation
.createDocumentType(
1856 node
.name
, node
.publicId
, node
.systemId
)
1857 clone
.ownerDocument
= newOwnerDocument
1859 clone
.entities
._seq
= []
1860 clone
.notations
._seq
= []
1861 for n
in node
.notations
._seq
:
1862 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1863 notation
.ownerDocument
= newOwnerDocument
1864 clone
.notations
._seq
.append(notation
)
1865 if hasattr(n
, '_call_user_data_handler'):
1866 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1867 for e
in node
.entities
._seq
:
1868 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1870 entity
.actualEncoding
= e
.actualEncoding
1871 entity
.encoding
= e
.encoding
1872 entity
.version
= e
.version
1873 entity
.ownerDocument
= newOwnerDocument
1874 clone
.entities
._seq
.append(entity
)
1875 if hasattr(e
, '_call_user_data_handler'):
1876 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1878 # Note the cloning of Document and DocumentType nodes is
1879 # implemenetation specific. minidom handles those cases
1880 # directly in the cloneNode() methods.
1881 raise xml
.dom
.NotSupportedErr("Cannot clone node %s" % repr(node
))
1883 # Check for _call_user_data_handler() since this could conceivably
1884 # used with other DOM implementations (one of the FourThought
1886 if hasattr(node
, '_call_user_data_handler'):
1887 node
._call
_user
_data
_handler
(operation
, node
, clone
)
1891 def _nssplit(qualifiedName
):
1892 fields
= qualifiedName
.split(':', 1)
1893 if len(fields
) == 2:
1896 return (None, fields
[0])
1899 def _get_StringIO():
1900 # we can't use cStringIO since it doesn't support Unicode strings
1901 from StringIO
import StringIO
1904 def _do_pulldom_parse(func
, args
, kwargs
):
1905 events
= apply(func
, args
, kwargs
)
1906 toktype
, rootNode
= events
.getEvent()
1907 events
.expandNode(rootNode
)
1911 def parse(file, parser
=None, bufsize
=None):
1912 """Parse a file into a DOM by filename or file object."""
1913 if parser
is None and not bufsize
:
1914 from xml
.dom
import expatbuilder
1915 return expatbuilder
.parse(file)
1917 from xml
.dom
import pulldom
1918 return _do_pulldom_parse(pulldom
.parse
, (file,),
1919 {'parser': parser
, 'bufsize': bufsize
})
1921 def parseString(string
, parser
=None):
1922 """Parse a file into a DOM from a string."""
1924 from xml
.dom
import expatbuilder
1925 return expatbuilder
.parseString(string
)
1927 from xml
.dom
import pulldom
1928 return _do_pulldom_parse(pulldom
.parseString
, (string
,),
1931 def getDOMImplementation(features
=None):
1933 if isinstance(features
, StringTypes
):
1934 features
= domreg
._parse
_feature
_string
(features
)
1935 for f
, v
in features
:
1936 if not Document
.implementation
.hasFeature(f
, v
):
1938 return Document
.implementation