--- minidom_orig.py 2006-08-03 09:25:02.000000000 -0500 +++ minidom.py 2006-12-24 09:09:47.984375000 -0600 @@ -794,6 +794,192 @@ def __repr__(self): return "" % (self.tagName, id(self)) + def normalizeNamespace(self, namespaces={}): + """ implements namespace normalization as described by + http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html + """ + + attrs = self._get_attributes() + + namespaces = namespaces.copy(); + + # Pick up local namespace declarations + for attribute in attrs.values(): + if attribute.prefix == "xmlns": + if attribute.localName == "xmlns": + # Note: The prefix xmlns is used only to declare namespace + # bindings and is by definition bound to the namespace + # name http://www.w3.org/2000/xmlns/. It must not be + # declared. No other prefix may be bound to this + # namespace name. + raise xml.dom.NamespaceErr( + "xmlns must not be declared") + if attribute.value == xml.dom.XMLNS_NAMESPACE: + raise xml.dom.NamespaceErr( + "no prefix can be bound to namespace %s " % + xml.dom.XMLNS_NAMESPACE) + + namespaces[attribute.localName] = attribute.value + + if attribute.prefix is None and attribute.localName == "xmlns": + namespaces[None] = attribute.value + + # Fixup element's namespace + + if self.namespaceURI is not None: + if namespaces.has_key(self.prefix) and \ + namespaces[self.prefix] == self.namespaceURI: + + # Element's prefix/namespace pair (or default namespace, + # if no prefix) are within the scope of a binding ) + + # ==> do nothing, declaration in scope is inherited + # See section "B.1.1: Scope of a binding" for an example + pass + else: + # Create a local namespace declaration attr for this + # namespace, with Element's current prefix (or a default + # namespace, if no prefix). If there's a conflicting local + # declaration already present, change its value to use this + # namespace. + + # See section "B.1.2: Conflicting namespace declaration" + # for an example + + #NOTE that this may break other nodes within this Element's + # subtree, if they're already using this prefix. + # They will be repaired when we reach them. + if self.prefix is None: + self.setAttribute("xmlns", + self.namespaceURI) + else: + self.setAttributeNS(xml.dom.XMLNS_NAMESPACE, + "xmlns:%s" % self.prefix, + self.namespaceURI) + namespaces[self.prefix] = self.namespaceURI + else: + # Element has no namespace URI: + if self.localName is None: + # DOM Level 1 node + # if in process of validation against a namespace aware schema + # (i.e XML Schema) report a fatal error: the + # processor can not recover + # in this situation. + # Otherwise, report an error: + # no namespace fixup will be performed on this node. + raise xml.dom.NamespaceErr("Node must have a local name") + elif namespaces.has_key(None): + # Element has no pseudo-prefix + + #there's a conflicting local default namespace declaration + # already present ) + # change its value to use this empty namespace. + # NOTE that this may break other nodes within this Element's + # subtree, if they're already using the default namespaces. + # They will be repaired when we reach them. + del namespaces[None] + + if self.hasAttribute("xmlns"): + self.removeAttribute("xmlns") + + + # Examine and polish the attributes + + for attribute in attrs.values(): + if attribute.prefix == "xmlns": + continue; + if attribute.name == "xmlns": + continue; + + + if attribute.namespaceURI is not None: + + if attribute.prefix is None or \ + not self.hasAttributeNS(xml.dom.XMLNS_NAMESPACE, + attribute.prefix) or \ + ( namespaces.has_key(attribute.prefix) and \ + namespaces[attribute.prefix] != attribute.namespaceURI): + + matched = False; + + # pick the most local binding available; + # if there is more than one pick one arbitrarily + # ==> change attribute's prefix. + for (key, value) in namespaces.items(): + if value == attribute.namespaceURI: + matched = True; + self.removeAttributeNode(attribute) + if key is None: + self.setAttributeNS(attribute.namespaceURI, + "%s" % attribute.localName, + attribute.value) + else: + self.setAttributeNS(attribute.namespaceURI, + "%s:%s" % (key , attribute.localName ), + attribute.value) + + break; + + if matched: + pass; + elif (attribute.prefix is not None and \ + not self.hasAttributeNS(xml.dom.XMLNS_NAMESPACE, + attribute.prefix)): + + self.setAttributeNS(xml.dom.XMLNS_NAMESPACE, + "xmlns:%s" % attribute.prefix, + attribute.namespaceURI) + namespaces[attribute.prefix] = attribute.namespaceURI + else: + # find a prefix following the pattern "NS" +index + # (starting at 1) + # make sure this prefix is not declared in the current + # scope. + # create a local namespace declaration attribute + # ==> change attribute's prefix. + index = 1 + while namespaces.has_key("NS%d" %index): + index+=1; + + prefix = "NS%d" %index + + self.setAttributeNS(xml.dom.XMLNS_NAMESPACE, + "xmlns:%s" % prefix, + attribute.namespaceURI) + + namespaces[prefix] = attribute.namespaceURI + + self.removeAttributeNode(attribute) + self.setAttributeNS(attribute.namespaceURI, + "%s:%s" % (prefix , attribute.localName ), + attribute.value) + else: + + # Attr[i] has no namespace URI + + if ( attribute.localName is None ): + # DOM Level 1 node + # ==> if in process of validation against a + # namespace aware schema + # (i.e XML Schema) report a fatal error: + # the processor can not recover + # in this situation. + # Otherwise, report an error: no namespace + # fixup will be performed on this node. + raise xml.dom.NamespaceErr("Attribute must have a name") + else: + # attr has no namespace URI and no prefix + # no action is required, since attrs don't use default + # ==> do nothing + pass + + # do this recursively + for childNode in self.childNodes: + if childNode.nodeType == childNode.ELEMENT_NODE: + childNode.normalizeNamespace(namespaces) + + + def writexml(self, writer, indent="", addindent="", newl=""): # indent = current indentation # addindent = indentation to add to higher levels @@ -1741,6 +1927,7 @@ else: writer.write('%s' % (encoding, newl)) for node in self.childNodes: + node.normalizeNamespace() node.writexml(writer, indent, addindent, newl) # DOM Level 3 (WD 9 April 2002)