python模块:xml.dom.minidom

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

 

转载于:https://www.cnblogs.com/lmgsanm/p/8379800.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值