warn user about bitcoin connection slots if p2p connect takes longer than 5 seconds
[p2pool.git] / wstools / WSDLTools.py
1 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
2 #
3 # This software is subject to the provisions of the Zope Public License,
4 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
5 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
6 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
7 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
8 # FOR A PARTICULAR PURPOSE.
9
10 ident = "$Id$"
11
12 import weakref
13 from cStringIO import StringIO
14 from Namespaces import OASIS, XMLNS, WSA, WSA_LIST, WSAW_LIST, WSRF_V1_2, WSRF
15 from Utility import Collection, CollectionNS, DOM, ElementProxy, basejoin
16 from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter
17
18
19 class WSDLReader:
20     """A WSDLReader creates WSDL instances from urls and xml data."""
21
22     # Custom subclasses of WSDLReader may wish to implement a caching
23     # strategy or other optimizations. Because application needs vary 
24     # so widely, we don't try to provide any caching by default.
25
26     def loadFromStream(self, stream, name=None):
27         """Return a WSDL instance loaded from a stream object."""
28         document = DOM.loadDocument(stream)
29         wsdl = WSDL()
30         if name:
31             wsdl.location = name
32         elif hasattr(stream, 'name'):
33             wsdl.location = stream.name
34         wsdl.load(document)
35         return wsdl
36
37     def loadFromURL(self, url):
38         """Return a WSDL instance loaded from the given url."""
39         document = DOM.loadFromURL(url)
40         wsdl = WSDL()
41         wsdl.location = url
42         wsdl.load(document)
43         return wsdl
44
45     def loadFromString(self, data):
46         """Return a WSDL instance loaded from an xml string."""
47         return self.loadFromStream(StringIO(data))
48
49     def loadFromFile(self, filename):
50         """Return a WSDL instance loaded from the given file."""
51         file = open(filename, 'rb')
52         try:
53             wsdl = self.loadFromStream(file)
54         finally:
55             file.close()
56         return wsdl
57
58 class WSDL:
59     """A WSDL object models a WSDL service description. WSDL objects
60        may be created manually or loaded from an xml representation
61        using a WSDLReader instance."""
62
63     def __init__(self, targetNamespace=None, strict=1):
64         self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
65         self.documentation = ''
66         self.location = None
67         self.document = None
68         self.name = None
69         self.services = CollectionNS(self)
70         self.messages = CollectionNS(self)
71         self.portTypes = CollectionNS(self)
72         self.bindings = CollectionNS(self)
73         self.imports = Collection(self)
74         self.types = Types(self)
75         self.extensions = []
76         self.strict = strict
77
78     def __del__(self):
79         if self.document is not None:
80             self.document.unlink()
81
82     version = '1.1'
83
84     def addService(self, name, documentation='', targetNamespace=None):
85         if self.services.has_key(name):
86             raise WSDLError(
87                 'Duplicate service element: %s' % name
88                 )
89         item = Service(name, documentation)
90         if targetNamespace:
91             item.targetNamespace = targetNamespace
92         self.services[name] = item
93         return item
94
95     def addMessage(self, name, documentation='', targetNamespace=None):
96         if self.messages.has_key(name):
97             raise WSDLError(
98                 'Duplicate message element: %s.' % name
99                 )
100         item = Message(name, documentation)
101         if targetNamespace:
102             item.targetNamespace = targetNamespace
103         self.messages[name] = item
104         return item
105
106     def addPortType(self, name, documentation='', targetNamespace=None):
107         if self.portTypes.has_key(name):
108             raise WSDLError(
109                 'Duplicate portType element: name'
110                 )
111         item = PortType(name, documentation)
112         if targetNamespace:
113             item.targetNamespace = targetNamespace
114         self.portTypes[name] = item
115         return item
116
117     def addBinding(self, name, type, documentation='', targetNamespace=None):
118         if self.bindings.has_key(name):
119             raise WSDLError(
120                 'Duplicate binding element: %s' % name
121                 )
122         item = Binding(name, type, documentation)
123         if targetNamespace:
124             item.targetNamespace = targetNamespace
125         self.bindings[name] = item
126         return item
127
128     def addImport(self, namespace, location):
129         item = ImportElement(namespace, location)
130         self.imports[namespace] = item
131         return item
132
133     def toDom(self):
134         """ Generate a DOM representation of the WSDL instance.
135         Not dealing with generating XML Schema, thus the targetNamespace
136         of all XML Schema elements or types used by WSDL message parts 
137         needs to be specified via import information items.
138         """
139         namespaceURI = DOM.GetWSDLUri(self.version)
140         self.document = DOM.createDocument(namespaceURI ,'wsdl:definitions')
141
142         # Set up a couple prefixes for easy reading.
143         child = DOM.getElement(self.document, None)
144         child.setAttributeNS(None, 'targetNamespace', self.targetNamespace)
145         child.setAttributeNS(XMLNS.BASE, 'xmlns:wsdl', namespaceURI)
146         child.setAttributeNS(XMLNS.BASE, 'xmlns:xsd', 'http://www.w3.org/1999/XMLSchema')
147         child.setAttributeNS(XMLNS.BASE, 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/')
148         child.setAttributeNS(XMLNS.BASE, 'xmlns:tns', self.targetNamespace)
149         
150         if self.name:
151             child.setAttributeNS(None, 'name', self.name)
152
153         # wsdl:import
154         for item in self.imports: 
155             item.toDom()
156         # wsdl:message
157         for item in self.messages:
158             item.toDom()
159         # wsdl:portType
160         for item in self.portTypes:
161             item.toDom()
162         # wsdl:binding
163         for item in self.bindings:
164             item.toDom()
165         # wsdl:service
166         for item in self.services:
167             item.toDom()
168
169     def load(self, document):
170         # We save a reference to the DOM document to ensure that elements
171         # saved as "extensions" will continue to have a meaningful context
172         # for things like namespace references. The lifetime of the DOM
173         # document is bound to the lifetime of the WSDL instance.
174         self.document = document
175
176         definitions = DOM.getElement(document, 'definitions', None, None)
177         if definitions is None:
178             raise WSDLError(
179                 'Missing <definitions> element.'
180                 )
181         self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
182         NS_WSDL = DOM.GetWSDLUri(self.version)
183
184         self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
185                                            None, None)
186         self.name = DOM.getAttr(definitions, 'name', None, None)
187         self.documentation = GetDocumentation(definitions)
188
189         # 
190         # Retrieve all <wsdl:import>'s, append all children of imported
191         # document to main document.  First iteration grab all original 
192         # <wsdl:import>'s from document, second iteration grab all 
193         # "imported" <wsdl:imports> from document, etc break out when 
194         # no more <wsdl:import>'s.
195         # 
196         imported = []
197         base_location = self.location
198         do_it = True
199         while do_it:
200             do_it = False
201             for element in DOM.getElements(definitions, 'import', NS_WSDL):
202                 location = DOM.getAttr(element, 'location')
203
204                 if base_location is not None:
205                     location = basejoin(base_location, location)
206                     
207                 if location not in imported:
208                     do_it = True
209                     self._import(document, element, base_location)
210                     imported.append(location)
211                 else:
212                     definitions.removeChild(element)
213
214             base_location = None
215
216         # 
217         # No more <wsdl:import>'s, now load up all other 
218         # WSDL information items.
219         # 
220         for element in DOM.getElements(definitions, None, None):
221             targetNamespace = DOM.getAttr(element, 'targetNamespace')
222             localName = element.localName
223
224             if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
225                 if localName == 'schema':
226                     tns = DOM.getAttr(element, 'targetNamespace')
227                     reader = SchemaReader(base_url=self.imports[tns].location)
228                     schema = reader.loadFromNode(WSDLToolsAdapter(self),
229                                                  element)
230 #                    schema.setBaseUrl(self.location)
231                     self.types.addSchema(schema)
232                 else:
233                     self.extensions.append(element)
234                 continue
235
236             elif localName == 'message':
237                 name = DOM.getAttr(element, 'name')
238                 docs = GetDocumentation(element)
239                 message = self.addMessage(name, docs, targetNamespace)
240                 parts = DOM.getElements(element, 'part', NS_WSDL)
241                 message.load(parts)
242                 continue
243
244             elif localName == 'portType':
245                 name = DOM.getAttr(element, 'name')
246                 docs = GetDocumentation(element)
247                 ptype = self.addPortType(name, docs, targetNamespace)
248                 #operations = DOM.getElements(element, 'operation', NS_WSDL)
249                 #ptype.load(operations)
250                 ptype.load(element)
251                 continue
252
253             elif localName == 'binding':
254                 name = DOM.getAttr(element, 'name')
255                 type = DOM.getAttr(element, 'type', default=None)
256                 if type is None:
257                     raise WSDLError(
258                         'Missing type attribute for binding %s.' % name
259                         )
260                 type = ParseQName(type, element)
261                 docs = GetDocumentation(element)
262                 binding = self.addBinding(name, type, docs, targetNamespace)
263                 operations = DOM.getElements(element, 'operation', NS_WSDL)
264                 binding.load(operations)
265                 binding.load_ex(GetExtensions(element))
266                 continue
267
268             elif localName == 'service':
269                 name = DOM.getAttr(element, 'name')
270                 docs = GetDocumentation(element)
271                 service = self.addService(name, docs, targetNamespace)
272                 ports = DOM.getElements(element, 'port', NS_WSDL)
273                 service.load(ports)
274                 service.load_ex(GetExtensions(element))
275                 continue
276
277             elif localName == 'types':
278                 self.types.documentation = GetDocumentation(element)
279                 base_location = DOM.getAttr(element, 'base-location')
280                 if base_location:
281                     element.removeAttribute('base-location')
282                 base_location = base_location or self.location
283                 reader = SchemaReader(base_url=base_location)
284                 for item in DOM.getElements(element, None, None):
285                     if item.localName == 'schema':
286                         schema = reader.loadFromNode(WSDLToolsAdapter(self), item)
287                         # XXX <types> could have been imported
288                         #schema.setBaseUrl(self.location)
289                         schema.setBaseUrl(base_location)
290                         self.types.addSchema(schema)
291                     else:
292                         self.types.addExtension(item)
293                 # XXX remove the attribute
294                 # element.removeAttribute('base-location')
295                 continue
296
297     def _import(self, document, element, base_location=None):
298         '''Algo take <import> element's children, clone them,
299         and add them to the main document.  Support for relative 
300         locations is a bit complicated.  The orig document context
301         is lost, so we need to store base location in DOM elements
302         representing <types>, by creating a special temporary 
303         "base-location" attribute,  and <import>, by resolving
304         the relative "location" and storing it as "location".
305         
306         document -- document we are loading
307         element -- DOM Element representing <import> 
308         base_location -- location of document from which this
309             <import> was gleaned.
310         '''
311         namespace = DOM.getAttr(element, 'namespace', default=None)
312         location = DOM.getAttr(element, 'location', default=None)
313         if namespace is None or location is None:
314             raise WSDLError(
315                 'Invalid import element (missing namespace or location).'
316                 )
317         if base_location:
318             location = basejoin(base_location, location)
319             element.setAttributeNS(None, 'location', location)
320
321         obimport = self.addImport(namespace, location)
322         obimport._loaded = 1
323
324         importdoc = DOM.loadFromURL(location)
325         try:
326             if location.find('#') > -1:
327                 idref = location.split('#')[-1]
328                 imported = DOM.getElementById(importdoc, idref)
329             else:
330                 imported = importdoc.documentElement
331             if imported is None:
332                 raise WSDLError(
333                     'Import target element not found for: %s' % location
334                     )
335
336             imported_tns = DOM.findTargetNS(imported)
337             if imported_tns != namespace:
338                 return
339
340             if imported.localName == 'definitions':
341                 imported_nodes = imported.childNodes
342             else:
343                 imported_nodes = [imported]
344             parent = element.parentNode
345
346             parent.removeChild(element)
347             
348             for node in imported_nodes:
349                 if node.nodeType != node.ELEMENT_NODE:
350                     continue
351                 child = DOM.importNode(document, node, 1)
352                 parent.appendChild(child)
353                 child.setAttribute('targetNamespace', namespace)
354                 attrsNS = imported._attrsNS
355                 for attrkey in attrsNS.keys():
356                     if attrkey[0] == DOM.NS_XMLNS:
357                         attr = attrsNS[attrkey].cloneNode(1)
358                         child.setAttributeNode(attr)
359
360                 #XXX Quick Hack, should be in WSDL Namespace.
361                 if child.localName == 'import':
362                     rlocation = child.getAttributeNS(None, 'location')
363                     alocation = basejoin(location, rlocation)
364                     child.setAttribute('location', alocation)
365                 elif child.localName == 'types':
366                     child.setAttribute('base-location', location)
367
368         finally:
369             importdoc.unlink()
370         return location
371
372 class Element:
373     """A class that provides common functions for WSDL element classes."""
374     def __init__(self, name=None, documentation=''):
375         self.name = name
376         self.documentation = documentation
377         self.extensions = []
378
379     def addExtension(self, item):
380         item.parent = weakref.ref(self)
381         self.extensions.append(item)
382         
383     def getWSDL(self):
384         """Return the WSDL object that contains this information item."""
385         parent = self
386         while 1:
387             # skip any collections
388             if isinstance(parent, WSDL):
389                 return parent
390             try: parent = parent.parent()
391             except: break
392             
393         return None
394
395
396 class ImportElement(Element):
397     def __init__(self, namespace, location):
398         self.namespace = namespace
399         self.location = location
400
401 #    def getWSDL(self):
402 #        """Return the WSDL object that contains this Message Part."""
403 #        return self.parent().parent()
404
405     def toDom(self):
406         wsdl = self.getWSDL()
407         ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
408         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'import')
409         epc.setAttributeNS(None, 'namespace', self.namespace)
410         epc.setAttributeNS(None, 'location', self.location)
411
412     _loaded = None
413
414
415 class Types(Collection):
416     default = lambda self,k: k.targetNamespace
417     def __init__(self, parent):
418         Collection.__init__(self, parent)
419         self.documentation = ''
420         self.extensions = []
421
422     def addSchema(self, schema):
423         name = schema.targetNamespace
424         self[name] = schema
425         return schema
426
427     def addExtension(self, item):
428         self.extensions.append(item)
429
430
431 class Message(Element):
432     def __init__(self, name, documentation=''):
433         Element.__init__(self, name, documentation)
434         self.parts = Collection(self)
435
436     def addPart(self, name, type=None, element=None):
437         if self.parts.has_key(name):
438             raise WSDLError(
439                 'Duplicate message part element: %s' % name
440                 )
441         if type is None and element is None:
442             raise WSDLError(
443                 'Missing type or element attribute for part: %s' % name
444                 )
445         item = MessagePart(name)
446         item.element = element
447         item.type = type
448         self.parts[name] = item
449         return item
450
451     def load(self, elements):
452         for element in elements:
453             name = DOM.getAttr(element, 'name')
454             part = MessagePart(name)
455             self.parts[name] = part
456             elemref = DOM.getAttr(element, 'element', default=None)
457             typeref = DOM.getAttr(element, 'type', default=None)
458             if typeref is None and elemref is None:
459                 raise WSDLError(
460                     'No type or element attribute for part: %s' % name
461                     )
462             if typeref is not None:
463                 part.type = ParseTypeRef(typeref, element)
464             if elemref is not None:
465                 part.element = ParseTypeRef(elemref, element)
466
467 #    def getElementDeclaration(self):
468 #        """Return the XMLSchema.ElementDeclaration instance or None"""
469 #        element = None
470 #        if self.element:
471 #            nsuri,name = self.element
472 #            wsdl = self.getWSDL()
473 #            if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].elements.has_key(name):
474 #                element = wsdl.types[nsuri].elements[name]
475 #        return element
476 #
477 #    def getTypeDefinition(self):
478 #        """Return the XMLSchema.TypeDefinition instance or None"""
479 #        type = None
480 #        if self.type:
481 #            nsuri,name = self.type
482 #            wsdl = self.getWSDL()
483 #            if wsdl.types.has_key(nsuri) and wsdl.types[nsuri].types.has_key(name):
484 #                type = wsdl.types[nsuri].types[name]
485 #        return type
486
487 #    def getWSDL(self):
488 #        """Return the WSDL object that contains this Message Part."""
489 #        return self.parent().parent()
490
491     def toDom(self):
492         wsdl = self.getWSDL()
493         ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
494         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'message')
495         epc.setAttributeNS(None, 'name', self.name)
496
497         for part in self.parts:
498             part.toDom(epc._getNode())
499
500
501 class MessagePart(Element):
502     def __init__(self, name):
503         Element.__init__(self, name, '')
504         self.element = None
505         self.type = None
506
507 #    def getWSDL(self):
508 #        """Return the WSDL object that contains this Message Part."""
509 #        return self.parent().parent().parent().parent()
510
511     def getTypeDefinition(self):
512         wsdl = self.getWSDL()
513         nsuri,name = self.type
514         schema = wsdl.types.get(nsuri, {})
515         return schema.get(name)
516
517     def getElementDeclaration(self):
518         wsdl = self.getWSDL()
519         nsuri,name = self.element
520         schema = wsdl.types.get(nsuri, {})
521         return schema.get(name)
522
523     def toDom(self, node):
524         """node -- node representing message"""
525         wsdl = self.getWSDL()
526         ep = ElementProxy(None, node)
527         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'part')
528         epc.setAttributeNS(None, 'name', self.name)
529
530         if self.element is not None:
531             ns,name = self.element
532             prefix = epc.getPrefix(ns)
533             epc.setAttributeNS(None, 'element', '%s:%s'%(prefix,name))
534         elif self.type is not None:
535             ns,name = self.type
536             prefix = epc.getPrefix(ns)
537             epc.setAttributeNS(None, 'type', '%s:%s'%(prefix,name))
538
539
540 class PortType(Element):
541     '''PortType has a anyAttribute, thus must provide for an extensible
542        mechanism for supporting such attributes.  ResourceProperties is
543        specified in WS-ResourceProperties.   wsa:Action is specified in
544        WS-Address.
545
546        Instance Data:
547            name -- name attribute
548            resourceProperties -- optional. wsr:ResourceProperties attribute,
549               value is a QName this is Parsed into a (namespaceURI, name)
550               that represents a Global Element Declaration.
551            operations
552     '''
553
554     def __init__(self, name, documentation=''):
555         Element.__init__(self, name, documentation)
556         self.operations = Collection(self)
557         self.resourceProperties = None
558
559 #    def getWSDL(self):
560 #        return self.parent().parent()
561
562     def getTargetNamespace(self):
563         return self.targetNamespace or self.getWSDL().targetNamespace
564
565     def getResourceProperties(self):
566         return self.resourceProperties
567
568     def addOperation(self, name, documentation='', parameterOrder=None):
569         item = Operation(name, documentation, parameterOrder)
570         self.operations[name] = item
571         return item
572
573     def load(self, element):
574         self.name = DOM.getAttr(element, 'name')
575         self.documentation = GetDocumentation(element)
576         self.targetNamespace = DOM.getAttr(element, 'targetNamespace')
577
578         for nsuri in WSRF_V1_2.PROPERTIES.XSD_LIST:
579             if DOM.hasAttr(element, 'ResourceProperties', nsuri):
580                 rpref = DOM.getAttr(element, 'ResourceProperties', nsuri)
581                 self.resourceProperties = ParseQName(rpref, element)
582
583         NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version)
584         elements = DOM.getElements(element, 'operation', NS_WSDL)
585         for element in elements:
586             name = DOM.getAttr(element, 'name')
587             docs = GetDocumentation(element)
588             param_order = DOM.getAttr(element, 'parameterOrder', default=None)
589             if param_order is not None:
590                 param_order = param_order.split(' ')
591             operation = self.addOperation(name, docs, param_order)
592
593             item = DOM.getElement(element, 'input', None, None)
594             if item is not None:
595                 name = DOM.getAttr(item, 'name')
596                 docs = GetDocumentation(item)
597                 msgref = DOM.getAttr(item, 'message')
598                 message = ParseQName(msgref, item)
599                 for WSA in WSA_LIST + WSAW_LIST:
600                     action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
601                     if action: break
602                 operation.setInput(message, name, docs, action)
603
604             item = DOM.getElement(element, 'output', None, None)
605             if item is not None:
606                 name = DOM.getAttr(item, 'name')
607                 docs = GetDocumentation(item)
608                 msgref = DOM.getAttr(item, 'message')
609                 message = ParseQName(msgref, item)
610                 for WSA in WSA_LIST + WSAW_LIST:
611                     action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
612                     if action: break
613                 operation.setOutput(message, name, docs, action)
614
615             for item in DOM.getElements(element, 'fault', None):
616                 name = DOM.getAttr(item, 'name')
617                 docs = GetDocumentation(item)
618                 msgref = DOM.getAttr(item, 'message')
619                 message = ParseQName(msgref, item)
620                 for WSA in WSA_LIST + WSAW_LIST:
621                     action = DOM.getAttr(item, 'Action', WSA.ADDRESS, None)
622                     if action: break
623                 operation.addFault(message, name, docs, action)
624                 
625     def toDom(self):
626         wsdl = self.getWSDL()
627
628         ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
629         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'portType')
630         epc.setAttributeNS(None, 'name', self.name)
631         if self.resourceProperties:
632             ns,name = self.resourceProperties
633             prefix = epc.getPrefix(ns)
634             epc.setAttributeNS(WSRF.PROPERTIES.LATEST, 'ResourceProperties', 
635                                 '%s:%s'%(prefix,name))
636
637         for op in self.operations:
638             op.toDom(epc._getNode())
639
640
641
642 class Operation(Element):
643     def __init__(self, name, documentation='', parameterOrder=None):
644         Element.__init__(self, name, documentation)
645         self.parameterOrder = parameterOrder
646         self.faults = Collection(self)
647         self.input = None
648         self.output = None
649
650     def getWSDL(self):
651         """Return the WSDL object that contains this Operation."""
652         return self.parent().parent().parent().parent()
653
654     def getPortType(self):
655         return self.parent().parent()
656
657     def getInputAction(self):
658         """wsa:Action attribute"""
659         return GetWSAActionInput(self)
660
661     def getInputMessage(self):
662         if self.input is None:
663             return None
664         wsdl = self.getPortType().getWSDL()
665         return wsdl.messages[self.input.message]
666
667     def getOutputAction(self):
668         """wsa:Action attribute"""
669         return GetWSAActionOutput(self)
670
671     def getOutputMessage(self):
672         if self.output is None:
673             return None
674         wsdl = self.getPortType().getWSDL()
675         return wsdl.messages[self.output.message]
676
677     def getFaultAction(self, name):
678         """wsa:Action attribute"""
679         return GetWSAActionFault(self, name)
680
681     def getFaultMessage(self, name):
682         wsdl = self.getPortType().getWSDL()
683         return wsdl.messages[self.faults[name].message]
684
685     def addFault(self, message, name, documentation='', action=None):
686         if self.faults.has_key(name):
687             raise WSDLError(
688                 'Duplicate fault element: %s' % name
689                 )
690         item = MessageRole('fault', message, name, documentation, action)
691         self.faults[name] = item
692         return item
693
694     def setInput(self, message, name='', documentation='', action=None):
695         self.input = MessageRole('input', message, name, documentation, action)
696         self.input.parent = weakref.ref(self)
697         return self.input
698
699     def setOutput(self, message, name='', documentation='', action=None):
700         self.output = MessageRole('output', message, name, documentation, action)
701         self.output.parent = weakref.ref(self)
702         return self.output
703
704     def toDom(self, node):
705         wsdl = self.getWSDL()
706
707         ep = ElementProxy(None, node)
708         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
709         epc.setAttributeNS(None, 'name', self.name)
710         node = epc._getNode()
711         if self.input:
712            self.input.toDom(node)
713         if self.output:
714            self.output.toDom(node)
715         for fault in self.faults:
716            fault.toDom(node)
717
718
719 class MessageRole(Element):
720     def __init__(self, type, message, name='', documentation='', action=None):
721         Element.__init__(self, name, documentation)
722         self.message = message
723         self.type = type
724         self.action = action
725         
726     def getWSDL(self):
727         """Return the WSDL object that contains this information item."""
728         parent = self
729         while 1:
730             # skip any collections
731             if isinstance(parent, WSDL):
732                 return parent
733             try: parent = parent.parent()
734             except: break
735             
736         return None
737
738     def getMessage(self):
739         """Return the WSDL object that represents the attribute message 
740         (namespaceURI, name) tuple
741         """
742         wsdl = self.getWSDL()
743         return wsdl.messages[self.message]
744
745     def toDom(self, node):
746         wsdl = self.getWSDL()
747
748         ep = ElementProxy(None, node)
749         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
750         if not isinstance(self.message, basestring) and len(self.message) == 2:
751             ns,name = self.message
752             prefix = epc.getPrefix(ns)
753             epc.setAttributeNS(None, 'message', '%s:%s' %(prefix,name))
754         else:
755             epc.setAttributeNS(None, 'message', self.message)
756
757         if self.action:
758             epc.setAttributeNS(WSA.ADDRESS, 'Action', self.action)
759             
760         if self.name:
761             epc.setAttributeNS(None, 'name', self.name)
762         
763
764 class Binding(Element):
765     def __init__(self, name, type, documentation=''):
766         Element.__init__(self, name, documentation)
767         self.operations = Collection(self)
768         self.type = type
769
770 #    def getWSDL(self):
771 #        """Return the WSDL object that contains this binding."""
772 #        return self.parent().parent()
773
774     def getPortType(self):
775         """Return the PortType object associated with this binding."""
776         return self.getWSDL().portTypes[self.type]
777
778     def findBinding(self, kind):
779         for item in self.extensions:
780             if isinstance(item, kind):
781                 return item
782         return None
783
784     def findBindings(self, kind):
785         return [ item for item in self.extensions if isinstance(item, kind) ]
786
787     def addOperationBinding(self, name, documentation=''):
788         item = OperationBinding(name, documentation)
789         self.operations[name] = item
790         return item
791
792     def load(self, elements):
793         for element in elements:
794             name = DOM.getAttr(element, 'name')
795             docs = GetDocumentation(element)
796             opbinding = self.addOperationBinding(name, docs)
797             opbinding.load_ex(GetExtensions(element))
798
799             item = DOM.getElement(element, 'input', None, None)
800             if item is not None:
801                 #TODO: addInputBinding?
802                 mbinding = MessageRoleBinding('input')
803                 mbinding.documentation = GetDocumentation(item)
804                 opbinding.input = mbinding
805                 mbinding.load_ex(GetExtensions(item))
806                 mbinding.parent = weakref.ref(opbinding)
807
808             item = DOM.getElement(element, 'output', None, None)
809             if item is not None:
810                 mbinding = MessageRoleBinding('output')
811                 mbinding.documentation = GetDocumentation(item)
812                 opbinding.output = mbinding
813                 mbinding.load_ex(GetExtensions(item))
814                 mbinding.parent = weakref.ref(opbinding)
815
816             for item in DOM.getElements(element, 'fault', None):
817                 name = DOM.getAttr(item, 'name')
818                 mbinding = MessageRoleBinding('fault', name)
819                 mbinding.documentation = GetDocumentation(item)
820                 opbinding.faults[name] = mbinding
821                 mbinding.load_ex(GetExtensions(item))
822                 mbinding.parent = weakref.ref(opbinding)
823
824     def load_ex(self, elements):
825         for e in elements:
826             ns, name = e.namespaceURI, e.localName
827             if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
828                 transport = DOM.getAttr(e, 'transport', default=None)
829                 style = DOM.getAttr(e, 'style', default='document')
830                 ob = SoapBinding(transport, style)
831                 self.addExtension(ob)
832                 continue
833             elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
834                 verb = DOM.getAttr(e, 'verb')
835                 ob = HttpBinding(verb)
836                 self.addExtension(ob)
837                 continue
838             else:
839                 self.addExtension(e)
840
841     def toDom(self):
842         wsdl = self.getWSDL()
843         ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
844         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'binding')
845         epc.setAttributeNS(None, 'name', self.name)
846
847         ns,name = self.type
848         prefix = epc.getPrefix(ns)
849         epc.setAttributeNS(None, 'type', '%s:%s' %(prefix,name))
850
851         node = epc._getNode()
852         for ext in self.extensions:
853             ext.toDom(node)
854         for op_binding in self.operations:
855             op_binding.toDom(node)
856
857
858 class OperationBinding(Element):
859     def __init__(self, name, documentation=''):
860         Element.__init__(self, name, documentation)
861         self.input = None
862         self.output = None
863         self.faults = Collection(self)
864
865 #    def getWSDL(self):
866 #        """Return the WSDL object that contains this binding."""
867 #        return self.parent().parent().parent().parent()
868
869
870     def getBinding(self):
871         """Return the parent Binding object of the operation binding."""
872         return self.parent().parent()
873
874     def getOperation(self):
875         """Return the abstract Operation associated with this binding."""
876         return self.getBinding().getPortType().operations[self.name]
877         
878     def findBinding(self, kind):
879         for item in self.extensions:
880             if isinstance(item, kind):
881                 return item
882         return None
883
884     def findBindings(self, kind):
885         return [ item for item in self.extensions if isinstance(item, kind) ]
886
887     def addInputBinding(self, binding):
888         if self.input is None:
889             self.input = MessageRoleBinding('input')
890             self.input.parent = weakref.ref(self)
891         self.input.addExtension(binding)
892         return binding
893
894     def addOutputBinding(self, binding):
895         if self.output is None:
896             self.output = MessageRoleBinding('output')
897             self.output.parent = weakref.ref(self)
898         self.output.addExtension(binding)
899         return binding
900
901     def addFaultBinding(self, name, binding):
902         fault = self.get(name, None)
903         if fault is None:
904             fault = MessageRoleBinding('fault', name)
905         fault.addExtension(binding)
906         return binding
907
908     def load_ex(self, elements):
909         for e in elements:
910             ns, name = e.namespaceURI, e.localName
911             if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
912                 soapaction = DOM.getAttr(e, 'soapAction', default=None)
913                 style = DOM.getAttr(e, 'style', default=None)
914                 ob = SoapOperationBinding(soapaction, style)
915                 self.addExtension(ob)
916                 continue
917             elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
918                 location = DOM.getAttr(e, 'location')
919                 ob = HttpOperationBinding(location)
920                 self.addExtension(ob)
921                 continue
922             else:
923                 self.addExtension(e)
924
925     def toDom(self, node):
926         wsdl = self.getWSDL()
927         ep = ElementProxy(None, node)
928         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), 'operation')
929         epc.setAttributeNS(None, 'name', self.name)
930
931         node = epc._getNode()
932         for ext in self.extensions:
933             ext.toDom(node)
934         if self.input:
935             self.input.toDom(node)
936         if self.output:
937             self.output.toDom(node)
938         for fault in self.faults:
939             fault.toDom(node)
940
941
942 class MessageRoleBinding(Element):
943     def __init__(self, type, name='', documentation=''):
944         Element.__init__(self, name, documentation)
945         self.type = type
946
947     def findBinding(self, kind):
948         for item in self.extensions:
949             if isinstance(item, kind):
950                 return item
951         return None
952
953     def findBindings(self, kind):
954         return [ item for item in self.extensions if isinstance(item, kind) ]
955
956     def load_ex(self, elements):
957         for e in elements:
958             ns, name = e.namespaceURI, e.localName
959             if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
960                 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
961                 namespace = DOM.getAttr(e, 'namespace', default=None)
962                 parts = DOM.getAttr(e, 'parts', default=None)
963                 use = DOM.getAttr(e, 'use', default=None)
964                 if use is None:
965                     raise WSDLError(
966                         'Invalid soap:body binding element.'
967                         )
968                 ob = SoapBodyBinding(use, namespace, encstyle, parts)
969                 self.addExtension(ob)
970                 continue
971
972             elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
973                 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
974                 namespace = DOM.getAttr(e, 'namespace', default=None)
975                 name = DOM.getAttr(e, 'name', default=None)
976                 use = DOM.getAttr(e, 'use', default=None)
977                 if use is None or name is None:
978                     raise WSDLError(
979                         'Invalid soap:fault binding element.'
980                         )
981                 ob = SoapFaultBinding(name, use, namespace, encstyle)
982                 self.addExtension(ob)
983                 continue
984
985             elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
986                 'header', 'headerfault'
987                 ):
988                 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
989                 namespace = DOM.getAttr(e, 'namespace', default=None)
990                 message = DOM.getAttr(e, 'message')
991                 part = DOM.getAttr(e, 'part')
992                 use = DOM.getAttr(e, 'use')
993                 if name == 'header':
994                     _class = SoapHeaderBinding
995                 else:
996                     _class = SoapHeaderFaultBinding
997                 message = ParseQName(message, e)
998                 ob = _class(message, part, use, namespace, encstyle)
999                 self.addExtension(ob)
1000                 continue
1001
1002             elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
1003                 ob = HttpUrlReplacementBinding()
1004                 self.addExtension(ob)
1005                 continue
1006
1007             elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
1008                 ob = HttpUrlEncodedBinding()
1009                 self.addExtension(ob)
1010                 continue
1011
1012             elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
1013                 ob = MimeMultipartRelatedBinding()
1014                 self.addExtension(ob)
1015                 ob.load_ex(GetExtensions(e))
1016                 continue
1017
1018             elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
1019                 part = DOM.getAttr(e, 'part', default=None)
1020                 type = DOM.getAttr(e, 'type', default=None)
1021                 ob = MimeContentBinding(part, type)
1022                 self.addExtension(ob)
1023                 continue
1024
1025             elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
1026                 part = DOM.getAttr(e, 'part', default=None)
1027                 ob = MimeXmlBinding(part)
1028                 self.addExtension(ob)
1029                 continue
1030
1031             else:
1032                 self.addExtension(e)
1033
1034     def toDom(self, node):
1035         wsdl = self.getWSDL()
1036         ep = ElementProxy(None, node)
1037         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), self.type)
1038
1039         node = epc._getNode()
1040         for item in self.extensions:
1041             if item: item.toDom(node)
1042
1043
1044 class Service(Element):
1045     def __init__(self, name, documentation=''):
1046         Element.__init__(self, name, documentation)
1047         self.ports = Collection(self)
1048
1049     def getWSDL(self):
1050         return self.parent().parent()
1051
1052     def addPort(self, name, binding, documentation=''):
1053         item = Port(name, binding, documentation)
1054         self.ports[name] = item
1055         return item
1056
1057     def load(self, elements):
1058         for element in elements:
1059             name = DOM.getAttr(element, 'name', default=None)
1060             docs = GetDocumentation(element)
1061             binding = DOM.getAttr(element, 'binding', default=None)
1062             if name is None or binding is None:
1063                 raise WSDLError(
1064                     'Invalid port element.'
1065                     )
1066             binding = ParseQName(binding, element)
1067             port = self.addPort(name, binding, docs)
1068             port.load_ex(GetExtensions(element))
1069
1070     def load_ex(self, elements):
1071         for e in elements:
1072             self.addExtension(e)
1073
1074     def toDom(self):
1075         wsdl = self.getWSDL()
1076         ep = ElementProxy(None, DOM.getElement(wsdl.document, None))
1077         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "service")
1078         epc.setAttributeNS(None, "name", self.name)
1079
1080         node = epc._getNode()
1081         for port in self.ports:
1082             port.toDom(node)
1083
1084
1085 class Port(Element):
1086     def __init__(self, name, binding, documentation=''):
1087         Element.__init__(self, name, documentation)
1088         self.binding = binding
1089
1090 #    def getWSDL(self):
1091 #        return self.parent().parent().getWSDL()
1092
1093     def getService(self):
1094         """Return the Service object associated with this port."""
1095         return self.parent().parent()
1096
1097     def getBinding(self):
1098         """Return the Binding object that is referenced by this port."""
1099         wsdl = self.getService().getWSDL()
1100         return wsdl.bindings[self.binding]
1101
1102     def getPortType(self):
1103         """Return the PortType object that is referenced by this port."""
1104         wsdl = self.getService().getWSDL()
1105         binding = wsdl.bindings[self.binding]
1106         return wsdl.portTypes[binding.type]
1107
1108     def getAddressBinding(self):
1109         """A convenience method to obtain the extension element used
1110            as the address binding for the port."""
1111         for item in self.extensions:
1112             if isinstance(item, SoapAddressBinding) or \
1113                isinstance(item, HttpAddressBinding):
1114                 return item
1115         raise WSDLError(
1116             'No address binding found in port.'
1117             )
1118
1119     def load_ex(self, elements):
1120         for e in elements:
1121             ns, name = e.namespaceURI, e.localName
1122             if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
1123                 location = DOM.getAttr(e, 'location', default=None)
1124                 ob = SoapAddressBinding(location)
1125                 self.addExtension(ob)
1126                 continue
1127             elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
1128                 location = DOM.getAttr(e, 'location', default=None)
1129                 ob = HttpAddressBinding(location)
1130                 self.addExtension(ob)
1131                 continue
1132             else:
1133                 self.addExtension(e)
1134
1135     def toDom(self, node):
1136         wsdl = self.getWSDL()
1137         ep = ElementProxy(None, node)
1138         epc = ep.createAppendElement(DOM.GetWSDLUri(wsdl.version), "port")
1139         epc.setAttributeNS(None, "name", self.name)
1140
1141         ns,name = self.binding
1142         prefix = epc.getPrefix(ns)
1143         epc.setAttributeNS(None, "binding", "%s:%s" %(prefix,name))
1144
1145         node = epc._getNode()
1146         for ext in self.extensions:
1147             ext.toDom(node)
1148
1149
1150 class SoapBinding:
1151     def __init__(self, transport, style='rpc'):
1152         self.transport = transport
1153         self.style = style
1154
1155     def getWSDL(self):
1156         return self.parent().getWSDL()
1157
1158     def toDom(self, node):
1159         wsdl = self.getWSDL()
1160         ep = ElementProxy(None, node)
1161         epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'binding')
1162         if self.transport:
1163             epc.setAttributeNS(None, "transport", self.transport)
1164         if self.style:
1165             epc.setAttributeNS(None, "style", self.style)
1166
1167 class SoapAddressBinding:
1168     def __init__(self, location):
1169         self.location = location
1170
1171     def getWSDL(self):
1172         return self.parent().getWSDL()
1173
1174     def toDom(self, node):
1175         wsdl = self.getWSDL()
1176         ep = ElementProxy(None, node)
1177         epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'address')
1178         epc.setAttributeNS(None, "location", self.location)
1179
1180
1181 class SoapOperationBinding:
1182     def __init__(self, soapAction=None, style=None):
1183         self.soapAction = soapAction
1184         self.style = style
1185
1186     def getWSDL(self):
1187         return self.parent().getWSDL()
1188
1189     def toDom(self, node):
1190         wsdl = self.getWSDL()
1191         ep = ElementProxy(None, node)
1192         epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'operation')
1193         if self.soapAction:
1194             epc.setAttributeNS(None, 'soapAction', self.soapAction)
1195         if self.style:
1196             epc.setAttributeNS(None, 'style', self.style)
1197
1198
1199 class SoapBodyBinding:
1200     def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
1201         if not use in ('literal', 'encoded'):
1202             raise WSDLError(
1203                 'Invalid use attribute value: %s' % use
1204                 )
1205         self.encodingStyle = encodingStyle
1206         self.namespace = namespace
1207         if type(parts) in (type(''), type(u'')):
1208             parts = parts.split()
1209         self.parts = parts
1210         self.use = use
1211
1212     def getWSDL(self):
1213         return self.parent().getWSDL()
1214
1215     def toDom(self, node):
1216         wsdl = self.getWSDL()
1217         ep = ElementProxy(None, node)
1218         epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
1219         epc.setAttributeNS(None, "use", self.use)
1220         epc.setAttributeNS(None, "namespace", self.namespace)
1221
1222
1223 class SoapFaultBinding:
1224     def __init__(self, name, use, namespace=None, encodingStyle=None):
1225         if not use in ('literal', 'encoded'):
1226             raise WSDLError(
1227                 'Invalid use attribute value: %s' % use
1228                 )
1229         self.encodingStyle = encodingStyle
1230         self.namespace = namespace
1231         self.name = name
1232         self.use = use
1233         
1234     def getWSDL(self):
1235         return self.parent().getWSDL()
1236     
1237     def toDom(self, node):
1238         wsdl = self.getWSDL()
1239         ep = ElementProxy(None, node)
1240         epc = ep.createAppendElement(DOM.GetWSDLSoapBindingUri(wsdl.version), 'body')
1241         epc.setAttributeNS(None, "use", self.use)
1242         epc.setAttributeNS(None, "name", self.name)
1243         if self.namespace is not None:
1244             epc.setAttributeNS(None, "namespace", self.namespace)
1245         if self.encodingStyle is not None:
1246             epc.setAttributeNS(None, "encodingStyle", self.encodingStyle)
1247
1248
1249 class SoapHeaderBinding:
1250     def __init__(self, message, part, use, namespace=None, encodingStyle=None):
1251         if not use in ('literal', 'encoded'):
1252             raise WSDLError(
1253                 'Invalid use attribute value: %s' % use
1254                 )
1255         self.encodingStyle = encodingStyle
1256         self.namespace = namespace
1257         self.message = message
1258         self.part = part
1259         self.use = use
1260
1261     tagname = 'header'
1262
1263 class SoapHeaderFaultBinding(SoapHeaderBinding):
1264     tagname = 'headerfault'
1265
1266
1267 class HttpBinding:
1268     def __init__(self, verb):
1269         self.verb = verb
1270
1271 class HttpAddressBinding:
1272     def __init__(self, location):
1273         self.location = location
1274
1275
1276 class HttpOperationBinding:
1277     def __init__(self, location):
1278         self.location = location
1279
1280 class HttpUrlReplacementBinding:
1281     pass
1282
1283
1284 class HttpUrlEncodedBinding:
1285     pass
1286
1287
1288 class MimeContentBinding:
1289     def __init__(self, part=None, type=None):
1290         self.part = part
1291         self.type = type
1292
1293
1294 class MimeXmlBinding:
1295     def __init__(self, part=None):
1296         self.part = part
1297
1298
1299 class MimeMultipartRelatedBinding:
1300     def __init__(self):
1301         self.parts = []
1302
1303     def load_ex(self, elements):
1304         for e in elements:
1305             ns, name = e.namespaceURI, e.localName
1306             if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
1307                 self.parts.append(MimePartBinding())
1308                 continue
1309
1310
1311 class MimePartBinding:
1312     def __init__(self):
1313         self.items = []
1314
1315     def load_ex(self, elements):
1316         for e in elements:
1317             ns, name = e.namespaceURI, e.localName
1318             if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
1319                 part = DOM.getAttr(e, 'part', default=None)
1320                 type = DOM.getAttr(e, 'type', default=None)
1321                 ob = MimeContentBinding(part, type)
1322                 self.items.append(ob)
1323                 continue
1324
1325             elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
1326                 part = DOM.getAttr(e, 'part', default=None)
1327                 ob = MimeXmlBinding(part)
1328                 self.items.append(ob)
1329                 continue
1330
1331             elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
1332                 encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
1333                 namespace = DOM.getAttr(e, 'namespace', default=None)
1334                 parts = DOM.getAttr(e, 'parts', default=None)
1335                 use = DOM.getAttr(e, 'use', default=None)
1336                 if use is None:
1337                     raise WSDLError(
1338                         'Invalid soap:body binding element.'
1339                         )
1340                 ob = SoapBodyBinding(use, namespace, encstyle, parts)
1341                 self.items.append(ob)
1342                 continue
1343
1344
1345 class WSDLError(Exception):
1346     pass
1347
1348
1349
1350 def DeclareNSPrefix(writer, prefix, nsuri):
1351     if writer.hasNSPrefix(nsuri):
1352         return
1353     writer.declareNSPrefix(prefix, nsuri)
1354
1355 def ParseTypeRef(value, element):
1356     parts = value.split(':', 1)
1357     if len(parts) == 1:
1358         return (DOM.findTargetNS(element), value)
1359     nsuri = DOM.findNamespaceURI(parts[0], element)
1360     return (nsuri, parts[1])
1361
1362 def ParseQName(value, element):
1363     nameref = value.split(':', 1)
1364     if len(nameref) == 2:
1365         nsuri = DOM.findNamespaceURI(nameref[0], element)
1366         name = nameref[-1]
1367     else:
1368         nsuri = DOM.findTargetNS(element)
1369         name  = nameref[-1]
1370     return nsuri, name
1371
1372 def GetDocumentation(element):
1373     docnode = DOM.getElement(element, 'documentation', None, None)
1374     if docnode is not None:
1375         return DOM.getElementText(docnode)
1376     return ''
1377
1378 def GetExtensions(element):
1379     return [ item for item in DOM.getElements(element, None, None)
1380         if item.namespaceURI != DOM.NS_WSDL ]
1381
1382 def GetWSAActionFault(operation, name):
1383     """Find wsa:Action attribute, and return value or WSA.FAULT
1384        for the default.
1385     """
1386     attr = operation.faults[name].action
1387     if attr is not None:
1388         return attr
1389     return WSA.FAULT
1390
1391 def GetWSAActionInput(operation):
1392     """Find wsa:Action attribute, and return value or the default."""
1393     attr = operation.input.action
1394     if attr is not None:
1395         return attr
1396     portType = operation.getPortType()
1397     targetNamespace = portType.getTargetNamespace()
1398     ptName = portType.name
1399     msgName = operation.input.name
1400     if not msgName:
1401         msgName = operation.name + 'Request'
1402     if targetNamespace.endswith('/'):
1403         return '%s%s/%s' %(targetNamespace, ptName, msgName)
1404     return '%s/%s/%s' %(targetNamespace, ptName, msgName)
1405
1406 def GetWSAActionOutput(operation):
1407     """Find wsa:Action attribute, and return value or the default."""
1408     attr = operation.output.action
1409     if attr is not None:
1410         return attr
1411     targetNamespace = operation.getPortType().getTargetNamespace()
1412     ptName = operation.getPortType().name
1413     msgName = operation.output.name
1414     if not msgName:
1415         msgName = operation.name + 'Response'
1416     if targetNamespace.endswith('/'):
1417         return '%s%s/%s' %(targetNamespace, ptName, msgName)
1418     return '%s/%s/%s' %(targetNamespace, ptName, msgName)
1419
1420 def FindExtensions(object, kind, t_type=type(())):
1421     if isinstance(kind, t_type):
1422         result = []
1423         namespaceURI, name = kind
1424         return [ item for item in object.extensions
1425                 if hasattr(item, 'nodeType') \
1426                 and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
1427                 and item.name == name ]
1428     return [ item for item in object.extensions if isinstance(item, kind) ]
1429
1430 def FindExtension(object, kind, t_type=type(())):
1431     if isinstance(kind, t_type):
1432         namespaceURI, name = kind
1433         for item in object.extensions:
1434             if hasattr(item, 'nodeType') \
1435             and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
1436             and item.name == name:
1437                 return item
1438     else:
1439         for item in object.extensions:
1440             if isinstance(item, kind):
1441                 return item
1442     return None
1443
1444
1445 class SOAPCallInfo:
1446     """SOAPCallInfo captures the important binding information about a 
1447        SOAP operation, in a structure that is easier to work with than
1448        raw WSDL structures."""
1449
1450     def __init__(self, methodName):
1451         self.methodName = methodName
1452         self.inheaders = []
1453         self.outheaders = []
1454         self.inparams = []
1455         self.outparams = []
1456         self.retval = None
1457
1458     encodingStyle = DOM.NS_SOAP_ENC
1459     documentation = ''
1460     soapAction = None
1461     transport = None
1462     namespace = None
1463     location = None
1464     use = 'encoded'
1465     style = 'rpc'
1466
1467     def addInParameter(self, name, type, namespace=None, element_type=0):
1468         """Add an input parameter description to the call info."""
1469         parameter = ParameterInfo(name, type, namespace, element_type)
1470         self.inparams.append(parameter)
1471         return parameter
1472
1473     def addOutParameter(self, name, type, namespace=None, element_type=0):
1474         """Add an output parameter description to the call info."""
1475         parameter = ParameterInfo(name, type, namespace, element_type)
1476         self.outparams.append(parameter)
1477         return parameter
1478
1479     def setReturnParameter(self, name, type, namespace=None, element_type=0):
1480         """Set the return parameter description for the call info."""
1481         parameter = ParameterInfo(name, type, namespace, element_type)
1482         self.retval = parameter
1483         return parameter
1484
1485     def addInHeaderInfo(self, name, type, namespace, element_type=0,
1486                         mustUnderstand=0):
1487         """Add an input SOAP header description to the call info."""
1488         headerinfo = HeaderInfo(name, type, namespace, element_type)
1489         if mustUnderstand:
1490             headerinfo.mustUnderstand = 1
1491         self.inheaders.append(headerinfo)
1492         return headerinfo
1493
1494     def addOutHeaderInfo(self, name, type, namespace, element_type=0,
1495                          mustUnderstand=0):
1496         """Add an output SOAP header description to the call info."""
1497         headerinfo = HeaderInfo(name, type, namespace, element_type)
1498         if mustUnderstand:
1499             headerinfo.mustUnderstand = 1
1500         self.outheaders.append(headerinfo)
1501         return headerinfo
1502
1503     def getInParameters(self):
1504         """Return a sequence of the in parameters of the method."""
1505         return self.inparams
1506
1507     def getOutParameters(self):
1508         """Return a sequence of the out parameters of the method."""
1509         return self.outparams
1510
1511     def getReturnParameter(self):
1512         """Return param info about the return value of the method."""
1513         return self.retval
1514
1515     def getInHeaders(self):
1516         """Return a sequence of the in headers of the method."""
1517         return self.inheaders
1518
1519     def getOutHeaders(self):
1520         """Return a sequence of the out headers of the method."""
1521         return self.outheaders
1522
1523
1524 class ParameterInfo:
1525     """A ParameterInfo object captures parameter binding information."""
1526     def __init__(self, name, type, namespace=None, element_type=0):
1527         if element_type:
1528             self.element_type = 1
1529         if namespace is not None:
1530             self.namespace = namespace
1531         self.name = name
1532         self.type = type
1533
1534     element_type = 0
1535     namespace = None
1536     default = None
1537
1538
1539 class HeaderInfo(ParameterInfo):
1540     """A HeaderInfo object captures SOAP header binding information."""
1541     def __init__(self, name, type, namespace, element_type=None):
1542         ParameterInfo.__init__(self, name, type, namespace, element_type)
1543
1544     mustUnderstand = 0
1545     actor = None
1546
1547
1548 def callInfoFromWSDL(port, name):
1549     """Return a SOAPCallInfo given a WSDL port and operation name."""
1550     wsdl = port.getService().getWSDL()
1551     binding = port.getBinding()
1552     portType = binding.getPortType()
1553     operation = portType.operations[name]
1554     opbinding = binding.operations[name]
1555     messages = wsdl.messages
1556     callinfo = SOAPCallInfo(name)
1557
1558     addrbinding = port.getAddressBinding()
1559     if not isinstance(addrbinding, SoapAddressBinding):
1560         raise ValueError, 'Unsupported binding type.'        
1561     callinfo.location = addrbinding.location
1562
1563     soapbinding = binding.findBinding(SoapBinding)
1564     if soapbinding is None:
1565         raise ValueError, 'Missing soap:binding element.'
1566     callinfo.transport = soapbinding.transport
1567     callinfo.style = soapbinding.style or 'document'
1568
1569     soap_op_binding = opbinding.findBinding(SoapOperationBinding)
1570     if soap_op_binding is not None:
1571         callinfo.soapAction = soap_op_binding.soapAction
1572         callinfo.style = soap_op_binding.style or callinfo.style
1573
1574     parameterOrder = operation.parameterOrder
1575
1576     if operation.input is not None:
1577         message = messages[operation.input.message]
1578         msgrole = opbinding.input
1579
1580         mime = msgrole.findBinding(MimeMultipartRelatedBinding)
1581         if mime is not None:
1582             raise ValueError, 'Mime bindings are not supported.'
1583         else:
1584             for item in msgrole.findBindings(SoapHeaderBinding):
1585                 part = messages[item.message].parts[item.part]
1586                 header = callinfo.addInHeaderInfo(
1587                     part.name,
1588                     part.element or part.type,
1589                     item.namespace,
1590                     element_type = part.element and 1 or 0
1591                     )
1592                 header.encodingStyle = item.encodingStyle
1593
1594             body = msgrole.findBinding(SoapBodyBinding)
1595             if body is None:
1596                 raise ValueError, 'Missing soap:body binding.'
1597             callinfo.encodingStyle = body.encodingStyle
1598             callinfo.namespace = body.namespace
1599             callinfo.use = body.use
1600
1601             if body.parts is not None:
1602                 parts = []
1603                 for name in body.parts:
1604                     parts.append(message.parts[name])
1605             else:
1606                 parts = message.parts.values()
1607
1608             for part in parts:
1609                 callinfo.addInParameter(
1610                     part.name,
1611                     part.element or part.type,
1612                     element_type = part.element and 1 or 0
1613                     )
1614
1615     if operation.output is not None:
1616         try:
1617             message = messages[operation.output.message]
1618         except KeyError:
1619             if self.strict:
1620                 raise RuntimeError(
1621                     "Recieved message not defined in the WSDL schema: %s" %
1622                     operation.output.message)
1623             else:
1624                 message = wsdl.addMessage(operation.output.message)
1625                 print "Warning:", \
1626                       "Recieved message not defined in the WSDL schema.", \
1627                       "Adding it."
1628                 print "Message:", operation.output.message
1629          
1630         msgrole = opbinding.output
1631
1632         mime = msgrole.findBinding(MimeMultipartRelatedBinding)
1633         if mime is not None:
1634             raise ValueError, 'Mime bindings are not supported.'
1635         else:
1636             for item in msgrole.findBindings(SoapHeaderBinding):
1637                 part = messages[item.message].parts[item.part]
1638                 header = callinfo.addOutHeaderInfo(
1639                     part.name,
1640                     part.element or part.type,
1641                     item.namespace,
1642                     element_type = part.element and 1 or 0
1643                     )
1644                 header.encodingStyle = item.encodingStyle
1645
1646             body = msgrole.findBinding(SoapBodyBinding)
1647             if body is None:
1648                 raise ValueError, 'Missing soap:body binding.'
1649             callinfo.encodingStyle = body.encodingStyle
1650             callinfo.namespace = body.namespace
1651             callinfo.use = body.use
1652
1653             if body.parts is not None:
1654                 parts = []
1655                 for name in body.parts:
1656                     parts.append(message.parts[name])
1657             else:
1658                 parts = message.parts.values()
1659
1660             if parts:
1661                 for part in parts:
1662                     callinfo.addOutParameter(
1663                         part.name,
1664                         part.element or part.type,
1665                         element_type = part.element and 1 or 0
1666                         )
1667
1668     return callinfo