Patch #407965: Improve Level 2 conformance of minidom

- addition of a DocumentFragment implementation and createDocumentFragment method
- proper setting of ownerDocument for all nodes
- setting of namespaceURI to None in Element as a class attribute
- addition of setAttributeNodeNS and removeAttributeNodeNS as aliases
  for setAttributeNode and removeAttributeNode
- support for inheriting from DOMImplementation to extend it with
  additional features (to override the Document class)
in pulldom:
- support for nodes (comment and PI) that occur before he document element;
  that became necessary as pulldom now delays creation of the document
  until it has the document element.
This commit is contained in:
Martin v. Löwis 2001-03-13 10:50:13 +00:00
parent a8f7e59761
commit 126f2f62db
2 changed files with 103 additions and 21 deletions

View File

@ -38,10 +38,11 @@ class Node(_Node):
_makeParentNodes = 1
debug = None
childNodeTypes = ()
namespaceURI = None # this is non-null only for elements and attributes
def __init__(self):
self.childNodes = []
self.parentNode = None
self.parentNode = self.ownerDocument = None
if Node._debug:
index = repr(id(self)) + repr(self.__class__)
Node.allnodes[index] = repr(self.__dict__)
@ -107,6 +108,11 @@ class Node(_Node):
return self.childNodes[-1]
def insertBefore(self, newChild, refChild):
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
for c in 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.childNodeTypes:
raise HierarchyRequestErr, \
"%s cannot be child of %s" % (repr(newChild), repr(self))
@ -130,6 +136,11 @@ class Node(_Node):
return newChild
def appendChild(self, node):
if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
for c in 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.childNodeTypes:
raise HierarchyRequestErr, \
"%s cannot be child of %s" % (repr(node), repr(self))
@ -148,6 +159,10 @@ class Node(_Node):
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.childNodeTypes:
raise HierarchyRequestErr, \
"%s cannot be child of %s" % (repr(newChild), repr(self))
@ -233,7 +248,7 @@ class Node(_Node):
# minidom-specific API:
def unlink(self):
self.parentNode = None
self.parentNode = self.ownerDocument = None
for child in self.childNodes:
child.unlink()
self.childNodes = None
@ -270,6 +285,21 @@ def _getElementsByTagNameNSHelper(parent, nsURI, localName, rc):
_getElementsByTagNameNSHelper(node, nsURI, localName, rc)
return rc
class DocumentFragment(Node):
nodeType = Node.DOCUMENT_FRAGMENT_NODE
nodeName = "#document-fragment"
nodeValue = None
attributes = None
parentNode = None
childNodeTypes = (Node.ELEMENT_NODE,
Node.TEXT_NODE,
Node.CDATA_SECTION_NODE,
Node.ENTITY_REFERENCE_NODE,
Node.PROCESSING_INSTRUCTION_NODE,
Node.COMMENT_NODE,
Node.NOTATION_NODE)
class Attr(Node):
nodeType = Node.ATTRIBUTE_NODE
attributes = None
@ -409,7 +439,7 @@ class Element(Node):
Node.COMMENT_NODE, Node.TEXT_NODE,
Node.CDATA_SECTION_NODE, Node.ENTITY_REFERENCE_NODE)
def __init__(self, tagName, namespaceURI="", prefix="",
def __init__(self, tagName, namespaceURI=None, prefix="",
localName=None):
Node.__init__(self)
self.tagName = self.nodeName = tagName
@ -494,6 +524,8 @@ class Element(Node):
# it doesn't represent a change, and should not be returned.
return old
setAttributeNodeNS = setAttributeNode
def removeAttribute(self, name):
attr = self._attrs[name]
self.removeAttributeNode(attr)
@ -507,6 +539,8 @@ class Element(Node):
del self._attrs[node.name]
del self._attrsNS[(node.namespaceURI, node.localName)]
removeAttributeNodeNS = removeAttributeNode
def hasAttribute(self, name):
return self._attrs.has_key(name)
@ -651,7 +685,7 @@ class DOMImplementation:
if doctype and doctype.parentNode is not None:
raise xml.dom.WrongDocumentErr(
"doctype object owned by another DOM tree")
doc = Document()
doc = self._createDocument()
if doctype is None:
doctype = self.createDocumentType(qualifiedName, None, None)
if not qualifiedName:
@ -671,7 +705,7 @@ class DOMImplementation:
"illegal use of prefix without namespaces")
element = doc.createElementNS(namespaceURI, qualifiedName)
doc.appendChild(element)
doctype.parentNode = doc
doctype.parentNode = doctype.ownerDocument = doc
doc.doctype = doctype
doc.implementation = self
return doc
@ -682,6 +716,9 @@ class DOMImplementation:
doctype.systemId = systemId
return doctype
# internal
def _createDocument(self):
return Document()
class Document(Node):
nodeType = Node.DOCUMENT_NODE
@ -690,6 +727,7 @@ class Document(Node):
attributes = None
doctype = None
parentNode = None
previousSibling = nextSibling = None
implementation = DOMImplementation()
childNodeTypes = (Node.ELEMENT_NODE, Node.PROCESSING_INSTRUCTION_NODE,
@ -728,25 +766,47 @@ class Document(Node):
self.doctype = None
Node.unlink(self)
createElement = Element
def createDocumentFragment(self):
d = DocumentFragment()
d.ownerDoc = self
return d
createTextNode = Text
def createElement(self, tagName):
e = Element(tagName)
e.ownerDocument = self
return e
createComment = Comment
def createTextNode(self, data):
t = Text(data)
t.ownerDocument = self
return t
createProcessingInstruction = ProcessingInstruction
def createComment(self, data):
c = Comment(data)
c.ownerDocument = self
return c
createAttribute = Attr
def createProcessingInstruction(self, target, data):
p = ProcessingInstruction(target, data)
p.ownerDocument = self
return p
def createAttribute(self, qName):
a = Attr(qName)
a.ownerDocument = self
return a
def createElementNS(self, namespaceURI, qualifiedName):
prefix, localName = _nssplit(qualifiedName)
return self.createElement(qualifiedName, namespaceURI,
prefix, localName)
e = Element(qualifiedName, namespaceURI, prefix, localName)
e.ownerDocument = self
return e
def createAttributeNS(self, namespaceURI, qualifiedName):
prefix, localName = _nssplit(qualifiedName)
return self.createAttribute(qualifiedName, namespaceURI,
localName, prefix)
a = Attr(qualifiedName, namespaceURI, localName, prefix)
a.ownerDocument = self
return a
def getElementsByTagNameNS(self, namespaceURI, localName):
_getElementsByTagNameNSHelper(self, namespaceURI, localName)

View File

@ -33,6 +33,7 @@ class PullDOM(xml.sax.ContentHandler):
pass
self._ns_contexts = [{}] # contains uri -> prefix dicts
self._current_context = self._ns_contexts[-1]
self.pending_events = []
def pop(self):
result = self.elementStack[-1]
@ -115,15 +116,22 @@ class PullDOM(xml.sax.ContentHandler):
self.lastEvent = self.lastEvent[1]
def comment(self, s):
node = self.document.createComment(s)
self.lastEvent[1] = [(COMMENT, node), None]
self.lastEvent = self.lastEvent[1]
if self.document:
node = self.document.createComment(s)
self.lastEvent[1] = [(COMMENT, node), None]
self.lastEvent = self.lastEvent[1]
else:
event = [(COMMENT, s), None]
self.pending_events.append(event)
def processingInstruction(self, target, data):
node = self.document.createProcessingInstruction(target, data)
self.lastEvent[1] = [(PROCESSING_INSTRUCTION, node), None]
self.lastEvent = self.lastEvent[1]
if self.document:
node = self.document.createProcessingInstruction(target, data)
self.lastEvent[1] = [(PROCESSING_INSTRUCTION, node), None]
self.lastEvent = self.lastEvent[1]
else:
event = [(PROCESSING_INSTRUCTION, target, data), None]
self.pending_events.append(event)
def ignorableWhitespace(self, chars):
node = self.document.createTextNode(chars)
@ -148,6 +156,20 @@ class PullDOM(xml.sax.ContentHandler):
self.lastEvent[1] = [(START_DOCUMENT, node), None]
self.lastEvent = self.lastEvent[1]
self.push(node)
# Put everything we have seen so far into the document
for e in self.pending_events:
if e[0][0] == PROCESSING_INSTRUCTION:
_,target,data = e[0]
n = self.document.createProcessingInstruction(target, data)
e[0] = (PROCESSING_INSTRUCTION, n)
elif e[0][0] == COMMENT:
n = self.document.createComment(e[0][1])
e[0] = (COMMENT, n)
else:
raise AssertionError("Unknown pending event ",e[0][0])
self.lastEvent[1] = e
self.lastEvent = e
self.pending_events = None
return node.firstChild
def endDocument(self):