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 xmlcore
.dom
import EMPTY_NAMESPACE
, EMPTY_PREFIX
, XMLNS_NAMESPACE
, domreg
20 from xmlcore
.dom
.minicompat
import *
21 from xmlcore
.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
= (xmlcore
.dom
.Node
.ELEMENT_NODE
,
29 xmlcore
.dom
.Node
.ENTITY_REFERENCE_NODE
)
32 class Node(xmlcore
.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 xmlcore
.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 xmlcore
.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 xmlcore
.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 xmlcore
.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 xmlcore
.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 xmlcore
.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 if data
and L
and L
[-1].nodeType
== child
.nodeType
:
184 node
.data
= node
.data
+ child
.data
185 node
.nextSibling
= child
.nextSibling
189 L
[-1].nextSibling
= child
190 child
.previousSibling
= L
[-1]
192 child
.previousSibling
= None
195 # empty text node; discard
199 L
[-1].nextSibling
= child
200 child
.previousSibling
= L
[-1]
202 child
.previousSibling
= None
204 if child
.nodeType
== Node
.ELEMENT_NODE
:
206 self
.childNodes
[:] = L
208 def cloneNode(self
, deep
):
209 return _clone_node(self
, deep
, self
.ownerDocument
or self
)
211 def isSupported(self
, feature
, version
):
212 return self
.ownerDocument
.implementation
.hasFeature(feature
, version
)
214 def _get_localName(self
):
215 # Overridden in Element and Attr where localName can be Non-Null
218 # Node interfaces from Level 3 (WD 9 April 2002)
220 def isSameNode(self
, other
):
223 def getInterface(self
, feature
):
224 if self
.isSupported(feature
, None):
229 # The "user data" functions use a dictionary that is only present
230 # if some user data has been set, so be careful not to assume it
233 def getUserData(self
, key
):
235 return self
._user
_data
[key
][0]
236 except (AttributeError, KeyError):
239 def setUserData(self
, key
, data
, handler
):
243 except AttributeError:
249 # ignore handlers passed for None
254 d
[key
] = (data
, handler
)
257 def _call_user_data_handler(self
, operation
, src
, dst
):
258 if hasattr(self
, "_user_data"):
259 for key
, (data
, handler
) in self
._user
_data
.items():
260 if handler
is not None:
261 handler
.handle(operation
, key
, data
, src
, dst
)
263 # minidom-specific API:
266 self
.parentNode
= self
.ownerDocument
= None
268 for child
in self
.childNodes
:
270 self
.childNodes
= NodeList()
271 self
.previousSibling
= None
272 self
.nextSibling
= None
274 defproperty(Node
, "firstChild", doc
="First child node, or None.")
275 defproperty(Node
, "lastChild", doc
="Last child node, or None.")
276 defproperty(Node
, "localName", doc
="Namespace-local name of this node.")
279 def _append_child(self
, node
):
280 # fast path with less checks; usable by DOM builders if careful
281 childNodes
= self
.childNodes
283 last
= childNodes
[-1]
284 node
.__dict
__["previousSibling"] = last
285 last
.__dict
__["nextSibling"] = node
286 childNodes
.append(node
)
287 node
.__dict
__["parentNode"] = self
289 def _in_document(node
):
290 # return True iff node is part of a document tree
291 while node
is not None:
292 if node
.nodeType
== Node
.DOCUMENT_NODE
:
294 node
= node
.parentNode
297 def _write_data(writer
, data
):
298 "Writes datachars to writer."
299 data
= data
.replace("&", "&").replace("<", "<")
300 data
= data
.replace("\"", """).replace(">", ">")
303 def _get_elements_by_tagName_helper(parent
, name
, rc
):
304 for node
in parent
.childNodes
:
305 if node
.nodeType
== Node
.ELEMENT_NODE
and \
306 (name
== "*" or node
.tagName
== name
):
308 _get_elements_by_tagName_helper(node
, name
, rc
)
311 def _get_elements_by_tagName_ns_helper(parent
, nsURI
, localName
, rc
):
312 for node
in parent
.childNodes
:
313 if node
.nodeType
== Node
.ELEMENT_NODE
:
314 if ((localName
== "*" or node
.localName
== localName
) and
315 (nsURI
== "*" or node
.namespaceURI
== nsURI
)):
317 _get_elements_by_tagName_ns_helper(node
, nsURI
, localName
, rc
)
320 class DocumentFragment(Node
):
321 nodeType
= Node
.DOCUMENT_FRAGMENT_NODE
322 nodeName
= "#document-fragment"
326 _child_node_types
= (Node
.ELEMENT_NODE
,
328 Node
.CDATA_SECTION_NODE
,
329 Node
.ENTITY_REFERENCE_NODE
,
330 Node
.PROCESSING_INSTRUCTION_NODE
,
335 self
.childNodes
= NodeList()
339 nodeType
= Node
.ATTRIBUTE_NODE
345 _child_node_types
= (Node
.TEXT_NODE
, Node
.ENTITY_REFERENCE_NODE
)
347 def __init__(self
, qName
, namespaceURI
=EMPTY_NAMESPACE
, localName
=None,
349 # skip setattr for performance
351 d
["nodeName"] = d
["name"] = qName
352 d
["namespaceURI"] = namespaceURI
354 d
['childNodes'] = NodeList()
356 # Add the single child node that represents the value of the attr
357 self
.childNodes
.append(Text())
359 # nodeValue and value are set elsewhere
361 def _get_localName(self
):
362 return self
.nodeName
.split(":", 1)[-1]
367 def _get_specified(self
):
368 return self
.specified
370 def __setattr__(self
, name
, value
):
372 if name
in ("value", "nodeValue"):
373 d
["value"] = d
["nodeValue"] = value
374 d2
= self
.childNodes
[0].__dict
__
375 d2
["data"] = d2
["nodeValue"] = value
376 if self
.ownerElement
is not None:
377 _clear_id_cache(self
.ownerElement
)
378 elif name
in ("name", "nodeName"):
379 d
["name"] = d
["nodeName"] = value
380 if self
.ownerElement
is not None:
381 _clear_id_cache(self
.ownerElement
)
385 def _set_prefix(self
, prefix
):
386 nsuri
= self
.namespaceURI
387 if prefix
== "xmlns":
388 if nsuri
and nsuri
!= XMLNS_NAMESPACE
:
389 raise xmlcore
.dom
.NamespaceErr(
390 "illegal use of 'xmlns' prefix for the wrong namespace")
394 newName
= self
.localName
396 newName
= "%s:%s" % (prefix
, self
.localName
)
397 if self
.ownerElement
:
398 _clear_id_cache(self
.ownerElement
)
399 d
['nodeName'] = d
['name'] = newName
401 def _set_value(self
, value
):
403 d
['value'] = d
['nodeValue'] = value
404 if self
.ownerElement
:
405 _clear_id_cache(self
.ownerElement
)
406 self
.childNodes
[0].data
= value
409 # This implementation does not call the base implementation
410 # since most of that is not needed, and the expense of the
411 # method call is not warranted. We duplicate the removal of
412 # children, but that's all we needed from the base class.
413 elem
= self
.ownerElement
415 del elem
._attrs
[self
.nodeName
]
416 del elem
._attrsNS
[(self
.namespaceURI
, self
.localName
)]
419 elem
._magic
_id
_nodes
-= 1
420 self
.ownerDocument
._magic
_id
_count
-= 1
421 for child
in self
.childNodes
:
423 del self
.childNodes
[:]
428 doc
= self
.ownerDocument
429 elem
= self
.ownerElement
430 if doc
is None or elem
is None:
433 info
= doc
._get
_elem
_info
(elem
)
436 if self
.namespaceURI
:
437 return info
.isIdNS(self
.namespaceURI
, self
.localName
)
439 return info
.isId(self
.nodeName
)
441 def _get_schemaType(self
):
442 doc
= self
.ownerDocument
443 elem
= self
.ownerElement
444 if doc
is None or elem
is None:
447 info
= doc
._get
_elem
_info
(elem
)
450 if self
.namespaceURI
:
451 return info
.getAttributeTypeNS(self
.namespaceURI
, self
.localName
)
453 return info
.getAttributeType(self
.nodeName
)
455 defproperty(Attr
, "isId", doc
="True if this attribute is an ID.")
456 defproperty(Attr
, "localName", doc
="Namespace-local name of this attribute.")
457 defproperty(Attr
, "schemaType", doc
="Schema type for this attribute.")
460 class NamedNodeMap(object):
461 """The attribute list is a transient interface to the underlying
462 dictionaries. Mutations here will change the underlying element's
465 Ordering is imposed artificially and does not reflect the order of
466 attributes as found in an input document.
469 __slots__
= ('_attrs', '_attrsNS', '_ownerElement')
471 def __init__(self
, attrs
, attrsNS
, ownerElement
):
473 self
._attrsNS
= attrsNS
474 self
._ownerElement
= ownerElement
476 def _get_length(self
):
477 return len(self
._attrs
)
479 def item(self
, index
):
481 return self
[self
._attrs
.keys()[index
]]
487 for node
in self
._attrs
.values():
488 L
.append((node
.nodeName
, node
.value
))
493 for node
in self
._attrs
.values():
494 L
.append(((node
.namespaceURI
, node
.localName
), node
.value
))
497 def has_key(self
, key
):
498 if isinstance(key
, StringTypes
):
499 return self
._attrs
.has_key(key
)
501 return self
._attrsNS
.has_key(key
)
504 return self
._attrs
.keys()
507 return self
._attrsNS
.keys()
510 return self
._attrs
.values()
512 def get(self
, name
, value
=None):
513 return self
._attrs
.get(name
, value
)
515 __len__
= _get_length
517 def __cmp__(self
, other
):
518 if self
._attrs
is getattr(other
, "_attrs", None):
521 return cmp(id(self
), id(other
))
523 def __getitem__(self
, attname_or_tuple
):
524 if isinstance(attname_or_tuple
, tuple):
525 return self
._attrsNS
[attname_or_tuple
]
527 return self
._attrs
[attname_or_tuple
]
530 def __setitem__(self
, attname
, value
):
531 if isinstance(value
, StringTypes
):
533 node
= self
._attrs
[attname
]
536 node
.ownerDocument
= self
._ownerElement
.ownerDocument
537 self
.setNamedItem(node
)
540 if not isinstance(value
, Attr
):
541 raise TypeError, "value must be a string or Attr object"
543 self
.setNamedItem(node
)
545 def getNamedItem(self
, name
):
547 return self
._attrs
[name
]
551 def getNamedItemNS(self
, namespaceURI
, localName
):
553 return self
._attrsNS
[(namespaceURI
, localName
)]
557 def removeNamedItem(self
, name
):
558 n
= self
.getNamedItem(name
)
560 _clear_id_cache(self
._ownerElement
)
561 del self
._attrs
[n
.nodeName
]
562 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
563 if n
.__dict
__.has_key('ownerElement'):
564 n
.__dict
__['ownerElement'] = None
567 raise xmlcore
.dom
.NotFoundErr()
569 def removeNamedItemNS(self
, namespaceURI
, localName
):
570 n
= self
.getNamedItemNS(namespaceURI
, localName
)
572 _clear_id_cache(self
._ownerElement
)
573 del self
._attrsNS
[(n
.namespaceURI
, n
.localName
)]
574 del self
._attrs
[n
.nodeName
]
575 if n
.__dict
__.has_key('ownerElement'):
576 n
.__dict
__['ownerElement'] = None
579 raise xmlcore
.dom
.NotFoundErr()
581 def setNamedItem(self
, node
):
582 if not isinstance(node
, Attr
):
583 raise xmlcore
.dom
.HierarchyRequestErr(
584 "%s cannot be child of %s" % (repr(node
), repr(self
)))
585 old
= self
._attrs
.get(node
.name
)
588 self
._attrs
[node
.name
] = node
589 self
._attrsNS
[(node
.namespaceURI
, node
.localName
)] = node
590 node
.ownerElement
= self
._ownerElement
591 _clear_id_cache(node
.ownerElement
)
594 def setNamedItemNS(self
, node
):
595 return self
.setNamedItem(node
)
597 def __delitem__(self
, attname_or_tuple
):
598 node
= self
[attname_or_tuple
]
599 _clear_id_cache(node
.ownerElement
)
602 def __getstate__(self
):
603 return self
._attrs
, self
._attrsNS
, self
._ownerElement
605 def __setstate__(self
, state
):
606 self
._attrs
, self
._attrsNS
, self
._ownerElement
= state
608 defproperty(NamedNodeMap
, "length",
609 doc
="Number of nodes in the NamedNodeMap.")
611 AttributeList
= NamedNodeMap
614 class TypeInfo(object):
615 __slots__
= 'namespace', 'name'
617 def __init__(self
, namespace
, name
):
618 self
.namespace
= namespace
623 return "<TypeInfo %r (from %r)>" % (self
.name
, self
.namespace
)
625 return "<TypeInfo %r>" % self
.name
630 def _get_namespace(self
):
631 return self
.namespace
633 _no_type
= TypeInfo(None, None)
636 nodeType
= Node
.ELEMENT_NODE
638 schemaType
= _no_type
642 _child_node_types
= (Node
.ELEMENT_NODE
,
643 Node
.PROCESSING_INSTRUCTION_NODE
,
646 Node
.CDATA_SECTION_NODE
,
647 Node
.ENTITY_REFERENCE_NODE
)
649 def __init__(self
, tagName
, namespaceURI
=EMPTY_NAMESPACE
, prefix
=None,
651 self
.tagName
= self
.nodeName
= tagName
653 self
.namespaceURI
= namespaceURI
654 self
.childNodes
= NodeList()
656 self
._attrs
= {} # attributes are double-indexed:
657 self
._attrsNS
= {} # tagName -> Attribute
658 # URI,localName -> Attribute
659 # in the future: consider lazy generation
660 # of attribute objects this is too tricky
661 # for now because of headaches with
664 def _get_localName(self
):
665 return self
.tagName
.split(":", 1)[-1]
667 def _get_tagName(self
):
671 for attr
in self
._attrs
.values():
677 def getAttribute(self
, attname
):
679 return self
._attrs
[attname
].value
683 def getAttributeNS(self
, namespaceURI
, localName
):
685 return self
._attrsNS
[(namespaceURI
, localName
)].value
689 def setAttribute(self
, attname
, value
):
690 attr
= self
.getAttributeNode(attname
)
695 d
["value"] = d
["nodeValue"] = value
696 d
["ownerDocument"] = self
.ownerDocument
697 self
.setAttributeNode(attr
)
698 elif value
!= attr
.value
:
700 d
["value"] = d
["nodeValue"] = value
702 _clear_id_cache(self
)
704 def setAttributeNS(self
, namespaceURI
, qualifiedName
, value
):
705 prefix
, localname
= _nssplit(qualifiedName
)
706 attr
= self
.getAttributeNodeNS(namespaceURI
, localname
)
709 attr
= Attr(qualifiedName
, namespaceURI
, localname
, prefix
)
712 d
["nodeName"] = qualifiedName
713 d
["value"] = d
["nodeValue"] = value
714 d
["ownerDocument"] = self
.ownerDocument
715 self
.setAttributeNode(attr
)
718 if value
!= attr
.value
:
719 d
["value"] = d
["nodeValue"] = value
721 _clear_id_cache(self
)
722 if attr
.prefix
!= prefix
:
724 d
["nodeName"] = qualifiedName
726 def getAttributeNode(self
, attrname
):
727 return self
._attrs
.get(attrname
)
729 def getAttributeNodeNS(self
, namespaceURI
, localName
):
730 return self
._attrsNS
.get((namespaceURI
, localName
))
732 def setAttributeNode(self
, attr
):
733 if attr
.ownerElement
not in (None, self
):
734 raise xmlcore
.dom
.InuseAttributeErr("attribute node already owned")
735 old1
= self
._attrs
.get(attr
.name
, None)
737 self
.removeAttributeNode(old1
)
738 old2
= self
._attrsNS
.get((attr
.namespaceURI
, attr
.localName
), None)
739 if old2
is not None and old2
is not old1
:
740 self
.removeAttributeNode(old2
)
741 _set_attribute_node(self
, attr
)
744 # It might have already been part of this node, in which case
745 # it doesn't represent a change, and should not be returned.
750 setAttributeNodeNS
= setAttributeNode
752 def removeAttribute(self
, name
):
754 attr
= self
._attrs
[name
]
756 raise xmlcore
.dom
.NotFoundErr()
757 self
.removeAttributeNode(attr
)
759 def removeAttributeNS(self
, namespaceURI
, localName
):
761 attr
= self
._attrsNS
[(namespaceURI
, localName
)]
763 raise xmlcore
.dom
.NotFoundErr()
764 self
.removeAttributeNode(attr
)
766 def removeAttributeNode(self
, node
):
768 raise xmlcore
.dom
.NotFoundErr()
770 self
._attrs
[node
.name
]
772 raise xmlcore
.dom
.NotFoundErr()
773 _clear_id_cache(self
)
775 # Restore this since the node is still useful and otherwise
777 node
.ownerDocument
= self
.ownerDocument
779 removeAttributeNodeNS
= removeAttributeNode
781 def hasAttribute(self
, name
):
782 return self
._attrs
.has_key(name
)
784 def hasAttributeNS(self
, namespaceURI
, localName
):
785 return self
._attrsNS
.has_key((namespaceURI
, localName
))
787 def getElementsByTagName(self
, name
):
788 return _get_elements_by_tagName_helper(self
, name
, NodeList())
790 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
791 return _get_elements_by_tagName_ns_helper(
792 self
, namespaceURI
, localName
, NodeList())
795 return "<DOM Element: %s at %#x>" % (self
.tagName
, id(self
))
797 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
798 # indent = current indentation
799 # addindent = indentation to add to higher levels
800 # newl = newline string
801 writer
.write(indent
+"<" + self
.tagName
)
803 attrs
= self
._get
_attributes
()
804 a_names
= attrs
.keys()
807 for a_name
in a_names
:
808 writer
.write(" %s=\"" % a_name
)
809 _write_data(writer
, attrs
[a_name
].value
)
812 writer
.write(">%s"%(newl))
813 for node
in self
.childNodes
:
814 node
.writexml(writer
,indent
+addindent
,addindent
,newl
)
815 writer
.write("%s</%s>%s" % (indent
,self
.tagName
,newl
))
817 writer
.write("/>%s"%(newl))
819 def _get_attributes(self
):
820 return NamedNodeMap(self
._attrs
, self
._attrsNS
, self
)
822 def hasAttributes(self
):
828 # DOM Level 3 attributes, based on the 22 Oct 2002 draft
830 def setIdAttribute(self
, name
):
831 idAttr
= self
.getAttributeNode(name
)
832 self
.setIdAttributeNode(idAttr
)
834 def setIdAttributeNS(self
, namespaceURI
, localName
):
835 idAttr
= self
.getAttributeNodeNS(namespaceURI
, localName
)
836 self
.setIdAttributeNode(idAttr
)
838 def setIdAttributeNode(self
, idAttr
):
839 if idAttr
is None or not self
.isSameNode(idAttr
.ownerElement
):
840 raise xmlcore
.dom
.NotFoundErr()
841 if _get_containing_entref(self
) is not None:
842 raise xmlcore
.dom
.NoModificationAllowedErr()
843 if not idAttr
._is
_id
:
844 idAttr
.__dict
__['_is_id'] = True
845 self
._magic
_id
_nodes
+= 1
846 self
.ownerDocument
._magic
_id
_count
+= 1
847 _clear_id_cache(self
)
849 defproperty(Element
, "attributes",
850 doc
="NamedNodeMap of attributes on the element.")
851 defproperty(Element
, "localName",
852 doc
="Namespace-local name of this element.")
855 def _set_attribute_node(element
, attr
):
856 _clear_id_cache(element
)
857 element
._attrs
[attr
.name
] = attr
858 element
._attrsNS
[(attr
.namespaceURI
, attr
.localName
)] = attr
860 # This creates a circular reference, but Element.unlink()
861 # breaks the cycle since the references to the attribute
862 # dictionaries are tossed.
863 attr
.__dict
__['ownerElement'] = element
867 """Mixin that makes childless-ness easy to implement and avoids
868 the complexity of the Node methods that deal with children.
872 childNodes
= EmptyNodeList()
876 def _get_firstChild(self
):
879 def _get_lastChild(self
):
882 def appendChild(self
, node
):
883 raise xmlcore
.dom
.HierarchyRequestErr(
884 self
.nodeName
+ " nodes cannot have children")
886 def hasChildNodes(self
):
889 def insertBefore(self
, newChild
, refChild
):
890 raise xmlcore
.dom
.HierarchyRequestErr(
891 self
.nodeName
+ " nodes do not have children")
893 def removeChild(self
, oldChild
):
894 raise xmlcore
.dom
.NotFoundErr(
895 self
.nodeName
+ " nodes do not have children")
897 def replaceChild(self
, newChild
, oldChild
):
898 raise xmlcore
.dom
.HierarchyRequestErr(
899 self
.nodeName
+ " nodes do not have children")
902 class ProcessingInstruction(Childless
, Node
):
903 nodeType
= Node
.PROCESSING_INSTRUCTION_NODE
905 def __init__(self
, target
, data
):
906 self
.target
= self
.nodeName
= target
907 self
.data
= self
.nodeValue
= data
911 def _set_data(self
, value
):
913 d
['data'] = d
['nodeValue'] = value
915 def _get_target(self
):
917 def _set_target(self
, value
):
919 d
['target'] = d
['nodeName'] = value
921 def __setattr__(self
, name
, value
):
922 if name
== "data" or name
== "nodeValue":
923 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
924 elif name
== "target" or name
== "nodeName":
925 self
.__dict
__['target'] = self
.__dict
__['nodeName'] = value
927 self
.__dict
__[name
] = value
929 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
930 writer
.write("%s<?%s %s?>%s" % (indent
,self
.target
, self
.data
, newl
))
933 class CharacterData(Childless
, Node
):
934 def _get_length(self
):
935 return len(self
.data
)
936 __len__
= _get_length
939 return self
.__dict
__['data']
940 def _set_data(self
, data
):
942 d
['data'] = d
['nodeValue'] = data
944 _get_nodeValue
= _get_data
945 _set_nodeValue
= _set_data
947 def __setattr__(self
, name
, value
):
948 if name
== "data" or name
== "nodeValue":
949 self
.__dict
__['data'] = self
.__dict
__['nodeValue'] = value
951 self
.__dict
__[name
] = value
959 return "<DOM %s node \"%s%s\">" % (
960 self
.__class
__.__name
__, data
[0:10], dotdotdot
)
962 def substringData(self
, offset
, count
):
964 raise xmlcore
.dom
.IndexSizeErr("offset cannot be negative")
965 if offset
>= len(self
.data
):
966 raise xmlcore
.dom
.IndexSizeErr("offset cannot be beyond end of data")
968 raise xmlcore
.dom
.IndexSizeErr("count cannot be negative")
969 return self
.data
[offset
:offset
+count
]
971 def appendData(self
, arg
):
972 self
.data
= self
.data
+ arg
974 def insertData(self
, offset
, arg
):
976 raise xmlcore
.dom
.IndexSizeErr("offset cannot be negative")
977 if offset
>= len(self
.data
):
978 raise xmlcore
.dom
.IndexSizeErr("offset cannot be beyond end of data")
980 self
.data
= "%s%s%s" % (
981 self
.data
[:offset
], arg
, self
.data
[offset
:])
983 def deleteData(self
, offset
, count
):
985 raise xmlcore
.dom
.IndexSizeErr("offset cannot be negative")
986 if offset
>= len(self
.data
):
987 raise xmlcore
.dom
.IndexSizeErr("offset cannot be beyond end of data")
989 raise xmlcore
.dom
.IndexSizeErr("count cannot be negative")
991 self
.data
= self
.data
[:offset
] + self
.data
[offset
+count
:]
993 def replaceData(self
, offset
, count
, arg
):
995 raise xmlcore
.dom
.IndexSizeErr("offset cannot be negative")
996 if offset
>= len(self
.data
):
997 raise xmlcore
.dom
.IndexSizeErr("offset cannot be beyond end of data")
999 raise xmlcore
.dom
.IndexSizeErr("count cannot be negative")
1001 self
.data
= "%s%s%s" % (
1002 self
.data
[:offset
], arg
, self
.data
[offset
+count
:])
1004 defproperty(CharacterData
, "length", doc
="Length of the string data.")
1007 class Text(CharacterData
):
1008 # Make sure we don't add an instance __dict__ if we don't already
1009 # have one, at least when that's possible:
1010 # XXX this does not work, CharacterData is an old-style class
1013 nodeType
= Node
.TEXT_NODE
1017 def splitText(self
, offset
):
1018 if offset
< 0 or offset
> len(self
.data
):
1019 raise xmlcore
.dom
.IndexSizeErr("illegal offset value")
1020 newText
= self
.__class
__()
1021 newText
.data
= self
.data
[offset
:]
1022 newText
.ownerDocument
= self
.ownerDocument
1023 next
= self
.nextSibling
1024 if self
.parentNode
and self
in self
.parentNode
.childNodes
:
1026 self
.parentNode
.appendChild(newText
)
1028 self
.parentNode
.insertBefore(newText
, next
)
1029 self
.data
= self
.data
[:offset
]
1032 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1033 _write_data(writer
, "%s%s%s"%(indent
, self
.data
, newl
))
1035 # DOM Level 3 (WD 9 April 2002)
1037 def _get_wholeText(self
):
1039 n
= self
.previousSibling
1040 while n
is not None:
1041 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1043 n
= n
.previousSibling
1046 n
= self
.nextSibling
1047 while n
is not None:
1048 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1055 def replaceWholeText(self
, content
):
1056 # XXX This needs to be seriously changed if minidom ever
1057 # supports EntityReference nodes.
1058 parent
= self
.parentNode
1059 n
= self
.previousSibling
1060 while n
is not None:
1061 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1062 next
= n
.previousSibling
1063 parent
.removeChild(n
)
1067 n
= self
.nextSibling
1069 parent
.removeChild(self
)
1070 while n
is not None:
1071 if n
.nodeType
in (Node
.TEXT_NODE
, Node
.CDATA_SECTION_NODE
):
1072 next
= n
.nextSibling
1073 parent
.removeChild(n
)
1080 d
['nodeValue'] = content
1085 def _get_isWhitespaceInElementContent(self
):
1086 if self
.data
.strip():
1088 elem
= _get_containing_element(self
)
1091 info
= self
.ownerDocument
._get
_elem
_info
(elem
)
1095 return info
.isElementContent()
1097 defproperty(Text
, "isWhitespaceInElementContent",
1098 doc
="True iff this text node contains only whitespace"
1099 " and is in element content.")
1100 defproperty(Text
, "wholeText",
1101 doc
="The text of all logically-adjacent text nodes.")
1104 def _get_containing_element(node
):
1106 while c
is not None:
1107 if c
.nodeType
== Node
.ELEMENT_NODE
:
1112 def _get_containing_entref(node
):
1114 while c
is not None:
1115 if c
.nodeType
== Node
.ENTITY_REFERENCE_NODE
:
1121 class Comment(Childless
, CharacterData
):
1122 nodeType
= Node
.COMMENT_NODE
1123 nodeName
= "#comment"
1125 def __init__(self
, data
):
1126 self
.data
= self
.nodeValue
= data
1128 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1129 writer
.write("%s<!--%s-->%s" % (indent
, self
.data
, newl
))
1132 class CDATASection(Text
):
1133 # Make sure we don't add an instance __dict__ if we don't already
1134 # have one, at least when that's possible:
1135 # XXX this does not work, Text is an old-style class
1138 nodeType
= Node
.CDATA_SECTION_NODE
1139 nodeName
= "#cdata-section"
1141 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1142 if self
.data
.find("]]>") >= 0:
1143 raise ValueError("']]>' not allowed in a CDATA section")
1144 writer
.write("<![CDATA[%s]]>" % self
.data
)
1147 class ReadOnlySequentialNamedNodeMap(object):
1150 def __init__(self
, seq
=()):
1151 # seq should be a list or tuple
1155 return len(self
._seq
)
1157 def _get_length(self
):
1158 return len(self
._seq
)
1160 def getNamedItem(self
, name
):
1162 if n
.nodeName
== name
:
1165 def getNamedItemNS(self
, namespaceURI
, localName
):
1167 if n
.namespaceURI
== namespaceURI
and n
.localName
== localName
:
1170 def __getitem__(self
, name_or_tuple
):
1171 if isinstance(name_or_tuple
, tuple):
1172 node
= self
.getNamedItemNS(*name_or_tuple
)
1174 node
= self
.getNamedItem(name_or_tuple
)
1176 raise KeyError, name_or_tuple
1179 def item(self
, index
):
1183 return self
._seq
[index
]
1187 def removeNamedItem(self
, name
):
1188 raise xmlcore
.dom
.NoModificationAllowedErr(
1189 "NamedNodeMap instance is read-only")
1191 def removeNamedItemNS(self
, namespaceURI
, localName
):
1192 raise xmlcore
.dom
.NoModificationAllowedErr(
1193 "NamedNodeMap instance is read-only")
1195 def setNamedItem(self
, node
):
1196 raise xmlcore
.dom
.NoModificationAllowedErr(
1197 "NamedNodeMap instance is read-only")
1199 def setNamedItemNS(self
, node
):
1200 raise xmlcore
.dom
.NoModificationAllowedErr(
1201 "NamedNodeMap instance is read-only")
1203 def __getstate__(self
):
1206 def __setstate__(self
, state
):
1207 self
._seq
= state
[0]
1209 defproperty(ReadOnlySequentialNamedNodeMap
, "length",
1210 doc
="Number of entries in the NamedNodeMap.")
1214 """Mix-in class that supports the publicId and systemId attributes."""
1216 # XXX this does not work, this is an old-style class
1217 # __slots__ = 'publicId', 'systemId'
1219 def _identified_mixin_init(self
, publicId
, systemId
):
1220 self
.publicId
= publicId
1221 self
.systemId
= systemId
1223 def _get_publicId(self
):
1224 return self
.publicId
1226 def _get_systemId(self
):
1227 return self
.systemId
1229 class DocumentType(Identified
, Childless
, Node
):
1230 nodeType
= Node
.DOCUMENT_TYPE_NODE
1235 internalSubset
= None
1237 def __init__(self
, qualifiedName
):
1238 self
.entities
= ReadOnlySequentialNamedNodeMap()
1239 self
.notations
= ReadOnlySequentialNamedNodeMap()
1241 prefix
, localname
= _nssplit(qualifiedName
)
1242 self
.name
= localname
1243 self
.nodeName
= self
.name
1245 def _get_internalSubset(self
):
1246 return self
.internalSubset
1248 def cloneNode(self
, deep
):
1249 if self
.ownerDocument
is None:
1251 clone
= DocumentType(None)
1252 clone
.name
= self
.name
1253 clone
.nodeName
= self
.name
1254 operation
= xmlcore
.dom
.UserDataHandler
.NODE_CLONED
1256 clone
.entities
._seq
= []
1257 clone
.notations
._seq
= []
1258 for n
in self
.notations
._seq
:
1259 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1260 clone
.notations
._seq
.append(notation
)
1261 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1262 for e
in self
.entities
._seq
:
1263 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1265 entity
.actualEncoding
= e
.actualEncoding
1266 entity
.encoding
= e
.encoding
1267 entity
.version
= e
.version
1268 clone
.entities
._seq
.append(entity
)
1269 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1270 self
._call
_user
_data
_handler
(operation
, self
, clone
)
1275 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
1276 writer
.write("<!DOCTYPE ")
1277 writer
.write(self
.name
)
1279 writer
.write("%s PUBLIC '%s'%s '%s'"
1280 % (newl
, self
.publicId
, newl
, self
.systemId
))
1282 writer
.write("%s SYSTEM '%s'" % (newl
, self
.systemId
))
1283 if self
.internalSubset
is not None:
1285 writer
.write(self
.internalSubset
)
1287 writer
.write(">"+newl
)
1289 class Entity(Identified
, Node
):
1291 nodeType
= Node
.ENTITY_NODE
1294 actualEncoding
= None
1298 def __init__(self
, name
, publicId
, systemId
, notation
):
1299 self
.nodeName
= name
1300 self
.notationName
= notation
1301 self
.childNodes
= NodeList()
1302 self
._identified
_mixin
_init
(publicId
, systemId
)
1304 def _get_actualEncoding(self
):
1305 return self
.actualEncoding
1307 def _get_encoding(self
):
1308 return self
.encoding
1310 def _get_version(self
):
1313 def appendChild(self
, newChild
):
1314 raise xmlcore
.dom
.HierarchyRequestErr(
1315 "cannot append children to an entity node")
1317 def insertBefore(self
, newChild
, refChild
):
1318 raise xmlcore
.dom
.HierarchyRequestErr(
1319 "cannot insert children below an entity node")
1321 def removeChild(self
, oldChild
):
1322 raise xmlcore
.dom
.HierarchyRequestErr(
1323 "cannot remove children from an entity node")
1325 def replaceChild(self
, newChild
, oldChild
):
1326 raise xmlcore
.dom
.HierarchyRequestErr(
1327 "cannot replace children of an entity node")
1329 class Notation(Identified
, Childless
, Node
):
1330 nodeType
= Node
.NOTATION_NODE
1333 def __init__(self
, name
, publicId
, systemId
):
1334 self
.nodeName
= name
1335 self
._identified
_mixin
_init
(publicId
, systemId
)
1338 class DOMImplementation(DOMImplementationLS
):
1339 _features
= [("core", "1.0"),
1351 def hasFeature(self
, feature
, version
):
1354 return (feature
.lower(), version
) in self
._features
1356 def createDocument(self
, namespaceURI
, qualifiedName
, doctype
):
1357 if doctype
and doctype
.parentNode
is not None:
1358 raise xmlcore
.dom
.WrongDocumentErr(
1359 "doctype object owned by another DOM tree")
1360 doc
= self
._create
_document
()
1362 add_root_element
= not (namespaceURI
is None
1363 and qualifiedName
is None
1364 and doctype
is None)
1366 if not qualifiedName
and add_root_element
:
1367 # The spec is unclear what to raise here; SyntaxErr
1368 # would be the other obvious candidate. Since Xerces raises
1369 # InvalidCharacterErr, and since SyntaxErr is not listed
1370 # for createDocument, that seems to be the better choice.
1371 # XXX: need to check for illegal characters here and in
1374 # DOM Level III clears this up when talking about the return value
1375 # of this function. If namespaceURI, qName and DocType are
1376 # Null the document is returned without a document element
1377 # Otherwise if doctype or namespaceURI are not None
1378 # Then we go back to the above problem
1379 raise xmlcore
.dom
.InvalidCharacterErr("Element with no name")
1381 if add_root_element
:
1382 prefix
, localname
= _nssplit(qualifiedName
)
1383 if prefix
== "xml" \
1384 and namespaceURI
!= "http://www.w3.org/XML/1998/namespace":
1385 raise xmlcore
.dom
.NamespaceErr("illegal use of 'xml' prefix")
1386 if prefix
and not namespaceURI
:
1387 raise xmlcore
.dom
.NamespaceErr(
1388 "illegal use of prefix without namespaces")
1389 element
= doc
.createElementNS(namespaceURI
, qualifiedName
)
1391 doc
.appendChild(doctype
)
1392 doc
.appendChild(element
)
1395 doctype
.parentNode
= doctype
.ownerDocument
= doc
1397 doc
.doctype
= doctype
1398 doc
.implementation
= self
1401 def createDocumentType(self
, qualifiedName
, publicId
, systemId
):
1402 doctype
= DocumentType(qualifiedName
)
1403 doctype
.publicId
= publicId
1404 doctype
.systemId
= systemId
1407 # DOM Level 3 (WD 9 April 2002)
1409 def getInterface(self
, feature
):
1410 if self
.hasFeature(feature
, None):
1416 def _create_document(self
):
1419 class ElementInfo(object):
1420 """Object that represents content-model information for an element.
1422 This implementation is not expected to be used in practice; DOM
1423 builders should provide implementations which do the right thing
1424 using information available to it.
1428 __slots__
= 'tagName',
1430 def __init__(self
, name
):
1433 def getAttributeType(self
, aname
):
1436 def getAttributeTypeNS(self
, namespaceURI
, localName
):
1439 def isElementContent(self
):
1443 """Returns true iff this element is declared to have an EMPTY
1447 def isId(self
, aname
):
1448 """Returns true iff the named attribte is a DTD-style ID."""
1451 def isIdNS(self
, namespaceURI
, localName
):
1452 """Returns true iff the identified attribute is a DTD-style ID."""
1455 def __getstate__(self
):
1458 def __setstate__(self
, state
):
1459 self
.tagName
= state
1461 def _clear_id_cache(node
):
1462 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1463 node
._id
_cache
.clear()
1464 node
._id
_search
_stack
= None
1465 elif _in_document(node
):
1466 node
.ownerDocument
._id
_cache
.clear()
1467 node
.ownerDocument
._id
_search
_stack
= None
1469 class Document(Node
, DocumentLS
):
1470 _child_node_types
= (Node
.ELEMENT_NODE
, Node
.PROCESSING_INSTRUCTION_NODE
,
1471 Node
.COMMENT_NODE
, Node
.DOCUMENT_TYPE_NODE
)
1473 nodeType
= Node
.DOCUMENT_NODE
1474 nodeName
= "#document"
1479 previousSibling
= nextSibling
= None
1481 implementation
= DOMImplementation()
1483 # Document attributes from Level 3 (WD 9 April 2002)
1485 actualEncoding
= None
1489 strictErrorChecking
= False
1496 self
.childNodes
= NodeList()
1497 # mapping of (namespaceURI, localName) -> ElementInfo
1498 # and tagName -> ElementInfo
1499 self
._elem
_info
= {}
1501 self
._id
_search
_stack
= None
1503 def _get_elem_info(self
, element
):
1504 if element
.namespaceURI
:
1505 key
= element
.namespaceURI
, element
.localName
1507 key
= element
.tagName
1508 return self
._elem
_info
.get(key
)
1510 def _get_actualEncoding(self
):
1511 return self
.actualEncoding
1513 def _get_doctype(self
):
1516 def _get_documentURI(self
):
1517 return self
.documentURI
1519 def _get_encoding(self
):
1520 return self
.encoding
1522 def _get_errorHandler(self
):
1523 return self
.errorHandler
1525 def _get_standalone(self
):
1526 return self
.standalone
1528 def _get_strictErrorChecking(self
):
1529 return self
.strictErrorChecking
1531 def _get_version(self
):
1534 def appendChild(self
, node
):
1535 if node
.nodeType
not in self
._child
_node
_types
:
1536 raise xmlcore
.dom
.HierarchyRequestErr(
1537 "%s cannot be child of %s" % (repr(node
), repr(self
)))
1538 if node
.parentNode
is not None:
1539 # This needs to be done before the next test since this
1540 # may *be* the document element, in which case it should
1541 # end up re-ordered to the end.
1542 node
.parentNode
.removeChild(node
)
1544 if node
.nodeType
== Node
.ELEMENT_NODE \
1545 and self
._get
_documentElement
():
1546 raise xmlcore
.dom
.HierarchyRequestErr(
1547 "two document elements disallowed")
1548 return Node
.appendChild(self
, node
)
1550 def removeChild(self
, oldChild
):
1552 self
.childNodes
.remove(oldChild
)
1554 raise xmlcore
.dom
.NotFoundErr()
1555 oldChild
.nextSibling
= oldChild
.previousSibling
= None
1556 oldChild
.parentNode
= None
1557 if self
.documentElement
is oldChild
:
1558 self
.documentElement
= None
1562 def _get_documentElement(self
):
1563 for node
in self
.childNodes
:
1564 if node
.nodeType
== Node
.ELEMENT_NODE
:
1568 if self
.doctype
is not None:
1569 self
.doctype
.unlink()
1573 def cloneNode(self
, deep
):
1576 clone
= self
.implementation
.createDocument(None, None, None)
1577 clone
.encoding
= self
.encoding
1578 clone
.standalone
= self
.standalone
1579 clone
.version
= self
.version
1580 for n
in self
.childNodes
:
1581 childclone
= _clone_node(n
, deep
, clone
)
1582 assert childclone
.ownerDocument
.isSameNode(clone
)
1583 clone
.childNodes
.append(childclone
)
1584 if childclone
.nodeType
== Node
.DOCUMENT_NODE
:
1585 assert clone
.documentElement
is None
1586 elif childclone
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1587 assert clone
.doctype
is None
1588 clone
.doctype
= childclone
1589 childclone
.parentNode
= clone
1590 self
._call
_user
_data
_handler
(xmlcore
.dom
.UserDataHandler
.NODE_CLONED
,
1594 def createDocumentFragment(self
):
1595 d
= DocumentFragment()
1596 d
.ownerDocument
= self
1599 def createElement(self
, tagName
):
1600 e
= Element(tagName
)
1601 e
.ownerDocument
= self
1604 def createTextNode(self
, data
):
1605 if not isinstance(data
, StringTypes
):
1606 raise TypeError, "node contents must be a string"
1609 t
.ownerDocument
= self
1612 def createCDATASection(self
, data
):
1613 if not isinstance(data
, StringTypes
):
1614 raise TypeError, "node contents must be a string"
1617 c
.ownerDocument
= self
1620 def createComment(self
, data
):
1622 c
.ownerDocument
= self
1625 def createProcessingInstruction(self
, target
, data
):
1626 p
= ProcessingInstruction(target
, data
)
1627 p
.ownerDocument
= self
1630 def createAttribute(self
, qName
):
1632 a
.ownerDocument
= self
1636 def createElementNS(self
, namespaceURI
, qualifiedName
):
1637 prefix
, localName
= _nssplit(qualifiedName
)
1638 e
= Element(qualifiedName
, namespaceURI
, prefix
)
1639 e
.ownerDocument
= self
1642 def createAttributeNS(self
, namespaceURI
, qualifiedName
):
1643 prefix
, localName
= _nssplit(qualifiedName
)
1644 a
= Attr(qualifiedName
, namespaceURI
, localName
, prefix
)
1645 a
.ownerDocument
= self
1649 # A couple of implementation-specific helpers to create node types
1650 # not supported by the W3C DOM specs:
1652 def _create_entity(self
, name
, publicId
, systemId
, notationName
):
1653 e
= Entity(name
, publicId
, systemId
, notationName
)
1654 e
.ownerDocument
= self
1657 def _create_notation(self
, name
, publicId
, systemId
):
1658 n
= Notation(name
, publicId
, systemId
)
1659 n
.ownerDocument
= self
1662 def getElementById(self
, id):
1663 if self
._id
_cache
.has_key(id):
1664 return self
._id
_cache
[id]
1665 if not (self
._elem
_info
or self
._magic
_id
_count
):
1668 stack
= self
._id
_search
_stack
1670 # we never searched before, or the cache has been cleared
1671 stack
= [self
.documentElement
]
1672 self
._id
_search
_stack
= stack
1674 # Previous search was completed and cache is still valid;
1681 # add child elements to stack for continued searching
1682 stack
.extend([child
for child
in node
.childNodes
1683 if child
.nodeType
in _nodeTypes_with_children
])
1685 info
= self
._get
_elem
_info
(node
)
1687 # We have to process all ID attributes before
1688 # returning in order to get all the attributes set to
1689 # be IDs using Element.setIdAttribute*().
1690 for attr
in node
.attributes
.values():
1691 if attr
.namespaceURI
:
1692 if info
.isIdNS(attr
.namespaceURI
, attr
.localName
):
1693 self
._id
_cache
[attr
.value
] = node
1694 if attr
.value
== id:
1696 elif not node
._magic
_id
_nodes
:
1698 elif info
.isId(attr
.name
):
1699 self
._id
_cache
[attr
.value
] = node
1700 if attr
.value
== id:
1702 elif not node
._magic
_id
_nodes
:
1705 self
._id
_cache
[attr
.value
] = node
1706 if attr
.value
== id:
1708 elif node
._magic
_id
_nodes
== 1:
1710 elif node
._magic
_id
_nodes
:
1711 for attr
in node
.attributes
.values():
1713 self
._id
_cache
[attr
.value
] = node
1714 if attr
.value
== id:
1716 if result
is not None:
1720 def getElementsByTagName(self
, name
):
1721 return _get_elements_by_tagName_helper(self
, name
, NodeList())
1723 def getElementsByTagNameNS(self
, namespaceURI
, localName
):
1724 return _get_elements_by_tagName_ns_helper(
1725 self
, namespaceURI
, localName
, NodeList())
1727 def isSupported(self
, feature
, version
):
1728 return self
.implementation
.hasFeature(feature
, version
)
1730 def importNode(self
, node
, deep
):
1731 if node
.nodeType
== Node
.DOCUMENT_NODE
:
1732 raise xmlcore
.dom
.NotSupportedErr("cannot import document nodes")
1733 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1734 raise xmlcore
.dom
.NotSupportedErr("cannot import document type nodes")
1735 return _clone_node(node
, deep
, self
)
1737 def writexml(self
, writer
, indent
="", addindent
="", newl
="",
1739 if encoding
is None:
1740 writer
.write('<?xml version="1.0" ?>'+newl
)
1742 writer
.write('<?xml version="1.0" encoding="%s"?>%s' % (encoding
, newl
))
1743 for node
in self
.childNodes
:
1744 node
.writexml(writer
, indent
, addindent
, newl
)
1746 # DOM Level 3 (WD 9 April 2002)
1748 def renameNode(self
, n
, namespaceURI
, name
):
1749 if n
.ownerDocument
is not self
:
1750 raise xmlcore
.dom
.WrongDocumentErr(
1751 "cannot rename nodes from other documents;\n"
1752 "expected %s,\nfound %s" % (self
, n
.ownerDocument
))
1753 if n
.nodeType
not in (Node
.ELEMENT_NODE
, Node
.ATTRIBUTE_NODE
):
1754 raise xmlcore
.dom
.NotSupportedErr(
1755 "renameNode() only applies to element and attribute nodes")
1756 if namespaceURI
!= EMPTY_NAMESPACE
:
1758 prefix
, localName
= name
.split(':', 1)
1759 if ( prefix
== "xmlns"
1760 and namespaceURI
!= xmlcore
.dom
.XMLNS_NAMESPACE
):
1761 raise xmlcore
.dom
.NamespaceErr(
1762 "illegal use of 'xmlns' prefix")
1764 if ( name
== "xmlns"
1765 and namespaceURI
!= xmlcore
.dom
.XMLNS_NAMESPACE
1766 and n
.nodeType
== Node
.ATTRIBUTE_NODE
):
1767 raise xmlcore
.dom
.NamespaceErr(
1768 "illegal use of the 'xmlns' attribute")
1774 if n
.nodeType
== Node
.ATTRIBUTE_NODE
:
1775 element
= n
.ownerElement
1776 if element
is not None:
1778 element
.removeAttributeNode(n
)
1783 d
['prefix'] = prefix
1784 d
['localName'] = localName
1785 d
['namespaceURI'] = namespaceURI
1786 d
['nodeName'] = name
1787 if n
.nodeType
== Node
.ELEMENT_NODE
:
1792 if element
is not None:
1793 element
.setAttributeNode(n
)
1795 element
.setIdAttributeNode(n
)
1796 # It's not clear from a semantic perspective whether we should
1797 # call the user data handlers for the NODE_RENAMED event since
1798 # we're re-using the existing node. The draft spec has been
1799 # interpreted as meaning "no, don't call the handler unless a
1800 # new node is created."
1803 defproperty(Document
, "documentElement",
1804 doc
="Top-level element of this document.")
1807 def _clone_node(node
, deep
, newOwnerDocument
):
1809 Clone a node and give it the new owner document.
1810 Called by Node.cloneNode and Document.importNode
1812 if node
.ownerDocument
.isSameNode(newOwnerDocument
):
1813 operation
= xmlcore
.dom
.UserDataHandler
.NODE_CLONED
1815 operation
= xmlcore
.dom
.UserDataHandler
.NODE_IMPORTED
1816 if node
.nodeType
== Node
.ELEMENT_NODE
:
1817 clone
= newOwnerDocument
.createElementNS(node
.namespaceURI
,
1819 for attr
in node
.attributes
.values():
1820 clone
.setAttributeNS(attr
.namespaceURI
, attr
.nodeName
, attr
.value
)
1821 a
= clone
.getAttributeNodeNS(attr
.namespaceURI
, attr
.localName
)
1822 a
.specified
= attr
.specified
1825 for child
in node
.childNodes
:
1826 c
= _clone_node(child
, deep
, newOwnerDocument
)
1827 clone
.appendChild(c
)
1829 elif node
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1830 clone
= newOwnerDocument
.createDocumentFragment()
1832 for child
in node
.childNodes
:
1833 c
= _clone_node(child
, deep
, newOwnerDocument
)
1834 clone
.appendChild(c
)
1836 elif node
.nodeType
== Node
.TEXT_NODE
:
1837 clone
= newOwnerDocument
.createTextNode(node
.data
)
1838 elif node
.nodeType
== Node
.CDATA_SECTION_NODE
:
1839 clone
= newOwnerDocument
.createCDATASection(node
.data
)
1840 elif node
.nodeType
== Node
.PROCESSING_INSTRUCTION_NODE
:
1841 clone
= newOwnerDocument
.createProcessingInstruction(node
.target
,
1843 elif node
.nodeType
== Node
.COMMENT_NODE
:
1844 clone
= newOwnerDocument
.createComment(node
.data
)
1845 elif node
.nodeType
== Node
.ATTRIBUTE_NODE
:
1846 clone
= newOwnerDocument
.createAttributeNS(node
.namespaceURI
,
1848 clone
.specified
= True
1849 clone
.value
= node
.value
1850 elif node
.nodeType
== Node
.DOCUMENT_TYPE_NODE
:
1851 assert node
.ownerDocument
is not newOwnerDocument
1852 operation
= xmlcore
.dom
.UserDataHandler
.NODE_IMPORTED
1853 clone
= newOwnerDocument
.implementation
.createDocumentType(
1854 node
.name
, node
.publicId
, node
.systemId
)
1855 clone
.ownerDocument
= newOwnerDocument
1857 clone
.entities
._seq
= []
1858 clone
.notations
._seq
= []
1859 for n
in node
.notations
._seq
:
1860 notation
= Notation(n
.nodeName
, n
.publicId
, n
.systemId
)
1861 notation
.ownerDocument
= newOwnerDocument
1862 clone
.notations
._seq
.append(notation
)
1863 if hasattr(n
, '_call_user_data_handler'):
1864 n
._call
_user
_data
_handler
(operation
, n
, notation
)
1865 for e
in node
.entities
._seq
:
1866 entity
= Entity(e
.nodeName
, e
.publicId
, e
.systemId
,
1868 entity
.actualEncoding
= e
.actualEncoding
1869 entity
.encoding
= e
.encoding
1870 entity
.version
= e
.version
1871 entity
.ownerDocument
= newOwnerDocument
1872 clone
.entities
._seq
.append(entity
)
1873 if hasattr(e
, '_call_user_data_handler'):
1874 e
._call
_user
_data
_handler
(operation
, n
, entity
)
1876 # Note the cloning of Document and DocumentType nodes is
1877 # implemenetation specific. minidom handles those cases
1878 # directly in the cloneNode() methods.
1879 raise xmlcore
.dom
.NotSupportedErr("Cannot clone node %s" % repr(node
))
1881 # Check for _call_user_data_handler() since this could conceivably
1882 # used with other DOM implementations (one of the FourThought
1884 if hasattr(node
, '_call_user_data_handler'):
1885 node
._call
_user
_data
_handler
(operation
, node
, clone
)
1889 def _nssplit(qualifiedName
):
1890 fields
= qualifiedName
.split(':', 1)
1891 if len(fields
) == 2:
1894 return (None, fields
[0])
1897 def _get_StringIO():
1898 # we can't use cStringIO since it doesn't support Unicode strings
1899 from StringIO
import StringIO
1902 def _do_pulldom_parse(func
, args
, kwargs
):
1903 events
= func(*args
, **kwargs
)
1904 toktype
, rootNode
= events
.getEvent()
1905 events
.expandNode(rootNode
)
1909 def parse(file, parser
=None, bufsize
=None):
1910 """Parse a file into a DOM by filename or file object."""
1911 if parser
is None and not bufsize
:
1912 from xmlcore
.dom
import expatbuilder
1913 return expatbuilder
.parse(file)
1915 from xmlcore
.dom
import pulldom
1916 return _do_pulldom_parse(pulldom
.parse
, (file,),
1917 {'parser': parser
, 'bufsize': bufsize
})
1919 def parseString(string
, parser
=None):
1920 """Parse a file into a DOM from a string."""
1922 from xmlcore
.dom
import expatbuilder
1923 return expatbuilder
.parseString(string
)
1925 from xmlcore
.dom
import pulldom
1926 return _do_pulldom_parse(pulldom
.parseString
, (string
,),
1929 def getDOMImplementation(features
=None):
1931 if isinstance(features
, StringTypes
):
1932 features
= domreg
._parse
_feature
_string
(features
)
1933 for f
, v
in features
:
1934 if not Document
.implementation
.hasFeature(f
, v
):
1936 return Document
.implementation