Set bug/patch count. Take a bow, everyone!
[pytest.git] / Lib / xmlcore / dom / minidom.py
bloba8abd1442e5bfd35d2ee7f4cb1f755a3b029dc8e
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 xmlcore.dom
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
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 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)
90 if refChild is None:
91 self.appendChild(newChild)
92 else:
93 try:
94 index = self.childNodes.index(refChild)
95 except ValueError:
96 raise xmlcore.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 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
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 xmlcore.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 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
158 return oldChild
160 def removeChild(self, oldChild):
161 try:
162 self.childNodes.remove(oldChild)
163 except ValueError:
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
174 return oldChild
176 def normalize(self):
177 L = []
178 for child in self.childNodes:
179 if child.nodeType == Node.TEXT_NODE:
180 data = child.data
181 if data and L and L[-1].nodeType == child.nodeType:
182 # collapse text node
183 node = L[-1]
184 node.data = node.data + child.data
185 node.nextSibling = child.nextSibling
186 child.unlink()
187 elif data:
188 if L:
189 L[-1].nextSibling = child
190 child.previousSibling = L[-1]
191 else:
192 child.previousSibling = None
193 L.append(child)
194 else:
195 # empty text node; discard
196 child.unlink()
197 else:
198 if L:
199 L[-1].nextSibling = child
200 child.previousSibling = L[-1]
201 else:
202 child.previousSibling = None
203 L.append(child)
204 if child.nodeType == Node.ELEMENT_NODE:
205 child.normalize()
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
216 return None
218 # Node interfaces from Level 3 (WD 9 April 2002)
220 def isSameNode(self, other):
221 return self is other
223 def getInterface(self, feature):
224 if self.isSupported(feature, None):
225 return self
226 else:
227 return 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
231 # exists.
233 def getUserData(self, key):
234 try:
235 return self._user_data[key][0]
236 except (AttributeError, KeyError):
237 return None
239 def setUserData(self, key, data, handler):
240 old = None
241 try:
242 d = self._user_data
243 except AttributeError:
244 d = {}
245 self._user_data = d
246 if d.has_key(key):
247 old = d[key][0]
248 if data is None:
249 # ignore handlers passed for None
250 handler = None
251 if old is not None:
252 del d[key]
253 else:
254 d[key] = (data, handler)
255 return old
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:
265 def unlink(self):
266 self.parentNode = self.ownerDocument = None
267 if self.childNodes:
268 for child in self.childNodes:
269 child.unlink()
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
282 if 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:
293 return True
294 node = node.parentNode
295 return False
297 def _write_data(writer, data):
298 "Writes datachars to writer."
299 data = data.replace("&", "&amp;").replace("<", "&lt;")
300 data = data.replace("\"", "&quot;").replace(">", "&gt;")
301 writer.write(data)
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):
307 rc.append(node)
308 _get_elements_by_tagName_helper(node, name, rc)
309 return 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)):
316 rc.append(node)
317 _get_elements_by_tagName_ns_helper(node, nsURI, localName, rc)
318 return rc
320 class DocumentFragment(Node):
321 nodeType = Node.DOCUMENT_FRAGMENT_NODE
322 nodeName = "#document-fragment"
323 nodeValue = None
324 attributes = None
325 parentNode = None
326 _child_node_types = (Node.ELEMENT_NODE,
327 Node.TEXT_NODE,
328 Node.CDATA_SECTION_NODE,
329 Node.ENTITY_REFERENCE_NODE,
330 Node.PROCESSING_INSTRUCTION_NODE,
331 Node.COMMENT_NODE,
332 Node.NOTATION_NODE)
334 def __init__(self):
335 self.childNodes = NodeList()
338 class Attr(Node):
339 nodeType = Node.ATTRIBUTE_NODE
340 attributes = None
341 ownerElement = None
342 specified = False
343 _is_id = False
345 _child_node_types = (Node.TEXT_NODE, Node.ENTITY_REFERENCE_NODE)
347 def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
348 prefix=None):
349 # skip setattr for performance
350 d = self.__dict__
351 d["nodeName"] = d["name"] = qName
352 d["namespaceURI"] = namespaceURI
353 d["prefix"] = prefix
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]
364 def _get_name(self):
365 return self.name
367 def _get_specified(self):
368 return self.specified
370 def __setattr__(self, name, value):
371 d = self.__dict__
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)
382 else:
383 d[name] = value
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")
391 d = self.__dict__
392 d['prefix'] = prefix
393 if prefix is None:
394 newName = self.localName
395 else:
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):
402 d = self.__dict__
403 d['value'] = d['nodeValue'] = value
404 if self.ownerElement:
405 _clear_id_cache(self.ownerElement)
406 self.childNodes[0].data = value
408 def unlink(self):
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
414 if elem is not None:
415 del elem._attrs[self.nodeName]
416 del elem._attrsNS[(self.namespaceURI, self.localName)]
417 if self._is_id:
418 self._is_id = False
419 elem._magic_id_nodes -= 1
420 self.ownerDocument._magic_id_count -= 1
421 for child in self.childNodes:
422 child.unlink()
423 del self.childNodes[:]
425 def _get_isId(self):
426 if self._is_id:
427 return True
428 doc = self.ownerDocument
429 elem = self.ownerElement
430 if doc is None or elem is None:
431 return False
433 info = doc._get_elem_info(elem)
434 if info is None:
435 return False
436 if self.namespaceURI:
437 return info.isIdNS(self.namespaceURI, self.localName)
438 else:
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:
445 return _no_type
447 info = doc._get_elem_info(elem)
448 if info is None:
449 return _no_type
450 if self.namespaceURI:
451 return info.getAttributeTypeNS(self.namespaceURI, self.localName)
452 else:
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
463 dictionary.
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):
472 self._attrs = attrs
473 self._attrsNS = attrsNS
474 self._ownerElement = ownerElement
476 def _get_length(self):
477 return len(self._attrs)
479 def item(self, index):
480 try:
481 return self[self._attrs.keys()[index]]
482 except IndexError:
483 return None
485 def items(self):
486 L = []
487 for node in self._attrs.values():
488 L.append((node.nodeName, node.value))
489 return L
491 def itemsNS(self):
492 L = []
493 for node in self._attrs.values():
494 L.append(((node.namespaceURI, node.localName), node.value))
495 return L
497 def has_key(self, key):
498 if isinstance(key, StringTypes):
499 return self._attrs.has_key(key)
500 else:
501 return self._attrsNS.has_key(key)
503 def keys(self):
504 return self._attrs.keys()
506 def keysNS(self):
507 return self._attrsNS.keys()
509 def values(self):
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):
519 return 0
520 else:
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]
526 else:
527 return self._attrs[attname_or_tuple]
529 # same as set
530 def __setitem__(self, attname, value):
531 if isinstance(value, StringTypes):
532 try:
533 node = self._attrs[attname]
534 except KeyError:
535 node = Attr(attname)
536 node.ownerDocument = self._ownerElement.ownerDocument
537 self.setNamedItem(node)
538 node.value = value
539 else:
540 if not isinstance(value, Attr):
541 raise TypeError, "value must be a string or Attr object"
542 node = value
543 self.setNamedItem(node)
545 def getNamedItem(self, name):
546 try:
547 return self._attrs[name]
548 except KeyError:
549 return None
551 def getNamedItemNS(self, namespaceURI, localName):
552 try:
553 return self._attrsNS[(namespaceURI, localName)]
554 except KeyError:
555 return None
557 def removeNamedItem(self, name):
558 n = self.getNamedItem(name)
559 if n is not None:
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
565 return n
566 else:
567 raise xmlcore.dom.NotFoundErr()
569 def removeNamedItemNS(self, namespaceURI, localName):
570 n = self.getNamedItemNS(namespaceURI, localName)
571 if n is not None:
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
577 return n
578 else:
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)
586 if old:
587 old.unlink()
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)
592 return old
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)
600 node.unlink()
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
619 self.name = name
621 def __repr__(self):
622 if self.namespace:
623 return "<TypeInfo %r (from %r)>" % (self.name, self.namespace)
624 else:
625 return "<TypeInfo %r>" % self.name
627 def _get_name(self):
628 return self.name
630 def _get_namespace(self):
631 return self.namespace
633 _no_type = TypeInfo(None, None)
635 class Element(Node):
636 nodeType = Node.ELEMENT_NODE
637 nodeValue = None
638 schemaType = _no_type
640 _magic_id_nodes = 0
642 _child_node_types = (Node.ELEMENT_NODE,
643 Node.PROCESSING_INSTRUCTION_NODE,
644 Node.COMMENT_NODE,
645 Node.TEXT_NODE,
646 Node.CDATA_SECTION_NODE,
647 Node.ENTITY_REFERENCE_NODE)
649 def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
650 localName=None):
651 self.tagName = self.nodeName = tagName
652 self.prefix = prefix
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
662 # namespaces.
664 def _get_localName(self):
665 return self.tagName.split(":", 1)[-1]
667 def _get_tagName(self):
668 return self.tagName
670 def unlink(self):
671 for attr in self._attrs.values():
672 attr.unlink()
673 self._attrs = None
674 self._attrsNS = None
675 Node.unlink(self)
677 def getAttribute(self, attname):
678 try:
679 return self._attrs[attname].value
680 except KeyError:
681 return ""
683 def getAttributeNS(self, namespaceURI, localName):
684 try:
685 return self._attrsNS[(namespaceURI, localName)].value
686 except KeyError:
687 return ""
689 def setAttribute(self, attname, value):
690 attr = self.getAttributeNode(attname)
691 if attr is None:
692 attr = Attr(attname)
693 # for performance
694 d = attr.__dict__
695 d["value"] = d["nodeValue"] = value
696 d["ownerDocument"] = self.ownerDocument
697 self.setAttributeNode(attr)
698 elif value != attr.value:
699 d = attr.__dict__
700 d["value"] = d["nodeValue"] = value
701 if attr.isId:
702 _clear_id_cache(self)
704 def setAttributeNS(self, namespaceURI, qualifiedName, value):
705 prefix, localname = _nssplit(qualifiedName)
706 attr = self.getAttributeNodeNS(namespaceURI, localname)
707 if attr is None:
708 # for performance
709 attr = Attr(qualifiedName, namespaceURI, localname, prefix)
710 d = attr.__dict__
711 d["prefix"] = prefix
712 d["nodeName"] = qualifiedName
713 d["value"] = d["nodeValue"] = value
714 d["ownerDocument"] = self.ownerDocument
715 self.setAttributeNode(attr)
716 else:
717 d = attr.__dict__
718 if value != attr.value:
719 d["value"] = d["nodeValue"] = value
720 if attr.isId:
721 _clear_id_cache(self)
722 if attr.prefix != prefix:
723 d["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)
736 if old1 is not 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)
743 if old1 is not 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.
746 return old1
747 if old2 is not attr:
748 return old2
750 setAttributeNodeNS = setAttributeNode
752 def removeAttribute(self, name):
753 try:
754 attr = self._attrs[name]
755 except KeyError:
756 raise xmlcore.dom.NotFoundErr()
757 self.removeAttributeNode(attr)
759 def removeAttributeNS(self, namespaceURI, localName):
760 try:
761 attr = self._attrsNS[(namespaceURI, localName)]
762 except KeyError:
763 raise xmlcore.dom.NotFoundErr()
764 self.removeAttributeNode(attr)
766 def removeAttributeNode(self, node):
767 if node is None:
768 raise xmlcore.dom.NotFoundErr()
769 try:
770 self._attrs[node.name]
771 except KeyError:
772 raise xmlcore.dom.NotFoundErr()
773 _clear_id_cache(self)
774 node.unlink()
775 # Restore this since the node is still useful and otherwise
776 # unlinked
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())
794 def __repr__(self):
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()
805 a_names.sort()
807 for a_name in a_names:
808 writer.write(" %s=\"" % a_name)
809 _write_data(writer, attrs[a_name].value)
810 writer.write("\"")
811 if self.childNodes:
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))
816 else:
817 writer.write("/>%s"%(newl))
819 def _get_attributes(self):
820 return NamedNodeMap(self._attrs, self._attrsNS, self)
822 def hasAttributes(self):
823 if self._attrs:
824 return True
825 else:
826 return False
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
866 class Childless:
867 """Mixin that makes childless-ness easy to implement and avoids
868 the complexity of the Node methods that deal with children.
871 attributes = None
872 childNodes = EmptyNodeList()
873 firstChild = None
874 lastChild = None
876 def _get_firstChild(self):
877 return None
879 def _get_lastChild(self):
880 return None
882 def appendChild(self, node):
883 raise xmlcore.dom.HierarchyRequestErr(
884 self.nodeName + " nodes cannot have children")
886 def hasChildNodes(self):
887 return False
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
909 def _get_data(self):
910 return self.data
911 def _set_data(self, value):
912 d = self.__dict__
913 d['data'] = d['nodeValue'] = value
915 def _get_target(self):
916 return self.target
917 def _set_target(self, value):
918 d = self.__dict__
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
926 else:
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
938 def _get_data(self):
939 return self.__dict__['data']
940 def _set_data(self, data):
941 d = self.__dict__
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
950 else:
951 self.__dict__[name] = value
953 def __repr__(self):
954 data = self.data
955 if len(data) > 10:
956 dotdotdot = "..."
957 else:
958 dotdotdot = ""
959 return "<DOM %s node \"%s%s\">" % (
960 self.__class__.__name__, data[0:10], dotdotdot)
962 def substringData(self, offset, count):
963 if offset < 0:
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")
967 if count < 0:
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):
975 if offset < 0:
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")
979 if arg:
980 self.data = "%s%s%s" % (
981 self.data[:offset], arg, self.data[offset:])
983 def deleteData(self, offset, count):
984 if offset < 0:
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")
988 if count < 0:
989 raise xmlcore.dom.IndexSizeErr("count cannot be negative")
990 if count:
991 self.data = self.data[:offset] + self.data[offset+count:]
993 def replaceData(self, offset, count, arg):
994 if offset < 0:
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")
998 if count < 0:
999 raise xmlcore.dom.IndexSizeErr("count cannot be negative")
1000 if count:
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
1011 # __slots__ = ()
1013 nodeType = Node.TEXT_NODE
1014 nodeName = "#text"
1015 attributes = None
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:
1025 if next is None:
1026 self.parentNode.appendChild(newText)
1027 else:
1028 self.parentNode.insertBefore(newText, next)
1029 self.data = self.data[:offset]
1030 return newText
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):
1038 L = [self.data]
1039 n = self.previousSibling
1040 while n is not None:
1041 if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
1042 L.insert(0, n.data)
1043 n = n.previousSibling
1044 else:
1045 break
1046 n = self.nextSibling
1047 while n is not None:
1048 if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):
1049 L.append(n.data)
1050 n = n.nextSibling
1051 else:
1052 break
1053 return ''.join(L)
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)
1064 n = next
1065 else:
1066 break
1067 n = self.nextSibling
1068 if not content:
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)
1074 n = next
1075 else:
1076 break
1077 if content:
1078 d = self.__dict__
1079 d['data'] = content
1080 d['nodeValue'] = content
1081 return self
1082 else:
1083 return None
1085 def _get_isWhitespaceInElementContent(self):
1086 if self.data.strip():
1087 return False
1088 elem = _get_containing_element(self)
1089 if elem is None:
1090 return False
1091 info = self.ownerDocument._get_elem_info(elem)
1092 if info is None:
1093 return False
1094 else:
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):
1105 c = node.parentNode
1106 while c is not None:
1107 if c.nodeType == Node.ELEMENT_NODE:
1108 return c
1109 c = c.parentNode
1110 return None
1112 def _get_containing_entref(node):
1113 c = node.parentNode
1114 while c is not None:
1115 if c.nodeType == Node.ENTITY_REFERENCE_NODE:
1116 return c
1117 c = c.parentNode
1118 return None
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
1136 # __slots__ = ()
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):
1148 __slots__ = '_seq',
1150 def __init__(self, seq=()):
1151 # seq should be a list or tuple
1152 self._seq = seq
1154 def __len__(self):
1155 return len(self._seq)
1157 def _get_length(self):
1158 return len(self._seq)
1160 def getNamedItem(self, name):
1161 for n in self._seq:
1162 if n.nodeName == name:
1163 return n
1165 def getNamedItemNS(self, namespaceURI, localName):
1166 for n in self._seq:
1167 if n.namespaceURI == namespaceURI and n.localName == localName:
1168 return n
1170 def __getitem__(self, name_or_tuple):
1171 if isinstance(name_or_tuple, tuple):
1172 node = self.getNamedItemNS(*name_or_tuple)
1173 else:
1174 node = self.getNamedItem(name_or_tuple)
1175 if node is None:
1176 raise KeyError, name_or_tuple
1177 return node
1179 def item(self, index):
1180 if index < 0:
1181 return None
1182 try:
1183 return self._seq[index]
1184 except IndexError:
1185 return None
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):
1204 return [self._seq]
1206 def __setstate__(self, state):
1207 self._seq = state[0]
1209 defproperty(ReadOnlySequentialNamedNodeMap, "length",
1210 doc="Number of entries in the NamedNodeMap.")
1213 class Identified:
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
1231 nodeValue = None
1232 name = None
1233 publicId = None
1234 systemId = None
1235 internalSubset = None
1237 def __init__(self, qualifiedName):
1238 self.entities = ReadOnlySequentialNamedNodeMap()
1239 self.notations = ReadOnlySequentialNamedNodeMap()
1240 if qualifiedName:
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:
1250 # it's ok
1251 clone = DocumentType(None)
1252 clone.name = self.name
1253 clone.nodeName = self.name
1254 operation = xmlcore.dom.UserDataHandler.NODE_CLONED
1255 if deep:
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,
1264 e.notationName)
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)
1271 return clone
1272 else:
1273 return None
1275 def writexml(self, writer, indent="", addindent="", newl=""):
1276 writer.write("<!DOCTYPE ")
1277 writer.write(self.name)
1278 if self.publicId:
1279 writer.write("%s PUBLIC '%s'%s '%s'"
1280 % (newl, self.publicId, newl, self.systemId))
1281 elif self.systemId:
1282 writer.write("%s SYSTEM '%s'" % (newl, self.systemId))
1283 if self.internalSubset is not None:
1284 writer.write(" [")
1285 writer.write(self.internalSubset)
1286 writer.write("]")
1287 writer.write(">"+newl)
1289 class Entity(Identified, Node):
1290 attributes = None
1291 nodeType = Node.ENTITY_NODE
1292 nodeValue = None
1294 actualEncoding = None
1295 encoding = None
1296 version = 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):
1311 return self.version
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
1331 nodeValue = None
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"),
1340 ("core", "2.0"),
1341 ("core", "3.0"),
1342 ("core", None),
1343 ("xml", "1.0"),
1344 ("xml", "2.0"),
1345 ("xml", "3.0"),
1346 ("xml", None),
1347 ("ls-load", "3.0"),
1348 ("ls-load", None),
1351 def hasFeature(self, feature, version):
1352 if version == "":
1353 version = None
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
1372 # createElement.
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)
1390 if doctype:
1391 doc.appendChild(doctype)
1392 doc.appendChild(element)
1394 if doctype:
1395 doctype.parentNode = doctype.ownerDocument = doc
1397 doc.doctype = doctype
1398 doc.implementation = self
1399 return doc
1401 def createDocumentType(self, qualifiedName, publicId, systemId):
1402 doctype = DocumentType(qualifiedName)
1403 doctype.publicId = publicId
1404 doctype.systemId = systemId
1405 return doctype
1407 # DOM Level 3 (WD 9 April 2002)
1409 def getInterface(self, feature):
1410 if self.hasFeature(feature, None):
1411 return self
1412 else:
1413 return None
1415 # internal
1416 def _create_document(self):
1417 return Document()
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):
1431 self.tagName = name
1433 def getAttributeType(self, aname):
1434 return _no_type
1436 def getAttributeTypeNS(self, namespaceURI, localName):
1437 return _no_type
1439 def isElementContent(self):
1440 return False
1442 def isEmpty(self):
1443 """Returns true iff this element is declared to have an EMPTY
1444 content model."""
1445 return False
1447 def isId(self, aname):
1448 """Returns true iff the named attribte is a DTD-style ID."""
1449 return False
1451 def isIdNS(self, namespaceURI, localName):
1452 """Returns true iff the identified attribute is a DTD-style ID."""
1453 return False
1455 def __getstate__(self):
1456 return self.tagName
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"
1475 nodeValue = None
1476 attributes = None
1477 doctype = None
1478 parentNode = None
1479 previousSibling = nextSibling = None
1481 implementation = DOMImplementation()
1483 # Document attributes from Level 3 (WD 9 April 2002)
1485 actualEncoding = None
1486 encoding = None
1487 standalone = None
1488 version = None
1489 strictErrorChecking = False
1490 errorHandler = None
1491 documentURI = None
1493 _magic_id_count = 0
1495 def __init__(self):
1496 self.childNodes = NodeList()
1497 # mapping of (namespaceURI, localName) -> ElementInfo
1498 # and tagName -> ElementInfo
1499 self._elem_info = {}
1500 self._id_cache = {}
1501 self._id_search_stack = None
1503 def _get_elem_info(self, element):
1504 if element.namespaceURI:
1505 key = element.namespaceURI, element.localName
1506 else:
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):
1514 return self.doctype
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):
1532 return self.version
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):
1551 try:
1552 self.childNodes.remove(oldChild)
1553 except ValueError:
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
1560 return oldChild
1562 def _get_documentElement(self):
1563 for node in self.childNodes:
1564 if node.nodeType == Node.ELEMENT_NODE:
1565 return node
1567 def unlink(self):
1568 if self.doctype is not None:
1569 self.doctype.unlink()
1570 self.doctype = None
1571 Node.unlink(self)
1573 def cloneNode(self, deep):
1574 if not deep:
1575 return None
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,
1591 self, clone)
1592 return clone
1594 def createDocumentFragment(self):
1595 d = DocumentFragment()
1596 d.ownerDocument = self
1597 return d
1599 def createElement(self, tagName):
1600 e = Element(tagName)
1601 e.ownerDocument = self
1602 return e
1604 def createTextNode(self, data):
1605 if not isinstance(data, StringTypes):
1606 raise TypeError, "node contents must be a string"
1607 t = Text()
1608 t.data = data
1609 t.ownerDocument = self
1610 return t
1612 def createCDATASection(self, data):
1613 if not isinstance(data, StringTypes):
1614 raise TypeError, "node contents must be a string"
1615 c = CDATASection()
1616 c.data = data
1617 c.ownerDocument = self
1618 return c
1620 def createComment(self, data):
1621 c = Comment(data)
1622 c.ownerDocument = self
1623 return c
1625 def createProcessingInstruction(self, target, data):
1626 p = ProcessingInstruction(target, data)
1627 p.ownerDocument = self
1628 return p
1630 def createAttribute(self, qName):
1631 a = Attr(qName)
1632 a.ownerDocument = self
1633 a.value = ""
1634 return a
1636 def createElementNS(self, namespaceURI, qualifiedName):
1637 prefix, localName = _nssplit(qualifiedName)
1638 e = Element(qualifiedName, namespaceURI, prefix)
1639 e.ownerDocument = self
1640 return e
1642 def createAttributeNS(self, namespaceURI, qualifiedName):
1643 prefix, localName = _nssplit(qualifiedName)
1644 a = Attr(qualifiedName, namespaceURI, localName, prefix)
1645 a.ownerDocument = self
1646 a.value = ""
1647 return a
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
1655 return e
1657 def _create_notation(self, name, publicId, systemId):
1658 n = Notation(name, publicId, systemId)
1659 n.ownerDocument = self
1660 return n
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):
1666 return None
1668 stack = self._id_search_stack
1669 if stack is None:
1670 # we never searched before, or the cache has been cleared
1671 stack = [self.documentElement]
1672 self._id_search_stack = stack
1673 elif not stack:
1674 # Previous search was completed and cache is still valid;
1675 # no matching node.
1676 return None
1678 result = None
1679 while stack:
1680 node = stack.pop()
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])
1684 # check this node
1685 info = self._get_elem_info(node)
1686 if info:
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:
1695 result = node
1696 elif not node._magic_id_nodes:
1697 break
1698 elif info.isId(attr.name):
1699 self._id_cache[attr.value] = node
1700 if attr.value == id:
1701 result = node
1702 elif not node._magic_id_nodes:
1703 break
1704 elif attr._is_id:
1705 self._id_cache[attr.value] = node
1706 if attr.value == id:
1707 result = node
1708 elif node._magic_id_nodes == 1:
1709 break
1710 elif node._magic_id_nodes:
1711 for attr in node.attributes.values():
1712 if attr._is_id:
1713 self._id_cache[attr.value] = node
1714 if attr.value == id:
1715 result = node
1716 if result is not None:
1717 break
1718 return result
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="",
1738 encoding = None):
1739 if encoding is None:
1740 writer.write('<?xml version="1.0" ?>'+newl)
1741 else:
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:
1757 if ':' in name:
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")
1763 else:
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")
1769 prefix = None
1770 localName = name
1771 else:
1772 prefix = None
1773 localName = None
1774 if n.nodeType == Node.ATTRIBUTE_NODE:
1775 element = n.ownerElement
1776 if element is not None:
1777 is_id = n._is_id
1778 element.removeAttributeNode(n)
1779 else:
1780 element = None
1781 # avoid __setattr__
1782 d = n.__dict__
1783 d['prefix'] = prefix
1784 d['localName'] = localName
1785 d['namespaceURI'] = namespaceURI
1786 d['nodeName'] = name
1787 if n.nodeType == Node.ELEMENT_NODE:
1788 d['tagName'] = name
1789 else:
1790 # attribute node
1791 d['name'] = name
1792 if element is not None:
1793 element.setAttributeNode(n)
1794 if is_id:
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."
1801 return n
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
1814 else:
1815 operation = xmlcore.dom.UserDataHandler.NODE_IMPORTED
1816 if node.nodeType == Node.ELEMENT_NODE:
1817 clone = newOwnerDocument.createElementNS(node.namespaceURI,
1818 node.nodeName)
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
1824 if deep:
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()
1831 if deep:
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,
1842 node.data)
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,
1847 node.nodeName)
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
1856 if deep:
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,
1867 e.notationName)
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)
1875 else:
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
1883 # DOMs, perhaps?).
1884 if hasattr(node, '_call_user_data_handler'):
1885 node._call_user_data_handler(operation, node, clone)
1886 return clone
1889 def _nssplit(qualifiedName):
1890 fields = qualifiedName.split(':', 1)
1891 if len(fields) == 2:
1892 return fields
1893 else:
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
1900 return StringIO()
1902 def _do_pulldom_parse(func, args, kwargs):
1903 events = func(*args, **kwargs)
1904 toktype, rootNode = events.getEvent()
1905 events.expandNode(rootNode)
1906 events.clear()
1907 return 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)
1914 else:
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."""
1921 if parser is None:
1922 from xmlcore.dom import expatbuilder
1923 return expatbuilder.parseString(string)
1924 else:
1925 from xmlcore.dom import pulldom
1926 return _do_pulldom_parse(pulldom.parseString, (string,),
1927 {'parser': parser})
1929 def getDOMImplementation(features=None):
1930 if features:
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):
1935 return None
1936 return Document.implementation