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:
parent
a8f7e59761
commit
126f2f62db
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue