Package pyxb :: Package utils :: Module domutils
[hide private]
[frames] | no frames]

Source Code for Module pyxb.utils.domutils

  1  # -*- coding: utf-8 -*- 
  2  # Copyright 2009-2012, Peter A. Bigot 
  3  # 
  4  # Licensed under the Apache License, Version 2.0 (the "License"); you may 
  5  # not use this file except in compliance with the License. You may obtain a 
  6  # copy of the License at: 
  7  # 
  8  #            http://www.apache.org/licenses/LICENSE-2.0 
  9  # 
 10  # Unless required by applicable law or agreed to in writing, software 
 11  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 12  # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 13  # License for the specific language governing permissions and limitations 
 14  # under the License. 
 15   
 16  """Functions that support activities related to the Document Object Model.""" 
 17   
 18  import pyxb 
 19  import pyxb.namespace 
 20  import pyxb.utils.saxutils 
 21  import pyxb.utils.saxdom 
 22  import xml.dom 
 23  import logging 
 24   
 25  _log = logging.getLogger(__name__) 
 26   
 27  # The DOM implementation to be used for all processing.  Default is whatever 
 28  # your Python install uses.  If it's minidom, it should work. 
 29  __DOMImplementation = xml.dom.getDOMImplementation() 
30 31 -def GetDOMImplementation ():
32 """Return the DOMImplementation object used for pyxb operations. 33 34 This is primarily used as the default implementation when generating DOM 35 trees from a binding instance. It defaults to whatever 36 xml.dom.getDOMImplementation() returns in your installation (often 37 xml.dom.minidom). It can be overridden with SetDOMImplementation().""" 38 39 global __DOMImplementation 40 return __DOMImplementation
41
42 -def SetDOMImplementation (dom_implementation):
43 """Override the default DOMImplementation object.""" 44 global __DOMImplementation 45 __DOMImplementation = dom_implementation 46 return __DOMImplementation
47
48 # Unfortunately, the DOMImplementation interface doesn't provide a parser. So 49 # abstract this in case somebody wants to substitute a different one. Haven't 50 # decided how to express that yet. 51 -def StringToDOM (text, **kw):
52 """Convert string to a DOM instance. 53 54 @see: L{pyxb._SetXMLStyle}.""" 55 if pyxb.XMLStyle_minidom == pyxb._XMLStyle: 56 parser = pyxb.utils.saxutils.make_parser() 57 if isinstance(text, unicode): 58 text = text.encode(pyxb._InputEncoding) 59 return xml.dom.minidom.parseString(text, parser) 60 return pyxb.utils.saxdom.parseString(text, **kw)
61
62 -def NodeAttribute (node, attribute_ncname, attribute_ns=None):
63 """Namespace-aware search for an optional attribute in a node. 64 65 @param attribute_ncname: The local name of the attribute. 66 @type attribute_ncname: C{str} or C{unicode} 67 68 @keyword attribute_ns: The namespace of the attribute. Defaults to None 69 since most attributes are not in a namespace. Can be provided as either a 70 L{pyxb.namespace.Namespace} instance, or a string URI. 71 @type attribute_ns: C{None} or C{str} or C{unicode} or L{pyxb.namespace.Namespace} 72 73 @return: The value of the attribute, or C{None} if the attribute is not 74 present. (Unless C{None}, the value will always be a (unicode) string.) 75 """ 76 77 ns_uri = attribute_ns 78 if isinstance(attribute_ns, pyxb.namespace.Namespace): 79 ns_uri = attribute_ns.uri() 80 attr = node.getAttributeNodeNS(ns_uri, attribute_ncname) 81 if attr is None: 82 return None 83 return attr.value
84
85 -def LocateUniqueChild (node, tag, absent_ok=True, namespace=pyxb.namespace.XMLSchema):
86 """Locate a unique child of the DOM node. 87 88 This function returns the sole child of node which is an ELEMENT_NODE 89 instance and has a tag consistent with the given tag. If multiple nodes 90 with a matching C{tag} are found, or C{absent_ok} is C{False} and no 91 matching tag is found, an exception is raised. 92 93 @param node: An a xml.dom.Node ELEMENT_NODE instance 94 @param tag: the NCName of an element in the namespace 95 @keyword absent_ok: If C{True} (default), C{None} is returned if no match 96 can be found. If C{False}, an exception is raised if no match can be 97 found. 98 @keyword namespace: The namespace to which the child element belongs. 99 Default is the XMLSchema namespace. 100 @rtype: C{xml.dom.Node} 101 102 @raise pyxb.SchemaValidationError: multiple elements are identified 103 @raise pyxb.SchemaValidationError: C{absent_ok} is C{False} and no element is identified. 104 """ 105 candidate = None 106 for cn in node.childNodes: 107 if (xml.dom.Node.ELEMENT_NODE == cn.nodeType) and namespace.nodeIsNamed(cn, tag): 108 if candidate: 109 raise pyxb.SchemaValidationError('Multiple %s elements nested in %s' % (tag, node.nodeName)) 110 candidate = cn 111 if (candidate is None) and not absent_ok: 112 raise pyxb.SchemaValidationError('Expected %s elements nested in %s' % (tag, node.nodeName)) 113 return candidate
114
115 -def LocateMatchingChildren (node, tag, namespace=pyxb.namespace.XMLSchema):
116 """Locate all children of the DOM node that have a particular tag. 117 118 This function returns a list of children of node which are ELEMENT_NODE 119 instances and have a tag consistent with the given tag. 120 121 @param node: An a xml.dom.Node ELEMENT_NODE instance. 122 @param tag: the NCName of an element in the namespace, which defaults to the 123 XMLSchema namespace. 124 @keyword namespace: The namespace to which the child element belongs. 125 Default is the XMLSchema namespace. 126 127 @rtype: C{list(xml.dom.Node)} 128 """ 129 matches = [] 130 for cn in node.childNodes: 131 if (xml.dom.Node.ELEMENT_NODE == cn.nodeType) and namespace.nodeIsNamed(cn, tag): 132 matches.append(cn) 133 return matches
134
135 -def LocateFirstChildElement (node, absent_ok=True, require_unique=False, ignore_annotations=True):
136 """Locate the first element child of the node. 137 138 139 @param node: An a xml.dom.Node ELEMENT_NODE instance. 140 @keyword absent_ok: If C{True} (default), C{None} is returned if no match 141 can be found. If C{False}, an exception is raised if no match can be 142 found. 143 @keyword require_unique: If C{False} (default), it is acceptable for there 144 to be multiple child elements. If C{True}, presence of multiple child 145 elements raises an exception. 146 @keyword ignore_annotations: If C{True} (default), annotations are skipped 147 wheen looking for the first child element. If C{False}, an annotation 148 counts as an element. 149 @rtype: C{xml.dom.Node} 150 151 @raise SchemaValidationError: C{absent_ok} is C{False} and no child 152 element was identified. 153 @raise SchemaValidationError: C{require_unique} is C{True} and multiple 154 child elements were identified 155 """ 156 157 candidate = None 158 for cn in node.childNodes: 159 if xml.dom.Node.ELEMENT_NODE == cn.nodeType: 160 if ignore_annotations and pyxb.namespace.XMLSchema.nodeIsNamed(cn, 'annotation'): 161 continue 162 if require_unique: 163 if candidate: 164 raise pyxb.SchemaValidationError('Multiple elements nested in %s' % (node.nodeName,)) 165 candidate = cn 166 else: 167 return cn 168 if (candidate is None) and not absent_ok: 169 raise pyxb.SchemaValidationError('No elements nested in %s' % (node.nodeName,)) 170 return candidate
171
172 -def HasNonAnnotationChild (node):
173 """Return True iff C{node} has an ELEMENT_NODE child that is not an 174 XMLSchema annotation node. 175 176 @rtype: C{bool} 177 """ 178 for cn in node.childNodes: 179 if (xml.dom.Node.ELEMENT_NODE == cn.nodeType) and (not pyxb.namespace.XMLSchema.nodeIsNamed(cn, 'annotation')): 180 return True 181 return False
182
183 -def ExtractTextContent (node):
184 """Walk all the children, extracting all text content and 185 catenating it into the return value. 186 187 Returns C{None} if no text content (including whitespace) is found. 188 189 This is mainly used to strip comments out of the content of complex 190 elements with simple types. 191 192 @rtype: C{unicode} or C{str} 193 """ 194 text = [] 195 for cn in node.childNodes: 196 if xml.dom.Node.TEXT_NODE == cn.nodeType: 197 text.append(cn.data) 198 elif xml.dom.Node.CDATA_SECTION_NODE == cn.nodeType: 199 text.append(cn.data) 200 elif xml.dom.Node.COMMENT_NODE == cn.nodeType: 201 pass 202 else: 203 raise pyxb.BadDocumentError('Non-text node %s found in content' % (cn,)) 204 if 0 == len(text): 205 return None 206 return ''.join(text)
207
208 -class _BDSNamespaceSupport (object):
209 """Class holding information relevant to generating the namespace aspects 210 of a DOM instance.""" 211 # Namespace declarations required on the top element 212 __namespaces = None 213 214 # Integer counter to help generate unique namespace prefixes 215 __namespacePrefixCounter = None 216
217 - def defaultNamespace (self):
218 """The registered default namespace. 219 220 @rtype: L{pyxb.namespace.Namespace} 221 """ 222 return self.__defaultNamespace
223 __defaultNamespace = None 224
225 - def setDefaultNamespace (self, default_namespace):
226 """Set the default namespace for the generated document. 227 228 Even if invoked post construction, the default namespace will affect 229 the entire document, as all namespace declarations are placed in the 230 document root. 231 232 @param default_namespace: The namespace to be defined as the default 233 namespace in the top-level element of the document. May be provided 234 as a real namespace, or just its URI. 235 @type default_namespace: L{pyxb.namespace.Namespace} or C{str} or 236 C{unicode}. 237 """ 238 239 if self.__defaultNamespace is not None: 240 del self.__namespaces[self.__defaultNamespace] 241 if isinstance(default_namespace, basestring): 242 default_namespace = pyxb.namespace.NamespaceForURI(default_namespace, create_if_missing=True) 243 if (default_namespace is not None) and default_namespace.isAbsentNamespace(): 244 raise pyxb.UsageError('Default namespace must not be an absent namespace') 245 self.__defaultNamespace = default_namespace 246 if self.__defaultNamespace is not None: 247 self.__namespaces[self.__defaultNamespace] = None
248
249 - def namespacePrefixMap (self):
250 """Return a map from Namespace instances to the prefix by which they 251 are represented in the DOM document.""" 252 return self.__namespacePrefixMap.copy()
253 __namespacePrefixMap = None 254
255 - def declareNamespace (self, namespace, prefix=None, add_to_map=False):
256 """Add the given namespace as one to be used in this document. 257 258 @param namespace: The namespace to be associated with the document. 259 @type namespace: L{pyxb.namespace.Namespace} 260 261 @keyword prefix: Optional prefix to be used with this namespace. If 262 not provided, a unique prefix is generated or a standard prefix is 263 used, depending on the namespace. 264 265 @keyword add_to_map: If C{False} (default), the prefix is not added to 266 the namespace prefix map. If C{True} it is added. (Often, things 267 added to the prefix map are preserved across resets, which is often 268 not desired for specific prefix/namespace pairs). 269 270 @todo: ensure multiple namespaces do not share the same prefix 271 @todo: provide default prefix in L{pyxb.namespace.Namespace} 272 @todo: support multiple prefixes for each namespace 273 """ 274 if not isinstance(namespace, pyxb.namespace.Namespace): 275 raise pyxb.UsageError('declareNamespace: must be given a namespace instance') 276 if namespace.isAbsentNamespace(): 277 raise pyxb.UsageError('declareNamespace: namespace must not be an absent namespace') 278 if prefix is None: 279 prefix = self.__namespaces.get(namespace) 280 if prefix is None: 281 prefix = self.__namespacePrefixMap.get(namespace) 282 if prefix is None: 283 prefix = namespace.prefix() 284 if prefix is None: 285 self.__namespacePrefixCounter += 1 286 prefix = 'ns%d' % (self.__namespacePrefixCounter,) 287 if prefix == self.__namespaces.get(namespace): 288 return prefix 289 if prefix in self.__prefixes: 290 raise pyxb.LogicError('Prefix %s is already in use' % (prefix,)) 291 self.__namespaces[namespace] = prefix 292 self.__prefixes.add(prefix) 293 if add_to_map: 294 self.__namespacePrefixMap[namespace] = prefix 295 return prefix
296
297 - def namespacePrefix (self, namespace):
298 """Return the prefix to be used for the given namespace. 299 300 This will L{declare <declareNamespace>} the namespace if it has not 301 yet been observed. 302 303 @param namespace: The namespace for which a prefix is needed. If the 304 provided namespace is C{None} or an absent namespace, the C{None} 305 value will be returned as the corresponding prefix. 306 """ 307 308 if (namespace is None) or namespace.isAbsentNamespace(): 309 return None 310 if isinstance(namespace, basestring): 311 namespace = pyxb.namespace.NamespaceForURI(namespace, create_if_missing=True) 312 if not (namespace in self.__namespaces): 313 return self.declareNamespace(namespace) 314 return self.__namespaces[namespace]
315
316 - def namespaces (self):
317 """Return the set of Namespace instances known to this instance.""" 318 return self.__namespaces
319 320 # Restore the namespace map to its default, which is the undeclared 321 # namespace for XML schema instances (C{xsi}
322 - def __resetNamespacePrefixMap (self):
324
325 - def reset (self, prefix_map=False):
326 """Reset this instance to the state it was when created. 327 328 This flushes the list of namespaces for the document. The 329 defaultNamespace is not modified.""" 330 self.__namespaces = { } 331 if self.__defaultNamespace is not None: 332 self.__namespaces[self.__defaultNamespace] = None 333 self.__prefixes = set() 334 self.__namespacePrefixCounter = 0 335 if prefix_map: 336 self.__resetNamespacePrefixMap()
337
338 - def __init__ (self, default_namespace=None, namespace_prefix_map=None, inherit_from=None):
339 """Create a new namespace declaration configuration. 340 341 @keyword default_namespace: Optional L{pyxb.namespace.Namespace} 342 instance that serves as the default namespace (applies to unqualified 343 names). 344 345 @keyword namespace_prefix_map: Optional map from 346 L{pyxb.namespace.Namespace} instances to C{str} values that are to be 347 used as the corresponding namespace prefix when constructing 348 U{qualified names<http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-qname>}. 349 350 @keyword inherit_from: Optional instance of this class from which 351 defaults are inherited. Inheritance is overridden by values of other 352 keywords in the initializer. 353 """ 354 self.__prefixes = set() 355 self.__namespacePrefixCounter = 0 356 self.__namespaces = { } 357 self.__defaultNamespace = None 358 self.__resetNamespacePrefixMap() 359 if inherit_from is not None: 360 if default_namespace is None: 361 default_namespace = inherit_from.defaultNamespace() 362 self.__namespacePrefixMap.update(inherit_from.__namespacePrefixMap) 363 self.__namespacePrefixCount = inherit_from.__namespacePrefixCounter 364 self.__namespaces.update(inherit_from.__namespaces) 365 self.__prefixes.update(inherit_from.__prefixes) 366 if default_namespace is not None: 367 self.setDefaultNamespace(default_namespace) 368 prefixes = set(self.__namespacePrefixMap.values()) 369 prefixes.update(self.__prefixes) 370 if namespace_prefix_map is not None: 371 prefixes = set() 372 for (ns, pfx) in namespace_prefix_map.items(): 373 ns = pyxb.namespace.NamespaceInstance(ns) 374 if pfx in prefixes: 375 raise pyxb.LogicError('Cannot assign same prefix to multiple namespacess: %s' % (pfx,)) 376 prefixes.add(pfx) 377 self.__namespacePrefixMap[ns] = pfx
378
379 -class BindingDOMSupport (object):
380 """This holds DOM-related information used when generating a DOM tree from 381 a binding instance.""" 382
383 - def implementation (self):
384 """The DOMImplementation object to be used. 385 386 Defaults to L{pyxb.utils.domutils.GetDOMImplementation()}, but can be 387 overridden in the constructor call using the C{implementation} 388 keyword.""" 389 return self.__implementation
390 __implementation = None 391
392 - def document (self):
393 """Return the document generated using this instance.""" 394 return self.__document
395 __document = None 396
397 - def requireXSIType (self):
398 """Indicates whether {xsi:type<http://www.w3.org/TR/xmlschema-1/#xsi_type>} should be added to all elements. 399 400 Certain WSDL styles and encodings seem to require explicit notation of 401 the type of each element, even if it was specified in the schema. 402 403 This value can only be set in the constructor.""" 404 return self.__requireXSIType
405 __requireXSIType = None 406
407 - def reset (self, **kw):
408 """Reset this instance to the state it was when created. 409 410 This creates a new root document with no content, and flushes the list 411 of namespaces for the document. The defaultNamespace and 412 requireXSIType are not modified.""" 413 self.__document = self.implementation().createDocument(None, None, None) 414 self.__namespaceSupport.reset(**kw)
415 416 @classmethod
417 - def Reset (cls, **kw):
418 """Reset the global defaults for default/prefix/namespace informmation.""" 419 cls.__NamespaceSupport.reset(**kw)
420
421 - def __init__ (self, implementation=None, default_namespace=None, require_xsi_type=False, namespace_prefix_map=None):
422 """Create a new instance used for building a single document. 423 424 @keyword implementation: The C{xml.dom} implementation to use. 425 Defaults to the one selected by L{GetDOMImplementation}. 426 427 @keyword default_namespace: The namespace to configure as the default 428 for the document. If not provided, there is no default namespace. 429 @type default_namespace: L{pyxb.namespace.Namespace} 430 431 @keyword require_xsi_type: If C{True}, an U{xsi:type 432 <http://www.w3.org/TR/xmlschema-1/#xsi_type>} attribute should be 433 placed in every element. 434 @type require_xsi_type: C{bool} 435 436 @keyword namespace_prefix_map: A map from pyxb.namespace.Namespace 437 instances to the preferred prefix to use for the namespace in xmlns 438 declarations. The default one assigns 'xsi' for the XMLSchema 439 instance namespace. 440 @type namespace_prefix_map: C{map} from L{pyxb.namespace.Namespace} to C{str} 441 442 @raise pyxb.LogicError: the same prefix is associated with multiple 443 namespaces in the C{namespace_prefix_map}. 444 445 """ 446 if implementation is None: 447 implementation = GetDOMImplementation() 448 self.__implementation = implementation 449 self.__requireXSIType = require_xsi_type 450 self.__namespaceSupport = _BDSNamespaceSupport(default_namespace, namespace_prefix_map, inherit_from=self.__NamespaceSupport) 451 self.reset()
452 453 __namespaceSupport = None 454 __NamespaceSupport = _BDSNamespaceSupport() 455 456 # Namespace declarations required on the top element
457 - def defaultNamespace (self):
458 """The default namespace for this instance""" 459 return self.__namespaceSupport.defaultNamespace()
460 @classmethod
461 - def DefaultNamespace (cls):
462 """The global default namespace (used on instance creation if not overridden)""" 463 return cls.__NamespaceSupport.defaultNamespace()
464
465 - def setDefaultNamespace (self, default_namespace):
466 return self.__namespaceSupport.setDefaultNamespace(default_namespace)
467 @classmethod
468 - def SetDefaultNamespace (cls, default_namespace):
469 return cls.__NamespaceSupport.setDefaultNamespace(default_namespace)
470
471 - def declareNamespace (self, namespace, prefix=None):
472 """Declare a namespace within this instance only.""" 473 return self.__namespaceSupport.declareNamespace(namespace, prefix, add_to_map=True)
474 @classmethod
475 - def DeclareNamespace (cls, namespace, prefix=None):
476 """Declare a namespace that will be added to each created instance.""" 477 return cls.__NamespaceSupport.declareNamespace(namespace, prefix, add_to_map=True)
478
479 - def namespacePrefix (self, namespace):
480 """Obtain the prefix for the given namespace using this instance's configuration.""" 481 return self.__namespaceSupport.namespacePrefix(namespace)
482
483 - def namespacePrefixMap (self):
484 """Get the map from namespaces to prefixes for this instance""" 485 return self.__namespaceSupport.namespacePrefixMap().copy()
486 @classmethod
487 - def NamespacePrefixMap (cls):
488 """Get the map of default namespace-to-prefix mappings""" 489 return cls.__NamespaceSupport.namespacePrefixMap().copy()
490
491 - def addAttribute (self, element, expanded_name, value):
492 """Add an attribute to the given element. 493 494 @param element: The element to which the attribute should be added 495 @type element: C{xml.dom.Element} 496 @param expanded_name: The name of the attribute. This may be a local 497 name if the attribute is not in a namespace. 498 @type expanded_name: L{pyxb.namespace.Namespace} or C{str} or C{unicode} 499 @param value: The value of the attribute 500 @type value: C{str} or C{unicode} 501 """ 502 name = expanded_name 503 namespace = None 504 if isinstance(name, pyxb.namespace.ExpandedName): 505 name = expanded_name.localName() 506 namespace = expanded_name.namespace() 507 prefix = self.namespacePrefix(namespace) 508 if prefix is not None: 509 name = '%s:%s' % (prefix, name) 510 element.setAttributeNS(namespace, name, value)
511
512 - def finalize (self):
513 """Do the final cleanup after generating the tree. This makes sure 514 that the document element includes XML Namespace declarations for all 515 namespaces referenced in the tree. 516 517 @return: The document that has been created. 518 @rtype: C{xml.dom.Document}""" 519 for ( ns, pfx ) in self.__namespaceSupport.namespaces().items(): 520 if pfx is None: 521 self.document().documentElement.setAttributeNS(pyxb.namespace.XMLNamespaces.uri(), 'xmlns', ns.uri()) 522 else: 523 self.document().documentElement.setAttributeNS(pyxb.namespace.XMLNamespaces.uri(), 'xmlns:%s' % (pfx,), ns.uri()) 524 return self.document()
525
526 - def createChildElement (self, expanded_name, parent=None):
527 """Create a new element node in the tree. 528 529 @param expanded_name: The name of the element. A plain string 530 indicates a name in no namespace. 531 @type expanded_name: L{pyxb.namespace.ExpandedName} or C{str} or C{unicode} 532 533 @keyword parent: The node in the tree that will serve as the child's 534 parent. If C{None}, the document element is used. (If there is no 535 document element, then this call creates it as a side-effect.) 536 537 @return: A newly created DOM element 538 @rtype: C{xml.dom.Element} 539 """ 540 541 if parent is None: 542 parent = self.document().documentElement 543 if parent is None: 544 parent = self.__document 545 if isinstance(expanded_name, (str, unicode)): 546 expanded_name = pyxb.namespace.ExpandedName(None, expanded_name) 547 if not isinstance(expanded_name, pyxb.namespace.ExpandedName): 548 raise pyxb.LogicError('Invalid type %s for expanded name' % (type(expanded_name),)) 549 ns = expanded_name.namespace() 550 name = expanded_name.localName() 551 ns_uri = xml.dom.EMPTY_NAMESPACE 552 pfx = self.namespacePrefix(ns) 553 if pfx is not None: 554 ns_uri = ns.uri() 555 name = '%s:%s' % (pfx, name) 556 element = self.__document.createElementNS(ns_uri, name) 557 return parent.appendChild(element)
558
559 - def _makeURINodeNamePair (self, node):
560 """Convert namespace information from a DOM node to text for new DOM node. 561 562 The namespaceURI and nodeName are extracted and parsed. The namespace 563 (if any) is registered within the document, along with any prefix from 564 the node name. A pair is returned where the first element is the 565 namespace URI or C{None}, and the second is a QName to be used for the 566 expanded name within this document. 567 568 @param node: An xml.dom.Node instance, presumably from a wildcard match. 569 @rtype: C{( str, str )}""" 570 ns = None 571 if node.namespaceURI is not None: 572 ns = pyxb.namespace.NamespaceForURI(node.namespaceURI, create_if_missing=True) 573 if node.ELEMENT_NODE == node.nodeType: 574 name = node.tagName 575 elif node.ATTRIBUTE_NODE == node.nodeType: 576 name = node.name 577 # saxdom uses the uriTuple as the name field while minidom uses 578 # the QName. @todo saxdom should be fixed. 579 if isinstance(name, tuple): 580 name = name[1] 581 else: 582 raise pyxb.UsageError('Unable to determine name from DOM node %s' % (node,)) 583 pfx = None 584 local_name = name 585 if 0 < name.find(':'): 586 (pfx, local_name) = name.split(':', 1) 587 if ns is None: 588 raise pyxb.LogicError('QName with prefix but no available namespace') 589 ns_uri = None 590 node_name = local_name 591 if ns is not None: 592 ns_uri = ns.uri() 593 self.declareNamespace(ns, pfx) 594 if pfx is None: 595 pfx = self.namespacePrefix(ns) 596 if pfx is not None: 597 node_name = '%s:%s' % (pfx, local_name) 598 return (ns_uri, node_name)
599
600 - def _deepClone (self, node, docnode):
601 if node.ELEMENT_NODE == node.nodeType: 602 (ns_uri, node_name) = self._makeURINodeNamePair(node) 603 clone_node = docnode.createElementNS(ns_uri, node_name) 604 attrs = node.attributes 605 for ai in xrange(attrs.length): 606 clone_node.setAttributeNodeNS(self._deepClone(attrs.item(ai), docnode)) 607 for child in node.childNodes: 608 clone_node.appendChild(self._deepClone(child, docnode)) 609 return clone_node 610 if node.TEXT_NODE == node.nodeType: 611 return docnode.createTextNode(node.data) 612 if node.ATTRIBUTE_NODE == node.nodeType: 613 (ns_uri, node_name) = self._makeURINodeNamePair(node) 614 clone_node = docnode.createAttributeNS(ns_uri, node_name) 615 clone_node.value = node.value 616 return clone_node 617 if node.COMMENT_NODE == node.nodeType: 618 return docnode.createComment(node.data) 619 raise ValueError('DOM node not supported in clone', node)
620
621 - def cloneIntoImplementation (self, node):
622 """Create a deep copy of the node in the target implementation. 623 624 Used when converting a DOM instance from one implementation (e.g., 625 L{pyxb.utils.saxdom}) into another (e.g., L{xml.dom.minidom}).""" 626 new_doc = self.implementation().createDocument(None, None, None) 627 return self._deepClone(node, new_doc)
628
629 - def appendChild (self, child, parent):
630 """Add the child to the parent. 631 632 @note: If the child and the parent use different DOM implementations, 633 this operation will clone the child into a new instance, and give that 634 to the parent. 635 636 @param child: The value to be appended 637 @type child: C{xml.dom.Node} 638 @param parent: The new parent of the child 639 @type parent: C{xml.dom.Node} 640 @rtype: C{xml.dom.Node}""" 641 642 # @todo This check is incomplete; is there a standard way to find the 643 # implementation of an xml.dom.Node instance? 644 if isinstance(child, pyxb.utils.saxdom.Node): 645 child = self.cloneIntoImplementation(child) 646 return parent.appendChild(child)
647 648 ## Local Variables: 649 ## fill-column:78 650 ## End: 651