fix some coding style
[python.git] / Lib / xml / dom / minidom.py
blobb8bd4512d1aaa7b82899f6353e6d3394879b648a
1 """\
2 minidom.py -- a lightweight DOM implementation.
4 parse("foo.xml")
6 parseString("<foo><bar/></foo>")
8 Todo:
9 =====
10 * convenience methods for getting elements and text.
11 * more testing
12 * bring some of the writer and linearizer code into conformance with this
13 interface
14 * SAX 2 namespaces
15 """
17 import xml.dom
19 from xml.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE, domreg
20 from xml.dom.minicompat import *
21 from xml.dom.xmlbuilder import DOMImplementationLS, DocumentLS
23 # This is used by the ID-cache invalidation checks; the list isn't
24 # actually complete, since the nodes being checked will never be the
25 # DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE. (The node being checked is
26 # the node being added or removed, not the node being modified.)
28 _nodeTypes_with_children = (xml.dom.Node.ELEMENT_NODE,
29 xml.dom.Node.ENTITY_REFERENCE_NODE)
32 class Node(xml.dom.Node):
33 namespaceURI = None # this is non-null only for elements and attributes
34 parentNode = None
35 ownerDocument = None
36 nextSibling = None
37 previousSibling = None
39 prefix = EMPTY_PREFIX # non-null only for NS elements and attributes
41 def __nonzero__(self):
42 return True
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:
52 import codecs
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)
58 else:
59 self.writexml(writer, "", indent, newl)
60 return writer.getvalue()
62 def hasChildNodes(self):
63 if self.childNodes:
64 return True
65 else:
66 return False
68 def _get_childNodes(self):
69 return self.childNodes
71 def _get_firstChild(self):
72 if self.childNodes:
73 return self.childNodes[0]
75 def _get_lastChild(self):
76 if self.childNodes:
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
84 return newChild
85 if newChild.nodeType not in self._child_node_types:
86 raise xml.dom.HierarchyRequestErr(
87 "%s cannot be child of %s" % (repr(newChild), repr(self)))
88 if newChild.parentNode is not None:
89 newChild.parentNode.removeChild(newChild)
90 if refChild is None:
91 self.appendChild(newChild)
92 else:
93 try:
94 index = self.childNodes.index(refChild)
95 except ValueError:
96 raise xml.dom.NotFoundErr()
97 if newChild.nodeType in _nodeTypes_with_children:
98 _clear_id_cache(self)
99 self.childNodes.insert(index, newChild)
100 newChild.nextSibling = refChild
101 refChild.previousSibling = newChild
102 if index:
103 node = self.childNodes[index-1]
104 node.nextSibling = newChild
105 newChild.previousSibling = node
106 else:
107 newChild.previousSibling = None
108 newChild.parentNode = self
109 return newChild
111 def appendChild(self, node):
112 if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
113 for c in tuple(node.childNodes):
114 self.appendChild(c)
115 ### The DOM does not clearly specify what to return in this case
116 return node
117 if node.nodeType not in self._child_node_types:
118 raise xml.dom.HierarchyRequestErr(
119 "%s cannot be child of %s" % (repr(node), repr(self)))
120 elif node.nodeType in _nodeTypes_with_children:
121 _clear_id_cache(self)
122 if node.parentNode is not None:
123 node.parentNode.removeChild(node)
124 _append_child(self, node)
125 node.nextSibling = None
126 return node
128 def replaceChild(self, newChild, oldChild):
129 if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
130 refChild = oldChild.nextSibling
131 self.removeChild(oldChild)
132 return self.insertBefore(newChild, refChild)
133 if newChild.nodeType not in self._child_node_types:
134 raise xml.dom.HierarchyRequestErr(
135 "%s cannot be child of %s" % (repr(newChild), repr(self)))
136 if newChild is oldChild:
137 return
138 if newChild.parentNode is not None:
139 newChild.parentNode.removeChild(newChild)
140 try:
141 index = self.childNodes.index(oldChild)
142 except ValueError:
143 raise xml.dom.NotFoundErr()
144 self.childNodes[index] = newChild
145 newChild.parentNode = self
146 oldChild.parentNode = None
147 if (newChild.nodeType in _nodeTypes_with_children
148 or oldChild.nodeType in _nodeTypes_with_children):
149 _clear_id_cache(self)
150 newChild.nextSibling = oldChild.nextSibling
151 newChild.previousSibling = oldChild.previousSibling
152 oldChild.nextSibling = None
153 oldChild.previousSibling = None
154 if newChild.previousSibling:
155 newChild.previousSibling.nextSibling = newChild
156 if newChild.nextSibling:
157 newChild.nextSibling.previousSibling = newChild
158 return oldChild
160 def removeChild(self, oldChild):
161 try:
162 self.childNodes.remove(oldChild)
163 except ValueError:
164 raise xml.dom.NotFoundErr()
165 if oldChild.nextSibling is not None:
166 oldChild.nextSibling.previousSibling = oldChild.previousSibling
167 if oldChild.previousSibling is not None:
168 oldChild.previousSibling.nextSibling = oldChild.nextSibling
169 oldChild.nextSibling = oldChild.previousSibling = None
170 if oldChild.nodeType in _nodeTypes_with_children:
171 _clear_id_cache(self)
173 oldChild.parentNode = None
174 return oldChild
176 def normalize(self):
177 L = []
178 for child in self.childNodes:
179 if child.nodeType == Node.TEXT_NODE:
180 if not child.data:
181 # empty text node; discard
182 if L:
183 L[-1].nextSibling = child.nextSibling
184 if child.nextSibling:
185 child.nextSibling.previousSibling = child.previousSibling
186 child.unlink()
187 elif L and L[-1].nodeType == child.nodeType:
188 # collapse text node
189 node = L[-1]
190 node.data = node.data + child.data
191 node.nextSibling = child.nextSibling
192 if child.nextSibling:
193 child.nextSibling.previousSibling = node
194 child.unlink()
195 else:
196 L.append(child)
197 else:
198 L.append(child)
199 if child.nodeType == Node.ELEMENT_NODE:
200 child.normalize()
201 self.childNodes[:] = L
203 def cloneNode(self, deep):
204 return _clone_node(self, deep, self.ownerDocument or self)
206 def isSupported(self, feature, version):
207 return self.ownerDocument.implementation.hasFeature(feature, version)
209 def _get_localName(self):
210 # Overridden in Element and Attr where localName can be Non-Null
211 return None
213 # Node interfaces from Level 3 (WD 9 April 2002)
215 def isSameNode(self, other):
216 return self is other
218 def getInterface(self, feature):
219 if self.isSupported(feature, None):
220 return self
221 else:
222 return None
224 # The "user data" functions use a dictionary that is only present
225 # if some user data has been set, so be careful not to assume it
226 # exists.
228 def getUserData(self, key):
229 try:
230 return self._user_data[key][0]
231 except (AttributeError, KeyError):
232 return None
234 def setUserData(self, key, data, handler):
235 old = None
236 try:
237 d = self._user_data
238 except AttributeError:
239 d = {}
240 self._user_data = d
241 if key in d:
242 old = d[key][0]
243 if data is None:
244 # ignore handlers passed for None
245 handler = None
246 if old is not None:
247 del d[key]
248 else:
249 d[key] = (data, handler)
250 return old
252 def _call_user_data_handler(self, operation, src, dst):
253 if hasattr(self, "_user_data"):
254 for key, (data, handler) in self._user_data.items():
255 if handler is not None:
256 handler.handle(operation, key, data, src, dst)
258 # minidom-specific API:
260 def unlink(self):
261 self.parentNode = self.ownerDocument = None
262 if self.childNodes:
263 for child in self.childNodes:
264 child.unlink()
265 self.childNodes = NodeList()
266 self.previousSibling = None
267 self.nextSibling = None
269 defproperty(Node, "firstChild", doc="First child node, or None.")
270 defproperty(Node, "lastChild", doc="Last child node, or None.")
271 defproperty(Node, "localName", doc="Namespace-local name of this node.")
274 def _append_child(self, node):
275 # fast path with less checks; usable by DOM builders if careful
276 childNodes = self.childNodes
277 if childNodes:
278 last = childNodes[-1]
279 node.__dict__["previousSibling"] = last
280 last.__dict__["nextSibling"] = node
281 childNodes.append(node)
282 node.__dict__["parentNode"] = self
284 def _in_document(node):
285 # return True iff node is part of a document tree
286 while node is not None:
287 if node.nodeType == Node.DOCUMENT_NODE:
288 return True
289 node = node.parentNode
290 return False
292 def _write_data(writer, data):
293 "Writes datachars to writer."
294 data = data.replace("&", "&amp;").replace("<", "&lt;")
295 data = data.replace("\"", "&quot;").replace(">", "&gt;")
296 writer.write(data)
298 def _get_elements_by_tagName_helper(parent, name, rc):
299 for node in parent.childNodes:
300 if node.nodeType == Node.ELEMENT_NODE and \
301 (name == "*" or node.tagName == name):
302 rc.append(node)
303 _get_elements_by_tagName_helper(node, name, rc)
304 return rc
306 def _get_elements_by_tagName_ns_helper(parent, nsURI, localName, rc):
307 for node in parent.childNodes:
308 if node.nodeType == Node.ELEMENT_NODE:
309 if ((localName == "*" or node.localName == localName) and
310 (nsURI == "*" or node.namespaceURI == nsURI)):
311 rc.append(node)
312 _get_elements_by_tagName_ns_helper(node, nsURI, localName, rc)
313 return rc
315 class DocumentFragment(Node):
316 nodeType = Node.DOCUMENT_FRAGMENT_NODE
317 nodeName = "#document-fragment"
318 nodeValue = None
319 attributes = None
320 parentNode = None
321 _child_node_types = (Node.ELEMENT_NODE,
322 Node.TEXT_NODE,
323 Node.CDATA_SECTION_NODE,
324 Node.ENTITY_REFERENCE_NODE,
325 Node.PROCESSING_INSTRUCTION_NODE,
326 Node.COMMENT_NODE,
327 Node.NOTATION_NODE)
329 def __init__(self):
330 self.childNodes = NodeList()
333 class Attr(Node):
334 nodeType = Node.ATTRIBUTE_NODE
335 attributes = None
336 ownerElement = None
337 specified = False
338 _is_id = False
340 _child_node_types = (Node.TEXT_NODE, Node.ENTITY_REFERENCE_NODE)
342 def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
343 prefix=None):
344 # skip setattr for performance
345 d = self.__dict__
346 d["nodeName"] = d["name"] = qName
347 d["namespaceURI"] = namespaceURI
348 d["prefix"] = prefix
349 d['childNodes'] = NodeList()
351 # Add the single child node that represents the value of the attr
352 self.childNodes.append(Text())
354 # nodeValue and value are set elsewhere
356 def _get_localName(self):
357 return self.nodeName.split(":", 1)[-1]
359 def _get_name(self):
360 return self.name
362 def _get_specified(self):
363 return self.specified
365 def __setattr__(self, name, value):
366 d = self.__dict__
367 if name in ("value", "nodeValue"):
368 d["value"] = d["nodeValue"] = value
369 d2 = self.childNodes[0].__dict__
370 d2["data"] = d2["nodeValue"] = value
371 if self.ownerElement is not None:
372 _clear_id_cache(self.ownerElement)
373 elif name in ("name", "nodeName"):
374 d["name"] = d["nodeName"] = value
375 if self.ownerElement is not None:
376 _clear_id_cache(self.ownerElement)
377 else:
378 d[name] = value
380 def _set_prefix(self, prefix):
381 nsuri = self.namespaceURI
382 if prefix == "xmlns":
383 if nsuri and nsuri != XMLNS_NAMESPACE:
384 raise xml.dom.NamespaceErr(
385 "illegal use of 'xmlns' prefix for the wrong namespace")
386 d = self.__dict__
387 d['prefix'] = prefix
388 if prefix is None:
389 newName = self.localName
390 else:
391 newName = "%s:%s" % (prefix, self.localName)
392 if self.ownerElement:
393 _clear_id_cache(self.ownerElement)
394 d['nodeName'] = d['name'] = newName
396 def _set_value(self, value):
397 d = self.__dict__
398 d['value'] = d['nodeValue'] = value
399 if self.ownerElement:
400 _clear_id_cache(self.ownerElement)
401 self.childNodes[0].data = value
403 def unlink(self):
404 # This implementation does not call the base implementation
405 # since most of that is not needed, and the expense of the
406 # method call is not warranted. We duplicate the removal of
407 # children, but that's all we needed from the base class.
408 elem = self.ownerElement
409 if elem is not None:
410 del elem._attrs[self.nodeName]
411 del elem._attrsNS[(self.namespaceURI, self.localName)]
412 if self._is_id:
413 self._is_id = False
414 elem._magic_id_nodes -= 1
415 self.ownerDocument._magic_id_count -= 1
416 for child in self.childNodes:
417 child.unlink()
418 del self.childNodes[:]
420 def _get_isId(self):
421 if self._is_id:
422 return True
423 doc = self.ownerDocument
424 elem = self.ownerElement
425 if doc is None or elem is None:
426 return False
428 info = doc._get_elem_info(elem)
429 if info is None:
430 return False
431 if self.namespaceURI:
432 return info.isIdNS(self.namespaceURI, self.localName)
433 else:
434 return info.isId(self.nodeName)
436 def _get_schemaType(self):
437 doc = self.ownerDocument
438 elem = self.ownerElement
439 if doc is None or elem is None:
440 return _no_type
442 info = doc._get_elem_info(elem)
443 if info is None:
444 return _no_type
445 if self.namespaceURI:
446 return info.getAttributeTypeNS(self.namespaceURI, self.localName)
447 else:
448 return info.getAttributeType(self.nodeName)
450 defproperty(Attr, "isId", doc="True if this attribute is an ID.")
451 defproperty(Attr, "localName", doc="Namespace-local name of this attribute.")
452 defproperty(Attr, "schemaType", doc="Schema type for this attribute.")
455 class NamedNodeMap(object):
456 """The attribute list is a transient interface to the underlying
457 dictionaries. Mutations here will change the underlying element's
458 dictionary.
460 Ordering is imposed artificially and does not reflect the order of
461 attributes as found in an input document.
464 __slots__ = ('_attrs', '_attrsNS', '_ownerElement')
466 def __init__(self, attrs, attrsNS, ownerElement):
467 self._attrs = attrs
468 self._attrsNS = attrsNS
469 self._ownerElement = ownerElement
471 def _get_length(self):
472 return len(self._attrs)
474 def item(self, index):
475 try:
476 return self[self._attrs.keys()[index]]
477 except IndexError:
478 return None
480 def items(self):
481 L = []
482 for node in self._attrs.values():
483 L.append((node.nodeName, node.value))
484 return L
486 def itemsNS(self):
487 L = []
488 for node in self._attrs.values():
489 L.append(((node.namespaceURI, node.localName), node.value))
490 return L
492 def has_key(self, key):
493 if isinstance(key, StringTypes):
494 return self._attrs.has_key(key)
495 else:
496 return self._attrsNS.has_key(key)
498 def keys(self):
499 return self._attrs.keys()
501 def keysNS(self):
502 return self._attrsNS.keys()
504 def values(self):
505 return self._attrs.values()
507 def get(self, name, value=None):
508 return self._attrs.get(name, value)
510 __len__ = _get_length
512 __hash__ = None # Mutable type can't be correctly hashed
513 def __cmp__(self, other):
514 if self._attrs is getattr(other, "_attrs", None):
515 return 0
516 else:
517 return cmp(id(self), id(other))
519 def __getitem__(self, attname_or_tuple):
520 if isinstance(attname_or_tuple, tuple):
521 return self._attrsNS[attname_or_tuple]
522 else:
523 return self._attrs[attname_or_tuple]
525 # same as set
526 def __setitem__(self, attname, value):
527 if isinstance(value, StringTypes):
528 try:
529 node = self._attrs[attname]
530 except KeyError:
531 node = Attr(attname)
532 node.ownerDocument = self._ownerElement.ownerDocument
533 self.setNamedItem(node)
534 node.value = value
535 else:
536 if not isinstance(value, Attr):
537 raise TypeError, "value must be a string or Attr object"
538 node = value
539 self.setNamedItem(node)
541 def getNamedItem(self, name):
542 try:
543 return self._attrs[name]
544 except KeyError:
545 return None
547 def getNamedItemNS(self, namespaceURI, localName):
548 try:
549 return self._attrsNS[(namespaceURI, localName)]
550 except KeyError:
551 return None
553 def removeNamedItem(self, name):
554 n = self.getNamedItem(name)
555 if n is not None:
556 _clear_id_cache(self._ownerElement)
557 del self._attrs[n.nodeName]
558 del self._attrsNS[(n.namespaceURI, n.localName)]
559 if 'ownerElement' in n.__dict__:
560 n.__dict__['ownerElement'] = None
561 return n
562 else:
563 raise xml.dom.NotFoundErr()
565 def removeNamedItemNS(self, namespaceURI, localName):
566 n = self.getNamedItemNS(namespaceURI, localName)
567 if n is not None:
568 _clear_id_cache(self._ownerElement)
569 del self._attrsNS[(n.namespaceURI, n.localName)]
570 del self._attrs[n.nodeName]
571 if 'ownerElement' in n.__dict__:
572 n.__dict__['ownerElement'] = None
573 return n
574 else:
575 raise xml.dom.NotFoundErr()
577 def setNamedItem(self, node):
578 if not isinstance(node, Attr):
579 raise xml.dom.HierarchyRequestErr(
580 "%s cannot be child of %s" % (repr(node), repr(self)))
581 old = self._attrs.get(node.name)
582 if old:
583 old.unlink()
584 self._attrs[node.name] = node
585 self._attrsNS[(node.namespaceURI, node.localName)] = node
586 node.ownerElement = self._ownerElement
587 _clear_id_cache(node.ownerElement)
588 return old
590 def setNamedItemNS(self, node):
591 return self.setNamedItem(node)
593 def __delitem__(self, attname_or_tuple):
594 node = self[attname_or_tuple]
595 _clear_id_cache(node.ownerElement)
596 node.unlink()
598 def __getstate__(self):
599 return self._attrs, self._attrsNS, self._ownerElement
601 def __setstate__(self, state):
602 self._attrs, self._attrsNS, self._ownerElement = state
604 defproperty(NamedNodeMap, "length",
605 doc="Number of nodes in the NamedNodeMap.")
607 AttributeList = NamedNodeMap
610 class TypeInfo(object):
611 __slots__ = 'namespace', 'name'
613 def __init__(self, namespace, name):
614 self.namespace = namespace
615 self.name = name
617 def __repr__(self):
618 if self.namespace:
619 return "<TypeInfo %r (from %r)>" % (self.name, self.namespace)
620 else:
621 return "<TypeInfo %r>" % self.name
623 def _get_name(self):
624 return self.name
626 def _get_namespace(self):
627 return self.namespace
629 _no_type = TypeInfo(None, None)
631 class Element(Node):
632 nodeType = Node.ELEMENT_NODE
633 nodeValue = None
634 schemaType = _no_type
636 _magic_id_nodes = 0
638 _child_node_types = (Node.ELEMENT_NODE,
639 Node.PROCESSING_INSTRUCTION_NODE,
640 Node.COMMENT_NODE,
641 Node.TEXT_NODE,
642 Node.CDATA_SECTION_NODE,
643 Node.ENTITY_REFERENCE_NODE)
645 def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
646 localName=None):
647 self.tagName = self.nodeName = tagName
648 self.prefix = prefix
649 self.namespaceURI = namespaceURI
650 self.childNodes = NodeList()
652 self._attrs = {} # attributes are double-indexed:
653 self._attrsNS = {} # tagName -> Attribute
654 # URI,localName -> Attribute
655 # in the future: consider lazy generation
656 # of attribute objects this is too tricky
657 # for now because of headaches with
658 # namespaces.
660 def _get_localName(self):
661 return self.tagName.split(":", 1)[-1]
663 def _get_tagName(self):
664 return self.tagName
666 def unlink(self):
667 for attr in self._attrs.values():
668 attr.unlink()
669 self._attrs = None
670 self._attrsNS = None
671 Node.unlink(self)
673 def getAttribute(self, attname):
674 try:
675 return self._attrs[attname].value
676 except KeyError:
677 return ""
679 def getAttributeNS(self, namespaceURI, localName):
680 try:
681 return self._attrsNS[(namespaceURI, localName)].value
682 except KeyError:
683 return ""
685 def setAttribute(self, attname, value):
686 attr = self.getAttributeNode(attname)
687 if attr is None:
688 attr = Attr(attname)
689 # for performance
690 d = attr.__dict__
691 d["value"] = d["nodeValue"] = value
692 d["ownerDocument"] = self.ownerDocument
693 self.setAttributeNode(attr)
694 elif value != attr.value:
695 d = attr.__dict__
696 d["value"] = d["nodeValue"] = value
697 if attr.isId:
698 _clear_id_cache(self)
700 def setAttributeNS(self, namespaceURI, qualifiedName, value):
701 prefix, localname = _nssplit(qualifiedName)
702 attr = self.getAttributeNodeNS(namespaceURI, localname)
703 if attr is None:
704 # for performance
705 attr = Attr(qualifiedName, namespaceURI, localname, prefix)
706 d = attr.__dict__
707 d["prefix"] = prefix
708 d["nodeName"] = qualifiedName
709 d["value"] = d["nodeValue"] = value
710 d["ownerDocument"] = self.ownerDocument
711 self.setAttributeNode(attr)
712 else:
713 d = attr.__dict__
714 if value != attr.value:
715 d["value"] = d["nodeValue"] = value
716 if attr.isId:
717 _clear_id_cache(self)
718 if attr.prefix != prefix:
719 d["prefix"] = prefix
720 d["nodeName"] = qualifiedName
722 def getAttributeNode(self, attrname):
723 return self._attrs.get(attrname)
725 def getAttributeNodeNS(self, namespaceURI, localName):
726 return self._attrsNS.get((namespaceURI, localName))
728 def setAttributeNode(self, attr):
729 if attr.ownerElement not in (None, self):
730 raise xml.dom.InuseAttributeErr("attribute node already owned")
731 old1 = self._attrs.get(attr.name, None)
732 if old1 is not None:
733 self.removeAttributeNode(old1)
734 old2 = self._attrsNS.get((attr.namespaceURI, attr.localName), None)
735 if old2 is not None and old2 is not old1:
736 self.removeAttributeNode(old2)
737 _set_attribute_node(self, attr)
739 if old1 is not attr:
740 # It might have already been part of this node, in which case
741 # it doesn't represent a change, and should not be returned.
742 return old1
743 if old2 is not attr:
744 return old2
746 setAttributeNodeNS = setAttributeNode
748 def removeAttribute(self, name):
749 try:
750 attr = self._attrs[name]
751 except KeyError:
752 raise xml.dom.NotFoundErr()
753 self.removeAttributeNode(attr)
755 def removeAttributeNS(self, namespaceURI, localName):
756 try:
757 attr = self._attrsNS[(namespaceURI, localName)]
758 except KeyError:
759 raise xml.dom.NotFoundErr()
760 self.removeAttributeNode(attr)
762 def removeAttributeNode(self, node):
763 if node is None:
764 raise xml.dom.NotFoundErr()
765 try:
766 self._attrs[node.name]
767 except KeyError:
768 raise xml.dom.NotFoundErr()
769 _clear_id_cache(self)
770 node.unlink()
771 # Restore this since the node is still useful and otherwise
772 # unlinked
773 node.ownerDocument = self.ownerDocument
775 removeAttributeNodeNS = removeAttributeNode
777 def hasAttribute(self, name):
778 return self._attrs.has_key(name)
780 def hasAttributeNS(self, namespaceURI, localName):
781 return self._attrsNS.has_key((namespaceURI, localName))
783 def getElementsByTagName(self, name):
784 return _get_elements_by_tagName_helper(self, name, NodeList())
786 def getElementsByTagNameNS(self, namespaceURI, localName):
787 return _get_elements_by_tagName_ns_helper(
788 self, namespaceURI, localName, NodeList())
790 def __repr__(self):
791 return "<DOM Element: %s at %#x>" % (self.tagName, id(self))
793 def writexml(self, writer, indent="", addindent="", newl=""):
794 # indent = current indentation
795 # addindent = indentation to add to higher levels
796 # newl = newline string
797 writer.write(indent+"<" + self.tagName)
799 attrs = self._get_attributes()
800 a_names = attrs.keys()
801 a_names.sort()
803 for a_name in a_names:
804 writer.write(" %s=\"" % a_name)
805 _write_data(writer, attrs[a_name].value)
806 writer.write("\"")
807 if self.childNodes:
808 writer.write(">%s"%(newl))
809 for node in self.childNodes:
810 node.writexml(writer,indent+addindent,addindent,newl)
811 writer.write("%s</%s>%s" % (indent,self.tagName,newl))
812 else:
813 writer.write("/>%s"%(newl))
815 def _get_attributes(self):
816 return NamedNodeMap(self._attrs, self._attrsNS, self)
818 def hasAttributes(self):
819 if self._attrs:
820 return True
821 else:
822 return False
824 # DOM Level 3 attributes, based on the 22 Oct 2002 draft
826 def setIdAttribute(self, name):
827 idAttr = self.getAttributeNode(name)
828 self.setIdAttributeNode(idAttr)
830 def setIdAttributeNS(self, namespaceURI, localName):
831 idAttr = self.getAttributeNodeNS(namespaceURI, localName)
832 self.setIdAttributeNode(idAttr)
834 def setIdAttributeNode(self, idAttr):
835 if idAttr is None or not self.isSameNode(idAttr.ownerElement):
836 raise xml.dom.NotFoundErr()
837 if _get_containing_entref(self) is not None:
838 raise xml.dom.NoModificationAllowedErr()
839 if not idAttr._is_id:
840 idAttr.__dict__['_is_id'] = True
841 self._magic_id_nodes += 1
842 self.ownerDocument._magic_id_count += 1
843 _clear_id_cache(self)
845 defproperty(Element, "attributes",
846 doc="NamedNodeMap of attributes on the element.")
847 defproperty(Element, "localName",
848 doc="Namespace-local name of this element.")
851 def _set_attribute_node(element, attr):
852 _clear_id_cache(element)
853 element._attrs[attr.name] = attr
854 element._attrsNS[(attr.namespaceURI, attr.localName)] = attr
856 # This creates a circular reference, but Element.unlink()
857 # breaks the cycle since the references to the attribute
858 # dictionaries are tossed.
859 attr.__dict__['ownerElement'] = element
862 class Childless:
863 """Mixin that makes childless-ness easy to implement and avoids
864 the complexity of the Node methods that deal with children.
867 attributes = None
868 childNodes = EmptyNodeList()
869 firstChild = None
870 lastChild = None
872 def _get_firstChild(self):
873 return None
875 def _get_lastChild(self):
876 return None
878 def appendChild(self, node):
879 raise xml.dom.HierarchyRequestErr(
880 self.nodeName + " nodes cannot have children")
882 def hasChildNodes(self):
883 return False
885 def insertBefore(self, newChild, refChild):
886 raise xml.dom.HierarchyRequestErr(
887 self.nodeName + " nodes do not have children")
889 def removeChild(self, oldChild):
890 raise xml.dom.NotFoundErr(
891 self.nodeName + " nodes do not have children")
893 def replaceChild(self, newChild, oldChild):
894 raise xml.dom.HierarchyRequestErr(
895 self.nodeName + " nodes do not have children")
898 class ProcessingInstruction(Childless, Node):
899 nodeType = Node.PROCESSING_INSTRUCTION_NODE
901 def __init__(self, target, data):
902 self.target = self.nodeName = target
903 self.data = self.nodeValue = data
905 def _get_data(self):
906 return self.data
907 def _set_data(self, value):
908 d = self.__dict__
909 d['data'] = d['nodeValue'] = value
911 def _get_target(self):
912 return self.target
913 def _set_target(self, value):
914 d = self.__dict__
915 d['target'] = d['nodeName'] = value
917 def __setattr__(self, name, value):
918 if name == "data" or name == "nodeValue":
919 self.__dict__['data'] = self.__dict__['nodeValue'] = value
920 elif name == "target" or name == "nodeName":
921 self.__dict__['target'] = self.__dict__['nodeName'] = value
922 else:
923 self.__dict__[name] = value
925 def writexml(self, writer, indent="", addindent="", newl=""):
926 writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl))
929 class CharacterData(Childless, Node):
930 def _get_length(self):
931 return len(self.data)
932 __len__ = _get_length
934 def _get_data(self):
935 return self.__dict__['data']
936 def _set_data(self, data):
937 d = self.__dict__
938 d['data'] = d['nodeValue'] = data
940 _get_nodeValue = _get_data
941 _set_nodeValue = _set_data
943 def __setattr__(self, name, value):
944 if name == "data" or name == "nodeValue":
945 self.__dict__['data'] = self.__dict__['nodeValue'] = value
946 else:
947 self.__dict__[name] = value
949 def __repr__(self):
950 data = self.data
951 if len(data) > 10:
952 dotdotdot = "..."
953 else:
954 dotdotdot = ""
955 return '<DOM %s node "%r%s">' % (
956 self.__class__.__name__, data[0:10], dotdotdot)
958 def substringData(self, offset, count):
959 if offset < 0:
960 raise xml.dom.IndexSizeErr("offset cannot be negative")
961 if offset >= len(self.data):
962 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
963 if count < 0:
964 raise xml.dom.IndexSizeErr("count cannot be negative")
965 return self.data[offset:offset+count]
967 def appendData(self, arg):
968 self.data = self.data + arg
970 def insertData(self, offset, arg):
971 if offset < 0:
972 raise xml.dom.IndexSizeErr("offset cannot be negative")
973 if offset >= len(self.data):
974 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
975 if arg:
976 self.data = "%s%s%s" % (
977 self.data[:offset], arg, self.data[offset:])
979 def deleteData(self, offset, count):
980 if offset < 0:
981 raise xml.dom.IndexSizeErr("offset cannot be negative")
982 if offset >= len(self.data):
983 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
984 if count < 0:
985 raise xml.dom.IndexSizeErr("count cannot be negative")
986 if count:
987 self.data = self.data[:offset] + self.data[offset+count:]
989 def replaceData(self, offset, count, arg):
990 if offset < 0:
991 raise xml.dom.IndexSizeErr("offset cannot be negative")
992 if offset >= len(self.data):
993 raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")
994 if count < 0:
995 raise xml.dom.IndexSizeErr("count cannot be negative")
996 if count:
997 self.data = "%s%s%s" % (
998 self.data[:offset], arg, self.data[offset+count:])
1000 defproperty(CharacterData, "length", doc="Length of the string data.")
1003 class Text(CharacterData):
1004 # Make sure we don't add an instance __dict__ if we don't already
1005 # have one, at least when that's possible:
1006 # XXX this does not work, CharacterData is an old-style class
1007 # __slots__ = ()
1009 nodeType = Node.TEXT_NODE
1010 nodeName = "#text"
1011 attributes = None
1013 def splitText(self, offset):
1014 if offset < 0 or offset > len(self.data):
1015 raise xml.dom.IndexSizeErr("illegal offset value")
1016 newText = self.__class__()
1017 newText.data = self.data[offset:]
1018 newText.ownerDocument = self.ownerDocument
1019 next = self.nextSibling
1020 if self.parentNode and self in self.parentNode.childNodes:
1021 if next is None:
1022 self.parentNode.appendChild(newText)
1023 else:
1024 self.parentNode.insertBefore(newText, next)
1025 self.data = self.data[:offset]
1026 return newText
1028 def writexml(self, writer, indent="", addindent="", newl=""):
1029 _write_data(writer, "%s%s%s"%(indent, self.data, newl))
1031 # DOM Level 3 (WD 9 April 2002)
1033 def _get_wholeText(self):
1034 L = [self.data]
1035 n = self.previousSibling
1036 while n is not None:
1037 if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
1038 L.insert(0, n.data)
1039 n = n.previousSibling
1040 else:
1041 break
1042 n = self.nextSibling
1043 while n is not None:
1044 if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
1045 L.append(n.data)
1046 n = n.nextSibling
1047 else:
1048 break
1049 return ''.join(L)
1051 def replaceWholeText(self, content):
1052 # XXX This needs to be seriously changed if minidom ever
1053 # supports EntityReference nodes.
1054 parent = self.parentNode
1055 n = self.previousSibling
1056 while n is not None:
1057 if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
1058 next = n.previousSibling
1059 parent.removeChild(n)
1060 n = next
1061 else:
1062 break
1063 n = self.nextSibling
1064 if not content:
1065 parent.removeChild(self)
1066 while n is not None:
1067 if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
1068 next = n.nextSibling
1069 parent.removeChild(n)
1070 n = next
1071 else:
1072 break
1073 if content:
1074 d = self.__dict__
1075 d['data'] = content
1076 d['nodeValue'] = content
1077 return self
1078 else:
1079 return None
1081 def _get_isWhitespaceInElementContent(self):
1082 if self.data.strip():
1083 return False
1084 elem = _get_containing_element(self)
1085 if elem is None:
1086 return False
1087 info = self.ownerDocument._get_elem_info(elem)
1088 if info is None:
1089 return False
1090 else:
1091 return info.isElementContent()
1093 defproperty(Text, "isWhitespaceInElementContent",
1094 doc="True iff this text node contains only whitespace"
1095 " and is in element content.")
1096 defproperty(Text, "wholeText",
1097 doc="The text of all logically-adjacent text nodes.")
1100 def _get_containing_element(node):
1101 c = node.parentNode
1102 while c is not None:
1103 if c.nodeType == Node.ELEMENT_NODE:
1104 return c
1105 c = c.parentNode
1106 return None
1108 def _get_containing_entref(node):
1109 c = node.parentNode
1110 while c is not None:
1111 if c.nodeType == Node.ENTITY_REFERENCE_NODE:
1112 return c
1113 c = c.parentNode
1114 return None
1117 class Comment(Childless, CharacterData):
1118 nodeType = Node.COMMENT_NODE
1119 nodeName = "#comment"
1121 def __init__(self, data):
1122 self.data = self.nodeValue = data
1124 def writexml(self, writer, indent="", addindent="", newl=""):
1125 if "--" in self.data:
1126 raise ValueError("'--' is not allowed in a comment node")
1127 writer.write("%s<!--%s-->%s" % (indent, self.data, newl))
1130 class CDATASection(Text):
1131 # Make sure we don't add an instance __dict__ if we don't already
1132 # have one, at least when that's possible:
1133 # XXX this does not work, Text is an old-style class
1134 # __slots__ = ()
1136 nodeType = Node.CDATA_SECTION_NODE
1137 nodeName = "#cdata-section"
1139 def writexml(self, writer, indent="", addindent="", newl=""):
1140 if self.data.find("]]>") >= 0:
1141 raise ValueError("']]>' not allowed in a CDATA section")
1142 writer.write("<![CDATA[%s]]>" % self.data)
1145 class ReadOnlySequentialNamedNodeMap(object):
1146 __slots__ = '_seq',
1148 def __init__(self, seq=()):
1149 # seq should be a list or tuple
1150 self._seq = seq
1152 def __len__(self):
1153 return len(self._seq)
1155 def _get_length(self):
1156 return len(self._seq)
1158 def getNamedItem(self, name):
1159 for n in self._seq:
1160 if n.nodeName == name:
1161 return n
1163 def getNamedItemNS(self, namespaceURI, localName):
1164 for n in self._seq:
1165 if n.namespaceURI == namespaceURI and n.localName == localName:
1166 return n
1168 def __getitem__(self, name_or_tuple):
1169 if isinstance(name_or_tuple, tuple):
1170 node = self.getNamedItemNS(*name_or_tuple)
1171 else:
1172 node = self.getNamedItem(name_or_tuple)
1173 if node is None:
1174 raise KeyError, name_or_tuple
1175 return node
1177 def item(self, index):
1178 if index < 0:
1179 return None
1180 try:
1181 return self._seq[index]
1182 except IndexError:
1183 return None
1185 def removeNamedItem(self, name):
1186 raise xml.dom.NoModificationAllowedErr(
1187 "NamedNodeMap instance is read-only")
1189 def removeNamedItemNS(self, namespaceURI, localName):
1190 raise xml.dom.NoModificationAllowedErr(
1191 "NamedNodeMap instance is read-only")
1193 def setNamedItem(self, node):
1194 raise xml.dom.NoModificationAllowedErr(
1195 "NamedNodeMap instance is read-only")
1197 def setNamedItemNS(self, node):
1198 raise xml.dom.NoModificationAllowedErr(
1199 "NamedNodeMap instance is read-only")
1201 def __getstate__(self):
1202 return [self._seq]
1204 def __setstate__(self, state):
1205 self._seq = state[0]
1207 defproperty(ReadOnlySequentialNamedNodeMap, "length",
1208 doc="Number of entries in the NamedNodeMap.")
1211 class Identified:
1212 """Mix-in class that supports the publicId and systemId attributes."""
1214 # XXX this does not work, this is an old-style class
1215 # __slots__ = 'publicId', 'systemId'
1217 def _identified_mixin_init(self, publicId, systemId):
1218 self.publicId = publicId
1219 self.systemId = systemId
1221 def _get_publicId(self):
1222 return self.publicId
1224 def _get_systemId(self):
1225 return self.systemId
1227 class DocumentType(Identified, Childless, Node):
1228 nodeType = Node.DOCUMENT_TYPE_NODE
1229 nodeValue = None
1230 name = None
1231 publicId = None
1232 systemId = None
1233 internalSubset = None
1235 def __init__(self, qualifiedName):
1236 self.entities = ReadOnlySequentialNamedNodeMap()
1237 self.notations = ReadOnlySequentialNamedNodeMap()
1238 if qualifiedName:
1239 prefix, localname = _nssplit(qualifiedName)
1240 self.name = localname
1241 self.nodeName = self.name
1243 def _get_internalSubset(self):
1244 return self.internalSubset
1246 def cloneNode(self, deep):
1247 if self.ownerDocument is None:
1248 # it's ok
1249 clone = DocumentType(None)
1250 clone.name = self.name
1251 clone.nodeName = self.name
1252 operation = xml.dom.UserDataHandler.NODE_CLONED
1253 if deep:
1254 clone.entities._seq = []
1255 clone.notations._seq = []
1256 for n in self.notations._seq:
1257 notation = Notation(n.nodeName, n.publicId, n.systemId)
1258 clone.notations._seq.append(notation)
1259 n._call_user_data_handler(operation, n, notation)
1260 for e in self.entities._seq:
1261 entity = Entity(e.nodeName, e.publicId, e.systemId,
1262 e.notationName)
1263 entity.actualEncoding = e.actualEncoding
1264 entity.encoding = e.encoding
1265 entity.version = e.version
1266 clone.entities._seq.append(entity)
1267 e._call_user_data_handler(operation, n, entity)
1268 self._call_user_data_handler(operation, self, clone)
1269 return clone
1270 else:
1271 return None
1273 def writexml(self, writer, indent="", addindent="", newl=""):
1274 writer.write("<!DOCTYPE ")
1275 writer.write(self.name)
1276 if self.publicId:
1277 writer.write("%s PUBLIC '%s'%s '%s'"
1278 % (newl, self.publicId, newl, self.systemId))
1279 elif self.systemId:
1280 writer.write("%s SYSTEM '%s'" % (newl, self.systemId))
1281 if self.internalSubset is not None:
1282 writer.write(" [")
1283 writer.write(self.internalSubset)
1284 writer.write("]")
1285 writer.write(">"+newl)
1287 class Entity(Identified, Node):
1288 attributes = None
1289 nodeType = Node.ENTITY_NODE
1290 nodeValue = None
1292 actualEncoding = None
1293 encoding = None
1294 version = None
1296 def __init__(self, name, publicId, systemId, notation):
1297 self.nodeName = name
1298 self.notationName = notation
1299 self.childNodes = NodeList()
1300 self._identified_mixin_init(publicId, systemId)
1302 def _get_actualEncoding(self):
1303 return self.actualEncoding
1305 def _get_encoding(self):
1306 return self.encoding
1308 def _get_version(self):
1309 return self.version
1311 def appendChild(self, newChild):
1312 raise xml.dom.HierarchyRequestErr(
1313 "cannot append children to an entity node")
1315 def insertBefore(self, newChild, refChild):
1316 raise xml.dom.HierarchyRequestErr(
1317 "cannot insert children below an entity node")
1319 def removeChild(self, oldChild):
1320 raise xml.dom.HierarchyRequestErr(
1321 "cannot remove children from an entity node")
1323 def replaceChild(self, newChild, oldChild):
1324 raise xml.dom.HierarchyRequestErr(
1325 "cannot replace children of an entity node")
1327 class Notation(Identified, Childless, Node):
1328 nodeType = Node.NOTATION_NODE
1329 nodeValue = None
1331 def __init__(self, name, publicId, systemId):
1332 self.nodeName = name
1333 self._identified_mixin_init(publicId, systemId)
1336 class DOMImplementation(DOMImplementationLS):
1337 _features = [("core", "1.0"),
1338 ("core", "2.0"),
1339 ("core", "3.0"),
1340 ("core", None),
1341 ("xml", "1.0"),
1342 ("xml", "2.0"),
1343 ("xml", "3.0"),
1344 ("xml", None),
1345 ("ls-load", "3.0"),
1346 ("ls-load", None),
1349 def hasFeature(self, feature, version):
1350 if version == "":
1351 version = None
1352 return (feature.lower(), version) in self._features
1354 def createDocument(self, namespaceURI, qualifiedName, doctype):
1355 if doctype and doctype.parentNode is not None:
1356 raise xml.dom.WrongDocumentErr(
1357 "doctype object owned by another DOM tree")
1358 doc = self._create_document()
1360 add_root_element = not (namespaceURI is None
1361 and qualifiedName is None
1362 and doctype is None)
1364 if not qualifiedName and add_root_element:
1365 # The spec is unclear what to raise here; SyntaxErr
1366 # would be the other obvious candidate. Since Xerces raises
1367 # InvalidCharacterErr, and since SyntaxErr is not listed
1368 # for createDocument, that seems to be the better choice.
1369 # XXX: need to check for illegal characters here and in
1370 # createElement.
1372 # DOM Level III clears this up when talking about the return value
1373 # of this function. If namespaceURI, qName and DocType are
1374 # Null the document is returned without a document element
1375 # Otherwise if doctype or namespaceURI are not None
1376 # Then we go back to the above problem
1377 raise xml.dom.InvalidCharacterErr("Element with no name")
1379 if add_root_element:
1380 prefix, localname = _nssplit(qualifiedName)
1381 if prefix == "xml" \
1382 and namespaceURI != "http://www.w3.org/XML/1998/namespace":
1383 raise xml.dom.NamespaceErr("illegal use of 'xml' prefix")
1384 if prefix and not namespaceURI:
1385 raise xml.dom.NamespaceErr(
1386 "illegal use of prefix without namespaces")
1387 element = doc.createElementNS(namespaceURI, qualifiedName)
1388 if doctype:
1389 doc.appendChild(doctype)
1390 doc.appendChild(element)
1392 if doctype:
1393 doctype.parentNode = doctype.ownerDocument = doc
1395 doc.doctype = doctype
1396 doc.implementation = self
1397 return doc
1399 def createDocumentType(self, qualifiedName, publicId, systemId):
1400 doctype = DocumentType(qualifiedName)
1401 doctype.publicId = publicId
1402 doctype.systemId = systemId
1403 return doctype
1405 # DOM Level 3 (WD 9 April 2002)
1407 def getInterface(self, feature):
1408 if self.hasFeature(feature, None):
1409 return self
1410 else:
1411 return None
1413 # internal
1414 def _create_document(self):
1415 return Document()
1417 class ElementInfo(object):
1418 """Object that represents content-model information for an element.
1420 This implementation is not expected to be used in practice; DOM
1421 builders should provide implementations which do the right thing
1422 using information available to it.
1426 __slots__ = 'tagName',
1428 def __init__(self, name):
1429 self.tagName = name
1431 def getAttributeType(self, aname):
1432 return _no_type
1434 def getAttributeTypeNS(self, namespaceURI, localName):
1435 return _no_type
1437 def isElementContent(self):
1438 return False
1440 def isEmpty(self):
1441 """Returns true iff this element is declared to have an EMPTY
1442 content model."""
1443 return False
1445 def isId(self, aname):
1446 """Returns true iff the named attribte is a DTD-style ID."""
1447 return False
1449 def isIdNS(self, namespaceURI, localName):
1450 """Returns true iff the identified attribute is a DTD-style ID."""
1451 return False
1453 def __getstate__(self):
1454 return self.tagName
1456 def __setstate__(self, state):
1457 self.tagName = state
1459 def _clear_id_cache(node):
1460 if node.nodeType == Node.DOCUMENT_NODE:
1461 node._id_cache.clear()
1462 node._id_search_stack = None
1463 elif _in_document(node):
1464 node.ownerDocument._id_cache.clear()
1465 node.ownerDocument._id_search_stack= None
1467 class Document(Node, DocumentLS):
1468 _child_node_types = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
1469 Node.COMMENT_NODE, Node.DOCUMENT_TYPE_NODE)
1471 nodeType = Node.DOCUMENT_NODE
1472 nodeName = "#document"
1473 nodeValue = None
1474 attributes = None
1475 doctype = None
1476 parentNode = None
1477 previousSibling = nextSibling = None
1479 implementation = DOMImplementation()
1481 # Document attributes from Level 3 (WD 9 April 2002)
1483 actualEncoding = None
1484 encoding = None
1485 standalone = None
1486 version = None
1487 strictErrorChecking = False
1488 errorHandler = None
1489 documentURI = None
1491 _magic_id_count = 0
1493 def __init__(self):
1494 self.childNodes = NodeList()
1495 # mapping of (namespaceURI, localName) -> ElementInfo
1496 # and tagName -> ElementInfo
1497 self._elem_info = {}
1498 self._id_cache = {}
1499 self._id_search_stack = None
1501 def _get_elem_info(self, element):
1502 if element.namespaceURI:
1503 key = element.namespaceURI, element.localName
1504 else:
1505 key = element.tagName
1506 return self._elem_info.get(key)
1508 def _get_actualEncoding(self):
1509 return self.actualEncoding
1511 def _get_doctype(self):
1512 return self.doctype
1514 def _get_documentURI(self):
1515 return self.documentURI
1517 def _get_encoding(self):
1518 return self.encoding
1520 def _get_errorHandler(self):
1521 return self.errorHandler
1523 def _get_standalone(self):
1524 return self.standalone
1526 def _get_strictErrorChecking(self):
1527 return self.strictErrorChecking
1529 def _get_version(self):
1530 return self.version
1532 def appendChild(self, node):
1533 if node.nodeType not in self._child_node_types:
1534 raise xml.dom.HierarchyRequestErr(
1535 "%s cannot be child of %s" % (repr(node), repr(self)))
1536 if node.parentNode is not None:
1537 # This needs to be done before the next test since this
1538 # may *be* the document element, in which case it should
1539 # end up re-ordered to the end.
1540 node.parentNode.removeChild(node)
1542 if node.nodeType == Node.ELEMENT_NODE \
1543 and self._get_documentElement():
1544 raise xml.dom.HierarchyRequestErr(
1545 "two document elements disallowed")
1546 return Node.appendChild(self, node)
1548 def removeChild(self, oldChild):
1549 try:
1550 self.childNodes.remove(oldChild)
1551 except ValueError:
1552 raise xml.dom.NotFoundErr()
1553 oldChild.nextSibling = oldChild.previousSibling = None
1554 oldChild.parentNode = None
1555 if self.documentElement is oldChild:
1556 self.documentElement = None
1558 return oldChild
1560 def _get_documentElement(self):
1561 for node in self.childNodes:
1562 if node.nodeType == Node.ELEMENT_NODE:
1563 return node
1565 def unlink(self):
1566 if self.doctype is not None:
1567 self.doctype.unlink()
1568 self.doctype = None
1569 Node.unlink(self)
1571 def cloneNode(self, deep):
1572 if not deep:
1573 return None
1574 clone = self.implementation.createDocument(None, None, None)
1575 clone.encoding = self.encoding
1576 clone.standalone = self.standalone
1577 clone.version = self.version
1578 for n in self.childNodes:
1579 childclone = _clone_node(n, deep, clone)
1580 assert childclone.ownerDocument.isSameNode(clone)
1581 clone.childNodes.append(childclone)
1582 if childclone.nodeType == Node.DOCUMENT_NODE:
1583 assert clone.documentElement is None
1584 elif childclone.nodeType == Node.DOCUMENT_TYPE_NODE:
1585 assert clone.doctype is None
1586 clone.doctype = childclone
1587 childclone.parentNode = clone
1588 self._call_user_data_handler(xml.dom.UserDataHandler.NODE_CLONED,
1589 self, clone)
1590 return clone
1592 def createDocumentFragment(self):
1593 d = DocumentFragment()
1594 d.ownerDocument = self
1595 return d
1597 def createElement(self, tagName):
1598 e = Element(tagName)
1599 e.ownerDocument = self
1600 return e
1602 def createTextNode(self, data):
1603 if not isinstance(data, StringTypes):
1604 raise TypeError, "node contents must be a string"
1605 t = Text()
1606 t.data = data
1607 t.ownerDocument = self
1608 return t
1610 def createCDATASection(self, data):
1611 if not isinstance(data, StringTypes):
1612 raise TypeError, "node contents must be a string"
1613 c = CDATASection()
1614 c.data = data
1615 c.ownerDocument = self
1616 return c
1618 def createComment(self, data):
1619 c = Comment(data)
1620 c.ownerDocument = self
1621 return c
1623 def createProcessingInstruction(self, target, data):
1624 p = ProcessingInstruction(target, data)
1625 p.ownerDocument = self
1626 return p
1628 def createAttribute(self, qName):
1629 a = Attr(qName)
1630 a.ownerDocument = self
1631 a.value = ""
1632 return a
1634 def createElementNS(self, namespaceURI, qualifiedName):
1635 prefix, localName = _nssplit(qualifiedName)
1636 e = Element(qualifiedName, namespaceURI, prefix)
1637 e.ownerDocument = self
1638 return e
1640 def createAttributeNS(self, namespaceURI, qualifiedName):
1641 prefix, localName = _nssplit(qualifiedName)
1642 a = Attr(qualifiedName, namespaceURI, localName, prefix)
1643 a.ownerDocument = self
1644 a.value = ""
1645 return a
1647 # A couple of implementation-specific helpers to create node types
1648 # not supported by the W3C DOM specs:
1650 def _create_entity(self, name, publicId, systemId, notationName):
1651 e = Entity(name, publicId, systemId, notationName)
1652 e.ownerDocument = self
1653 return e
1655 def _create_notation(self, name, publicId, systemId):
1656 n = Notation(name, publicId, systemId)
1657 n.ownerDocument = self
1658 return n
1660 def getElementById(self, id):
1661 if id in self._id_cache:
1662 return self._id_cache[id]
1663 if not (self._elem_info or self._magic_id_count):
1664 return None
1666 stack = self._id_search_stack
1667 if stack is None:
1668 # we never searched before, or the cache has been cleared
1669 stack = [self.documentElement]
1670 self._id_search_stack = stack
1671 elif not stack:
1672 # Previous search was completed and cache is still valid;
1673 # no matching node.
1674 return None
1676 result = None
1677 while stack:
1678 node = stack.pop()
1679 # add child elements to stack for continued searching
1680 stack.extend([child for child in node.childNodes
1681 if child.nodeType in _nodeTypes_with_children])
1682 # check this node
1683 info = self._get_elem_info(node)
1684 if info:
1685 # We have to process all ID attributes before
1686 # returning in order to get all the attributes set to
1687 # be IDs using Element.setIdAttribute*().
1688 for attr in node.attributes.values():
1689 if attr.namespaceURI:
1690 if info.isIdNS(attr.namespaceURI, attr.localName):
1691 self._id_cache[attr.value] = node
1692 if attr.value == id:
1693 result = node
1694 elif not node._magic_id_nodes:
1695 break
1696 elif info.isId(attr.name):
1697 self._id_cache[attr.value] = node
1698 if attr.value == id:
1699 result = node
1700 elif not node._magic_id_nodes:
1701 break
1702 elif attr._is_id:
1703 self._id_cache[attr.value] = node
1704 if attr.value == id:
1705 result = node
1706 elif node._magic_id_nodes == 1:
1707 break
1708 elif node._magic_id_nodes:
1709 for attr in node.attributes.values():
1710 if attr._is_id:
1711 self._id_cache[attr.value] = node
1712 if attr.value == id:
1713 result = node
1714 if result is not None:
1715 break
1716 return result
1718 def getElementsByTagName(self, name):
1719 return _get_elements_by_tagName_helper(self, name, NodeList())
1721 def getElementsByTagNameNS(self, namespaceURI, localName):
1722 return _get_elements_by_tagName_ns_helper(
1723 self, namespaceURI, localName, NodeList())
1725 def isSupported(self, feature, version):
1726 return self.implementation.hasFeature(feature, version)
1728 def importNode(self, node, deep):
1729 if node.nodeType == Node.DOCUMENT_NODE:
1730 raise xml.dom.NotSupportedErr("cannot import document nodes")
1731 elif node.nodeType == Node.DOCUMENT_TYPE_NODE:
1732 raise xml.dom.NotSupportedErr("cannot import document type nodes")
1733 return _clone_node(node, deep, self)
1735 def writexml(self, writer, indent="", addindent="", newl="",
1736 encoding = None):
1737 if encoding is None:
1738 writer.write('<?xml version="1.0" ?>'+newl)
1739 else:
1740 writer.write('<?xml version="1.0" encoding="%s"?>%s' % (encoding, newl))
1741 for node in self.childNodes:
1742 node.writexml(writer, indent, addindent, newl)
1744 # DOM Level 3 (WD 9 April 2002)
1746 def renameNode(self, n, namespaceURI, name):
1747 if n.ownerDocument is not self:
1748 raise xml.dom.WrongDocumentErr(
1749 "cannot rename nodes from other documents;\n"
1750 "expected %s,\nfound %s" % (self, n.ownerDocument))
1751 if n.nodeType not in (Node.ELEMENT_NODE, Node.ATTRIBUTE_NODE):
1752 raise xml.dom.NotSupportedErr(
1753 "renameNode() only applies to element and attribute nodes")
1754 if namespaceURI != EMPTY_NAMESPACE:
1755 if ':' in name:
1756 prefix, localName = name.split(':', 1)
1757 if ( prefix == "xmlns"
1758 and namespaceURI != xml.dom.XMLNS_NAMESPACE):
1759 raise xml.dom.NamespaceErr(
1760 "illegal use of 'xmlns' prefix")
1761 else:
1762 if ( name == "xmlns"
1763 and namespaceURI != xml.dom.XMLNS_NAMESPACE
1764 and n.nodeType == Node.ATTRIBUTE_NODE):
1765 raise xml.dom.NamespaceErr(
1766 "illegal use of the 'xmlns' attribute")
1767 prefix = None
1768 localName = name
1769 else:
1770 prefix = None
1771 localName = None
1772 if n.nodeType == Node.ATTRIBUTE_NODE:
1773 element = n.ownerElement
1774 if element is not None:
1775 is_id = n._is_id
1776 element.removeAttributeNode(n)
1777 else:
1778 element = None
1779 # avoid __setattr__
1780 d = n.__dict__
1781 d['prefix'] = prefix
1782 d['localName'] = localName
1783 d['namespaceURI'] = namespaceURI
1784 d['nodeName'] = name
1785 if n.nodeType == Node.ELEMENT_NODE:
1786 d['tagName'] = name
1787 else:
1788 # attribute node
1789 d['name'] = name
1790 if element is not None:
1791 element.setAttributeNode(n)
1792 if is_id:
1793 element.setIdAttributeNode(n)
1794 # It's not clear from a semantic perspective whether we should
1795 # call the user data handlers for the NODE_RENAMED event since
1796 # we're re-using the existing node. The draft spec has been
1797 # interpreted as meaning "no, don't call the handler unless a
1798 # new node is created."
1799 return n
1801 defproperty(Document, "documentElement",
1802 doc="Top-level element of this document.")
1805 def _clone_node(node, deep, newOwnerDocument):
1807 Clone a node and give it the new owner document.
1808 Called by Node.cloneNode and Document.importNode
1810 if node.ownerDocument.isSameNode(newOwnerDocument):
1811 operation = xml.dom.UserDataHandler.NODE_CLONED
1812 else:
1813 operation = xml.dom.UserDataHandler.NODE_IMPORTED
1814 if node.nodeType == Node.ELEMENT_NODE:
1815 clone = newOwnerDocument.createElementNS(node.namespaceURI,
1816 node.nodeName)
1817 for attr in node.attributes.values():
1818 clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)
1819 a = clone.getAttributeNodeNS(attr.namespaceURI, attr.localName)
1820 a.specified = attr.specified
1822 if deep:
1823 for child in node.childNodes:
1824 c = _clone_node(child, deep, newOwnerDocument)
1825 clone.appendChild(c)
1827 elif node.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1828 clone = newOwnerDocument.createDocumentFragment()
1829 if deep:
1830 for child in node.childNodes:
1831 c = _clone_node(child, deep, newOwnerDocument)
1832 clone.appendChild(c)
1834 elif node.nodeType == Node.TEXT_NODE:
1835 clone = newOwnerDocument.createTextNode(node.data)
1836 elif node.nodeType == Node.CDATA_SECTION_NODE:
1837 clone = newOwnerDocument.createCDATASection(node.data)
1838 elif node.nodeType == Node.PROCESSING_INSTRUCTION_NODE:
1839 clone = newOwnerDocument.createProcessingInstruction(node.target,
1840 node.data)
1841 elif node.nodeType == Node.COMMENT_NODE:
1842 clone = newOwnerDocument.createComment(node.data)
1843 elif node.nodeType == Node.ATTRIBUTE_NODE:
1844 clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
1845 node.nodeName)
1846 clone.specified = True
1847 clone.value = node.value
1848 elif node.nodeType == Node.DOCUMENT_TYPE_NODE:
1849 assert node.ownerDocument is not newOwnerDocument
1850 operation = xml.dom.UserDataHandler.NODE_IMPORTED
1851 clone = newOwnerDocument.implementation.createDocumentType(
1852 node.name, node.publicId, node.systemId)
1853 clone.ownerDocument = newOwnerDocument
1854 if deep:
1855 clone.entities._seq = []
1856 clone.notations._seq = []
1857 for n in node.notations._seq:
1858 notation = Notation(n.nodeName, n.publicId, n.systemId)
1859 notation.ownerDocument = newOwnerDocument
1860 clone.notations._seq.append(notation)
1861 if hasattr(n, '_call_user_data_handler'):
1862 n._call_user_data_handler(operation, n, notation)
1863 for e in node.entities._seq:
1864 entity = Entity(e.nodeName, e.publicId, e.systemId,
1865 e.notationName)
1866 entity.actualEncoding = e.actualEncoding
1867 entity.encoding = e.encoding
1868 entity.version = e.version
1869 entity.ownerDocument = newOwnerDocument
1870 clone.entities._seq.append(entity)
1871 if hasattr(e, '_call_user_data_handler'):
1872 e._call_user_data_handler(operation, n, entity)
1873 else:
1874 # Note the cloning of Document and DocumentType nodes is
1875 # implemenetation specific. minidom handles those cases
1876 # directly in the cloneNode() methods.
1877 raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))
1879 # Check for _call_user_data_handler() since this could conceivably
1880 # used with other DOM implementations (one of the FourThought
1881 # DOMs, perhaps?).
1882 if hasattr(node, '_call_user_data_handler'):
1883 node._call_user_data_handler(operation, node, clone)
1884 return clone
1887 def _nssplit(qualifiedName):
1888 fields = qualifiedName.split(':', 1)
1889 if len(fields) == 2:
1890 return fields
1891 else:
1892 return (None, fields[0])
1895 def _get_StringIO():
1896 # we can't use cStringIO since it doesn't support Unicode strings
1897 from StringIO import StringIO
1898 return StringIO()
1900 def _do_pulldom_parse(func, args, kwargs):
1901 events = func(*args, **kwargs)
1902 toktype, rootNode = events.getEvent()
1903 events.expandNode(rootNode)
1904 events.clear()
1905 return rootNode
1907 def parse(file, parser=None, bufsize=None):
1908 """Parse a file into a DOM by filename or file object."""
1909 if parser is None and not bufsize:
1910 from xml.dom import expatbuilder
1911 return expatbuilder.parse(file)
1912 else:
1913 from xml.dom import pulldom
1914 return _do_pulldom_parse(pulldom.parse, (file,),
1915 {'parser': parser, 'bufsize': bufsize})
1917 def parseString(string, parser=None):
1918 """Parse a file into a DOM from a string."""
1919 if parser is None:
1920 from xml.dom import expatbuilder
1921 return expatbuilder.parseString(string)
1922 else:
1923 from xml.dom import pulldom
1924 return _do_pulldom_parse(pulldom.parseString, (string,),
1925 {'parser': parser})
1927 def getDOMImplementation(features=None):
1928 if features:
1929 if isinstance(features, StringTypes):
1930 features = domreg._parse_feature_string(features)
1931 for f, v in features:
1932 if not Document.implementation.hasFeature(f, v):
1933 return None
1934 return Document.implementation