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