1 | """4Suite XML substitution for default DOM parsing and outputting for |
---|
2 | ZSI based services. |
---|
3 | |
---|
4 | This code is included for NDG Security code from pyGridWare package by |
---|
5 | permission of the authors. See copyright notice below. |
---|
6 | """ |
---|
7 | __author__ = "Joshua R. Boverhof, LBNL" |
---|
8 | __date__ = "04/03/09" |
---|
9 | __copyright__ = ( |
---|
10 | "Copyright (c) 2004, The Regents of the University of California, through " |
---|
11 | "Lawrence Berkeley National Laboratory (subject to receipt of any required " |
---|
12 | "approvals from the U.S. Dept. of Energy). All rights reserved." |
---|
13 | ) |
---|
14 | |
---|
15 | __license__ = "BSD - see LICENSE file in top-level directory" |
---|
16 | __contact__ = "Philip.Kershaw@stfc.ac.uk" |
---|
17 | __revision__ = '$Id: $' |
---|
18 | from cStringIO import StringIO |
---|
19 | from string import join, strip, split |
---|
20 | |
---|
21 | from xml.dom import Node |
---|
22 | from Ft.Xml.Domlette import NonvalidatingReaderBase, NonvalidatingReader, \ |
---|
23 | CanonicalPrint |
---|
24 | import Ft.Xml.Domlette |
---|
25 | from Ft.Xml import XPath |
---|
26 | |
---|
27 | from ZSI.wstools.c14n import Canonicalize |
---|
28 | from ZSI.wstools.Namespaces import SCHEMA, SOAP, XMLNS, DSIG |
---|
29 | from ZSI.wstools.Utility import DOMException, SplitQName |
---|
30 | from ZSI.wstools.Utility import NamespaceError, MessageInterface, ElementProxy |
---|
31 | |
---|
32 | |
---|
33 | class DomletteReader(NonvalidatingReaderBase): |
---|
34 | '''Used with ZSI.parse.ParsedSoap |
---|
35 | ''' |
---|
36 | fromString = NonvalidatingReaderBase.parseString |
---|
37 | fromStream = NonvalidatingReaderBase.parseStream |
---|
38 | |
---|
39 | def __init__(self, *arg, **kw): |
---|
40 | NonvalidatingReaderBase.__init__(self, *arg, **kw) |
---|
41 | self._context = None |
---|
42 | self.processorNss = None |
---|
43 | |
---|
44 | |
---|
45 | class DomletteElementProxy(ElementProxy): |
---|
46 | expression_dict = {} |
---|
47 | |
---|
48 | def __init__(self, sw, message=None): |
---|
49 | '''Initialize. |
---|
50 | sw -- SoapWriter |
---|
51 | ''' |
---|
52 | ElementProxy.__init__(self, sw, message) |
---|
53 | self._dom = DOM |
---|
54 | self._context = None |
---|
55 | |
---|
56 | def evaluate(self, expression, processorNss=None): |
---|
57 | '''expression -- XPath statement or compiled expression. |
---|
58 | ''' |
---|
59 | if isinstance(expression, basestring): |
---|
60 | if not self.expression_dict.has_key(expression): |
---|
61 | self.expression_dict[expression] = XPath.Compile(expression) |
---|
62 | expression = self.expression_dict[expression] |
---|
63 | |
---|
64 | context = self._context |
---|
65 | if context is None: |
---|
66 | context = XPath.Context.Context(self.node, processorNss=processorNss or self.processorNss) |
---|
67 | result = expression.evaluate(context) |
---|
68 | if type(result) in (list,tuple): |
---|
69 | #return map(lambda node: DomletteElementProxy(self.sw,node), result) |
---|
70 | l = [] |
---|
71 | for node in result: |
---|
72 | item = node |
---|
73 | if node.nodeType == Node.ELEMENT_NODE: |
---|
74 | item = DomletteElementProxy(self.sw,node) |
---|
75 | #elif node.nodeType == Node.TEXT_NODE: |
---|
76 | # item = node.nodeValue |
---|
77 | # probably dont want to wrap other stuff... |
---|
78 | l.append(item) |
---|
79 | result = l |
---|
80 | else: |
---|
81 | if node.nodeType == Node.ELEMENT_NODE: |
---|
82 | result = DomletteElementProxy(self.sw,result) |
---|
83 | |
---|
84 | return result |
---|
85 | |
---|
86 | def isContextInitialized(self, processorNss=None): |
---|
87 | return self._context is not None |
---|
88 | def setContext(self, processorNss=None): |
---|
89 | self._context = XPath.Context.Context(self.node, processorNss=processorNss or self.processorNss) |
---|
90 | |
---|
91 | ############################################# |
---|
92 | # Methods for checking/setting the |
---|
93 | # classes (namespaceURI,name) node. |
---|
94 | ############################################# |
---|
95 | def checkNode(self, namespaceURI=None, localName=None): |
---|
96 | ''' |
---|
97 | namespaceURI -- namespace of element |
---|
98 | localName -- local name of element |
---|
99 | ''' |
---|
100 | namespaceURI = namespaceURI or self.namespaceURI |
---|
101 | localName = localName or self.name |
---|
102 | check = False |
---|
103 | if localName and self.node: |
---|
104 | check = self._dom.isElement(self.node, localName, namespaceURI) |
---|
105 | if not check: |
---|
106 | raise NamespaceError, 'unexpected node type %s, expecting %s' %(self.node, localName) |
---|
107 | |
---|
108 | def setNode(self, node=None): |
---|
109 | if node: |
---|
110 | if isinstance(node, DomletteElementProxy): |
---|
111 | self.node = node._getNode() |
---|
112 | else: |
---|
113 | self.node = node |
---|
114 | elif self.node: |
---|
115 | node = self._dom.getElement(self.node, self.name, self.namespaceURI, default=None) |
---|
116 | if not node: |
---|
117 | raise NamespaceError, 'cant find element (%s,%s)' %(self.namespaceURI,self.name) |
---|
118 | self.node = node |
---|
119 | else: |
---|
120 | #self.node = self._dom.create(self.node, self.name, self.namespaceURI, default=None) |
---|
121 | self.createDocument(self.namespaceURI, localName=self.name, doctype=None) |
---|
122 | |
---|
123 | self.checkNode() |
---|
124 | |
---|
125 | ############################################# |
---|
126 | # Wrapper Methods for direct DOM Element Node access |
---|
127 | ############################################# |
---|
128 | def _getNode(self): |
---|
129 | return self.node |
---|
130 | |
---|
131 | def _getElements(self): |
---|
132 | return self._dom.getElements(self.node, name=None) |
---|
133 | |
---|
134 | def _getOwnerDocument(self): |
---|
135 | return self.node.ownerDocument or self.node |
---|
136 | |
---|
137 | def _getUniquePrefix(self): |
---|
138 | '''I guess we need to resolve all potential prefixes |
---|
139 | because when the current node is attached it copies the |
---|
140 | namespaces into the parent node. |
---|
141 | ''' |
---|
142 | while 1: |
---|
143 | self._indx += 1 |
---|
144 | prefix = 'ns%d' %self._indx |
---|
145 | try: |
---|
146 | self._dom.findNamespaceURI(prefix, self._getNode()) |
---|
147 | except (AttributeError, DOMException), ex: |
---|
148 | break |
---|
149 | return prefix |
---|
150 | |
---|
151 | def _getPrefix(self, node, nsuri): |
---|
152 | ''' |
---|
153 | Keyword arguments: |
---|
154 | node -- DOM Element Node |
---|
155 | nsuri -- namespace of attribute value |
---|
156 | ''' |
---|
157 | try: |
---|
158 | if node and (node.nodeType == node.ELEMENT_NODE) and \ |
---|
159 | (nsuri == self._dom.findDefaultNS(node)): |
---|
160 | return None |
---|
161 | except DOMException, ex: |
---|
162 | pass |
---|
163 | if nsuri == XMLNS.XML: |
---|
164 | return self._xml_prefix |
---|
165 | if node.nodeType == Node.ELEMENT_NODE: |
---|
166 | for attr in node.attributes.values(): |
---|
167 | if attr.namespaceURI == XMLNS.BASE \ |
---|
168 | and nsuri == attr.value: |
---|
169 | return attr.localName |
---|
170 | else: |
---|
171 | if node.parentNode: |
---|
172 | return self._getPrefix(node.parentNode, nsuri) |
---|
173 | raise NamespaceError, 'namespaceURI "%s" is not defined' %nsuri |
---|
174 | |
---|
175 | def _appendChild(self, node): |
---|
176 | ''' |
---|
177 | Keyword arguments: |
---|
178 | node -- DOM Element Node |
---|
179 | ''' |
---|
180 | if node is None: |
---|
181 | raise TypeError, 'node is None' |
---|
182 | self.node.appendChild(node) |
---|
183 | |
---|
184 | def _insertBefore(self, newChild, refChild): |
---|
185 | ''' |
---|
186 | Keyword arguments: |
---|
187 | child -- DOM Element Node to insert |
---|
188 | refChild -- DOM Element Node |
---|
189 | ''' |
---|
190 | self.node.insertBefore(newChild, refChild) |
---|
191 | |
---|
192 | def _setAttributeNS(self, namespaceURI, qualifiedName, value): |
---|
193 | ''' |
---|
194 | Keyword arguments: |
---|
195 | namespaceURI -- namespace of attribute |
---|
196 | qualifiedName -- qualified name of new attribute value |
---|
197 | value -- value of attribute |
---|
198 | ''' |
---|
199 | self.node.setAttributeNS(namespaceURI, qualifiedName, value) |
---|
200 | |
---|
201 | ############################################# |
---|
202 | #General Methods |
---|
203 | ############################################# |
---|
204 | def isFault(self): |
---|
205 | '''check to see if this is a soap:fault message. |
---|
206 | ''' |
---|
207 | return False |
---|
208 | |
---|
209 | def getPrefix(self, namespaceURI): |
---|
210 | try: |
---|
211 | prefix = self._getPrefix(node=self.node, nsuri=namespaceURI) |
---|
212 | except NamespaceError, ex: |
---|
213 | prefix = self._getUniquePrefix() |
---|
214 | self.setNamespaceAttribute(prefix, namespaceURI) |
---|
215 | return prefix |
---|
216 | |
---|
217 | def getDocument(self): |
---|
218 | return self._getOwnerDocument() |
---|
219 | |
---|
220 | def setDocument(self, document): |
---|
221 | self.node = document |
---|
222 | |
---|
223 | def importFromString(self, xmlString): |
---|
224 | doc = self._dom.loadDocument(StringIO(xmlString)) |
---|
225 | node = self._dom.getElement(doc, name=None) |
---|
226 | clone = self.importNode(node) |
---|
227 | self._appendChild(clone) |
---|
228 | |
---|
229 | def importNode(self, node): |
---|
230 | if isinstance(node, DomletteElementProxy): |
---|
231 | node = node._getNode() |
---|
232 | return self._dom.importNode(self._getOwnerDocument(), node, deep=1) |
---|
233 | |
---|
234 | def loadFromString(self, data): |
---|
235 | self.node = self._dom.loadDocument(StringIO(data)) |
---|
236 | |
---|
237 | # def canonicalize(self, algorithm=DSIG.C14N, unsuppressedPrefixes=[]): |
---|
238 | # if algorithm == DSIG.C14N_EXCL: |
---|
239 | # return Canonicalize(self.node, unsuppressedPrefixes=unsuppressedPrefixes) |
---|
240 | # else: |
---|
241 | # return Canonicalize(self.node) |
---|
242 | |
---|
243 | def canonicalize(self, algorithm=DSIG.C14N, unsuppressedPrefixes=[]): |
---|
244 | '''4Suite-XML based canonicalization''' |
---|
245 | f = StringIO() |
---|
246 | if algorithm == DSIG.C14N_EXCL: |
---|
247 | CanonicalPrint(self.node, stream=f, exclusive=True, |
---|
248 | inclusivePrefixes=unsuppressedPrefixes) |
---|
249 | else: |
---|
250 | CanonicalPrint(self.node, stream=f) |
---|
251 | |
---|
252 | c14n = f.getvalue() |
---|
253 | return c14n |
---|
254 | |
---|
255 | def toString(self): |
---|
256 | s = StringIO() |
---|
257 | FastPrint(self.node, output=s) |
---|
258 | return s.getvalue() |
---|
259 | |
---|
260 | def createDocument(self, namespaceURI, localName, doctype=None): |
---|
261 | prefix = self._soap_env_prefix |
---|
262 | if namespaceURI == self.reserved_ns[prefix]: |
---|
263 | qualifiedName = '%s:%s' %(prefix,localName) |
---|
264 | elif namespaceURI is localName is None: |
---|
265 | self.node = self._dom.createDocument(None,None,None) |
---|
266 | return |
---|
267 | else: |
---|
268 | raise KeyError, 'only support creation of document in %s' %self.reserved_ns[prefix] |
---|
269 | |
---|
270 | qualifiedName = '%s:%s' %(prefix,localName) |
---|
271 | document = self._dom.createDocument(nsuri=namespaceURI, qname=qualifiedName, doctype=doctype) |
---|
272 | self.node = document.childNodes[0] |
---|
273 | |
---|
274 | #set up reserved namespace attributes |
---|
275 | for prefix,nsuri in self.reserved_ns.items(): |
---|
276 | self._setAttributeNS(namespaceURI=self._xmlns_nsuri, |
---|
277 | qualifiedName='%s:%s' %(self._xmlns_prefix,prefix), |
---|
278 | value=nsuri) |
---|
279 | |
---|
280 | ############################################# |
---|
281 | #Methods for attributes |
---|
282 | ############################################# |
---|
283 | def hasAttribute(self, namespaceURI, localName): |
---|
284 | return self._dom.hasAttr(self._getNode(), name=localName, nsuri=namespaceURI) |
---|
285 | |
---|
286 | def setAttributeType(self, namespaceURI, localName): |
---|
287 | '''set xsi:type |
---|
288 | Keyword arguments: |
---|
289 | namespaceURI -- namespace of attribute value |
---|
290 | localName -- name of new attribute value |
---|
291 | |
---|
292 | ''' |
---|
293 | self.logger.debug('setAttributeType: (%s,%s)', namespaceURI, localName) |
---|
294 | value = localName |
---|
295 | if namespaceURI: |
---|
296 | value = '%s:%s' %(self.getPrefix(namespaceURI),localName) |
---|
297 | self._setAttributeNS(self._xsi_nsuri, '%s:type' %self._xsi_prefix, value) |
---|
298 | |
---|
299 | def createAttributeNS(self, namespace, name, value): |
---|
300 | document = self._getOwnerDocument() |
---|
301 | attrNode = document.createAttributeNS(namespace, name, value) |
---|
302 | |
---|
303 | def setAttributeNS(self, namespaceURI, localName, value): |
---|
304 | ''' |
---|
305 | Keyword arguments: |
---|
306 | namespaceURI -- namespace of attribute to create, None is for |
---|
307 | attributes in no namespace. |
---|
308 | localName -- local name of new attribute |
---|
309 | value -- value of new attribute |
---|
310 | ''' |
---|
311 | prefix = None |
---|
312 | if namespaceURI: |
---|
313 | try: |
---|
314 | prefix = self.getPrefix(namespaceURI) |
---|
315 | except KeyError, ex: |
---|
316 | prefix = 'ns2' |
---|
317 | self.setNamespaceAttribute(prefix, namespaceURI) |
---|
318 | qualifiedName = localName |
---|
319 | if prefix: |
---|
320 | qualifiedName = '%s:%s' %(prefix, localName) |
---|
321 | self._setAttributeNS(namespaceURI, qualifiedName, value) |
---|
322 | |
---|
323 | def setNamespaceAttribute(self, prefix, namespaceURI): |
---|
324 | ''' |
---|
325 | Keyword arguments: |
---|
326 | prefix -- xmlns prefix |
---|
327 | namespaceURI -- value of prefix |
---|
328 | ''' |
---|
329 | self._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI) |
---|
330 | |
---|
331 | ############################################# |
---|
332 | #Methods for elements |
---|
333 | ############################################# |
---|
334 | def createElementNS(self, namespace, qname): |
---|
335 | ''' |
---|
336 | Keyword arguments: |
---|
337 | namespace -- namespace of element to create |
---|
338 | qname -- qualified name of new element |
---|
339 | ''' |
---|
340 | document = self._getOwnerDocument() |
---|
341 | node = document.createElementNS(namespace, qname) |
---|
342 | return DomletteElementProxy(self.sw, node) |
---|
343 | |
---|
344 | def createAppendSetElement(self, namespaceURI, localName, prefix=None): |
---|
345 | '''Create a new element (namespaceURI,name), append it |
---|
346 | to current node, then set it to be the current node. |
---|
347 | Keyword arguments: |
---|
348 | namespaceURI -- namespace of element to create |
---|
349 | localName -- local name of new element |
---|
350 | prefix -- if namespaceURI is not defined, declare prefix. defaults |
---|
351 | to 'ns1' if left unspecified. |
---|
352 | ''' |
---|
353 | node = self.createAppendElement(namespaceURI, localName, prefix=None) |
---|
354 | node=node._getNode() |
---|
355 | self._setNode(node._getNode()) |
---|
356 | |
---|
357 | def createAppendElement(self, namespaceURI, localName, prefix=None): |
---|
358 | '''Create a new element (namespaceURI,name), append it |
---|
359 | to current node, and return the newly created node. |
---|
360 | Keyword arguments: |
---|
361 | namespaceURI -- namespace of element to create |
---|
362 | localName -- local name of new element |
---|
363 | prefix -- if namespaceURI is not defined, declare prefix. defaults |
---|
364 | to 'ns1' if left unspecified. |
---|
365 | ''' |
---|
366 | declare = False |
---|
367 | qualifiedName = localName |
---|
368 | if namespaceURI: |
---|
369 | try: |
---|
370 | prefix = self.getPrefix(namespaceURI) |
---|
371 | except: |
---|
372 | declare = True |
---|
373 | prefix = prefix or self._getUniquePrefix() |
---|
374 | if prefix: |
---|
375 | qualifiedName = '%s:%s' %(prefix, localName) |
---|
376 | node = self.createElementNS(namespaceURI, qualifiedName) |
---|
377 | if declare: |
---|
378 | node._setAttributeNS(XMLNS.BASE, 'xmlns:%s' %prefix, namespaceURI) |
---|
379 | self._appendChild(node=node._getNode()) |
---|
380 | return node |
---|
381 | |
---|
382 | def createInsertBefore(self, namespaceURI, localName, refChild): |
---|
383 | qualifiedName = localName |
---|
384 | prefix = self.getPrefix(namespaceURI) |
---|
385 | if prefix: |
---|
386 | qualifiedName = '%s:%s' %(prefix, localName) |
---|
387 | node = self.createElementNS(namespaceURI, qualifiedName) |
---|
388 | self._insertBefore(newChild=node._getNode(), refChild=refChild._getNode()) |
---|
389 | return node |
---|
390 | |
---|
391 | def getElement(self, namespaceURI, localName): |
---|
392 | ''' |
---|
393 | Keyword arguments: |
---|
394 | namespaceURI -- namespace of element |
---|
395 | localName -- local name of element |
---|
396 | ''' |
---|
397 | node = self._dom.getElement(self.node, localName, namespaceURI, default=None) |
---|
398 | if node: |
---|
399 | return DomletteElementProxy(self.sw, node) |
---|
400 | return None |
---|
401 | |
---|
402 | def getElements(self, namespaceURI=None, localName=None): |
---|
403 | ''' |
---|
404 | Keyword arguments: |
---|
405 | namespaceURI -- namespace of element |
---|
406 | localName -- local name of element |
---|
407 | ''' |
---|
408 | nodes = self._dom.getElements(self.node,localName, namespaceURI) |
---|
409 | nodesList = [] |
---|
410 | if nodes: |
---|
411 | for node in nodes: |
---|
412 | nodesList.append(DomletteElementProxy(self.sw, node)) |
---|
413 | |
---|
414 | return nodesList |
---|
415 | |
---|
416 | def getAttributeValue(self, namespaceURI, localName): |
---|
417 | ''' |
---|
418 | Keyword arguments: |
---|
419 | namespaceURI -- namespace of attribute |
---|
420 | localName -- local name of attribute |
---|
421 | ''' |
---|
422 | if self.hasAttribute(namespaceURI, localName): |
---|
423 | attr = self.node.getAttributeNodeNS(namespaceURI,localName) |
---|
424 | return attr.value |
---|
425 | return None |
---|
426 | |
---|
427 | def getValue(self): |
---|
428 | return self._dom.getElementText(self.node, preserve_ws=True) |
---|
429 | |
---|
430 | ############################################# |
---|
431 | #Methods for text nodes |
---|
432 | ############################################# |
---|
433 | def createAppendTextNode(self, pyobj): |
---|
434 | node = self.createTextNode(pyobj) |
---|
435 | self._appendChild(node=node._getNode()) |
---|
436 | return node |
---|
437 | |
---|
438 | def createTextNode(self, pyobj): |
---|
439 | document = self._getOwnerDocument() |
---|
440 | node = document.createTextNode(pyobj) |
---|
441 | return DomletteElementProxy(self.sw, node) |
---|
442 | |
---|
443 | ############################################# |
---|
444 | #Methods for retrieving namespaceURI's |
---|
445 | ############################################# |
---|
446 | def findNamespaceURI(self, qualifiedName): |
---|
447 | parts = SplitQName(qualifiedName) |
---|
448 | element = self._getNode() |
---|
449 | if len(parts) == 1: |
---|
450 | return (self._dom.findTargetNS(element), value) |
---|
451 | return self._dom.findNamespaceURI(parts[0], element) |
---|
452 | |
---|
453 | def resolvePrefix(self, prefix): |
---|
454 | element = self._getNode() |
---|
455 | return self._dom.findNamespaceURI(prefix, element) |
---|
456 | |
---|
457 | def getSOAPEnvURI(self): |
---|
458 | return self._soap_env_nsuri |
---|
459 | |
---|
460 | def isEmpty(self): |
---|
461 | return not self.node |
---|
462 | |
---|
463 | |
---|
464 | class DOM: |
---|
465 | """The DOM singleton defines a number of XML related constants and |
---|
466 | provides a number of utility methods for DOM related tasks. It |
---|
467 | also provides some basic abstractions so that the rest of the |
---|
468 | package need not care about actual DOM implementation in use.""" |
---|
469 | |
---|
470 | # Namespace stuff related to the SOAP specification. |
---|
471 | |
---|
472 | NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/' |
---|
473 | NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/' |
---|
474 | |
---|
475 | NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope' |
---|
476 | NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding' |
---|
477 | |
---|
478 | NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2) |
---|
479 | NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2) |
---|
480 | |
---|
481 | NS_SOAP_ENV = NS_SOAP_ENV_1_1 |
---|
482 | NS_SOAP_ENC = NS_SOAP_ENC_1_1 |
---|
483 | |
---|
484 | _soap_uri_mapping = { |
---|
485 | NS_SOAP_ENV_1_1 : '1.1', |
---|
486 | NS_SOAP_ENV_1_2 : '1.2', |
---|
487 | } |
---|
488 | |
---|
489 | SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next' |
---|
490 | SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next' |
---|
491 | SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2) |
---|
492 | |
---|
493 | def SOAPUriToVersion(self, uri): |
---|
494 | """Return the SOAP version related to an envelope uri.""" |
---|
495 | value = self._soap_uri_mapping.get(uri) |
---|
496 | if value is not None: |
---|
497 | return value |
---|
498 | raise ValueError( |
---|
499 | 'Unsupported SOAP envelope uri: %s' % uri |
---|
500 | ) |
---|
501 | |
---|
502 | def GetSOAPEnvUri(self, version): |
---|
503 | """Return the appropriate SOAP envelope uri for a given |
---|
504 | human-friendly SOAP version string (e.g. '1.1').""" |
---|
505 | attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_') |
---|
506 | value = getattr(self, attrname, None) |
---|
507 | if value is not None: |
---|
508 | return value |
---|
509 | raise ValueError( |
---|
510 | 'Unsupported SOAP version: %s' % version |
---|
511 | ) |
---|
512 | |
---|
513 | def GetSOAPEncUri(self, version): |
---|
514 | """Return the appropriate SOAP encoding uri for a given |
---|
515 | human-friendly SOAP version string (e.g. '1.1').""" |
---|
516 | attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_') |
---|
517 | value = getattr(self, attrname, None) |
---|
518 | if value is not None: |
---|
519 | return value |
---|
520 | raise ValueError( |
---|
521 | 'Unsupported SOAP version: %s' % version |
---|
522 | ) |
---|
523 | |
---|
524 | def GetSOAPActorNextUri(self, version): |
---|
525 | """Return the right special next-actor uri for a given |
---|
526 | human-friendly SOAP version string (e.g. '1.1').""" |
---|
527 | attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_') |
---|
528 | value = getattr(self, attrname, None) |
---|
529 | if value is not None: |
---|
530 | return value |
---|
531 | raise ValueError( |
---|
532 | 'Unsupported SOAP version: %s' % version |
---|
533 | ) |
---|
534 | |
---|
535 | |
---|
536 | # Namespace stuff related to XML Schema. |
---|
537 | |
---|
538 | NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema' |
---|
539 | NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance' |
---|
540 | |
---|
541 | NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema' |
---|
542 | NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance' |
---|
543 | |
---|
544 | NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema' |
---|
545 | NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance' |
---|
546 | |
---|
547 | NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01) |
---|
548 | NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01) |
---|
549 | |
---|
550 | NS_XSD = NS_XSD_01 |
---|
551 | NS_XSI = NS_XSI_01 |
---|
552 | |
---|
553 | _xsd_uri_mapping = { |
---|
554 | NS_XSD_99 : NS_XSI_99, |
---|
555 | NS_XSD_00 : NS_XSI_00, |
---|
556 | NS_XSD_01 : NS_XSI_01, |
---|
557 | } |
---|
558 | |
---|
559 | for key, value in _xsd_uri_mapping.items(): |
---|
560 | _xsd_uri_mapping[value] = key |
---|
561 | |
---|
562 | |
---|
563 | def InstanceUriForSchemaUri(self, uri): |
---|
564 | """Return the appropriate matching XML Schema instance uri for |
---|
565 | the given XML Schema namespace uri.""" |
---|
566 | return self._xsd_uri_mapping.get(uri) |
---|
567 | |
---|
568 | def SchemaUriForInstanceUri(self, uri): |
---|
569 | """Return the appropriate matching XML Schema namespace uri for |
---|
570 | the given XML Schema instance namespace uri.""" |
---|
571 | return self._xsd_uri_mapping.get(uri) |
---|
572 | |
---|
573 | |
---|
574 | # Namespace stuff related to WSDL. |
---|
575 | |
---|
576 | NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/' |
---|
577 | NS_WSDL_ALL = (NS_WSDL_1_1,) |
---|
578 | NS_WSDL = NS_WSDL_1_1 |
---|
579 | |
---|
580 | NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/' |
---|
581 | NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/' |
---|
582 | NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/' |
---|
583 | |
---|
584 | NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,) |
---|
585 | NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,) |
---|
586 | NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,) |
---|
587 | |
---|
588 | NS_SOAP_BINDING = NS_SOAP_BINDING_1_1 |
---|
589 | NS_HTTP_BINDING = NS_HTTP_BINDING_1_1 |
---|
590 | NS_MIME_BINDING = NS_MIME_BINDING_1_1 |
---|
591 | |
---|
592 | NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http' |
---|
593 | NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,) |
---|
594 | NS_SOAP_HTTP = NS_SOAP_HTTP_1_1 |
---|
595 | |
---|
596 | |
---|
597 | _wsdl_uri_mapping = { |
---|
598 | NS_WSDL_1_1 : '1.1', |
---|
599 | } |
---|
600 | |
---|
601 | def WSDLUriToVersion(self, uri): |
---|
602 | """Return the WSDL version related to a WSDL namespace uri.""" |
---|
603 | value = self._wsdl_uri_mapping.get(uri) |
---|
604 | if value is not None: |
---|
605 | return value |
---|
606 | raise ValueError( |
---|
607 | 'Unsupported SOAP envelope uri: %s' % uri |
---|
608 | ) |
---|
609 | |
---|
610 | def GetWSDLUri(self, version): |
---|
611 | attr = 'NS_WSDL_%s' % join(split(version, '.'), '_') |
---|
612 | value = getattr(self, attr, None) |
---|
613 | if value is not None: |
---|
614 | return value |
---|
615 | raise ValueError( |
---|
616 | 'Unsupported WSDL version: %s' % version |
---|
617 | ) |
---|
618 | |
---|
619 | def GetWSDLSoapBindingUri(self, version): |
---|
620 | attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_') |
---|
621 | value = getattr(self, attr, None) |
---|
622 | if value is not None: |
---|
623 | return value |
---|
624 | raise ValueError( |
---|
625 | 'Unsupported WSDL version: %s' % version |
---|
626 | ) |
---|
627 | |
---|
628 | def GetWSDLHttpBindingUri(self, version): |
---|
629 | attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_') |
---|
630 | value = getattr(self, attr, None) |
---|
631 | if value is not None: |
---|
632 | return value |
---|
633 | raise ValueError( |
---|
634 | 'Unsupported WSDL version: %s' % version |
---|
635 | ) |
---|
636 | |
---|
637 | def GetWSDLMimeBindingUri(self, version): |
---|
638 | attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_') |
---|
639 | value = getattr(self, attr, None) |
---|
640 | if value is not None: |
---|
641 | return value |
---|
642 | raise ValueError( |
---|
643 | 'Unsupported WSDL version: %s' % version |
---|
644 | ) |
---|
645 | |
---|
646 | def GetWSDLHttpTransportUri(self, version): |
---|
647 | attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_') |
---|
648 | value = getattr(self, attr, None) |
---|
649 | if value is not None: |
---|
650 | return value |
---|
651 | raise ValueError( |
---|
652 | 'Unsupported WSDL version: %s' % version |
---|
653 | ) |
---|
654 | |
---|
655 | |
---|
656 | # Other xml namespace constants. |
---|
657 | NS_XMLNS = 'http://www.w3.org/2000/xmlns/' |
---|
658 | |
---|
659 | |
---|
660 | |
---|
661 | def isElement(self, node, name, nsuri=None): |
---|
662 | """Return true if the given node is an element with the given |
---|
663 | name and optional namespace uri.""" |
---|
664 | if node.nodeType != node.ELEMENT_NODE: |
---|
665 | return 0 |
---|
666 | return node.localName == name and \ |
---|
667 | (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri)) |
---|
668 | |
---|
669 | def getElement(self, node, name, nsuri=None, default=join): |
---|
670 | """Return the first child of node with a matching name and |
---|
671 | namespace uri, or the default if one is provided.""" |
---|
672 | nsmatch = self.nsUriMatch |
---|
673 | ELEMENT_NODE = node.ELEMENT_NODE |
---|
674 | for child in node.childNodes: |
---|
675 | if child.nodeType == ELEMENT_NODE: |
---|
676 | if ((child.localName == name or name is None) and |
---|
677 | (nsuri is None or nsmatch(child.namespaceURI, nsuri)) |
---|
678 | ): |
---|
679 | return child |
---|
680 | if default is not join: |
---|
681 | return default |
---|
682 | raise KeyError, name |
---|
683 | |
---|
684 | def getElementById(self, node, id, default=join): |
---|
685 | """Return the first child of node matching an id reference.""" |
---|
686 | attrget = self.getAttr |
---|
687 | ELEMENT_NODE = node.ELEMENT_NODE |
---|
688 | for child in node.childNodes: |
---|
689 | if child.nodeType == ELEMENT_NODE: |
---|
690 | if attrget(child, 'id') == id: |
---|
691 | return child |
---|
692 | if default is not join: |
---|
693 | return default |
---|
694 | raise KeyError, name |
---|
695 | |
---|
696 | def getMappingById(self, document, depth=None, element=None, |
---|
697 | mapping=None, level=1): |
---|
698 | """Create an id -> element mapping of those elements within a |
---|
699 | document that define an id attribute. The depth of the search |
---|
700 | may be controlled by using the (1-based) depth argument.""" |
---|
701 | if document is not None: |
---|
702 | element = document.documentElement |
---|
703 | mapping = {} |
---|
704 | attr = element._attrs.get('id', None) |
---|
705 | if attr is not None: |
---|
706 | mapping[attr.value] = element |
---|
707 | if depth is None or depth > level: |
---|
708 | level = level + 1 |
---|
709 | ELEMENT_NODE = element.ELEMENT_NODE |
---|
710 | for child in element.childNodes: |
---|
711 | if child.nodeType == ELEMENT_NODE: |
---|
712 | self.getMappingById(None, depth, child, mapping, level) |
---|
713 | return mapping |
---|
714 | |
---|
715 | def getElements(self, node, name, nsuri=None): |
---|
716 | """Return a sequence of the child elements of the given node that |
---|
717 | match the given name and optional namespace uri.""" |
---|
718 | nsmatch = self.nsUriMatch |
---|
719 | result = [] |
---|
720 | ELEMENT_NODE = node.ELEMENT_NODE |
---|
721 | for child in node.childNodes: |
---|
722 | if child.nodeType == ELEMENT_NODE: |
---|
723 | if ((child.localName == name or name is None) and ( |
---|
724 | (nsuri is None) or nsmatch(child.namespaceURI, nsuri))): |
---|
725 | result.append(child) |
---|
726 | return result |
---|
727 | |
---|
728 | def hasAttr(self, node, name, nsuri=None): |
---|
729 | """Return true if element has attribute with the given name and |
---|
730 | optional nsuri. If nsuri is not specified, returns true if an |
---|
731 | attribute exists with the given name with any namespace.""" |
---|
732 | return node.hasAttributeNS(nsuri, name) |
---|
733 | |
---|
734 | def getAttr(self, node, name, nsuri=None, default=join): |
---|
735 | """Return the value of the attribute named 'name' with the |
---|
736 | optional nsuri, or the default if one is specified. If |
---|
737 | nsuri is not specified, an attribute that matches the |
---|
738 | given name will be returned regardless of namespace.""" |
---|
739 | result = node.getAttributeNS(nsuri,name) |
---|
740 | if result is not None and result != '': |
---|
741 | return result |
---|
742 | if default is not join: |
---|
743 | return default |
---|
744 | return '' |
---|
745 | |
---|
746 | def getAttrs(self, node): |
---|
747 | """Return a Collection of all attributes |
---|
748 | """ |
---|
749 | attrs = {} |
---|
750 | for k,v in node._attrs.items(): |
---|
751 | attrs[k] = v.value |
---|
752 | return attrs |
---|
753 | |
---|
754 | def getElementText(self, node, preserve_ws=None): |
---|
755 | """Return the text value of an xml element node. Leading and trailing |
---|
756 | whitespace is stripped from the value unless the preserve_ws flag |
---|
757 | is passed with a true value.""" |
---|
758 | result = [] |
---|
759 | for child in node.childNodes: |
---|
760 | nodetype = child.nodeType |
---|
761 | if nodetype == child.TEXT_NODE or \ |
---|
762 | nodetype == child.CDATA_SECTION_NODE: |
---|
763 | result.append(child.nodeValue) |
---|
764 | value = join(result, '') |
---|
765 | if preserve_ws is None: |
---|
766 | value = strip(value) |
---|
767 | return value |
---|
768 | |
---|
769 | def findNamespaceURI(self, prefix, node): |
---|
770 | """Find a namespace uri given a prefix and a context node.""" |
---|
771 | attrkey = (self.NS_XMLNS, prefix) |
---|
772 | DOCUMENT_NODE = node.DOCUMENT_NODE |
---|
773 | ELEMENT_NODE = node.ELEMENT_NODE |
---|
774 | while 1: |
---|
775 | if node.nodeType != ELEMENT_NODE: |
---|
776 | node = node.parentNode |
---|
777 | continue |
---|
778 | #result = node._attrsNS.get(attrkey, None) |
---|
779 | #if result is not None: |
---|
780 | # return result.value |
---|
781 | result = node.getAttributeNS(*attrkey) |
---|
782 | if result != '': |
---|
783 | return result |
---|
784 | |
---|
785 | if hasattr(node, '__imported__'): |
---|
786 | raise DOMException('Value for prefix %s not found.' % prefix) |
---|
787 | node = node.parentNode |
---|
788 | if node.nodeType == DOCUMENT_NODE: |
---|
789 | raise DOMException('Value for prefix %s not found.' % prefix) |
---|
790 | |
---|
791 | def findDefaultNS(self, node): |
---|
792 | """Return the current default namespace uri for the given node.""" |
---|
793 | attrkey = (self.NS_XMLNS, 'xmlns') |
---|
794 | DOCUMENT_NODE = node.DOCUMENT_NODE |
---|
795 | ELEMENT_NODE = node.ELEMENT_NODE |
---|
796 | while 1: |
---|
797 | if node.nodeType != ELEMENT_NODE: |
---|
798 | node = node.parentNode |
---|
799 | continue |
---|
800 | #result = node._attrsNS.get(attrkey, None) |
---|
801 | #if result is not None: |
---|
802 | # return result.value |
---|
803 | result = node.getAttributeNS(*attrkey) |
---|
804 | if result != '': |
---|
805 | return result |
---|
806 | |
---|
807 | if hasattr(node, '__imported__'): |
---|
808 | raise DOMException('Cannot determine default namespace.') |
---|
809 | node = node.parentNode |
---|
810 | if node.nodeType == DOCUMENT_NODE: |
---|
811 | raise DOMException('Cannot determine default namespace.') |
---|
812 | |
---|
813 | def findTargetNS(self, node): |
---|
814 | """Return the defined target namespace uri for the given node.""" |
---|
815 | attrget = self.getAttr |
---|
816 | attrkey = (self.NS_XMLNS, 'xmlns') |
---|
817 | DOCUMENT_NODE = node.DOCUMENT_NODE |
---|
818 | ELEMENT_NODE = node.ELEMENT_NODE |
---|
819 | while 1: |
---|
820 | if node.nodeType != ELEMENT_NODE: |
---|
821 | node = node.parentNode |
---|
822 | continue |
---|
823 | result = attrget(node, 'targetNamespace', default=None) |
---|
824 | if result is not None: |
---|
825 | return result |
---|
826 | node = node.parentNode |
---|
827 | if node.nodeType == DOCUMENT_NODE: |
---|
828 | raise DOMException('Cannot determine target namespace.') |
---|
829 | |
---|
830 | def getTypeRef(self, element): |
---|
831 | """Return (namespaceURI, name) for a type attribue of the given |
---|
832 | element, or None if the element does not have a type attribute.""" |
---|
833 | typeattr = self.getAttr(element, 'type', default=None) |
---|
834 | if typeattr is None: |
---|
835 | return None |
---|
836 | parts = typeattr.split(':', 1) |
---|
837 | if len(parts) == 2: |
---|
838 | nsuri = self.findNamespaceURI(parts[0], element) |
---|
839 | else: |
---|
840 | nsuri = self.findDefaultNS(element) |
---|
841 | return (nsuri, parts[1]) |
---|
842 | |
---|
843 | def importNode(self, document, node, deep=0): |
---|
844 | """Implements (well enough for our purposes) DOM node import.""" |
---|
845 | nodetype = node.nodeType |
---|
846 | if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE): |
---|
847 | raise DOMException('Illegal node type for importNode') |
---|
848 | if nodetype == node.ENTITY_REFERENCE_NODE: |
---|
849 | deep = 0 |
---|
850 | clone = node.cloneNode(deep) |
---|
851 | self._setOwnerDoc(document, clone) |
---|
852 | clone.__imported__ = 1 |
---|
853 | return clone |
---|
854 | |
---|
855 | def _setOwnerDoc(self, document, node): |
---|
856 | node.ownerDocument = document |
---|
857 | for child in node.childNodes: |
---|
858 | self._setOwnerDoc(document, child) |
---|
859 | |
---|
860 | def nsUriMatch(self, value, wanted, strict=0, tt=type(())): |
---|
861 | """Return a true value if two namespace uri values match.""" |
---|
862 | if value == wanted or (type(wanted) is tt) and value in wanted: |
---|
863 | return 1 |
---|
864 | if not strict: |
---|
865 | wanted = type(wanted) is tt and wanted or (wanted,) |
---|
866 | value = value[-1:] != '/' and value or value[:-1] |
---|
867 | for item in wanted: |
---|
868 | if item == value or item[:-1] == value: |
---|
869 | return 1 |
---|
870 | return 0 |
---|
871 | |
---|
872 | def createDocument(self, nsuri, qname, doctype=None): |
---|
873 | """Create a new writable DOM document object.""" |
---|
874 | #impl = xml.dom.minidom.getDOMImplementation() |
---|
875 | impl = Ft.Xml.Domlette.implementation |
---|
876 | return impl.createDocument(nsuri, qname, doctype) |
---|
877 | |
---|
878 | def loadDocument(self, data): |
---|
879 | """Load an xml file from a file-like object and return a DOM |
---|
880 | document instance.""" |
---|
881 | #return xml.dom.minidom.parse(data) |
---|
882 | return NonvalidatingReader.parseStream(data) |
---|
883 | |
---|
884 | def loadFromURL(self, url): |
---|
885 | """Load an xml file from a URL and return a DOM document.""" |
---|
886 | file = urlopen(url) |
---|
887 | try: result = self.loadDocument(file) |
---|
888 | finally: file.close() |
---|
889 | return result |
---|
890 | |
---|
891 | def unlink(self, document): |
---|
892 | """When you are finished with a DOM, you should clean it up. |
---|
893 | This is necessary because some versions of Python do not support |
---|
894 | garbage collection of objects that refer to each other in a cycle. |
---|
895 | Until this restriction is removed from all versions of Python, it |
---|
896 | is safest to write your code as if cycles would not be cleaned up.""" |
---|
897 | #if hasattr(document, 'unlink'): |
---|
898 | # document.unlink() |
---|
899 | return |
---|
900 | |
---|
901 | DOM = DOM() |
---|
902 | |
---|
903 | |
---|
904 | """ |
---|
905 | Some code from c14n modified to not do sorting. |
---|
906 | ~30% faster than c14n.Canonicalize |
---|
907 | ~12% faster than Ft.Xml.Domlette.Print |
---|
908 | |
---|
909 | --Ft.Xml.Domlette.Print(self.node, stream=s) |
---|
910 | This function produces XML with a different canonical form |
---|
911 | from the source. |
---|
912 | """ |
---|
913 | _attrs = lambda E: (E.attributes and E.attributes.values()) or [] |
---|
914 | _children = lambda E: E.childNodes or [] |
---|
915 | _IN_XML_NS = lambda n: n.namespaceURI == XMLNS.XML |
---|
916 | _LesserElement, _Element, _GreaterElement = range(3) |
---|
917 | |
---|
918 | def _utilized(n, node, other_attrs, unsuppressedPrefixes): |
---|
919 | '''_utilized(n, node, other_attrs, unsuppressedPrefixes) -> boolean |
---|
920 | Return true if that nodespace is utilized within the node''' |
---|
921 | |
---|
922 | if n.startswith('xmlns:'): |
---|
923 | n = n[6:] |
---|
924 | elif n.startswith('xmlns'): |
---|
925 | n = n[5:] |
---|
926 | if n == node.prefix or n in unsuppressedPrefixes: return 1 |
---|
927 | for attr in other_attrs: |
---|
928 | if n == attr.prefix: return 1 |
---|
929 | return 0 |
---|
930 | |
---|
931 | _in_subset = lambda subset, node: not subset or node in subset |
---|
932 | |
---|
933 | class _implementation: |
---|
934 | '''Implementation class for C14N. This accompanies a node during it's |
---|
935 | processing and includes the parameters and processing state.''' |
---|
936 | |
---|
937 | # Handler for each node type; populated during module instantiation. |
---|
938 | handlers = {} |
---|
939 | |
---|
940 | def __init__(self, node, write, **kw): |
---|
941 | '''Create and run the implementation.''' |
---|
942 | |
---|
943 | self.write = write |
---|
944 | self.subset = kw.get('subset') |
---|
945 | if self.subset: |
---|
946 | self.comments = kw.get('comments', 1) |
---|
947 | else: |
---|
948 | self.comments = kw.get('comments', 0) |
---|
949 | self.unsuppressedPrefixes = kw.get('unsuppressedPrefixes') |
---|
950 | nsdict = kw.get('nsdict', { 'xml': XMLNS.XML, 'xmlns': XMLNS.BASE }) |
---|
951 | |
---|
952 | # Processing state. |
---|
953 | self.state = (nsdict, ['xml'], []) |
---|
954 | |
---|
955 | #ATTRIBUTE_NODE |
---|
956 | #CDATA_SECTION_NODE |
---|
957 | #COMMENT_NODE |
---|
958 | #DOCUMENT_FRAGMENT_NODE |
---|
959 | #DOCUMENT_NODE |
---|
960 | #DOCUMENT_TYPE_NODE |
---|
961 | #ELEMENT_NODE |
---|
962 | #ENTITY_NODE |
---|
963 | #ENTITY_REFERENCE_NODE |
---|
964 | #NOTATION_NODE |
---|
965 | #PROCESSING_INSTRUCTION_NODE |
---|
966 | #TEXT_NODE |
---|
967 | #TREE_POSITION_SAME_NODE |
---|
968 | if node.nodeType == Node.DOCUMENT_NODE: |
---|
969 | self._do_document(node) |
---|
970 | elif node.nodeType == Node.ELEMENT_NODE: |
---|
971 | self.documentOrder = _Element # At document element |
---|
972 | if self.unsuppressedPrefixes is not None: |
---|
973 | self._do_element(node) |
---|
974 | else: |
---|
975 | inherited = self._inherit_context(node) |
---|
976 | self._do_element(node, inherited) |
---|
977 | elif node.nodeType == Node.DOCUMENT_TYPE_NODE: |
---|
978 | pass |
---|
979 | else: |
---|
980 | raise TypeError, str(node) |
---|
981 | |
---|
982 | |
---|
983 | def _inherit_context(self, node): |
---|
984 | '''_inherit_context(self, node) -> list |
---|
985 | Scan ancestors of attribute and namespace context. Used only |
---|
986 | for single element node canonicalization, not for subset |
---|
987 | canonicalization.''' |
---|
988 | |
---|
989 | # Collect the initial list of xml:foo attributes. |
---|
990 | xmlattrs = filter(_IN_XML_NS, _attrs(node)) |
---|
991 | |
---|
992 | # Walk up and get all xml:XXX attributes we inherit. |
---|
993 | inherited, parent = [], node.parentNode |
---|
994 | while parent and parent.nodeType == Node.ELEMENT_NODE: |
---|
995 | for a in filter(_IN_XML_NS, _attrs(parent)): |
---|
996 | n = a.localName |
---|
997 | if n not in xmlattrs: |
---|
998 | xmlattrs.append(n) |
---|
999 | inherited.append(a) |
---|
1000 | parent = parent.parentNode |
---|
1001 | return inherited |
---|
1002 | |
---|
1003 | |
---|
1004 | def _do_document(self, node): |
---|
1005 | '''_do_document(self, node) -> None |
---|
1006 | Process a document node. documentOrder holds whether the document |
---|
1007 | element has been encountered such that PIs/comments can be written |
---|
1008 | as specified.''' |
---|
1009 | |
---|
1010 | self.documentOrder = _LesserElement |
---|
1011 | for child in node.childNodes: |
---|
1012 | if child.nodeType == Node.ELEMENT_NODE: |
---|
1013 | self.documentOrder = _Element # At document element |
---|
1014 | self._do_element(child) |
---|
1015 | self.documentOrder = _GreaterElement # After document element |
---|
1016 | elif child.nodeType == Node.PROCESSING_INSTRUCTION_NODE: |
---|
1017 | self._do_pi(child) |
---|
1018 | elif child.nodeType == Node.COMMENT_NODE: |
---|
1019 | self._do_comment(child) |
---|
1020 | elif child.nodeType == Node.DOCUMENT_TYPE_NODE: |
---|
1021 | pass |
---|
1022 | else: |
---|
1023 | raise TypeError, str(child) |
---|
1024 | handlers[Node.DOCUMENT_NODE] = _do_document |
---|
1025 | |
---|
1026 | |
---|
1027 | def _do_text(self, node): |
---|
1028 | '''_do_text(self, node) -> None |
---|
1029 | Process a text or CDATA node. Render various special characters |
---|
1030 | as their C14N entity representations.''' |
---|
1031 | if not _in_subset(self.subset, node): return |
---|
1032 | s = node.data \ |
---|
1033 | .replace("&", "&") \ |
---|
1034 | .replace("<", "<") \ |
---|
1035 | .replace(">", ">") \ |
---|
1036 | .replace("\015", "
") |
---|
1037 | if s: self.write(s) |
---|
1038 | handlers[Node.TEXT_NODE] = _do_text |
---|
1039 | handlers[Node.CDATA_SECTION_NODE] = _do_text |
---|
1040 | |
---|
1041 | |
---|
1042 | def _do_pi(self, node): |
---|
1043 | '''_do_pi(self, node) -> None |
---|
1044 | Process a PI node. Render a leading or trailing #xA if the |
---|
1045 | document order of the PI is greater or lesser (respectively) |
---|
1046 | than the document element. |
---|
1047 | ''' |
---|
1048 | if not _in_subset(self.subset, node): return |
---|
1049 | W = self.write |
---|
1050 | if self.documentOrder == _GreaterElement: W('\n') |
---|
1051 | W('<?') |
---|
1052 | W(node.nodeName) |
---|
1053 | s = node.data |
---|
1054 | if s: |
---|
1055 | W(' ') |
---|
1056 | W(s) |
---|
1057 | W('?>') |
---|
1058 | if self.documentOrder == _LesserElement: W('\n') |
---|
1059 | handlers[Node.PROCESSING_INSTRUCTION_NODE] = _do_pi |
---|
1060 | |
---|
1061 | |
---|
1062 | def _do_comment(self, node): |
---|
1063 | '''_do_comment(self, node) -> None |
---|
1064 | Process a comment node. Render a leading or trailing #xA if the |
---|
1065 | document order of the comment is greater or lesser (respectively) |
---|
1066 | than the document element. |
---|
1067 | ''' |
---|
1068 | if not _in_subset(self.subset, node): return |
---|
1069 | if self.comments: |
---|
1070 | W = self.write |
---|
1071 | if self.documentOrder == _GreaterElement: W('\n') |
---|
1072 | W('<!--') |
---|
1073 | W(node.data) |
---|
1074 | W('-->') |
---|
1075 | if self.documentOrder == _LesserElement: W('\n') |
---|
1076 | handlers[Node.COMMENT_NODE] = _do_comment |
---|
1077 | |
---|
1078 | |
---|
1079 | def _do_attr(self, n, value): |
---|
1080 | ''''_do_attr(self, node) -> None |
---|
1081 | Process an attribute.''' |
---|
1082 | |
---|
1083 | W = self.write |
---|
1084 | W(' ') |
---|
1085 | W(n) |
---|
1086 | W('="') |
---|
1087 | s = value \ |
---|
1088 | .replace("&", "&") \ |
---|
1089 | .replace("<", "<") \ |
---|
1090 | .replace('"', '"') \ |
---|
1091 | .replace('\011', '	') \ |
---|
1092 | .replace('\012', '
') \ |
---|
1093 | .replace('\015', '
') |
---|
1094 | W(s) |
---|
1095 | W('"') |
---|
1096 | |
---|
1097 | def _do_element(self, node, initial_other_attrs = []): |
---|
1098 | '''_do_element(self, node, initial_other_attrs = []) -> None |
---|
1099 | Process an element (and its children).''' |
---|
1100 | |
---|
1101 | # Get state (from the stack) make local copies. |
---|
1102 | # ns_parent -- NS declarations in parent |
---|
1103 | # ns_rendered -- NS nodes rendered by ancestors |
---|
1104 | # xml_attrs -- Attributes in XML namespace from parent |
---|
1105 | # ns_local -- NS declarations relevant to this element |
---|
1106 | ns_parent, ns_rendered, xml_attrs = \ |
---|
1107 | self.state[0], self.state[1][:], self.state[2][:] |
---|
1108 | ns_local = ns_parent.copy() |
---|
1109 | |
---|
1110 | # Divide attributes into NS, XML, and others. |
---|
1111 | other_attrs = initial_other_attrs[:] |
---|
1112 | in_subset = _in_subset(self.subset, node) |
---|
1113 | for a in _attrs(node): |
---|
1114 | if a.namespaceURI == XMLNS.BASE: |
---|
1115 | n = a.nodeName |
---|
1116 | if n == "xmlns:": n = "xmlns" # DOM bug workaround |
---|
1117 | ns_local[n] = a.nodeValue |
---|
1118 | elif a.namespaceURI == XMLNS.XML: |
---|
1119 | if self.unsuppressedPrefixes is None or in_subset: |
---|
1120 | xml_attrs.append(a) |
---|
1121 | else: |
---|
1122 | other_attrs.append(a) |
---|
1123 | |
---|
1124 | # Render the node |
---|
1125 | W, name = self.write, None |
---|
1126 | if in_subset: |
---|
1127 | name = node.nodeName |
---|
1128 | W('<') |
---|
1129 | W(name) |
---|
1130 | |
---|
1131 | # Create list of NS attributes to render. |
---|
1132 | ns_to_render = [] |
---|
1133 | for n,v in ns_local.items(): |
---|
1134 | pval = ns_parent.get(n) |
---|
1135 | |
---|
1136 | # If default namespace is XMLNS.BASE or empty, skip |
---|
1137 | if n == "xmlns" \ |
---|
1138 | and v in [ XMLNS.BASE, '' ] and pval in [ XMLNS.BASE, '' ]: |
---|
1139 | continue |
---|
1140 | |
---|
1141 | # "omit namespace node with local name xml, which defines |
---|
1142 | # the xml prefix, if its string value is |
---|
1143 | # http://www.w3.org/XML/1998/namespace." |
---|
1144 | if n == "xmlns:xml" \ |
---|
1145 | and v in [ 'http://www.w3.org/XML/1998/namespace' ]: |
---|
1146 | continue |
---|
1147 | |
---|
1148 | # If different from parent, or parent didn't render |
---|
1149 | # and if not exclusive, or this prefix is needed or |
---|
1150 | # not suppressed |
---|
1151 | if (v != pval or n not in ns_rendered) \ |
---|
1152 | and (self.unsuppressedPrefixes is None or \ |
---|
1153 | _utilized(n, node, other_attrs, self.unsuppressedPrefixes)): |
---|
1154 | ns_to_render.append((n, v)) |
---|
1155 | |
---|
1156 | # Sort and render the ns, marking what was rendered. |
---|
1157 | #ns_to_render.sort(_sorter_ns) |
---|
1158 | for n,v in ns_to_render: |
---|
1159 | self._do_attr(n, v) |
---|
1160 | ns_rendered.append(n) |
---|
1161 | |
---|
1162 | # Add in the XML attributes (don't pass to children, since |
---|
1163 | # we're rendering them), sort, and render. |
---|
1164 | other_attrs.extend(xml_attrs) |
---|
1165 | xml_attrs = [] |
---|
1166 | #other_attrs.sort(_sorter) |
---|
1167 | for a in other_attrs: |
---|
1168 | self._do_attr(a.nodeName, a.value) |
---|
1169 | W('>') |
---|
1170 | |
---|
1171 | # Push state, recurse, pop state. |
---|
1172 | state, self.state = self.state, (ns_local, ns_rendered, xml_attrs) |
---|
1173 | for c in _children(node): |
---|
1174 | _implementation.handlers[c.nodeType](self, c) |
---|
1175 | self.state = state |
---|
1176 | |
---|
1177 | if name: W('</%s>' % name) |
---|
1178 | handlers[Node.ELEMENT_NODE] = _do_element |
---|
1179 | |
---|
1180 | |
---|
1181 | def FastPrint(node, output=None, **kw): |
---|
1182 | """FastPrint(node, output=None, **kw) -> UTF-8 |
---|
1183 | |
---|
1184 | Output a DOM document/element node and all descendents. |
---|
1185 | Return the text; if output is specified then output.write will |
---|
1186 | be called to output the text and None will be returned |
---|
1187 | Keyword parameters: |
---|
1188 | comments: keep comments if non-zero (default is 0) |
---|
1189 | |
---|
1190 | """ |
---|
1191 | if output: |
---|
1192 | _implementation(node, output.write, **kw) |
---|
1193 | else: |
---|
1194 | s = StringIO.StringIO() |
---|
1195 | _implementation(node, s.write, **kw) |
---|
1196 | return s.getvalue() |
---|
1197 | |
---|
1198 | |
---|
1199 | if __name__ == '__main__': print _copyright |
---|