1936 lines · 64.4 KB · Python
RawDownload
"""\minidom.py -- a lightweight DOM implementation. parse("foo.xml") parseString("<foo><bar/></foo>") Todo:===== * convenience methods for getting elements and text. * more testing * bring some of the writer and linearizer code into conformance with this        interface * SAX 2 namespaces""" import xml.dom from xml.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE, domregfrom xml.dom.minicompat import *from xml.dom.xmlbuilder import DOMImplementationLS, DocumentLS # This is used by the ID-cache invalidation checks; the list isn't# actually complete, since the nodes being checked will never be the# DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE.  (The node being checked is# the node being added or removed, not the node being modified.)#_nodeTypes_with_children = (xml.dom.Node.ELEMENT_NODE,                            xml.dom.Node.ENTITY_REFERENCE_NODE)  class Node(xml.dom.Node):    namespaceURI = None # this is non-null only for elements and attributes    parentNode = None    ownerDocument = None    nextSibling = None    previousSibling = None     prefix = EMPTY_PREFIX # non-null only for NS elements and attributes     def __nonzero__(self):        return True     def toxml(self, encoding = None):        return self.toprettyxml("", "", encoding)     def toprettyxml(self, indent="\t", newl="\n", encoding = None):        # indent = the indentation string to prepend, per level        # newl = the newline string to append        writer = _get_StringIO()        if encoding is not None:            import codecs            # Can't use codecs.getwriter to preserve 2.0 compatibility            writer = codecs.lookup(encoding)[3](writer)        if self.nodeType == Node.DOCUMENT_NODE:            # Can pass encoding only to document, to put it into XML header            self.writexml(writer, "", indent, newl, encoding)        else:            self.writexml(writer, "", indent, newl)        return writer.getvalue()     def hasChildNodes(self):        if self.childNodes:            return True        else:            return False     def _get_childNodes(self):        return self.childNodes     def _get_firstChild(self):        if self.childNodes:            return self.childNodes[0]     def _get_lastChild(self):        if self.childNodes:            return self.childNodes[-1]     def insertBefore(self, newChild, refChild):        if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:            for c in tuple(newChild.childNodes):                self.insertBefore(c, refChild)            ### The DOM does not clearly specify what to return in this case            return newChild        if newChild.nodeType not in self._child_node_types:            raise xml.dom.HierarchyRequestErr(                "%s cannot be child of %s" % (repr(newChild), repr(self)))        if newChild.parentNode is not None:            newChild.parentNode.removeChild(newChild)        if refChild is None:            self.appendChild(newChild)        else:            try:                index = self.childNodes.index(refChild)            except ValueError:                raise xml.dom.NotFoundErr()            if newChild.nodeType in _nodeTypes_with_children:                _clear_id_cache(self)            self.childNodes.insert(index, newChild)            newChild.nextSibling = refChild            refChild.previousSibling = newChild            if index:                node = self.childNodes[index-1]                node.nextSibling = newChild                newChild.previousSibling = node            else:                newChild.previousSibling = None            newChild.parentNode = self        return newChild     def appendChild(self, node):        if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:            for c in tuple(node.childNodes):                self.appendChild(c)            ### The DOM does not clearly specify what to return in this case            return node        if node.nodeType not in self._child_node_types:            raise xml.dom.HierarchyRequestErr(                "%s cannot be child of %s" % (repr(node), repr(self)))        elif node.nodeType in _nodeTypes_with_children:            _clear_id_cache(self)        if node.parentNode is not None:            node.parentNode.removeChild(node)        _append_child(self, node)        node.nextSibling = None        return node     def replaceChild(self, newChild, oldChild):        if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:            refChild = oldChild.nextSibling            self.removeChild(oldChild)            return self.insertBefore(newChild, refChild)        if newChild.nodeType not in self._child_node_types:            raise xml.dom.HierarchyRequestErr(                "%s cannot be child of %s" % (repr(newChild), repr(self)))        if newChild is oldChild:            return        if newChild.parentNode is not None:            newChild.parentNode.removeChild(newChild)        try:            index = self.childNodes.index(oldChild)        except ValueError:            raise xml.dom.NotFoundErr()        self.childNodes[index] = newChild        newChild.parentNode = self        oldChild.parentNode = None        if (newChild.nodeType in _nodeTypes_with_children            or oldChild.nodeType in _nodeTypes_with_children):            _clear_id_cache(self)        newChild.nextSibling = oldChild.nextSibling        newChild.previousSibling = oldChild.previousSibling        oldChild.nextSibling = None        oldChild.previousSibling = None        if newChild.previousSibling:            newChild.previousSibling.nextSibling = newChild        if newChild.nextSibling:            newChild.nextSibling.previousSibling = newChild        return oldChild     def removeChild(self, oldChild):        try:            self.childNodes.remove(oldChild)        except ValueError:            raise xml.dom.NotFoundErr()        if oldChild.nextSibling is not None:            oldChild.nextSibling.previousSibling = oldChild.previousSibling        if oldChild.previousSibling is not None:            oldChild.previousSibling.nextSibling = oldChild.nextSibling        oldChild.nextSibling = oldChild.previousSibling = None        if oldChild.nodeType in _nodeTypes_with_children:            _clear_id_cache(self)         oldChild.parentNode = None        return oldChild     def normalize(self):        L = []        for child in self.childNodes:            if child.nodeType == Node.TEXT_NODE:                data = child.data                if data and L and L[-1].nodeType == child.nodeType:                    # collapse text node                    node = L[-1]                    node.data = node.data + child.data                    node.nextSibling = child.nextSibling                    child.unlink()                elif data:                    if L:                        L[-1].nextSibling = child                        child.previousSibling = L[-1]                    else:                        child.previousSibling = None                    L.append(child)                else:                    # empty text node; discard                    child.unlink()            else:                if L:                    L[-1].nextSibling = child                    child.previousSibling = L[-1]                else:                    child.previousSibling = None                L.append(child)                if child.nodeType == Node.ELEMENT_NODE:                    child.normalize()        self.childNodes[:] = L     def cloneNode(self, deep):        return _clone_node(self, deep, self.ownerDocument or self)     def isSupported(self, feature, version):        return self.ownerDocument.implementation.hasFeature(feature, version)     def _get_localName(self):        # Overridden in Element and Attr where localName can be Non-Null        return None     # Node interfaces from Level 3 (WD 9 April 2002)     def isSameNode(self, other):        return self is other     def getInterface(self, feature):        if self.isSupported(feature, None):            return self        else:            return None     # The "user data" functions use a dictionary that is only present    # if some user data has been set, so be careful not to assume it    # exists.     def getUserData(self, key):        try:            return self._user_data[key][0]        except (AttributeError, KeyError):            return None     def setUserData(self, key, data, handler):        old = None        try:            d = self._user_data        except AttributeError:            d = {}            self._user_data = d        if d.has_key(key):            old = d[key][0]        if data is None:            # ignore handlers passed for None            handler = None            if old is not None:                del d[key]        else:            d[key] = (data, handler)        return old     def _call_user_data_handler(self, operation, src, dst):        if hasattr(self, "_user_data"):            for key, (data, handler) in self._user_data.items():                if handler is not None:                    handler.handle(operation, key, data, src, dst)     # minidom-specific API:     def unlink(self):        self.parentNode = self.ownerDocument = None        if self.childNodes:            for child in self.childNodes:                child.unlink()            self.childNodes = NodeList()        self.previousSibling = None        self.nextSibling = None defproperty(Node, "firstChild", doc="First child node, or None.")defproperty(Node, "lastChild",  doc="Last child node, or None.")defproperty(Node, "localName",  doc="Namespace-local name of this node.")  def _append_child(self, node):    # fast path with less checks; usable by DOM builders if careful    childNodes = self.childNodes    if childNodes:        last = childNodes[-1]        node.__dict__["previousSibling"] = last        last.__dict__["nextSibling"] = node    childNodes.append(node)    node.__dict__["parentNode"] = self def _in_document(node):    # return True iff node is part of a document tree    while node is not None:        if node.nodeType == Node.DOCUMENT_NODE:            return True        node = node.parentNode    return False def _write_data(writer, data):    "Writes datachars to writer."    data = data.replace("&", "&amp;").replace("<", "&lt;")    data = data.replace("\"", "&quot;").replace(">", "&gt;")    writer.write(data) def _get_elements_by_tagName_helper(parent, name, rc):    for node in parent.childNodes:        if node.nodeType == Node.ELEMENT_NODE and \            (name == "*" or node.tagName == name):            rc.append(node)        _get_elements_by_tagName_helper(node, name, rc)    return rc def _get_elements_by_tagName_ns_helper(parent, nsURI, localName, rc):    for node in parent.childNodes:        if node.nodeType == Node.ELEMENT_NODE:            if ((localName == "*" or node.localName == localName) and                (nsURI == "*" or node.namespaceURI == nsURI)):                rc.append(node)            _get_elements_by_tagName_ns_helper(node, nsURI, localName, rc)    return rc class DocumentFragment(Node):    nodeType = Node.DOCUMENT_FRAGMENT_NODE    nodeName = "#document-fragment"    nodeValue = None    attributes = None    parentNode = None    _child_node_types = (Node.ELEMENT_NODE,                         Node.TEXT_NODE,                         Node.CDATA_SECTION_NODE,                         Node.ENTITY_REFERENCE_NODE,                         Node.PROCESSING_INSTRUCTION_NODE,                         Node.COMMENT_NODE,                         Node.NOTATION_NODE)     def __init__(self):        self.childNodes = NodeList()  class Attr(Node):    nodeType = Node.ATTRIBUTE_NODE    attributes = None    ownerElement = None    specified = False    _is_id = False     _child_node_types = (Node.TEXT_NODE, Node.ENTITY_REFERENCE_NODE)     def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,                 prefix=None):        # skip setattr for performance        d = self.__dict__        d["nodeName"] = d["name"] = qName        d["namespaceURI"] = namespaceURI        d["prefix"] = prefix        d['childNodes'] = NodeList()         # Add the single child node that represents the value of the attr        self.childNodes.append(Text())         # nodeValue and value are set elsewhere     def _get_localName(self):        return self.nodeName.split(":", 1)[-1]     def _get_name(self):        return self.name     def _get_specified(self):        return self.specified     def __setattr__(self, name, value):        d = self.__dict__        if name in ("value", "nodeValue"):            d["value"] = d["nodeValue"] = value            d2 = self.childNodes[0].__dict__            d2["data"] = d2["nodeValue"] = value            if self.ownerElement is not None:                _clear_id_cache(self.ownerElement)        elif name in ("name", "nodeName"):            d["name"] = d["nodeName"] = value            if self.ownerElement is not None:                _clear_id_cache(self.ownerElement)        else:            d[name] = value     def _set_prefix(self, prefix):        nsuri = self.namespaceURI        if prefix == "xmlns":            if nsuri and nsuri != XMLNS_NAMESPACE:                raise xml.dom.NamespaceErr(                    "illegal use of 'xmlns' prefix for the wrong namespace")        d = self.__dict__        d['prefix'] = prefix        if prefix is None:            newName = self.localName        else:            newName = "%s:%s" % (prefix, self.localName)        if self.ownerElement:            _clear_id_cache(self.ownerElement)        d['nodeName'] = d['name'] = newName     def _set_value(self, value):        d = self.__dict__        d['value'] = d['nodeValue'] = value        if self.ownerElement:            _clear_id_cache(self.ownerElement)        self.childNodes[0].data = value     def unlink(self):        # This implementation does not call the base implementation        # since most of that is not needed, and the expense of the        # method call is not warranted.  We duplicate the removal of        # children, but that's all we needed from the base class.        elem = self.ownerElement        if elem is not None:            del elem._attrs[self.nodeName]            del elem._attrsNS[(self.namespaceURI, self.localName)]            if self._is_id:                self._is_id = False                elem._magic_id_nodes -= 1                self.ownerDocument._magic_id_count -= 1        for child in self.childNodes:            child.unlink()        del self.childNodes[:]     def _get_isId(self):        if self._is_id:            return True        doc = self.ownerDocument        elem = self.ownerElement        if doc is None or elem is None:            return False         info = doc._get_elem_info(elem)        if info is None:            return False        if self.namespaceURI:            return info.isIdNS(self.namespaceURI, self.localName)        else:            return info.isId(self.nodeName)     def _get_schemaType(self):        doc = self.ownerDocument        elem = self.ownerElement        if doc is None or elem is None:            return _no_type         info = doc._get_elem_info(elem)        if info is None:            return _no_type        if self.namespaceURI:            return info.getAttributeTypeNS(self.namespaceURI, self.localName)        else:            return info.getAttributeType(self.nodeName) defproperty(Attr, "isId",       doc="True if this attribute is an ID.")defproperty(Attr, "localName",  doc="Namespace-local name of this attribute.")defproperty(Attr, "schemaType", doc="Schema type for this attribute.")  class NamedNodeMap(object):    """The attribute list is a transient interface to the underlying    dictionaries.  Mutations here will change the underlying element's    dictionary.     Ordering is imposed artificially and does not reflect the order of    attributes as found in an input document.    """     __slots__ = ('_attrs', '_attrsNS', '_ownerElement')     def __init__(self, attrs, attrsNS, ownerElement):        self._attrs = attrs        self._attrsNS = attrsNS        self._ownerElement = ownerElement     def _get_length(self):        return len(self._attrs)     def item(self, index):        try:            return self[self._attrs.keys()[index]]        except IndexError:            return None     def items(self):        L = []        for node in self._attrs.values():            L.append((node.nodeName, node.value))        return L     def itemsNS(self):        L = []        for node in self._attrs.values():            L.append(((node.namespaceURI, node.localName), node.value))        return L     def has_key(self, key):        if isinstance(key, StringTypes):            return self._attrs.has_key(key)        else:

Lines 1–500 of 1936.