00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 ident = "$Id: Utility.py 1405 2007-07-10 20:25:44Z boverhof $"
00016
00017 import sys, types, httplib, urllib, socket, weakref
00018 from os.path import isfile
00019 from string import join, strip, split
00020 from UserDict import UserDict
00021 from cStringIO import StringIO
00022 from TimeoutSocket import TimeoutSocket, TimeoutError
00023 from urlparse import urlparse
00024 from httplib import HTTPConnection, HTTPSConnection
00025 from exceptions import Exception
00026 try:
00027 from ZSI import _get_idstr
00028 except:
00029 def _get_idstr(pyobj):
00030 '''Python 2.3.x generates a FutureWarning for negative IDs, so
00031 we use a different prefix character to ensure uniqueness, and
00032 call abs() to avoid the warning.'''
00033 x = id(pyobj)
00034 if x < 0:
00035 return 'x%x' % abs(x)
00036 return 'o%x' % x
00037
00038 import xml.dom.minidom
00039 from xml.dom import Node
00040
00041 import logging
00042 from c14n import Canonicalize
00043 from Namespaces import SCHEMA, SOAP, XMLNS, ZSI_SCHEMA_URI
00044
00045
00046 try:
00047 from xml.dom.ext import SplitQName
00048 except:
00049 def SplitQName(qname):
00050 '''SplitQName(qname) -> (string, string)
00051
00052 Split Qualified Name into a tuple of len 2, consisting
00053 of the prefix and the local name.
00054
00055 (prefix, localName)
00056
00057 Special Cases:
00058 xmlns -- (localName, 'xmlns')
00059 None -- (None, localName)
00060 '''
00061
00062 l = qname.split(':')
00063 if len(l) == 1:
00064 l.insert(0, None)
00065 elif len(l) == 2:
00066 if l[0] == 'xmlns':
00067 l.reverse()
00068 else:
00069 return
00070 return tuple(l)
00071
00072
00073
00074
00075
00076 basejoin = urllib.basejoin
00077 if sys.version_info[0:2] < (2, 4, 0, 'final', 0)[0:2]:
00078
00079 token = './'
00080 def basejoin(base, url):
00081 if url.startswith(token) is True:
00082 return urllib.basejoin(base,url[2:])
00083 return urllib.basejoin(base,url)
00084
00085 class NamespaceError(Exception):
00086 """Used to indicate a Namespace Error."""
00087
00088
00089 class RecursionError(Exception):
00090 """Used to indicate a HTTP redirect recursion."""
00091
00092
00093 class ParseError(Exception):
00094 """Used to indicate a XML parsing error."""
00095
00096
00097 class DOMException(Exception):
00098 """Used to indicate a problem processing DOM."""
00099
00100
00101 class Base:
00102 """Base class for instance level Logging"""
00103 def __init__(self, module=__name__):
00104 self.logger = logging.getLogger('%s-%s(%s)' %(module, self.__class__, _get_idstr(self)))
00105
00106
00107 class HTTPResponse:
00108 """Captures the information in an HTTP response message."""
00109
00110 def __init__(self, response):
00111 self.status = response.status
00112 self.reason = response.reason
00113 self.headers = response.msg
00114 self.body = response.read() or None
00115 response.close()
00116
00117 class TimeoutHTTP(HTTPConnection):
00118 """A custom http connection object that supports socket timeout."""
00119 def __init__(self, host, port=None, timeout=20):
00120 HTTPConnection.__init__(self, host, port)
00121 self.timeout = timeout
00122
00123 def connect(self):
00124 self.sock = TimeoutSocket(self.timeout)
00125 self.sock.connect((self.host, self.port))
00126
00127
00128 class TimeoutHTTPS(HTTPSConnection):
00129 """A custom https object that supports socket timeout. Note that this
00130 is not really complete. The builtin SSL support in the Python socket
00131 module requires a real socket (type) to be passed in to be hooked to
00132 SSL. That means our fake socket won't work and our timeout hacks are
00133 bypassed for send and recv calls. Since our hack _is_ in place at
00134 connect() time, it should at least provide some timeout protection."""
00135 def __init__(self, host, port=None, timeout=20, **kwargs):
00136 HTTPSConnection.__init__(self, str(host), port, **kwargs)
00137 self.timeout = timeout
00138
00139 def connect(self):
00140 sock = TimeoutSocket(self.timeout)
00141 sock.connect((self.host, self.port))
00142 realsock = getattr(sock.sock, '_sock', sock.sock)
00143 ssl = socket.ssl(realsock, self.key_file, self.cert_file)
00144 self.sock = httplib.FakeSocket(sock, ssl)
00145
00146
00147 def urlopen(url, timeout=20, redirects=None):
00148 """A minimal urlopen replacement hack that supports timeouts for http.
00149 Note that this supports GET only."""
00150 scheme, host, path, params, query, frag = urlparse(url)
00151
00152 if not scheme in ('http', 'https'):
00153 return urllib.urlopen(url)
00154 if params: path = '%s;%s' % (path, params)
00155 if query: path = '%s?%s' % (path, query)
00156 if frag: path = '%s#%s' % (path, frag)
00157
00158 if scheme == 'https':
00159
00160
00161
00162 try:
00163 import M2Crypto
00164 except ImportError:
00165 if not hasattr(socket, 'ssl'):
00166 raise RuntimeError, 'no built-in SSL Support'
00167
00168 conn = TimeoutHTTPS(host, None, timeout)
00169 else:
00170 ctx = M2Crypto.SSL.Context()
00171 ctx.set_session_timeout(timeout)
00172 conn = M2Crypto.httpslib.HTTPSConnection(host, ssl_context=ctx)
00173 conn.set_debuglevel(1)
00174
00175 else:
00176 conn = TimeoutHTTP(host, None, timeout)
00177
00178 conn.putrequest('GET', path)
00179 conn.putheader('Connection', 'close')
00180 conn.endheaders()
00181 response = None
00182 while 1:
00183 response = conn.getresponse()
00184 if response.status != 100:
00185 break
00186 conn._HTTPConnection__state = httplib._CS_REQ_SENT
00187 conn._HTTPConnection__response = None
00188
00189 status = response.status
00190
00191
00192 if status >= 300 and status < 400:
00193 location = response.msg.getheader('location')
00194 if location is not None:
00195 response.close()
00196 if redirects is not None and redirects.has_key(location):
00197 raise RecursionError(
00198 'Circular HTTP redirection detected.'
00199 )
00200 if redirects is None:
00201 redirects = {}
00202 redirects[location] = 1
00203 return urlopen(location, timeout, redirects)
00204 raise HTTPResponse(response)
00205
00206 if not (status >= 200 and status < 300):
00207 raise HTTPResponse(response)
00208
00209 body = StringIO(response.read())
00210 response.close()
00211 return body
00212
00213 class DOM:
00214 """The DOM singleton defines a number of XML related constants and
00215 provides a number of utility methods for DOM related tasks. It
00216 also provides some basic abstractions so that the rest of the
00217 package need not care about actual DOM implementation in use."""
00218
00219
00220
00221 NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'
00222 NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/'
00223
00224 NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope'
00225 NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding'
00226
00227 NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2)
00228 NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2)
00229
00230 NS_SOAP_ENV = NS_SOAP_ENV_1_1
00231 NS_SOAP_ENC = NS_SOAP_ENC_1_1
00232
00233 _soap_uri_mapping = {
00234 NS_SOAP_ENV_1_1 : '1.1',
00235 NS_SOAP_ENV_1_2 : '1.2',
00236 }
00237
00238 SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next'
00239 SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next'
00240 SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2)
00241
00242 def SOAPUriToVersion(self, uri):
00243 """Return the SOAP version related to an envelope uri."""
00244 value = self._soap_uri_mapping.get(uri)
00245 if value is not None:
00246 return value
00247 raise ValueError(
00248 'Unsupported SOAP envelope uri: %s' % uri
00249 )
00250
00251 def GetSOAPEnvUri(self, version):
00252 """Return the appropriate SOAP envelope uri for a given
00253 human-friendly SOAP version string (e.g. '1.1')."""
00254 attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_')
00255 value = getattr(self, attrname, None)
00256 if value is not None:
00257 return value
00258 raise ValueError(
00259 'Unsupported SOAP version: %s' % version
00260 )
00261
00262 def GetSOAPEncUri(self, version):
00263 """Return the appropriate SOAP encoding uri for a given
00264 human-friendly SOAP version string (e.g. '1.1')."""
00265 attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_')
00266 value = getattr(self, attrname, None)
00267 if value is not None:
00268 return value
00269 raise ValueError(
00270 'Unsupported SOAP version: %s' % version
00271 )
00272
00273 def GetSOAPActorNextUri(self, version):
00274 """Return the right special next-actor uri for a given
00275 human-friendly SOAP version string (e.g. '1.1')."""
00276 attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_')
00277 value = getattr(self, attrname, None)
00278 if value is not None:
00279 return value
00280 raise ValueError(
00281 'Unsupported SOAP version: %s' % version
00282 )
00283
00284
00285
00286
00287 NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema'
00288 NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance'
00289
00290 NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema'
00291 NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance'
00292
00293 NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema'
00294 NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance'
00295
00296 NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01)
00297 NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01)
00298
00299 NS_XSD = NS_XSD_01
00300 NS_XSI = NS_XSI_01
00301
00302 _xsd_uri_mapping = {
00303 NS_XSD_99 : NS_XSI_99,
00304 NS_XSD_00 : NS_XSI_00,
00305 NS_XSD_01 : NS_XSI_01,
00306 }
00307
00308 for key, value in _xsd_uri_mapping.items():
00309 _xsd_uri_mapping[value] = key
00310
00311
00312 def InstanceUriForSchemaUri(self, uri):
00313 """Return the appropriate matching XML Schema instance uri for
00314 the given XML Schema namespace uri."""
00315 return self._xsd_uri_mapping.get(uri)
00316
00317 def SchemaUriForInstanceUri(self, uri):
00318 """Return the appropriate matching XML Schema namespace uri for
00319 the given XML Schema instance namespace uri."""
00320 return self._xsd_uri_mapping.get(uri)
00321
00322
00323
00324
00325 NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/'
00326 NS_WSDL_ALL = (NS_WSDL_1_1,)
00327 NS_WSDL = NS_WSDL_1_1
00328
00329 NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'
00330 NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/'
00331 NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/'
00332
00333 NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,)
00334 NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,)
00335 NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,)
00336
00337 NS_SOAP_BINDING = NS_SOAP_BINDING_1_1
00338 NS_HTTP_BINDING = NS_HTTP_BINDING_1_1
00339 NS_MIME_BINDING = NS_MIME_BINDING_1_1
00340
00341 NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http'
00342 NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,)
00343 NS_SOAP_HTTP = NS_SOAP_HTTP_1_1
00344
00345
00346 _wsdl_uri_mapping = {
00347 NS_WSDL_1_1 : '1.1',
00348 }
00349
00350 def WSDLUriToVersion(self, uri):
00351 """Return the WSDL version related to a WSDL namespace uri."""
00352 value = self._wsdl_uri_mapping.get(uri)
00353 if value is not None:
00354 return value
00355 raise ValueError(
00356 'Unsupported SOAP envelope uri: %s' % uri
00357 )
00358
00359 def GetWSDLUri(self, version):
00360 attr = 'NS_WSDL_%s' % join(split(version, '.'), '_')
00361 value = getattr(self, attr, None)
00362 if value is not None:
00363 return value
00364 raise ValueError(
00365 'Unsupported WSDL version: %s' % version
00366 )
00367
00368 def GetWSDLSoapBindingUri(self, version):
00369 attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_')
00370 value = getattr(self, attr, None)
00371 if value is not None:
00372 return value
00373 raise ValueError(
00374 'Unsupported WSDL version: %s' % version
00375 )
00376
00377 def GetWSDLHttpBindingUri(self, version):
00378 attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_')
00379 value = getattr(self, attr, None)
00380 if value is not None:
00381 return value
00382 raise ValueError(
00383 'Unsupported WSDL version: %s' % version
00384 )
00385
00386 def GetWSDLMimeBindingUri(self, version):
00387 attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_')
00388 value = getattr(self, attr, None)
00389 if value is not None:
00390 return value
00391 raise ValueError(
00392 'Unsupported WSDL version: %s' % version
00393 )
00394
00395 def GetWSDLHttpTransportUri(self, version):
00396 attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_')
00397 value = getattr(self, attr, None)
00398 if value is not None:
00399 return value
00400 raise ValueError(
00401 'Unsupported WSDL version: %s' % version
00402 )
00403
00404
00405
00406 NS_XMLNS = 'http://www.w3.org/2000/xmlns/'
00407
00408
00409
00410 def isElement(self, node, name, nsuri=None):
00411 """Return true if the given node is an element with the given
00412 name and optional namespace uri."""
00413 if node.nodeType != node.ELEMENT_NODE:
00414 return 0
00415 return node.localName == name and \
00416 (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri))
00417
00418 def getElement(self, node, name, nsuri=None, default=join):
00419 """Return the first child of node with a matching name and
00420 namespace uri, or the default if one is provided."""
00421 nsmatch = self.nsUriMatch
00422 ELEMENT_NODE = node.ELEMENT_NODE
00423 for child in node.childNodes:
00424 if child.nodeType == ELEMENT_NODE:
00425 if ((child.localName == name or name is None) and
00426 (nsuri is None or nsmatch(child.namespaceURI, nsuri))
00427 ):
00428 return child
00429 if default is not join:
00430 return default
00431 raise KeyError, name
00432
00433 def getElementById(self, node, id, default=join):
00434 """Return the first child of node matching an id reference."""
00435 attrget = self.getAttr
00436 ELEMENT_NODE = node.ELEMENT_NODE
00437 for child in node.childNodes:
00438 if child.nodeType == ELEMENT_NODE:
00439 if attrget(child, 'id') == id:
00440 return child
00441 if default is not join:
00442 return default
00443 raise KeyError, name
00444
00445 def getMappingById(self, document, depth=None, element=None,
00446 mapping=None, level=1):
00447 """Create an id -> element mapping of those elements within a
00448 document that define an id attribute. The depth of the search
00449 may be controlled by using the (1-based) depth argument."""
00450 if document is not None:
00451 element = document.documentElement
00452 mapping = {}
00453 attr = element._attrs.get('id', None)
00454 if attr is not None:
00455 mapping[attr.value] = element
00456 if depth is None or depth > level:
00457 level = level + 1
00458 ELEMENT_NODE = element.ELEMENT_NODE
00459 for child in element.childNodes:
00460 if child.nodeType == ELEMENT_NODE:
00461 self.getMappingById(None, depth, child, mapping, level)
00462 return mapping
00463
00464 def getElements(self, node, name, nsuri=None):
00465 """Return a sequence of the child elements of the given node that
00466 match the given name and optional namespace uri."""
00467 nsmatch = self.nsUriMatch
00468 result = []
00469 ELEMENT_NODE = node.ELEMENT_NODE
00470 for child in node.childNodes:
00471 if child.nodeType == ELEMENT_NODE:
00472 if ((child.localName == name or name is None) and (
00473 (nsuri is None) or nsmatch(child.namespaceURI, nsuri))):
00474 result.append(child)
00475 return result
00476
00477 def hasAttr(self, node, name, nsuri=None):
00478 """Return true if element has attribute with the given name and
00479 optional nsuri. If nsuri is not specified, returns true if an
00480 attribute exists with the given name with any namespace."""
00481 if nsuri is None:
00482 if node.hasAttribute(name):
00483 return True
00484 return False
00485 return node.hasAttributeNS(nsuri, name)
00486
00487 def getAttr(self, node, name, nsuri=None, default=join):
00488 """Return the value of the attribute named 'name' with the
00489 optional nsuri, or the default if one is specified. If
00490 nsuri is not specified, an attribute that matches the
00491 given name will be returned regardless of namespace."""
00492 if nsuri is None:
00493 result = node._attrs.get(name, None)
00494 if result is None:
00495 for item in node._attrsNS.keys():
00496 if item[1] == name:
00497 result = node._attrsNS[item]
00498 break
00499 else:
00500 result = node._attrsNS.get((nsuri, name), None)
00501 if result is not None:
00502 return result.value
00503 if default is not join:
00504 return default
00505 return ''
00506
00507 def getAttrs(self, node):
00508 """Return a Collection of all attributes
00509 """
00510 attrs = {}
00511 for k,v in node._attrs.items():
00512 attrs[k] = v.value
00513 return attrs
00514
00515 def getElementText(self, node, preserve_ws=None):
00516 """Return the text value of an xml element node. Leading and trailing
00517 whitespace is stripped from the value unless the preserve_ws flag
00518 is passed with a true value."""
00519 result = []
00520 for child in node.childNodes:
00521 nodetype = child.nodeType
00522 if nodetype == child.TEXT_NODE or \
00523 nodetype == child.CDATA_SECTION_NODE:
00524 result.append(child.nodeValue)
00525 value = join(result, '')
00526 if preserve_ws is None:
00527 value = strip(value)
00528 return value
00529
00530 def findNamespaceURI(self, prefix, node):
00531 """Find a namespace uri given a prefix and a context node."""
00532 attrkey = (self.NS_XMLNS, prefix)
00533 DOCUMENT_NODE = node.DOCUMENT_NODE
00534 ELEMENT_NODE = node.ELEMENT_NODE
00535 while 1:
00536 if node is None:
00537 raise DOMException('Value for prefix %s not found.' % prefix)
00538 if node.nodeType != ELEMENT_NODE:
00539 node = node.parentNode
00540 continue
00541 result = node._attrsNS.get(attrkey, None)
00542 if result is not None:
00543 return result.value
00544 if hasattr(node, '__imported__'):
00545 raise DOMException('Value for prefix %s not found.' % prefix)
00546 node = node.parentNode
00547 if node.nodeType == DOCUMENT_NODE:
00548 raise DOMException('Value for prefix %s not found.' % prefix)
00549
00550 def findDefaultNS(self, node):
00551 """Return the current default namespace uri for the given node."""
00552 attrkey = (self.NS_XMLNS, 'xmlns')
00553 DOCUMENT_NODE = node.DOCUMENT_NODE
00554 ELEMENT_NODE = node.ELEMENT_NODE
00555 while 1:
00556 if node.nodeType != ELEMENT_NODE:
00557 node = node.parentNode
00558 continue
00559 result = node._attrsNS.get(attrkey, None)
00560 if result is not None:
00561 return result.value
00562 if hasattr(node, '__imported__'):
00563 raise DOMException('Cannot determine default namespace.')
00564 node = node.parentNode
00565 if node.nodeType == DOCUMENT_NODE:
00566 raise DOMException('Cannot determine default namespace.')
00567
00568 def findTargetNS(self, node):
00569 """Return the defined target namespace uri for the given node."""
00570 attrget = self.getAttr
00571 attrkey = (self.NS_XMLNS, 'xmlns')
00572 DOCUMENT_NODE = node.DOCUMENT_NODE
00573 ELEMENT_NODE = node.ELEMENT_NODE
00574 while 1:
00575 if node.nodeType != ELEMENT_NODE:
00576 node = node.parentNode
00577 continue
00578 result = attrget(node, 'targetNamespace', default=None)
00579 if result is not None:
00580 return result
00581 node = node.parentNode
00582 if node.nodeType == DOCUMENT_NODE:
00583 raise DOMException('Cannot determine target namespace.')
00584
00585 def getTypeRef(self, element):
00586 """Return (namespaceURI, name) for a type attribue of the given
00587 element, or None if the element does not have a type attribute."""
00588 typeattr = self.getAttr(element, 'type', default=None)
00589 if typeattr is None:
00590 return None
00591 parts = typeattr.split(':', 1)
00592 if len(parts) == 2:
00593 nsuri = self.findNamespaceURI(parts[0], element)
00594 else:
00595 nsuri = self.findDefaultNS(element)
00596 return (nsuri, parts[1])
00597
00598 def importNode(self, document, node, deep=0):
00599 """Implements (well enough for our purposes) DOM node import."""
00600 nodetype = node.nodeType
00601 if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE):
00602 raise DOMException('Illegal node type for importNode')
00603 if nodetype == node.ENTITY_REFERENCE_NODE:
00604 deep = 0
00605 clone = node.cloneNode(deep)
00606 self._setOwnerDoc(document, clone)
00607 clone.__imported__ = 1
00608 return clone
00609
00610 def _setOwnerDoc(self, document, node):
00611 node.ownerDocument = document
00612 for child in node.childNodes:
00613 self._setOwnerDoc(document, child)
00614
00615 def nsUriMatch(self, value, wanted, strict=0, tt=type(())):
00616 """Return a true value if two namespace uri values match."""
00617 if value == wanted or (type(wanted) is tt) and value in wanted:
00618 return 1
00619 if not strict and value is not None:
00620 wanted = type(wanted) is tt and wanted or (wanted,)
00621 value = value[-1:] != '/' and value or value[:-1]
00622 for item in wanted:
00623 if item == value or item[:-1] == value:
00624 return 1
00625 return 0
00626
00627 def createDocument(self, nsuri, qname, doctype=None):
00628 """Create a new writable DOM document object."""
00629 impl = xml.dom.minidom.getDOMImplementation()
00630 return impl.createDocument(nsuri, qname, doctype)
00631
00632 def loadDocument(self, data):
00633 """Load an xml file from a file-like object and return a DOM
00634 document instance."""
00635 return xml.dom.minidom.parse(data)
00636
00637 def loadFromURL(self, url):
00638 """Load an xml file from a URL and return a DOM document."""
00639 if isfile(url) is True:
00640 file = open(url, 'r')
00641 else:
00642 file = urlopen(url)
00643
00644 try:
00645 result = self.loadDocument(file)
00646 except Exception, ex:
00647 file.close()
00648 raise ParseError(('Failed to load document %s' %url,) + ex.args)
00649 else:
00650 file.close()
00651 return result
00652
00653 DOM = DOM()
00654
00655
00656 class MessageInterface:
00657 '''Higher Level Interface, delegates to DOM singleton, must
00658 be subclassed and implement all methods that throw NotImplementedError.
00659 '''
00660 def __init__(self, sw):
00661 '''Constructor, May be extended, do not override.
00662 sw -- soapWriter instance
00663 '''
00664 self.sw = None
00665 if type(sw) != weakref.ReferenceType and sw is not None:
00666 self.sw = weakref.ref(sw)
00667 else:
00668 self.sw = sw
00669
00670 def AddCallback(self, func, *arglist):
00671 self.sw().AddCallback(func, *arglist)
00672
00673 def Known(self, obj):
00674 return self.sw().Known(obj)
00675
00676 def Forget(self, obj):
00677 return self.sw().Forget(obj)
00678
00679 def canonicalize(self):
00680 '''canonicalize the underlying DOM, and return as string.
00681 '''
00682 raise NotImplementedError, ''
00683
00684 def createDocument(self, namespaceURI=SOAP.ENV, localName='Envelope'):
00685 '''create Document
00686 '''
00687 raise NotImplementedError, ''
00688
00689 def createAppendElement(self, namespaceURI, localName):
00690 '''create and append element(namespaceURI,localName), and return
00691 the node.
00692 '''
00693 raise NotImplementedError, ''
00694
00695 def findNamespaceURI(self, qualifiedName):
00696 raise NotImplementedError, ''
00697
00698 def resolvePrefix(self, prefix):
00699 raise NotImplementedError, ''
00700
00701 def setAttributeNS(self, namespaceURI, localName, value):
00702 '''set attribute (namespaceURI, localName)=value
00703 '''
00704 raise NotImplementedError, ''
00705
00706 def setAttributeType(self, namespaceURI, localName):
00707 '''set attribute xsi:type=(namespaceURI, localName)
00708 '''
00709 raise NotImplementedError, ''
00710
00711 def setNamespaceAttribute(self, namespaceURI, prefix):
00712 '''set namespace attribute xmlns:prefix=namespaceURI
00713 '''
00714 raise NotImplementedError, ''
00715
00716
00717 class ElementProxy(Base, MessageInterface):
00718 '''
00719 '''
00720 _soap_env_prefix = 'SOAP-ENV'
00721 _soap_enc_prefix = 'SOAP-ENC'
00722 _zsi_prefix = 'ZSI'
00723 _xsd_prefix = 'xsd'
00724 _xsi_prefix = 'xsi'
00725 _xml_prefix = 'xml'
00726 _xmlns_prefix = 'xmlns'
00727
00728 _soap_env_nsuri = SOAP.ENV
00729 _soap_enc_nsuri = SOAP.ENC
00730 _zsi_nsuri = ZSI_SCHEMA_URI
00731 _xsd_nsuri = SCHEMA.XSD3
00732 _xsi_nsuri = SCHEMA.XSI3
00733 _xml_nsuri = XMLNS.XML
00734 _xmlns_nsuri = XMLNS.BASE
00735
00736 standard_ns = {\
00737 _xml_prefix:_xml_nsuri,
00738 _xmlns_prefix:_xmlns_nsuri
00739 }
00740 reserved_ns = {\
00741 _soap_env_prefix:_soap_env_nsuri,
00742 _soap_enc_prefix:_soap_enc_nsuri,
00743 _zsi_prefix:_zsi_nsuri,
00744 _xsd_prefix:_xsd_nsuri,
00745 _xsi_prefix:_xsi_nsuri,
00746 }
00747 name = None
00748 namespaceURI = None
00749
00750 def __init__(self, sw, message=None):
00751 '''Initialize.
00752 sw -- SoapWriter
00753 '''
00754 self._indx = 0
00755 MessageInterface.__init__(self, sw)
00756 Base.__init__(self)
00757 self._dom = DOM
00758 self.node = None
00759 if type(message) in (types.StringType,types.UnicodeType):
00760 self.loadFromString(message)
00761 elif isinstance(message, ElementProxy):
00762 self.node = message._getNode()
00763 else:
00764 self.node = message
00765 self.processorNss = self.standard_ns.copy()
00766 self.processorNss.update(self.reserved_ns)
00767
00768 def __str__(self):
00769 return self.toString()
00770
00771 def evaluate(self, expression, processorNss=None):
00772 '''expression -- XPath compiled expression
00773 '''
00774 from Ft.Xml import XPath
00775 if not processorNss:
00776 context = XPath.Context.Context(self.node, processorNss=self.processorNss)
00777 else:
00778 context = XPath.Context.Context(self.node, processorNss=processorNss)
00779 nodes = expression.evaluate(context)
00780 return map(lambda node: ElementProxy(self.sw,node), nodes)
00781
00782
00783
00784
00785
00786 def checkNode(self, namespaceURI=None, localName=None):
00787 '''
00788 namespaceURI -- namespace of element
00789 localName -- local name of element
00790 '''
00791 namespaceURI = namespaceURI or self.namespaceURI
00792 localName = localName or self.name
00793 check = False
00794 if localName and self.node:
00795 check = self._dom.isElement(self.node, localName, namespaceURI)
00796 if not check:
00797 raise NamespaceError, 'unexpected node type %s, expecting %s' %(self.node, localName)
00798
00799 def setNode(self, node=None):
00800 if node:
00801 if isinstance(node, ElementProxy):
00802 self.node = node._getNode()
00803 else:
00804 self.node = node
00805 elif self.node:
00806 node = self._dom.getElement(self.node, self.name, self.namespaceURI, default=None)
00807 if not node:
00808 raise NamespaceError, 'cant find element (%s,%s)' %(self.namespaceURI,self.name)
00809 self.node = node
00810 else:
00811
00812 self.createDocument(self.namespaceURI, localName=self.name, doctype=None)
00813
00814 self.checkNode()
00815
00816
00817
00818
00819 def _getNode(self):
00820 return self.node
00821
00822 def _getElements(self):
00823 return self._dom.getElements(self.node, name=None)
00824
00825 def _getOwnerDocument(self):
00826 return self.node.ownerDocument or self.node
00827
00828 def _getUniquePrefix(self):
00829 '''I guess we need to resolve all potential prefixes
00830 because when the current node is attached it copies the
00831 namespaces into the parent node.
00832 '''
00833 while 1:
00834 self._indx += 1
00835 prefix = 'ns%d' %self._indx
00836 try:
00837 self._dom.findNamespaceURI(prefix, self._getNode())
00838 except DOMException, ex:
00839 break
00840 return prefix
00841
00842 def _getPrefix(self, node, nsuri):
00843 '''
00844 Keyword arguments:
00845 node -- DOM Element Node
00846 nsuri -- namespace of attribute value
00847 '''
00848 try:
00849 if node and (node.nodeType == node.ELEMENT_NODE) and \
00850 (nsuri == self._dom.findDefaultNS(node)):
00851 return None
00852 except DOMException, ex:
00853 pass
00854 if nsuri == XMLNS.XML:
00855 return self._xml_prefix
00856 if node.nodeType == Node.ELEMENT_NODE:
00857 for attr in node.attributes.values():
00858 if attr.namespaceURI == XMLNS.BASE \
00859 and nsuri == attr.value:
00860 return attr.localName
00861 else:
00862 if node.parentNode:
00863 return self._getPrefix(node.parentNode, nsuri)
00864 raise NamespaceError, 'namespaceURI "%s" is not defined' %nsuri
00865
00866 def _appendChild(self, node):
00867 '''
00868 Keyword arguments:
00869 node -- DOM Element Node
00870 '''
00871 if node is None:
00872 raise TypeError, 'node is None'
00873 self.node.appendChild(node)
00874
00875 def _insertBefore(self, newChild, refChild):
00876 '''
00877 Keyword arguments:
00878 child -- DOM Element Node to insert
00879 refChild -- DOM Element Node
00880 '''
00881 self.node.insertBefore(newChild, refChild)
00882
00883 def _setAttributeNS(self, namespaceURI, qualifiedName, value):
00884 '''
00885 Keyword arguments:
00886 namespaceURI -- namespace of attribute
00887 qualifiedName -- qualified name of new attribute value
00888 value -- value of attribute
00889 '''
00890 self.node.setAttributeNS(namespaceURI, qualifiedName, value)
00891
00892
00893
00894
00895 def isFault(self):
00896 '''check to see if this is a soap:fault message.
00897 '''
00898 return False
00899
00900 def getPrefix(self, namespaceURI):
00901 try:
00902 prefix = self._getPrefix(node=self.node, nsuri=namespaceURI)
00903 except NamespaceError, ex:
00904 prefix = self._getUniquePrefix()
00905 self.setNamespaceAttribute(prefix, namespaceURI)
00906 return prefix
00907
00908 def getDocument(self):
00909 return self._getOwnerDocument()
00910
00911 def setDocument(self, document):
00912 self.node = document
00913
00914 def importFromString(self, xmlString):
00915 doc = self._dom.loadDocument(StringIO(xmlString))
00916 node = self._dom.getElement(doc, name=None)
00917 clone = self.importNode(node)
00918 self._appendChild(clone)
00919
00920 def importNode(self, node):
00921 if isinstance(node, ElementProxy):
00922 node = node._getNode()
00923 return self._dom.importNode(self._getOwnerDocument(), node, deep=1)
00924
00925 def loadFromString(self, data):
00926 self.node = self._dom.loadDocument(StringIO(data))
00927
00928 def canonicalize(self):
00929 return Canonicalize(self.node)
00930
00931 def toString(self):
00932 return self.canonicalize()
00933
00934 def createDocument(self, namespaceURI, localName, doctype=None):
00935 '''If specified must be a SOAP envelope, else may contruct an empty document.
00936 '''
00937 prefix = self._soap_env_prefix
00938
00939 if namespaceURI == self.reserved_ns[prefix]:
00940 qualifiedName = '%s:%s' %(prefix,localName)
00941 elif namespaceURI is localName is None:
00942 self.node = self._dom.createDocument(None,None,None)
00943 return
00944 else:
00945 raise KeyError, 'only support creation of document in %s' %self.reserved_ns[prefix]
00946
00947 document = self._dom.createDocument(nsuri=namespaceURI, qname=qualifiedName, doctype=doctype)
00948 self.node = document.childNodes[0]
00949
00950
00951 for prefix,nsuri in self.reserved_ns.items():
00952 self._setAttributeNS(namespaceURI=self._xmlns_nsuri,
00953 qualifiedName='%s:%s' %(self._xmlns_prefix,prefix),
00954 value=nsuri)
00955
00956
00957
00958
00959 def hasAttribute(self, namespaceURI, localName):
00960 return self._dom.hasAttr(self._getNode(), name=localName, nsuri=namespaceURI)
00961
00962 def setAttributeType(self, namespaceURI, localName):
00963 '''set xsi:type
00964 Keyword arguments:
00965 namespaceURI -- namespace of attribute value
00966 localName -- name of new attribute value
00967
00968 '''
00969 self.logger.debug('setAttributeType: (%s,%s)', namespaceURI, localName)
00970 value = localName
00971 if namespaceURI:
00972 value = '%s:%s' %(self.getPrefix(namespaceURI),localName)
00973
00974 xsi_prefix = self.getPrefix(self._xsi_nsuri)
00975 self._setAttributeNS(self._xsi_nsuri, '%s:type' %xsi_prefix, value)
00976
00977 def createAttributeNS(self, namespace, name, value):
00978 document = self._getOwnerDocument()
00979 attrNode = document.createAttributeNS(namespace, name, value)
00980
00981 def setAttributeNS(self, namespaceURI, localName, value):
00982 '''
00983 Keyword arguments:
00984 namespaceURI -- namespace of attribute to create, None is for
00985 attributes in no namespace.
00986 localName -- local name of new attribute
00987 value -- value of new attribute
00988 '''
00989 prefix = None
00990 if namespaceURI:
00991 try:
00992 prefix = self.getPrefix(namespaceURI)
00993 except KeyError, ex:
00994 prefix = 'ns2'
00995 self.setNamespaceAttribute(prefix, namespaceURI)
00996 qualifiedName = localName
00997 if prefix:
00998 qualifiedName = '%s:%s' %(prefix, localName)
00999 self._setAttributeNS(namespaceURI, qualifiedName, value)
01000
01001 def setNamespaceAttribute(self, prefix, namespaceURI):
01002 '''
01003 Keyword arguments:
01004 prefix -- xmlns prefix
01005 namespaceURI -- value of prefix
01006 '''
01007 self._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
01008
01009
01010
01011
01012 def createElementNS(self, namespace, qname):
01013 '''
01014 Keyword arguments:
01015 namespace -- namespace of element to create
01016 qname -- qualified name of new element
01017 '''
01018 document = self._getOwnerDocument()
01019 node = document.createElementNS(namespace, qname)
01020 return ElementProxy(self.sw, node)
01021
01022 def createAppendSetElement(self, namespaceURI, localName, prefix=None):
01023 '''Create a new element (namespaceURI,name), append it
01024 to current node, then set it to be the current node.
01025 Keyword arguments:
01026 namespaceURI -- namespace of element to create
01027 localName -- local name of new element
01028 prefix -- if namespaceURI is not defined, declare prefix. defaults
01029 to 'ns1' if left unspecified.
01030 '''
01031 node = self.createAppendElement(namespaceURI, localName, prefix=None)
01032 node=node._getNode()
01033 self._setNode(node._getNode())
01034
01035 def createAppendElement(self, namespaceURI, localName, prefix=None):
01036 '''Create a new element (namespaceURI,name), append it
01037 to current node, and return the newly created node.
01038 Keyword arguments:
01039 namespaceURI -- namespace of element to create
01040 localName -- local name of new element
01041 prefix -- if namespaceURI is not defined, declare prefix. defaults
01042 to 'ns1' if left unspecified.
01043 '''
01044 declare = False
01045 qualifiedName = localName
01046 if namespaceURI:
01047 try:
01048 prefix = self.getPrefix(namespaceURI)
01049 except:
01050 declare = True
01051 prefix = prefix or self._getUniquePrefix()
01052 if prefix:
01053 qualifiedName = '%s:%s' %(prefix, localName)
01054 node = self.createElementNS(namespaceURI, qualifiedName)
01055 if declare:
01056 node._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI)
01057 self._appendChild(node=node._getNode())
01058 return node
01059
01060 def createInsertBefore(self, namespaceURI, localName, refChild):
01061 qualifiedName = localName
01062 prefix = self.getPrefix(namespaceURI)
01063 if prefix:
01064 qualifiedName = '%s:%s' %(prefix, localName)
01065 node = self.createElementNS(namespaceURI, qualifiedName)
01066 self._insertBefore(newChild=node._getNode(), refChild=refChild._getNode())
01067 return node
01068
01069 def getElement(self, namespaceURI, localName):
01070 '''
01071 Keyword arguments:
01072 namespaceURI -- namespace of element
01073 localName -- local name of element
01074 '''
01075 node = self._dom.getElement(self.node, localName, namespaceURI, default=None)
01076 if node:
01077 return ElementProxy(self.sw, node)
01078 return None
01079
01080 def getAttributeValue(self, namespaceURI, localName):
01081 '''
01082 Keyword arguments:
01083 namespaceURI -- namespace of attribute
01084 localName -- local name of attribute
01085 '''
01086 if self.hasAttribute(namespaceURI, localName):
01087 attr = self.node.getAttributeNodeNS(namespaceURI,localName)
01088 return attr.value
01089 return None
01090
01091 def getValue(self):
01092 return self._dom.getElementText(self.node, preserve_ws=True)
01093
01094
01095
01096
01097 def createAppendTextNode(self, pyobj):
01098 node = self.createTextNode(pyobj)
01099 self._appendChild(node=node._getNode())
01100 return node
01101
01102 def createTextNode(self, pyobj):
01103 document = self._getOwnerDocument()
01104 node = document.createTextNode(pyobj)
01105 return ElementProxy(self.sw, node)
01106
01107
01108
01109
01110 def findNamespaceURI(self, qualifiedName):
01111 parts = SplitQName(qualifiedName)
01112 element = self._getNode()
01113 if len(parts) == 1:
01114 return (self._dom.findTargetNS(element), value)
01115 return self._dom.findNamespaceURI(parts[0], element)
01116
01117 def resolvePrefix(self, prefix):
01118 element = self._getNode()
01119 return self._dom.findNamespaceURI(prefix, element)
01120
01121 def getSOAPEnvURI(self):
01122 return self._soap_env_nsuri
01123
01124 def isEmpty(self):
01125 return not self.node
01126
01127
01128
01129 class Collection(UserDict):
01130 """Helper class for maintaining ordered named collections."""
01131 default = lambda self,k: k.name
01132 def __init__(self, parent, key=None):
01133 UserDict.__init__(self)
01134 self.parent = weakref.ref(parent)
01135 self.list = []
01136 self._func = key or self.default
01137
01138 def __getitem__(self, key):
01139 if type(key) is type(1):
01140 return self.list[key]
01141 return self.data[key]
01142
01143 def __setitem__(self, key, item):
01144 item.parent = weakref.ref(self)
01145 self.list.append(item)
01146 self.data[key] = item
01147
01148 def keys(self):
01149 return map(lambda i: self._func(i), self.list)
01150
01151 def items(self):
01152 return map(lambda i: (self._func(i), i), self.list)
01153
01154 def values(self):
01155 return self.list
01156
01157
01158 class CollectionNS(UserDict):
01159 """Helper class for maintaining ordered named collections."""
01160 default = lambda self,k: k.name
01161 def __init__(self, parent, key=None):
01162 UserDict.__init__(self)
01163 self.parent = weakref.ref(parent)
01164 self.targetNamespace = None
01165 self.list = []
01166 self._func = key or self.default
01167
01168 def __getitem__(self, key):
01169 self.targetNamespace = self.parent().targetNamespace
01170 if type(key) is types.IntType:
01171 return self.list[key]
01172 elif self.__isSequence(key):
01173 nsuri,name = key
01174 return self.data[nsuri][name]
01175 return self.data[self.parent().targetNamespace][key]
01176
01177 def __setitem__(self, key, item):
01178 item.parent = weakref.ref(self)
01179 self.list.append(item)
01180 targetNamespace = getattr(item, 'targetNamespace', self.parent().targetNamespace)
01181 if not self.data.has_key(targetNamespace):
01182 self.data[targetNamespace] = {}
01183 self.data[targetNamespace][key] = item
01184
01185 def __isSequence(self, key):
01186 return (type(key) in (types.TupleType,types.ListType) and len(key) == 2)
01187
01188 def keys(self):
01189 keys = []
01190 for tns in self.data.keys():
01191 keys.append(map(lambda i: (tns,self._func(i)), self.data[tns].values()))
01192 return keys
01193
01194 def items(self):
01195 return map(lambda i: (self._func(i), i), self.list)
01196
01197 def values(self):
01198 return self.list
01199
01200
01201
01202
01203
01204
01205
01206
01207 from xml.dom.pulldom import PullDOM, START_ELEMENT
01208 if 1:
01209 def startPrefixMapping(self, prefix, uri):
01210 if not hasattr(self, '_xmlns_attrs'):
01211 self._xmlns_attrs = []
01212 self._xmlns_attrs.append((prefix or 'xmlns', uri))
01213 self._ns_contexts.append(self._current_context.copy())
01214 self._current_context[uri] = prefix or ''
01215
01216 PullDOM.startPrefixMapping = startPrefixMapping
01217
01218 def startElementNS(self, name, tagName , attrs):
01219
01220 xmlns_uri = 'http://www.w3.org/2000/xmlns/'
01221 xmlns_attrs = getattr(self, '_xmlns_attrs', None)
01222 if xmlns_attrs is not None:
01223 for aname, value in xmlns_attrs:
01224 attrs._attrs[(xmlns_uri, aname)] = value
01225 self._xmlns_attrs = []
01226 uri, localname = name
01227 if uri:
01228
01229
01230
01231 if tagName is None:
01232 prefix = self._current_context[uri]
01233 if prefix:
01234 tagName = prefix + ":" + localname
01235 else:
01236 tagName = localname
01237 if self.document:
01238 node = self.document.createElementNS(uri, tagName)
01239 else:
01240 node = self.buildDocument(uri, tagName)
01241 else:
01242
01243
01244 if self.document:
01245 node = self.document.createElement(localname)
01246 else:
01247 node = self.buildDocument(None, localname)
01248
01249 for aname,value in attrs.items():
01250 a_uri, a_localname = aname
01251 if a_uri == xmlns_uri:
01252 if a_localname == 'xmlns':
01253 qname = a_localname
01254 else:
01255 qname = 'xmlns:' + a_localname
01256 attr = self.document.createAttributeNS(a_uri, qname)
01257 node.setAttributeNodeNS(attr)
01258 elif a_uri:
01259 prefix = self._current_context[a_uri]
01260 if prefix:
01261 qname = prefix + ":" + a_localname
01262 else:
01263 qname = a_localname
01264 attr = self.document.createAttributeNS(a_uri, qname)
01265 node.setAttributeNodeNS(attr)
01266 else:
01267 attr = self.document.createAttribute(a_localname)
01268 node.setAttributeNode(attr)
01269 attr.value = value
01270
01271 self.lastEvent[1] = [(START_ELEMENT, node), None]
01272 self.lastEvent = self.lastEvent[1]
01273 self.push(node)
01274
01275 PullDOM.startElementNS = startElementNS
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292 if 1:
01293 def _clone_node(node, deep, newOwnerDocument):
01294 """
01295 Clone a node and give it the new owner document.
01296 Called by Node.cloneNode and Document.importNode
01297 """
01298 if node.ownerDocument.isSameNode(newOwnerDocument):
01299 operation = xml.dom.UserDataHandler.NODE_CLONED
01300 else:
01301 operation = xml.dom.UserDataHandler.NODE_IMPORTED
01302 if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
01303 clone = newOwnerDocument.createElementNS(node.namespaceURI,
01304 node.nodeName)
01305 for attr in node.attributes.values():
01306 clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value)
01307
01308 prefix, tag = xml.dom.minidom._nssplit(attr.nodeName)
01309 if prefix == 'xmlns':
01310 a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
01311 elif prefix:
01312 a = clone.getAttributeNodeNS(attr.namespaceURI, tag)
01313 else:
01314 a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName)
01315 a.specified = attr.specified
01316
01317 if deep:
01318 for child in node.childNodes:
01319 c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
01320 clone.appendChild(c)
01321 elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE:
01322 clone = newOwnerDocument.createDocumentFragment()
01323 if deep:
01324 for child in node.childNodes:
01325 c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument)
01326 clone.appendChild(c)
01327
01328 elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE:
01329 clone = newOwnerDocument.createTextNode(node.data)
01330 elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE:
01331 clone = newOwnerDocument.createCDATASection(node.data)
01332 elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE:
01333 clone = newOwnerDocument.createProcessingInstruction(node.target,
01334 node.data)
01335 elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
01336 clone = newOwnerDocument.createComment(node.data)
01337 elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE:
01338 clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
01339 node.nodeName)
01340 clone.specified = True
01341 clone.value = node.value
01342 elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE:
01343 assert node.ownerDocument is not newOwnerDocument
01344 operation = xml.dom.UserDataHandler.NODE_IMPORTED
01345 clone = newOwnerDocument.implementation.createDocumentType(
01346 node.name, node.publicId, node.systemId)
01347 clone.ownerDocument = newOwnerDocument
01348 if deep:
01349 clone.entities._seq = []
01350 clone.notations._seq = []
01351 for n in node.notations._seq:
01352 notation = xml.dom.minidom.Notation(n.nodeName, n.publicId, n.systemId)
01353 notation.ownerDocument = newOwnerDocument
01354 clone.notations._seq.append(notation)
01355 if hasattr(n, '_call_user_data_handler'):
01356 n._call_user_data_handler(operation, n, notation)
01357 for e in node.entities._seq:
01358 entity = xml.dom.minidom.Entity(e.nodeName, e.publicId, e.systemId,
01359 e.notationName)
01360 entity.actualEncoding = e.actualEncoding
01361 entity.encoding = e.encoding
01362 entity.version = e.version
01363 entity.ownerDocument = newOwnerDocument
01364 clone.entities._seq.append(entity)
01365 if hasattr(e, '_call_user_data_handler'):
01366 e._call_user_data_handler(operation, n, entity)
01367 else:
01368
01369
01370
01371 raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node))
01372
01373
01374
01375
01376 if hasattr(node, '_call_user_data_handler'):
01377 node._call_user_data_handler(operation, node, clone)
01378 return clone
01379
01380 xml.dom.minidom._clone_node = _clone_node
01381