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

Source Code for Module pyxb.utils.domutils

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