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):
    351          self.__namespacePrefixMap = { pyxb.namespace.XMLSchema_instance : 'xsi' }
    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