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