--- /dev/null
+"""Simple implementation of the Level 1 DOM.\r
+\r
+Namespaces and other minor Level 2 features are also supported.\r
+\r
+parse("foo.xml")\r
+\r
+parseString("<foo><bar/></foo>")\r
+\r
+Todo:\r
+=====\r
+ * convenience methods for getting elements and text.\r
+ * more testing\r
+ * bring some of the writer and linearizer code into conformance with this\r
+ interface\r
+ * SAX 2 namespaces\r
+"""\r
+\r
+import xml.dom\r
+\r
+from xml.dom import EMPTY_NAMESPACE, EMPTY_PREFIX, XMLNS_NAMESPACE, domreg\r
+from xml.dom.minicompat import *\r
+from xml.dom.xmlbuilder import DOMImplementationLS, DocumentLS\r
+\r
+# This is used by the ID-cache invalidation checks; the list isn't\r
+# actually complete, since the nodes being checked will never be the\r
+# DOCUMENT_NODE or DOCUMENT_FRAGMENT_NODE. (The node being checked is\r
+# the node being added or removed, not the node being modified.)\r
+#\r
+_nodeTypes_with_children = (xml.dom.Node.ELEMENT_NODE,\r
+ xml.dom.Node.ENTITY_REFERENCE_NODE)\r
+\r
+\r
+class Node(xml.dom.Node):\r
+ namespaceURI = None # this is non-null only for elements and attributes\r
+ parentNode = None\r
+ ownerDocument = None\r
+ nextSibling = None\r
+ previousSibling = None\r
+\r
+ prefix = EMPTY_PREFIX # non-null only for NS elements and attributes\r
+\r
+ def __nonzero__(self):\r
+ return True\r
+\r
+ def toxml(self, encoding = None):\r
+ return self.toprettyxml("", "", encoding)\r
+\r
+ def toprettyxml(self, indent="\t", newl="\n", encoding = None):\r
+ # indent = the indentation string to prepend, per level\r
+ # newl = the newline string to append\r
+ writer = _get_StringIO()\r
+ if encoding is not None:\r
+ import codecs\r
+ # Can't use codecs.getwriter to preserve 2.0 compatibility\r
+ writer = codecs.lookup(encoding)[3](writer)\r
+ if self.nodeType == Node.DOCUMENT_NODE:\r
+ # Can pass encoding only to document, to put it into XML header\r
+ self.writexml(writer, "", indent, newl, encoding)\r
+ else:\r
+ self.writexml(writer, "", indent, newl)\r
+ return writer.getvalue()\r
+\r
+ def hasChildNodes(self):\r
+ if self.childNodes:\r
+ return True\r
+ else:\r
+ return False\r
+\r
+ def _get_childNodes(self):\r
+ return self.childNodes\r
+\r
+ def _get_firstChild(self):\r
+ if self.childNodes:\r
+ return self.childNodes[0]\r
+\r
+ def _get_lastChild(self):\r
+ if self.childNodes:\r
+ return self.childNodes[-1]\r
+\r
+ def insertBefore(self, newChild, refChild):\r
+ if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:\r
+ for c in tuple(newChild.childNodes):\r
+ self.insertBefore(c, refChild)\r
+ ### The DOM does not clearly specify what to return in this case\r
+ return newChild\r
+ if newChild.nodeType not in self._child_node_types:\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "%s cannot be child of %s" % (repr(newChild), repr(self)))\r
+ if newChild.parentNode is not None:\r
+ newChild.parentNode.removeChild(newChild)\r
+ if refChild is None:\r
+ self.appendChild(newChild)\r
+ else:\r
+ try:\r
+ index = self.childNodes.index(refChild)\r
+ except ValueError:\r
+ raise xml.dom.NotFoundErr()\r
+ if newChild.nodeType in _nodeTypes_with_children:\r
+ _clear_id_cache(self)\r
+ self.childNodes.insert(index, newChild)\r
+ newChild.nextSibling = refChild\r
+ refChild.previousSibling = newChild\r
+ if index:\r
+ node = self.childNodes[index-1]\r
+ node.nextSibling = newChild\r
+ newChild.previousSibling = node\r
+ else:\r
+ newChild.previousSibling = None\r
+ newChild.parentNode = self\r
+ return newChild\r
+\r
+ def appendChild(self, node):\r
+ if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:\r
+ for c in tuple(node.childNodes):\r
+ self.appendChild(c)\r
+ ### The DOM does not clearly specify what to return in this case\r
+ return node\r
+ if node.nodeType not in self._child_node_types:\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "%s cannot be child of %s" % (repr(node), repr(self)))\r
+ elif node.nodeType in _nodeTypes_with_children:\r
+ _clear_id_cache(self)\r
+ if node.parentNode is not None:\r
+ node.parentNode.removeChild(node)\r
+ _append_child(self, node)\r
+ node.nextSibling = None\r
+ return node\r
+\r
+ def replaceChild(self, newChild, oldChild):\r
+ if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:\r
+ refChild = oldChild.nextSibling\r
+ self.removeChild(oldChild)\r
+ return self.insertBefore(newChild, refChild)\r
+ if newChild.nodeType not in self._child_node_types:\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "%s cannot be child of %s" % (repr(newChild), repr(self)))\r
+ if newChild is oldChild:\r
+ return\r
+ if newChild.parentNode is not None:\r
+ newChild.parentNode.removeChild(newChild)\r
+ try:\r
+ index = self.childNodes.index(oldChild)\r
+ except ValueError:\r
+ raise xml.dom.NotFoundErr()\r
+ self.childNodes[index] = newChild\r
+ newChild.parentNode = self\r
+ oldChild.parentNode = None\r
+ if (newChild.nodeType in _nodeTypes_with_children\r
+ or oldChild.nodeType in _nodeTypes_with_children):\r
+ _clear_id_cache(self)\r
+ newChild.nextSibling = oldChild.nextSibling\r
+ newChild.previousSibling = oldChild.previousSibling\r
+ oldChild.nextSibling = None\r
+ oldChild.previousSibling = None\r
+ if newChild.previousSibling:\r
+ newChild.previousSibling.nextSibling = newChild\r
+ if newChild.nextSibling:\r
+ newChild.nextSibling.previousSibling = newChild\r
+ return oldChild\r
+\r
+ def removeChild(self, oldChild):\r
+ try:\r
+ self.childNodes.remove(oldChild)\r
+ except ValueError:\r
+ raise xml.dom.NotFoundErr()\r
+ if oldChild.nextSibling is not None:\r
+ oldChild.nextSibling.previousSibling = oldChild.previousSibling\r
+ if oldChild.previousSibling is not None:\r
+ oldChild.previousSibling.nextSibling = oldChild.nextSibling\r
+ oldChild.nextSibling = oldChild.previousSibling = None\r
+ if oldChild.nodeType in _nodeTypes_with_children:\r
+ _clear_id_cache(self)\r
+\r
+ oldChild.parentNode = None\r
+ return oldChild\r
+\r
+ def normalize(self):\r
+ L = []\r
+ for child in self.childNodes:\r
+ if child.nodeType == Node.TEXT_NODE:\r
+ if not child.data:\r
+ # empty text node; discard\r
+ if L:\r
+ L[-1].nextSibling = child.nextSibling\r
+ if child.nextSibling:\r
+ child.nextSibling.previousSibling = child.previousSibling\r
+ child.unlink()\r
+ elif L and L[-1].nodeType == child.nodeType:\r
+ # collapse text node\r
+ node = L[-1]\r
+ node.data = node.data + child.data\r
+ node.nextSibling = child.nextSibling\r
+ if child.nextSibling:\r
+ child.nextSibling.previousSibling = node\r
+ child.unlink()\r
+ else:\r
+ L.append(child)\r
+ else:\r
+ L.append(child)\r
+ if child.nodeType == Node.ELEMENT_NODE:\r
+ child.normalize()\r
+ self.childNodes[:] = L\r
+\r
+ def cloneNode(self, deep):\r
+ return _clone_node(self, deep, self.ownerDocument or self)\r
+\r
+ def isSupported(self, feature, version):\r
+ return self.ownerDocument.implementation.hasFeature(feature, version)\r
+\r
+ def _get_localName(self):\r
+ # Overridden in Element and Attr where localName can be Non-Null\r
+ return None\r
+\r
+ # Node interfaces from Level 3 (WD 9 April 2002)\r
+\r
+ def isSameNode(self, other):\r
+ return self is other\r
+\r
+ def getInterface(self, feature):\r
+ if self.isSupported(feature, None):\r
+ return self\r
+ else:\r
+ return None\r
+\r
+ # The "user data" functions use a dictionary that is only present\r
+ # if some user data has been set, so be careful not to assume it\r
+ # exists.\r
+\r
+ def getUserData(self, key):\r
+ try:\r
+ return self._user_data[key][0]\r
+ except (AttributeError, KeyError):\r
+ return None\r
+\r
+ def setUserData(self, key, data, handler):\r
+ old = None\r
+ try:\r
+ d = self._user_data\r
+ except AttributeError:\r
+ d = {}\r
+ self._user_data = d\r
+ if key in d:\r
+ old = d[key][0]\r
+ if data is None:\r
+ # ignore handlers passed for None\r
+ handler = None\r
+ if old is not None:\r
+ del d[key]\r
+ else:\r
+ d[key] = (data, handler)\r
+ return old\r
+\r
+ def _call_user_data_handler(self, operation, src, dst):\r
+ if hasattr(self, "_user_data"):\r
+ for key, (data, handler) in self._user_data.items():\r
+ if handler is not None:\r
+ handler.handle(operation, key, data, src, dst)\r
+\r
+ # minidom-specific API:\r
+\r
+ def unlink(self):\r
+ self.parentNode = self.ownerDocument = None\r
+ if self.childNodes:\r
+ for child in self.childNodes:\r
+ child.unlink()\r
+ self.childNodes = NodeList()\r
+ self.previousSibling = None\r
+ self.nextSibling = None\r
+\r
+defproperty(Node, "firstChild", doc="First child node, or None.")\r
+defproperty(Node, "lastChild", doc="Last child node, or None.")\r
+defproperty(Node, "localName", doc="Namespace-local name of this node.")\r
+\r
+\r
+def _append_child(self, node):\r
+ # fast path with less checks; usable by DOM builders if careful\r
+ childNodes = self.childNodes\r
+ if childNodes:\r
+ last = childNodes[-1]\r
+ node.__dict__["previousSibling"] = last\r
+ last.__dict__["nextSibling"] = node\r
+ childNodes.append(node)\r
+ node.__dict__["parentNode"] = self\r
+\r
+def _in_document(node):\r
+ # return True iff node is part of a document tree\r
+ while node is not None:\r
+ if node.nodeType == Node.DOCUMENT_NODE:\r
+ return True\r
+ node = node.parentNode\r
+ return False\r
+\r
+def _write_data(writer, data):\r
+ "Writes datachars to writer."\r
+ if data:\r
+ data = data.replace("&", "&").replace("<", "<"). \\r
+ replace("\"", """).replace(">", ">")\r
+ writer.write(data)\r
+\r
+def _get_elements_by_tagName_helper(parent, name, rc):\r
+ for node in parent.childNodes:\r
+ if node.nodeType == Node.ELEMENT_NODE and \\r
+ (name == "*" or node.tagName == name):\r
+ rc.append(node)\r
+ _get_elements_by_tagName_helper(node, name, rc)\r
+ return rc\r
+\r
+def _get_elements_by_tagName_ns_helper(parent, nsURI, localName, rc):\r
+ for node in parent.childNodes:\r
+ if node.nodeType == Node.ELEMENT_NODE:\r
+ if ((localName == "*" or node.localName == localName) and\r
+ (nsURI == "*" or node.namespaceURI == nsURI)):\r
+ rc.append(node)\r
+ _get_elements_by_tagName_ns_helper(node, nsURI, localName, rc)\r
+ return rc\r
+\r
+class DocumentFragment(Node):\r
+ nodeType = Node.DOCUMENT_FRAGMENT_NODE\r
+ nodeName = "#document-fragment"\r
+ nodeValue = None\r
+ attributes = None\r
+ parentNode = None\r
+ _child_node_types = (Node.ELEMENT_NODE,\r
+ Node.TEXT_NODE,\r
+ Node.CDATA_SECTION_NODE,\r
+ Node.ENTITY_REFERENCE_NODE,\r
+ Node.PROCESSING_INSTRUCTION_NODE,\r
+ Node.COMMENT_NODE,\r
+ Node.NOTATION_NODE)\r
+\r
+ def __init__(self):\r
+ self.childNodes = NodeList()\r
+\r
+\r
+class Attr(Node):\r
+ nodeType = Node.ATTRIBUTE_NODE\r
+ attributes = None\r
+ ownerElement = None\r
+ specified = False\r
+ _is_id = False\r
+\r
+ _child_node_types = (Node.TEXT_NODE, Node.ENTITY_REFERENCE_NODE)\r
+\r
+ def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,\r
+ prefix=None):\r
+ # skip setattr for performance\r
+ d = self.__dict__\r
+ d["nodeName"] = d["name"] = qName\r
+ d["namespaceURI"] = namespaceURI\r
+ d["prefix"] = prefix\r
+ d['childNodes'] = NodeList()\r
+\r
+ # Add the single child node that represents the value of the attr\r
+ self.childNodes.append(Text())\r
+\r
+ # nodeValue and value are set elsewhere\r
+\r
+ def _get_localName(self):\r
+ return self.nodeName.split(":", 1)[-1]\r
+\r
+ def _get_specified(self):\r
+ return self.specified\r
+\r
+ def __setattr__(self, name, value):\r
+ d = self.__dict__\r
+ if name in ("value", "nodeValue"):\r
+ d["value"] = d["nodeValue"] = value\r
+ d2 = self.childNodes[0].__dict__\r
+ d2["data"] = d2["nodeValue"] = value\r
+ if self.ownerElement is not None:\r
+ _clear_id_cache(self.ownerElement)\r
+ elif name in ("name", "nodeName"):\r
+ d["name"] = d["nodeName"] = value\r
+ if self.ownerElement is not None:\r
+ _clear_id_cache(self.ownerElement)\r
+ else:\r
+ d[name] = value\r
+\r
+ def _set_prefix(self, prefix):\r
+ nsuri = self.namespaceURI\r
+ if prefix == "xmlns":\r
+ if nsuri and nsuri != XMLNS_NAMESPACE:\r
+ raise xml.dom.NamespaceErr(\r
+ "illegal use of 'xmlns' prefix for the wrong namespace")\r
+ d = self.__dict__\r
+ d['prefix'] = prefix\r
+ if prefix is None:\r
+ newName = self.localName\r
+ else:\r
+ newName = "%s:%s" % (prefix, self.localName)\r
+ if self.ownerElement:\r
+ _clear_id_cache(self.ownerElement)\r
+ d['nodeName'] = d['name'] = newName\r
+\r
+ def _set_value(self, value):\r
+ d = self.__dict__\r
+ d['value'] = d['nodeValue'] = value\r
+ if self.ownerElement:\r
+ _clear_id_cache(self.ownerElement)\r
+ self.childNodes[0].data = value\r
+\r
+ def unlink(self):\r
+ # This implementation does not call the base implementation\r
+ # since most of that is not needed, and the expense of the\r
+ # method call is not warranted. We duplicate the removal of\r
+ # children, but that's all we needed from the base class.\r
+ elem = self.ownerElement\r
+ if elem is not None:\r
+ del elem._attrs[self.nodeName]\r
+ del elem._attrsNS[(self.namespaceURI, self.localName)]\r
+ if self._is_id:\r
+ self._is_id = False\r
+ elem._magic_id_nodes -= 1\r
+ self.ownerDocument._magic_id_count -= 1\r
+ for child in self.childNodes:\r
+ child.unlink()\r
+ del self.childNodes[:]\r
+\r
+ def _get_isId(self):\r
+ if self._is_id:\r
+ return True\r
+ doc = self.ownerDocument\r
+ elem = self.ownerElement\r
+ if doc is None or elem is None:\r
+ return False\r
+\r
+ info = doc._get_elem_info(elem)\r
+ if info is None:\r
+ return False\r
+ if self.namespaceURI:\r
+ return info.isIdNS(self.namespaceURI, self.localName)\r
+ else:\r
+ return info.isId(self.nodeName)\r
+\r
+ def _get_schemaType(self):\r
+ doc = self.ownerDocument\r
+ elem = self.ownerElement\r
+ if doc is None or elem is None:\r
+ return _no_type\r
+\r
+ info = doc._get_elem_info(elem)\r
+ if info is None:\r
+ return _no_type\r
+ if self.namespaceURI:\r
+ return info.getAttributeTypeNS(self.namespaceURI, self.localName)\r
+ else:\r
+ return info.getAttributeType(self.nodeName)\r
+\r
+defproperty(Attr, "isId", doc="True if this attribute is an ID.")\r
+defproperty(Attr, "localName", doc="Namespace-local name of this attribute.")\r
+defproperty(Attr, "schemaType", doc="Schema type for this attribute.")\r
+\r
+\r
+class NamedNodeMap(object):\r
+ """The attribute list is a transient interface to the underlying\r
+ dictionaries. Mutations here will change the underlying element's\r
+ dictionary.\r
+\r
+ Ordering is imposed artificially and does not reflect the order of\r
+ attributes as found in an input document.\r
+ """\r
+\r
+ __slots__ = ('_attrs', '_attrsNS', '_ownerElement')\r
+\r
+ def __init__(self, attrs, attrsNS, ownerElement):\r
+ self._attrs = attrs\r
+ self._attrsNS = attrsNS\r
+ self._ownerElement = ownerElement\r
+\r
+ def _get_length(self):\r
+ return len(self._attrs)\r
+\r
+ def item(self, index):\r
+ try:\r
+ return self[self._attrs.keys()[index]]\r
+ except IndexError:\r
+ return None\r
+\r
+ def items(self):\r
+ L = []\r
+ for node in self._attrs.values():\r
+ L.append((node.nodeName, node.value))\r
+ return L\r
+\r
+ def itemsNS(self):\r
+ L = []\r
+ for node in self._attrs.values():\r
+ L.append(((node.namespaceURI, node.localName), node.value))\r
+ return L\r
+\r
+ def has_key(self, key):\r
+ if isinstance(key, StringTypes):\r
+ return key in self._attrs\r
+ else:\r
+ return key in self._attrsNS\r
+\r
+ def keys(self):\r
+ return self._attrs.keys()\r
+\r
+ def keysNS(self):\r
+ return self._attrsNS.keys()\r
+\r
+ def values(self):\r
+ return self._attrs.values()\r
+\r
+ def get(self, name, value=None):\r
+ return self._attrs.get(name, value)\r
+\r
+ __len__ = _get_length\r
+\r
+ __hash__ = None # Mutable type can't be correctly hashed\r
+ def __cmp__(self, other):\r
+ if self._attrs is getattr(other, "_attrs", None):\r
+ return 0\r
+ else:\r
+ return cmp(id(self), id(other))\r
+\r
+ def __getitem__(self, attname_or_tuple):\r
+ if isinstance(attname_or_tuple, tuple):\r
+ return self._attrsNS[attname_or_tuple]\r
+ else:\r
+ return self._attrs[attname_or_tuple]\r
+\r
+ # same as set\r
+ def __setitem__(self, attname, value):\r
+ if isinstance(value, StringTypes):\r
+ try:\r
+ node = self._attrs[attname]\r
+ except KeyError:\r
+ node = Attr(attname)\r
+ node.ownerDocument = self._ownerElement.ownerDocument\r
+ self.setNamedItem(node)\r
+ node.value = value\r
+ else:\r
+ if not isinstance(value, Attr):\r
+ raise TypeError, "value must be a string or Attr object"\r
+ node = value\r
+ self.setNamedItem(node)\r
+\r
+ def getNamedItem(self, name):\r
+ try:\r
+ return self._attrs[name]\r
+ except KeyError:\r
+ return None\r
+\r
+ def getNamedItemNS(self, namespaceURI, localName):\r
+ try:\r
+ return self._attrsNS[(namespaceURI, localName)]\r
+ except KeyError:\r
+ return None\r
+\r
+ def removeNamedItem(self, name):\r
+ n = self.getNamedItem(name)\r
+ if n is not None:\r
+ _clear_id_cache(self._ownerElement)\r
+ del self._attrs[n.nodeName]\r
+ del self._attrsNS[(n.namespaceURI, n.localName)]\r
+ if 'ownerElement' in n.__dict__:\r
+ n.__dict__['ownerElement'] = None\r
+ return n\r
+ else:\r
+ raise xml.dom.NotFoundErr()\r
+\r
+ def removeNamedItemNS(self, namespaceURI, localName):\r
+ n = self.getNamedItemNS(namespaceURI, localName)\r
+ if n is not None:\r
+ _clear_id_cache(self._ownerElement)\r
+ del self._attrsNS[(n.namespaceURI, n.localName)]\r
+ del self._attrs[n.nodeName]\r
+ if 'ownerElement' in n.__dict__:\r
+ n.__dict__['ownerElement'] = None\r
+ return n\r
+ else:\r
+ raise xml.dom.NotFoundErr()\r
+\r
+ def setNamedItem(self, node):\r
+ if not isinstance(node, Attr):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "%s cannot be child of %s" % (repr(node), repr(self)))\r
+ old = self._attrs.get(node.name)\r
+ if old:\r
+ old.unlink()\r
+ self._attrs[node.name] = node\r
+ self._attrsNS[(node.namespaceURI, node.localName)] = node\r
+ node.ownerElement = self._ownerElement\r
+ _clear_id_cache(node.ownerElement)\r
+ return old\r
+\r
+ def setNamedItemNS(self, node):\r
+ return self.setNamedItem(node)\r
+\r
+ def __delitem__(self, attname_or_tuple):\r
+ node = self[attname_or_tuple]\r
+ _clear_id_cache(node.ownerElement)\r
+ node.unlink()\r
+\r
+ def __getstate__(self):\r
+ return self._attrs, self._attrsNS, self._ownerElement\r
+\r
+ def __setstate__(self, state):\r
+ self._attrs, self._attrsNS, self._ownerElement = state\r
+\r
+defproperty(NamedNodeMap, "length",\r
+ doc="Number of nodes in the NamedNodeMap.")\r
+\r
+AttributeList = NamedNodeMap\r
+\r
+\r
+class TypeInfo(object):\r
+ __slots__ = 'namespace', 'name'\r
+\r
+ def __init__(self, namespace, name):\r
+ self.namespace = namespace\r
+ self.name = name\r
+\r
+ def __repr__(self):\r
+ if self.namespace:\r
+ return "<TypeInfo %r (from %r)>" % (self.name, self.namespace)\r
+ else:\r
+ return "<TypeInfo %r>" % self.name\r
+\r
+ def _get_name(self):\r
+ return self.name\r
+\r
+ def _get_namespace(self):\r
+ return self.namespace\r
+\r
+_no_type = TypeInfo(None, None)\r
+\r
+class Element(Node):\r
+ nodeType = Node.ELEMENT_NODE\r
+ nodeValue = None\r
+ schemaType = _no_type\r
+\r
+ _magic_id_nodes = 0\r
+\r
+ _child_node_types = (Node.ELEMENT_NODE,\r
+ Node.PROCESSING_INSTRUCTION_NODE,\r
+ Node.COMMENT_NODE,\r
+ Node.TEXT_NODE,\r
+ Node.CDATA_SECTION_NODE,\r
+ Node.ENTITY_REFERENCE_NODE)\r
+\r
+ def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,\r
+ localName=None):\r
+ self.tagName = self.nodeName = tagName\r
+ self.prefix = prefix\r
+ self.namespaceURI = namespaceURI\r
+ self.childNodes = NodeList()\r
+\r
+ self._attrs = {} # attributes are double-indexed:\r
+ self._attrsNS = {} # tagName -> Attribute\r
+ # URI,localName -> Attribute\r
+ # in the future: consider lazy generation\r
+ # of attribute objects this is too tricky\r
+ # for now because of headaches with\r
+ # namespaces.\r
+\r
+ def _get_localName(self):\r
+ return self.tagName.split(":", 1)[-1]\r
+\r
+ def _get_tagName(self):\r
+ return self.tagName\r
+\r
+ def unlink(self):\r
+ for attr in self._attrs.values():\r
+ attr.unlink()\r
+ self._attrs = None\r
+ self._attrsNS = None\r
+ Node.unlink(self)\r
+\r
+ def getAttribute(self, attname):\r
+ try:\r
+ return self._attrs[attname].value\r
+ except KeyError:\r
+ return ""\r
+\r
+ def getAttributeNS(self, namespaceURI, localName):\r
+ try:\r
+ return self._attrsNS[(namespaceURI, localName)].value\r
+ except KeyError:\r
+ return ""\r
+\r
+ def setAttribute(self, attname, value):\r
+ attr = self.getAttributeNode(attname)\r
+ if attr is None:\r
+ attr = Attr(attname)\r
+ # for performance\r
+ d = attr.__dict__\r
+ d["value"] = d["nodeValue"] = value\r
+ d["ownerDocument"] = self.ownerDocument\r
+ self.setAttributeNode(attr)\r
+ elif value != attr.value:\r
+ d = attr.__dict__\r
+ d["value"] = d["nodeValue"] = value\r
+ if attr.isId:\r
+ _clear_id_cache(self)\r
+\r
+ def setAttributeNS(self, namespaceURI, qualifiedName, value):\r
+ prefix, localname = _nssplit(qualifiedName)\r
+ attr = self.getAttributeNodeNS(namespaceURI, localname)\r
+ if attr is None:\r
+ # for performance\r
+ attr = Attr(qualifiedName, namespaceURI, localname, prefix)\r
+ d = attr.__dict__\r
+ d["prefix"] = prefix\r
+ d["nodeName"] = qualifiedName\r
+ d["value"] = d["nodeValue"] = value\r
+ d["ownerDocument"] = self.ownerDocument\r
+ self.setAttributeNode(attr)\r
+ else:\r
+ d = attr.__dict__\r
+ if value != attr.value:\r
+ d["value"] = d["nodeValue"] = value\r
+ if attr.isId:\r
+ _clear_id_cache(self)\r
+ if attr.prefix != prefix:\r
+ d["prefix"] = prefix\r
+ d["nodeName"] = qualifiedName\r
+\r
+ def getAttributeNode(self, attrname):\r
+ return self._attrs.get(attrname)\r
+\r
+ def getAttributeNodeNS(self, namespaceURI, localName):\r
+ return self._attrsNS.get((namespaceURI, localName))\r
+\r
+ def setAttributeNode(self, attr):\r
+ if attr.ownerElement not in (None, self):\r
+ raise xml.dom.InuseAttributeErr("attribute node already owned")\r
+ old1 = self._attrs.get(attr.name, None)\r
+ if old1 is not None:\r
+ self.removeAttributeNode(old1)\r
+ old2 = self._attrsNS.get((attr.namespaceURI, attr.localName), None)\r
+ if old2 is not None and old2 is not old1:\r
+ self.removeAttributeNode(old2)\r
+ _set_attribute_node(self, attr)\r
+\r
+ if old1 is not attr:\r
+ # It might have already been part of this node, in which case\r
+ # it doesn't represent a change, and should not be returned.\r
+ return old1\r
+ if old2 is not attr:\r
+ return old2\r
+\r
+ setAttributeNodeNS = setAttributeNode\r
+\r
+ def removeAttribute(self, name):\r
+ try:\r
+ attr = self._attrs[name]\r
+ except KeyError:\r
+ raise xml.dom.NotFoundErr()\r
+ self.removeAttributeNode(attr)\r
+\r
+ def removeAttributeNS(self, namespaceURI, localName):\r
+ try:\r
+ attr = self._attrsNS[(namespaceURI, localName)]\r
+ except KeyError:\r
+ raise xml.dom.NotFoundErr()\r
+ self.removeAttributeNode(attr)\r
+\r
+ def removeAttributeNode(self, node):\r
+ if node is None:\r
+ raise xml.dom.NotFoundErr()\r
+ try:\r
+ self._attrs[node.name]\r
+ except KeyError:\r
+ raise xml.dom.NotFoundErr()\r
+ _clear_id_cache(self)\r
+ node.unlink()\r
+ # Restore this since the node is still useful and otherwise\r
+ # unlinked\r
+ node.ownerDocument = self.ownerDocument\r
+\r
+ removeAttributeNodeNS = removeAttributeNode\r
+\r
+ def hasAttribute(self, name):\r
+ return name in self._attrs\r
+\r
+ def hasAttributeNS(self, namespaceURI, localName):\r
+ return (namespaceURI, localName) in self._attrsNS\r
+\r
+ def getElementsByTagName(self, name):\r
+ return _get_elements_by_tagName_helper(self, name, NodeList())\r
+\r
+ def getElementsByTagNameNS(self, namespaceURI, localName):\r
+ return _get_elements_by_tagName_ns_helper(\r
+ self, namespaceURI, localName, NodeList())\r
+\r
+ def __repr__(self):\r
+ return "<DOM Element: %s at %#x>" % (self.tagName, id(self))\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl=""):\r
+ # indent = current indentation\r
+ # addindent = indentation to add to higher levels\r
+ # newl = newline string\r
+ writer.write(indent+"<" + self.tagName)\r
+\r
+ attrs = self._get_attributes()\r
+ a_names = attrs.keys()\r
+ a_names.sort()\r
+\r
+ for a_name in a_names:\r
+ writer.write(" %s=\"" % a_name)\r
+ _write_data(writer, attrs[a_name].value)\r
+ writer.write("\"")\r
+ if self.childNodes:\r
+ writer.write(">")\r
+ if (len(self.childNodes) == 1 and\r
+ self.childNodes[0].nodeType == Node.TEXT_NODE):\r
+ self.childNodes[0].writexml(writer, '', '', '')\r
+ else:\r
+ writer.write(newl)\r
+ for node in self.childNodes:\r
+ node.writexml(writer, indent+addindent, addindent, newl)\r
+ writer.write(indent)\r
+ writer.write("</%s>%s" % (self.tagName, newl))\r
+ else:\r
+ writer.write("/>%s"%(newl))\r
+\r
+ def _get_attributes(self):\r
+ return NamedNodeMap(self._attrs, self._attrsNS, self)\r
+\r
+ def hasAttributes(self):\r
+ if self._attrs:\r
+ return True\r
+ else:\r
+ return False\r
+\r
+ # DOM Level 3 attributes, based on the 22 Oct 2002 draft\r
+\r
+ def setIdAttribute(self, name):\r
+ idAttr = self.getAttributeNode(name)\r
+ self.setIdAttributeNode(idAttr)\r
+\r
+ def setIdAttributeNS(self, namespaceURI, localName):\r
+ idAttr = self.getAttributeNodeNS(namespaceURI, localName)\r
+ self.setIdAttributeNode(idAttr)\r
+\r
+ def setIdAttributeNode(self, idAttr):\r
+ if idAttr is None or not self.isSameNode(idAttr.ownerElement):\r
+ raise xml.dom.NotFoundErr()\r
+ if _get_containing_entref(self) is not None:\r
+ raise xml.dom.NoModificationAllowedErr()\r
+ if not idAttr._is_id:\r
+ idAttr.__dict__['_is_id'] = True\r
+ self._magic_id_nodes += 1\r
+ self.ownerDocument._magic_id_count += 1\r
+ _clear_id_cache(self)\r
+\r
+defproperty(Element, "attributes",\r
+ doc="NamedNodeMap of attributes on the element.")\r
+defproperty(Element, "localName",\r
+ doc="Namespace-local name of this element.")\r
+\r
+\r
+def _set_attribute_node(element, attr):\r
+ _clear_id_cache(element)\r
+ element._attrs[attr.name] = attr\r
+ element._attrsNS[(attr.namespaceURI, attr.localName)] = attr\r
+\r
+ # This creates a circular reference, but Element.unlink()\r
+ # breaks the cycle since the references to the attribute\r
+ # dictionaries are tossed.\r
+ attr.__dict__['ownerElement'] = element\r
+\r
+\r
+class Childless:\r
+ """Mixin that makes childless-ness easy to implement and avoids\r
+ the complexity of the Node methods that deal with children.\r
+ """\r
+\r
+ attributes = None\r
+ childNodes = EmptyNodeList()\r
+ firstChild = None\r
+ lastChild = None\r
+\r
+ def _get_firstChild(self):\r
+ return None\r
+\r
+ def _get_lastChild(self):\r
+ return None\r
+\r
+ def appendChild(self, node):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ self.nodeName + " nodes cannot have children")\r
+\r
+ def hasChildNodes(self):\r
+ return False\r
+\r
+ def insertBefore(self, newChild, refChild):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ self.nodeName + " nodes do not have children")\r
+\r
+ def removeChild(self, oldChild):\r
+ raise xml.dom.NotFoundErr(\r
+ self.nodeName + " nodes do not have children")\r
+\r
+ def normalize(self):\r
+ # For childless nodes, normalize() has nothing to do.\r
+ pass\r
+\r
+ def replaceChild(self, newChild, oldChild):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ self.nodeName + " nodes do not have children")\r
+\r
+\r
+class ProcessingInstruction(Childless, Node):\r
+ nodeType = Node.PROCESSING_INSTRUCTION_NODE\r
+\r
+ def __init__(self, target, data):\r
+ self.target = self.nodeName = target\r
+ self.data = self.nodeValue = data\r
+\r
+ def _get_data(self):\r
+ return self.data\r
+ def _set_data(self, value):\r
+ d = self.__dict__\r
+ d['data'] = d['nodeValue'] = value\r
+\r
+ def _get_target(self):\r
+ return self.target\r
+ def _set_target(self, value):\r
+ d = self.__dict__\r
+ d['target'] = d['nodeName'] = value\r
+\r
+ def __setattr__(self, name, value):\r
+ if name == "data" or name == "nodeValue":\r
+ self.__dict__['data'] = self.__dict__['nodeValue'] = value\r
+ elif name == "target" or name == "nodeName":\r
+ self.__dict__['target'] = self.__dict__['nodeName'] = value\r
+ else:\r
+ self.__dict__[name] = value\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl=""):\r
+ writer.write("%s<?%s %s?>%s" % (indent,self.target, self.data, newl))\r
+\r
+\r
+class CharacterData(Childless, Node):\r
+ def _get_length(self):\r
+ return len(self.data)\r
+ __len__ = _get_length\r
+\r
+ def _get_data(self):\r
+ return self.__dict__['data']\r
+ def _set_data(self, data):\r
+ d = self.__dict__\r
+ d['data'] = d['nodeValue'] = data\r
+\r
+ _get_nodeValue = _get_data\r
+ _set_nodeValue = _set_data\r
+\r
+ def __setattr__(self, name, value):\r
+ if name == "data" or name == "nodeValue":\r
+ self.__dict__['data'] = self.__dict__['nodeValue'] = value\r
+ else:\r
+ self.__dict__[name] = value\r
+\r
+ def __repr__(self):\r
+ data = self.data\r
+ if len(data) > 10:\r
+ dotdotdot = "..."\r
+ else:\r
+ dotdotdot = ""\r
+ return '<DOM %s node "%r%s">' % (\r
+ self.__class__.__name__, data[0:10], dotdotdot)\r
+\r
+ def substringData(self, offset, count):\r
+ if offset < 0:\r
+ raise xml.dom.IndexSizeErr("offset cannot be negative")\r
+ if offset >= len(self.data):\r
+ raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")\r
+ if count < 0:\r
+ raise xml.dom.IndexSizeErr("count cannot be negative")\r
+ return self.data[offset:offset+count]\r
+\r
+ def appendData(self, arg):\r
+ self.data = self.data + arg\r
+\r
+ def insertData(self, offset, arg):\r
+ if offset < 0:\r
+ raise xml.dom.IndexSizeErr("offset cannot be negative")\r
+ if offset >= len(self.data):\r
+ raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")\r
+ if arg:\r
+ self.data = "%s%s%s" % (\r
+ self.data[:offset], arg, self.data[offset:])\r
+\r
+ def deleteData(self, offset, count):\r
+ if offset < 0:\r
+ raise xml.dom.IndexSizeErr("offset cannot be negative")\r
+ if offset >= len(self.data):\r
+ raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")\r
+ if count < 0:\r
+ raise xml.dom.IndexSizeErr("count cannot be negative")\r
+ if count:\r
+ self.data = self.data[:offset] + self.data[offset+count:]\r
+\r
+ def replaceData(self, offset, count, arg):\r
+ if offset < 0:\r
+ raise xml.dom.IndexSizeErr("offset cannot be negative")\r
+ if offset >= len(self.data):\r
+ raise xml.dom.IndexSizeErr("offset cannot be beyond end of data")\r
+ if count < 0:\r
+ raise xml.dom.IndexSizeErr("count cannot be negative")\r
+ if count:\r
+ self.data = "%s%s%s" % (\r
+ self.data[:offset], arg, self.data[offset+count:])\r
+\r
+defproperty(CharacterData, "length", doc="Length of the string data.")\r
+\r
+\r
+class Text(CharacterData):\r
+ # Make sure we don't add an instance __dict__ if we don't already\r
+ # have one, at least when that's possible:\r
+ # XXX this does not work, CharacterData is an old-style class\r
+ # __slots__ = ()\r
+\r
+ nodeType = Node.TEXT_NODE\r
+ nodeName = "#text"\r
+ attributes = None\r
+\r
+ def splitText(self, offset):\r
+ if offset < 0 or offset > len(self.data):\r
+ raise xml.dom.IndexSizeErr("illegal offset value")\r
+ newText = self.__class__()\r
+ newText.data = self.data[offset:]\r
+ newText.ownerDocument = self.ownerDocument\r
+ next = self.nextSibling\r
+ if self.parentNode and self in self.parentNode.childNodes:\r
+ if next is None:\r
+ self.parentNode.appendChild(newText)\r
+ else:\r
+ self.parentNode.insertBefore(newText, next)\r
+ self.data = self.data[:offset]\r
+ return newText\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl=""):\r
+ _write_data(writer, "%s%s%s" % (indent, self.data, newl))\r
+\r
+ # DOM Level 3 (WD 9 April 2002)\r
+\r
+ def _get_wholeText(self):\r
+ L = [self.data]\r
+ n = self.previousSibling\r
+ while n is not None:\r
+ if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):\r
+ L.insert(0, n.data)\r
+ n = n.previousSibling\r
+ else:\r
+ break\r
+ n = self.nextSibling\r
+ while n is not None:\r
+ if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):\r
+ L.append(n.data)\r
+ n = n.nextSibling\r
+ else:\r
+ break\r
+ return ''.join(L)\r
+\r
+ def replaceWholeText(self, content):\r
+ # XXX This needs to be seriously changed if minidom ever\r
+ # supports EntityReference nodes.\r
+ parent = self.parentNode\r
+ n = self.previousSibling\r
+ while n is not None:\r
+ if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):\r
+ next = n.previousSibling\r
+ parent.removeChild(n)\r
+ n = next\r
+ else:\r
+ break\r
+ n = self.nextSibling\r
+ if not content:\r
+ parent.removeChild(self)\r
+ while n is not None:\r
+ if n.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE):\r
+ next = n.nextSibling\r
+ parent.removeChild(n)\r
+ n = next\r
+ else:\r
+ break\r
+ if content:\r
+ d = self.__dict__\r
+ d['data'] = content\r
+ d['nodeValue'] = content\r
+ return self\r
+ else:\r
+ return None\r
+\r
+ def _get_isWhitespaceInElementContent(self):\r
+ if self.data.strip():\r
+ return False\r
+ elem = _get_containing_element(self)\r
+ if elem is None:\r
+ return False\r
+ info = self.ownerDocument._get_elem_info(elem)\r
+ if info is None:\r
+ return False\r
+ else:\r
+ return info.isElementContent()\r
+\r
+defproperty(Text, "isWhitespaceInElementContent",\r
+ doc="True iff this text node contains only whitespace"\r
+ " and is in element content.")\r
+defproperty(Text, "wholeText",\r
+ doc="The text of all logically-adjacent text nodes.")\r
+\r
+\r
+def _get_containing_element(node):\r
+ c = node.parentNode\r
+ while c is not None:\r
+ if c.nodeType == Node.ELEMENT_NODE:\r
+ return c\r
+ c = c.parentNode\r
+ return None\r
+\r
+def _get_containing_entref(node):\r
+ c = node.parentNode\r
+ while c is not None:\r
+ if c.nodeType == Node.ENTITY_REFERENCE_NODE:\r
+ return c\r
+ c = c.parentNode\r
+ return None\r
+\r
+\r
+class Comment(Childless, CharacterData):\r
+ nodeType = Node.COMMENT_NODE\r
+ nodeName = "#comment"\r
+\r
+ def __init__(self, data):\r
+ self.data = self.nodeValue = data\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl=""):\r
+ if "--" in self.data:\r
+ raise ValueError("'--' is not allowed in a comment node")\r
+ writer.write("%s<!--%s-->%s" % (indent, self.data, newl))\r
+\r
+\r
+class CDATASection(Text):\r
+ # Make sure we don't add an instance __dict__ if we don't already\r
+ # have one, at least when that's possible:\r
+ # XXX this does not work, Text is an old-style class\r
+ # __slots__ = ()\r
+\r
+ nodeType = Node.CDATA_SECTION_NODE\r
+ nodeName = "#cdata-section"\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl=""):\r
+ if self.data.find("]]>") >= 0:\r
+ raise ValueError("']]>' not allowed in a CDATA section")\r
+ writer.write("<![CDATA[%s]]>" % self.data)\r
+\r
+\r
+class ReadOnlySequentialNamedNodeMap(object):\r
+ __slots__ = '_seq',\r
+\r
+ def __init__(self, seq=()):\r
+ # seq should be a list or tuple\r
+ self._seq = seq\r
+\r
+ def __len__(self):\r
+ return len(self._seq)\r
+\r
+ def _get_length(self):\r
+ return len(self._seq)\r
+\r
+ def getNamedItem(self, name):\r
+ for n in self._seq:\r
+ if n.nodeName == name:\r
+ return n\r
+\r
+ def getNamedItemNS(self, namespaceURI, localName):\r
+ for n in self._seq:\r
+ if n.namespaceURI == namespaceURI and n.localName == localName:\r
+ return n\r
+\r
+ def __getitem__(self, name_or_tuple):\r
+ if isinstance(name_or_tuple, tuple):\r
+ node = self.getNamedItemNS(*name_or_tuple)\r
+ else:\r
+ node = self.getNamedItem(name_or_tuple)\r
+ if node is None:\r
+ raise KeyError, name_or_tuple\r
+ return node\r
+\r
+ def item(self, index):\r
+ if index < 0:\r
+ return None\r
+ try:\r
+ return self._seq[index]\r
+ except IndexError:\r
+ return None\r
+\r
+ def removeNamedItem(self, name):\r
+ raise xml.dom.NoModificationAllowedErr(\r
+ "NamedNodeMap instance is read-only")\r
+\r
+ def removeNamedItemNS(self, namespaceURI, localName):\r
+ raise xml.dom.NoModificationAllowedErr(\r
+ "NamedNodeMap instance is read-only")\r
+\r
+ def setNamedItem(self, node):\r
+ raise xml.dom.NoModificationAllowedErr(\r
+ "NamedNodeMap instance is read-only")\r
+\r
+ def setNamedItemNS(self, node):\r
+ raise xml.dom.NoModificationAllowedErr(\r
+ "NamedNodeMap instance is read-only")\r
+\r
+ def __getstate__(self):\r
+ return [self._seq]\r
+\r
+ def __setstate__(self, state):\r
+ self._seq = state[0]\r
+\r
+defproperty(ReadOnlySequentialNamedNodeMap, "length",\r
+ doc="Number of entries in the NamedNodeMap.")\r
+\r
+\r
+class Identified:\r
+ """Mix-in class that supports the publicId and systemId attributes."""\r
+\r
+ # XXX this does not work, this is an old-style class\r
+ # __slots__ = 'publicId', 'systemId'\r
+\r
+ def _identified_mixin_init(self, publicId, systemId):\r
+ self.publicId = publicId\r
+ self.systemId = systemId\r
+\r
+ def _get_publicId(self):\r
+ return self.publicId\r
+\r
+ def _get_systemId(self):\r
+ return self.systemId\r
+\r
+class DocumentType(Identified, Childless, Node):\r
+ nodeType = Node.DOCUMENT_TYPE_NODE\r
+ nodeValue = None\r
+ name = None\r
+ publicId = None\r
+ systemId = None\r
+ internalSubset = None\r
+\r
+ def __init__(self, qualifiedName):\r
+ self.entities = ReadOnlySequentialNamedNodeMap()\r
+ self.notations = ReadOnlySequentialNamedNodeMap()\r
+ if qualifiedName:\r
+ prefix, localname = _nssplit(qualifiedName)\r
+ self.name = localname\r
+ self.nodeName = self.name\r
+\r
+ def _get_internalSubset(self):\r
+ return self.internalSubset\r
+\r
+ def cloneNode(self, deep):\r
+ if self.ownerDocument is None:\r
+ # it's ok\r
+ clone = DocumentType(None)\r
+ clone.name = self.name\r
+ clone.nodeName = self.name\r
+ operation = xml.dom.UserDataHandler.NODE_CLONED\r
+ if deep:\r
+ clone.entities._seq = []\r
+ clone.notations._seq = []\r
+ for n in self.notations._seq:\r
+ notation = Notation(n.nodeName, n.publicId, n.systemId)\r
+ clone.notations._seq.append(notation)\r
+ n._call_user_data_handler(operation, n, notation)\r
+ for e in self.entities._seq:\r
+ entity = Entity(e.nodeName, e.publicId, e.systemId,\r
+ e.notationName)\r
+ entity.actualEncoding = e.actualEncoding\r
+ entity.encoding = e.encoding\r
+ entity.version = e.version\r
+ clone.entities._seq.append(entity)\r
+ e._call_user_data_handler(operation, n, entity)\r
+ self._call_user_data_handler(operation, self, clone)\r
+ return clone\r
+ else:\r
+ return None\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl=""):\r
+ writer.write("<!DOCTYPE ")\r
+ writer.write(self.name)\r
+ if self.publicId:\r
+ writer.write("%s PUBLIC '%s'%s '%s'"\r
+ % (newl, self.publicId, newl, self.systemId))\r
+ elif self.systemId:\r
+ writer.write("%s SYSTEM '%s'" % (newl, self.systemId))\r
+ if self.internalSubset is not None:\r
+ writer.write(" [")\r
+ writer.write(self.internalSubset)\r
+ writer.write("]")\r
+ writer.write(">"+newl)\r
+\r
+class Entity(Identified, Node):\r
+ attributes = None\r
+ nodeType = Node.ENTITY_NODE\r
+ nodeValue = None\r
+\r
+ actualEncoding = None\r
+ encoding = None\r
+ version = None\r
+\r
+ def __init__(self, name, publicId, systemId, notation):\r
+ self.nodeName = name\r
+ self.notationName = notation\r
+ self.childNodes = NodeList()\r
+ self._identified_mixin_init(publicId, systemId)\r
+\r
+ def _get_actualEncoding(self):\r
+ return self.actualEncoding\r
+\r
+ def _get_encoding(self):\r
+ return self.encoding\r
+\r
+ def _get_version(self):\r
+ return self.version\r
+\r
+ def appendChild(self, newChild):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "cannot append children to an entity node")\r
+\r
+ def insertBefore(self, newChild, refChild):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "cannot insert children below an entity node")\r
+\r
+ def removeChild(self, oldChild):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "cannot remove children from an entity node")\r
+\r
+ def replaceChild(self, newChild, oldChild):\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "cannot replace children of an entity node")\r
+\r
+class Notation(Identified, Childless, Node):\r
+ nodeType = Node.NOTATION_NODE\r
+ nodeValue = None\r
+\r
+ def __init__(self, name, publicId, systemId):\r
+ self.nodeName = name\r
+ self._identified_mixin_init(publicId, systemId)\r
+\r
+\r
+class DOMImplementation(DOMImplementationLS):\r
+ _features = [("core", "1.0"),\r
+ ("core", "2.0"),\r
+ ("core", None),\r
+ ("xml", "1.0"),\r
+ ("xml", "2.0"),\r
+ ("xml", None),\r
+ ("ls-load", "3.0"),\r
+ ("ls-load", None),\r
+ ]\r
+\r
+ def hasFeature(self, feature, version):\r
+ if version == "":\r
+ version = None\r
+ return (feature.lower(), version) in self._features\r
+\r
+ def createDocument(self, namespaceURI, qualifiedName, doctype):\r
+ if doctype and doctype.parentNode is not None:\r
+ raise xml.dom.WrongDocumentErr(\r
+ "doctype object owned by another DOM tree")\r
+ doc = self._create_document()\r
+\r
+ add_root_element = not (namespaceURI is None\r
+ and qualifiedName is None\r
+ and doctype is None)\r
+\r
+ if not qualifiedName and add_root_element:\r
+ # The spec is unclear what to raise here; SyntaxErr\r
+ # would be the other obvious candidate. Since Xerces raises\r
+ # InvalidCharacterErr, and since SyntaxErr is not listed\r
+ # for createDocument, that seems to be the better choice.\r
+ # XXX: need to check for illegal characters here and in\r
+ # createElement.\r
+\r
+ # DOM Level III clears this up when talking about the return value\r
+ # of this function. If namespaceURI, qName and DocType are\r
+ # Null the document is returned without a document element\r
+ # Otherwise if doctype or namespaceURI are not None\r
+ # Then we go back to the above problem\r
+ raise xml.dom.InvalidCharacterErr("Element with no name")\r
+\r
+ if add_root_element:\r
+ prefix, localname = _nssplit(qualifiedName)\r
+ if prefix == "xml" \\r
+ and namespaceURI != "http://www.w3.org/XML/1998/namespace":\r
+ raise xml.dom.NamespaceErr("illegal use of 'xml' prefix")\r
+ if prefix and not namespaceURI:\r
+ raise xml.dom.NamespaceErr(\r
+ "illegal use of prefix without namespaces")\r
+ element = doc.createElementNS(namespaceURI, qualifiedName)\r
+ if doctype:\r
+ doc.appendChild(doctype)\r
+ doc.appendChild(element)\r
+\r
+ if doctype:\r
+ doctype.parentNode = doctype.ownerDocument = doc\r
+\r
+ doc.doctype = doctype\r
+ doc.implementation = self\r
+ return doc\r
+\r
+ def createDocumentType(self, qualifiedName, publicId, systemId):\r
+ doctype = DocumentType(qualifiedName)\r
+ doctype.publicId = publicId\r
+ doctype.systemId = systemId\r
+ return doctype\r
+\r
+ # DOM Level 3 (WD 9 April 2002)\r
+\r
+ def getInterface(self, feature):\r
+ if self.hasFeature(feature, None):\r
+ return self\r
+ else:\r
+ return None\r
+\r
+ # internal\r
+ def _create_document(self):\r
+ return Document()\r
+\r
+class ElementInfo(object):\r
+ """Object that represents content-model information for an element.\r
+\r
+ This implementation is not expected to be used in practice; DOM\r
+ builders should provide implementations which do the right thing\r
+ using information available to it.\r
+\r
+ """\r
+\r
+ __slots__ = 'tagName',\r
+\r
+ def __init__(self, name):\r
+ self.tagName = name\r
+\r
+ def getAttributeType(self, aname):\r
+ return _no_type\r
+\r
+ def getAttributeTypeNS(self, namespaceURI, localName):\r
+ return _no_type\r
+\r
+ def isElementContent(self):\r
+ return False\r
+\r
+ def isEmpty(self):\r
+ """Returns true iff this element is declared to have an EMPTY\r
+ content model."""\r
+ return False\r
+\r
+ def isId(self, aname):\r
+ """Returns true iff the named attribute is a DTD-style ID."""\r
+ return False\r
+\r
+ def isIdNS(self, namespaceURI, localName):\r
+ """Returns true iff the identified attribute is a DTD-style ID."""\r
+ return False\r
+\r
+ def __getstate__(self):\r
+ return self.tagName\r
+\r
+ def __setstate__(self, state):\r
+ self.tagName = state\r
+\r
+def _clear_id_cache(node):\r
+ if node.nodeType == Node.DOCUMENT_NODE:\r
+ node._id_cache.clear()\r
+ node._id_search_stack = None\r
+ elif _in_document(node):\r
+ node.ownerDocument._id_cache.clear()\r
+ node.ownerDocument._id_search_stack= None\r
+\r
+class Document(Node, DocumentLS):\r
+ _child_node_types = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,\r
+ Node.COMMENT_NODE, Node.DOCUMENT_TYPE_NODE)\r
+\r
+ nodeType = Node.DOCUMENT_NODE\r
+ nodeName = "#document"\r
+ nodeValue = None\r
+ attributes = None\r
+ doctype = None\r
+ parentNode = None\r
+ previousSibling = nextSibling = None\r
+\r
+ implementation = DOMImplementation()\r
+\r
+ # Document attributes from Level 3 (WD 9 April 2002)\r
+\r
+ actualEncoding = None\r
+ encoding = None\r
+ standalone = None\r
+ version = None\r
+ strictErrorChecking = False\r
+ errorHandler = None\r
+ documentURI = None\r
+\r
+ _magic_id_count = 0\r
+\r
+ def __init__(self):\r
+ self.childNodes = NodeList()\r
+ # mapping of (namespaceURI, localName) -> ElementInfo\r
+ # and tagName -> ElementInfo\r
+ self._elem_info = {}\r
+ self._id_cache = {}\r
+ self._id_search_stack = None\r
+\r
+ def _get_elem_info(self, element):\r
+ if element.namespaceURI:\r
+ key = element.namespaceURI, element.localName\r
+ else:\r
+ key = element.tagName\r
+ return self._elem_info.get(key)\r
+\r
+ def _get_actualEncoding(self):\r
+ return self.actualEncoding\r
+\r
+ def _get_doctype(self):\r
+ return self.doctype\r
+\r
+ def _get_documentURI(self):\r
+ return self.documentURI\r
+\r
+ def _get_encoding(self):\r
+ return self.encoding\r
+\r
+ def _get_errorHandler(self):\r
+ return self.errorHandler\r
+\r
+ def _get_standalone(self):\r
+ return self.standalone\r
+\r
+ def _get_strictErrorChecking(self):\r
+ return self.strictErrorChecking\r
+\r
+ def _get_version(self):\r
+ return self.version\r
+\r
+ def appendChild(self, node):\r
+ if node.nodeType not in self._child_node_types:\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "%s cannot be child of %s" % (repr(node), repr(self)))\r
+ if node.parentNode is not None:\r
+ # This needs to be done before the next test since this\r
+ # may *be* the document element, in which case it should\r
+ # end up re-ordered to the end.\r
+ node.parentNode.removeChild(node)\r
+\r
+ if node.nodeType == Node.ELEMENT_NODE \\r
+ and self._get_documentElement():\r
+ raise xml.dom.HierarchyRequestErr(\r
+ "two document elements disallowed")\r
+ return Node.appendChild(self, node)\r
+\r
+ def removeChild(self, oldChild):\r
+ try:\r
+ self.childNodes.remove(oldChild)\r
+ except ValueError:\r
+ raise xml.dom.NotFoundErr()\r
+ oldChild.nextSibling = oldChild.previousSibling = None\r
+ oldChild.parentNode = None\r
+ if self.documentElement is oldChild:\r
+ self.documentElement = None\r
+\r
+ return oldChild\r
+\r
+ def _get_documentElement(self):\r
+ for node in self.childNodes:\r
+ if node.nodeType == Node.ELEMENT_NODE:\r
+ return node\r
+\r
+ def unlink(self):\r
+ if self.doctype is not None:\r
+ self.doctype.unlink()\r
+ self.doctype = None\r
+ Node.unlink(self)\r
+\r
+ def cloneNode(self, deep):\r
+ if not deep:\r
+ return None\r
+ clone = self.implementation.createDocument(None, None, None)\r
+ clone.encoding = self.encoding\r
+ clone.standalone = self.standalone\r
+ clone.version = self.version\r
+ for n in self.childNodes:\r
+ childclone = _clone_node(n, deep, clone)\r
+ assert childclone.ownerDocument.isSameNode(clone)\r
+ clone.childNodes.append(childclone)\r
+ if childclone.nodeType == Node.DOCUMENT_NODE:\r
+ assert clone.documentElement is None\r
+ elif childclone.nodeType == Node.DOCUMENT_TYPE_NODE:\r
+ assert clone.doctype is None\r
+ clone.doctype = childclone\r
+ childclone.parentNode = clone\r
+ self._call_user_data_handler(xml.dom.UserDataHandler.NODE_CLONED,\r
+ self, clone)\r
+ return clone\r
+\r
+ def createDocumentFragment(self):\r
+ d = DocumentFragment()\r
+ d.ownerDocument = self\r
+ return d\r
+\r
+ def createElement(self, tagName):\r
+ e = Element(tagName)\r
+ e.ownerDocument = self\r
+ return e\r
+\r
+ def createTextNode(self, data):\r
+ if not isinstance(data, StringTypes):\r
+ raise TypeError, "node contents must be a string"\r
+ t = Text()\r
+ t.data = data\r
+ t.ownerDocument = self\r
+ return t\r
+\r
+ def createCDATASection(self, data):\r
+ if not isinstance(data, StringTypes):\r
+ raise TypeError, "node contents must be a string"\r
+ c = CDATASection()\r
+ c.data = data\r
+ c.ownerDocument = self\r
+ return c\r
+\r
+ def createComment(self, data):\r
+ c = Comment(data)\r
+ c.ownerDocument = self\r
+ return c\r
+\r
+ def createProcessingInstruction(self, target, data):\r
+ p = ProcessingInstruction(target, data)\r
+ p.ownerDocument = self\r
+ return p\r
+\r
+ def createAttribute(self, qName):\r
+ a = Attr(qName)\r
+ a.ownerDocument = self\r
+ a.value = ""\r
+ return a\r
+\r
+ def createElementNS(self, namespaceURI, qualifiedName):\r
+ prefix, localName = _nssplit(qualifiedName)\r
+ e = Element(qualifiedName, namespaceURI, prefix)\r
+ e.ownerDocument = self\r
+ return e\r
+\r
+ def createAttributeNS(self, namespaceURI, qualifiedName):\r
+ prefix, localName = _nssplit(qualifiedName)\r
+ a = Attr(qualifiedName, namespaceURI, localName, prefix)\r
+ a.ownerDocument = self\r
+ a.value = ""\r
+ return a\r
+\r
+ # A couple of implementation-specific helpers to create node types\r
+ # not supported by the W3C DOM specs:\r
+\r
+ def _create_entity(self, name, publicId, systemId, notationName):\r
+ e = Entity(name, publicId, systemId, notationName)\r
+ e.ownerDocument = self\r
+ return e\r
+\r
+ def _create_notation(self, name, publicId, systemId):\r
+ n = Notation(name, publicId, systemId)\r
+ n.ownerDocument = self\r
+ return n\r
+\r
+ def getElementById(self, id):\r
+ if id in self._id_cache:\r
+ return self._id_cache[id]\r
+ if not (self._elem_info or self._magic_id_count):\r
+ return None\r
+\r
+ stack = self._id_search_stack\r
+ if stack is None:\r
+ # we never searched before, or the cache has been cleared\r
+ stack = [self.documentElement]\r
+ self._id_search_stack = stack\r
+ elif not stack:\r
+ # Previous search was completed and cache is still valid;\r
+ # no matching node.\r
+ return None\r
+\r
+ result = None\r
+ while stack:\r
+ node = stack.pop()\r
+ # add child elements to stack for continued searching\r
+ stack.extend([child for child in node.childNodes\r
+ if child.nodeType in _nodeTypes_with_children])\r
+ # check this node\r
+ info = self._get_elem_info(node)\r
+ if info:\r
+ # We have to process all ID attributes before\r
+ # returning in order to get all the attributes set to\r
+ # be IDs using Element.setIdAttribute*().\r
+ for attr in node.attributes.values():\r
+ if attr.namespaceURI:\r
+ if info.isIdNS(attr.namespaceURI, attr.localName):\r
+ self._id_cache[attr.value] = node\r
+ if attr.value == id:\r
+ result = node\r
+ elif not node._magic_id_nodes:\r
+ break\r
+ elif info.isId(attr.name):\r
+ self._id_cache[attr.value] = node\r
+ if attr.value == id:\r
+ result = node\r
+ elif not node._magic_id_nodes:\r
+ break\r
+ elif attr._is_id:\r
+ self._id_cache[attr.value] = node\r
+ if attr.value == id:\r
+ result = node\r
+ elif node._magic_id_nodes == 1:\r
+ break\r
+ elif node._magic_id_nodes:\r
+ for attr in node.attributes.values():\r
+ if attr._is_id:\r
+ self._id_cache[attr.value] = node\r
+ if attr.value == id:\r
+ result = node\r
+ if result is not None:\r
+ break\r
+ return result\r
+\r
+ def getElementsByTagName(self, name):\r
+ return _get_elements_by_tagName_helper(self, name, NodeList())\r
+\r
+ def getElementsByTagNameNS(self, namespaceURI, localName):\r
+ return _get_elements_by_tagName_ns_helper(\r
+ self, namespaceURI, localName, NodeList())\r
+\r
+ def isSupported(self, feature, version):\r
+ return self.implementation.hasFeature(feature, version)\r
+\r
+ def importNode(self, node, deep):\r
+ if node.nodeType == Node.DOCUMENT_NODE:\r
+ raise xml.dom.NotSupportedErr("cannot import document nodes")\r
+ elif node.nodeType == Node.DOCUMENT_TYPE_NODE:\r
+ raise xml.dom.NotSupportedErr("cannot import document type nodes")\r
+ return _clone_node(node, deep, self)\r
+\r
+ def writexml(self, writer, indent="", addindent="", newl="",\r
+ encoding = None):\r
+ if encoding is None:\r
+ writer.write('<?xml version="1.0" ?>'+newl)\r
+ else:\r
+ writer.write('<?xml version="1.0" encoding="%s"?>%s' % (encoding, newl))\r
+ for node in self.childNodes:\r
+ node.writexml(writer, indent, addindent, newl)\r
+\r
+ # DOM Level 3 (WD 9 April 2002)\r
+\r
+ def renameNode(self, n, namespaceURI, name):\r
+ if n.ownerDocument is not self:\r
+ raise xml.dom.WrongDocumentErr(\r
+ "cannot rename nodes from other documents;\n"\r
+ "expected %s,\nfound %s" % (self, n.ownerDocument))\r
+ if n.nodeType not in (Node.ELEMENT_NODE, Node.ATTRIBUTE_NODE):\r
+ raise xml.dom.NotSupportedErr(\r
+ "renameNode() only applies to element and attribute nodes")\r
+ if namespaceURI != EMPTY_NAMESPACE:\r
+ if ':' in name:\r
+ prefix, localName = name.split(':', 1)\r
+ if ( prefix == "xmlns"\r
+ and namespaceURI != xml.dom.XMLNS_NAMESPACE):\r
+ raise xml.dom.NamespaceErr(\r
+ "illegal use of 'xmlns' prefix")\r
+ else:\r
+ if ( name == "xmlns"\r
+ and namespaceURI != xml.dom.XMLNS_NAMESPACE\r
+ and n.nodeType == Node.ATTRIBUTE_NODE):\r
+ raise xml.dom.NamespaceErr(\r
+ "illegal use of the 'xmlns' attribute")\r
+ prefix = None\r
+ localName = name\r
+ else:\r
+ prefix = None\r
+ localName = None\r
+ if n.nodeType == Node.ATTRIBUTE_NODE:\r
+ element = n.ownerElement\r
+ if element is not None:\r
+ is_id = n._is_id\r
+ element.removeAttributeNode(n)\r
+ else:\r
+ element = None\r
+ # avoid __setattr__\r
+ d = n.__dict__\r
+ d['prefix'] = prefix\r
+ d['localName'] = localName\r
+ d['namespaceURI'] = namespaceURI\r
+ d['nodeName'] = name\r
+ if n.nodeType == Node.ELEMENT_NODE:\r
+ d['tagName'] = name\r
+ else:\r
+ # attribute node\r
+ d['name'] = name\r
+ if element is not None:\r
+ element.setAttributeNode(n)\r
+ if is_id:\r
+ element.setIdAttributeNode(n)\r
+ # It's not clear from a semantic perspective whether we should\r
+ # call the user data handlers for the NODE_RENAMED event since\r
+ # we're re-using the existing node. The draft spec has been\r
+ # interpreted as meaning "no, don't call the handler unless a\r
+ # new node is created."\r
+ return n\r
+\r
+defproperty(Document, "documentElement",\r
+ doc="Top-level element of this document.")\r
+\r
+\r
+def _clone_node(node, deep, newOwnerDocument):\r
+ """\r
+ Clone a node and give it the new owner document.\r
+ Called by Node.cloneNode and Document.importNode\r
+ """\r
+ if node.ownerDocument.isSameNode(newOwnerDocument):\r
+ operation = xml.dom.UserDataHandler.NODE_CLONED\r
+ else:\r
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED\r
+ if node.nodeType == Node.ELEMENT_NODE:\r
+ clone = newOwnerDocument.createElementNS(node.namespaceURI,\r
+ node.nodeName)\r
+ for attr in node.attributes.values():\r
+ clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)\r
+ a = clone.getAttributeNodeNS(attr.namespaceURI, attr.localName)\r
+ a.specified = attr.specified\r
+\r
+ if deep:\r
+ for child in node.childNodes:\r
+ c = _clone_node(child, deep, newOwnerDocument)\r
+ clone.appendChild(c)\r
+\r
+ elif node.nodeType == Node.DOCUMENT_FRAGMENT_NODE:\r
+ clone = newOwnerDocument.createDocumentFragment()\r
+ if deep:\r
+ for child in node.childNodes:\r
+ c = _clone_node(child, deep, newOwnerDocument)\r
+ clone.appendChild(c)\r
+\r
+ elif node.nodeType == Node.TEXT_NODE:\r
+ clone = newOwnerDocument.createTextNode(node.data)\r
+ elif node.nodeType == Node.CDATA_SECTION_NODE:\r
+ clone = newOwnerDocument.createCDATASection(node.data)\r
+ elif node.nodeType == Node.PROCESSING_INSTRUCTION_NODE:\r
+ clone = newOwnerDocument.createProcessingInstruction(node.target,\r
+ node.data)\r
+ elif node.nodeType == Node.COMMENT_NODE:\r
+ clone = newOwnerDocument.createComment(node.data)\r
+ elif node.nodeType == Node.ATTRIBUTE_NODE:\r
+ clone = newOwnerDocument.createAttributeNS(node.namespaceURI,\r
+ node.nodeName)\r
+ clone.specified = True\r
+ clone.value = node.value\r
+ elif node.nodeType == Node.DOCUMENT_TYPE_NODE:\r
+ assert node.ownerDocument is not newOwnerDocument\r
+ operation = xml.dom.UserDataHandler.NODE_IMPORTED\r
+ clone = newOwnerDocument.implementation.createDocumentType(\r
+ node.name, node.publicId, node.systemId)\r
+ clone.ownerDocument = newOwnerDocument\r
+ if deep:\r
+ clone.entities._seq = []\r
+ clone.notations._seq = []\r
+ for n in node.notations._seq:\r
+ notation = Notation(n.nodeName, n.publicId, n.systemId)\r
+ notation.ownerDocument = newOwnerDocument\r
+ clone.notations._seq.append(notation)\r
+ if hasattr(n, '_call_user_data_handler'):\r
+ n._call_user_data_handler(operation, n, notation)\r
+ for e in node.entities._seq:\r
+ entity = Entity(e.nodeName, e.publicId, e.systemId,\r
+ e.notationName)\r
+ entity.actualEncoding = e.actualEncoding\r
+ entity.encoding = e.encoding\r
+ entity.version = e.version\r
+ entity.ownerDocument = newOwnerDocument\r
+ clone.entities._seq.append(entity)\r
+ if hasattr(e, '_call_user_data_handler'):\r
+ e._call_user_data_handler(operation, n, entity)\r
+ else:\r
+ # Note the cloning of Document and DocumentType nodes is\r
+ # implementation specific. minidom handles those cases\r
+ # directly in the cloneNode() methods.\r
+ raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))\r
+\r
+ # Check for _call_user_data_handler() since this could conceivably\r
+ # used with other DOM implementations (one of the FourThought\r
+ # DOMs, perhaps?).\r
+ if hasattr(node, '_call_user_data_handler'):\r
+ node._call_user_data_handler(operation, node, clone)\r
+ return clone\r
+\r
+\r
+def _nssplit(qualifiedName):\r
+ fields = qualifiedName.split(':', 1)\r
+ if len(fields) == 2:\r
+ return fields\r
+ else:\r
+ return (None, fields[0])\r
+\r
+\r
+def _get_StringIO():\r
+ # we can't use cStringIO since it doesn't support Unicode strings\r
+ from StringIO import StringIO\r
+ return StringIO()\r
+\r
+def _do_pulldom_parse(func, args, kwargs):\r
+ events = func(*args, **kwargs)\r
+ toktype, rootNode = events.getEvent()\r
+ events.expandNode(rootNode)\r
+ events.clear()\r
+ return rootNode\r
+\r
+def parse(file, parser=None, bufsize=None):\r
+ """Parse a file into a DOM by filename or file object."""\r
+ if parser is None and not bufsize:\r
+ from xml.dom import expatbuilder\r
+ return expatbuilder.parse(file)\r
+ else:\r
+ from xml.dom import pulldom\r
+ return _do_pulldom_parse(pulldom.parse, (file,),\r
+ {'parser': parser, 'bufsize': bufsize})\r
+\r
+def parseString(string, parser=None):\r
+ """Parse a file into a DOM from a string."""\r
+ if parser is None:\r
+ from xml.dom import expatbuilder\r
+ return expatbuilder.parseString(string)\r
+ else:\r
+ from xml.dom import pulldom\r
+ return _do_pulldom_parse(pulldom.parseString, (string,),\r
+ {'parser': parser})\r
+\r
+def getDOMImplementation(features=None):\r
+ if features:\r
+ if isinstance(features, StringTypes):\r
+ features = domreg._parse_feature_string(features)\r
+ for f, v in features:\r
+ if not Document.implementation.hasFeature(f, v):\r
+ return None\r
+ return Document.implementation\r