From b3575379e46d74a1fec5f5f77e186e20c8e4704c Mon Sep 17 00:00:00 2001 From: Joshua Boverhof Date: Thu, 11 Mar 2004 19:14:32 +0000 Subject: [PATCH] ---------------------------------------------------------------------- Modified Files: Utility.py -- added a CollectionNS class that keys items via (targetNamespace, name). WSDLTools.py -- Made WSDL Collections into CollectionNS instances, this fixes problem with collisions, caused by wsdl:imports, between items with same name but defined in different targetNamespaces. So now all items can be accessed via (namespace,name), but ONLY those items defined in WSDL.targetNamepsace (not an import.targetNamespace) can be accessed using just 'name'. Also changed how portType is "loaded". Now instead of dropping all the operation nodes in "load", I drop the portType node into "load". This makes sense because portType really should know about itself, and the XML Schema definition of "portType" includes an "anyAttribute" and I need to make this stuff available. I may change the other WSDL information items to do this to be consistent. ---------------------------------------------------------------------- --- Utility.py | 52 +++++++++++++++++++++++++++++++++++ WSDLTools.py | 76 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 111 insertions(+), 17 deletions(-) diff --git a/Utility.py b/Utility.py index 00ef014..a62a6ae 100755 --- a/Utility.py +++ b/Utility.py @@ -9,6 +9,7 @@ ident = "$Id$" +import types from string import join, strip, split from UserDict import UserDict from StringIO import StringIO @@ -421,6 +422,14 @@ class DOM: return default return '' + def getAttrs(self, node): + """Return a Collection of all attributes + """ + attrs = {} + for k,v in node._attrs.items(): + attrs[k] = v.value + return attrs + def getElementText(self, node, preserve_ws=None): """Return the text value of an xml element node. Leading and trailing whitespace is stripped from the value unless the preserve_ws flag @@ -584,6 +593,49 @@ class Collection(UserDict): return self.list +class CollectionNS(UserDict): + """Helper class for maintaining ordered named collections.""" + default = lambda self,k: k.name + def __init__(self, parent, key=None): + UserDict.__init__(self) + self.parent = weakref.ref(parent) + self.targetNamespace = None + self.list = [] + self._func = key or self.default + + def __getitem__(self, key): + self.targetNamespace = self.parent().targetNamespace + if type(key) is types.IntType: + return self.list[key] + elif self.__isSequence(key): + nsuri,name = key + return self.data[nsuri][name] + return self.data[self.parent().targetNamespace][key] + + def __setitem__(self, key, item): + item.parent = weakref.ref(self) + self.list.append(item) + targetNamespace = getattr(item, 'targetNamespace', self.parent().targetNamespace) + if not self.data.has_key(targetNamespace): + self.data[targetNamespace] = {} + self.data[targetNamespace][key] = item + + def __isSequence(self, key): + return (type(key) in (types.TupleType,types.ListType) and len(key) == 2) + + def keys(self): + keys = [] + for tns in self.data.keys(): + keys.append(map(lambda i: (tns,self._func(i)), self.data[tns].values())) + return keys + + def items(self): + return map(lambda i: (self._func(i), i), self.list) + + def values(self): + return self.list + + # This is a runtime guerilla patch for pulldom (used by minidom) so # that xml namespace declaration attributes are not lost in parsing. diff --git a/WSDLTools.py b/WSDLTools.py index 72d1cce..e086610 100755 --- a/WSDLTools.py +++ b/WSDLTools.py @@ -9,7 +9,7 @@ ident = "$Id$" -from Utility import DOM, Collection +from Utility import DOM, Collection, CollectionNS from XMLSchema import XMLSchema, SchemaReader, WSDLToolsAdapter from StringIO import StringIO import urllib @@ -64,10 +64,10 @@ class WSDL: self.location = None self.document = None self.name = None - self.services = Collection(self) - self.messages = Collection(self) - self.portTypes = Collection(self) - self.bindings = Collection(self) + self.services = CollectionNS(self) + self.messages = CollectionNS(self) + self.portTypes = CollectionNS(self) + self.bindings = CollectionNS(self) self.imports = Collection(self) self.types = Types(self) self.extensions = [] @@ -80,39 +80,47 @@ class WSDL: version = '1.1' - def addService(self, name, documentation=''): + def addService(self, name, documentation='', targetNamespace=None): if self.services.has_key(name): raise WSDLError( 'Duplicate service element: %s' % name ) item = Service(name, documentation) + if targetNamespace: + item.targetNamespace = targetNamespace self.services[name] = item return item - def addMessage(self, name, documentation=''): + def addMessage(self, name, documentation='', targetNamespace=None): if self.messages.has_key(name): raise WSDLError( 'Duplicate message element: %s.' % name ) item = Message(name, documentation) + if targetNamespace: + item.targetNamespace = targetNamespace self.messages[name] = item return item - def addPortType(self, name, documentation=''): + def addPortType(self, name, documentation='', targetNamespace=None): if self.portTypes.has_key(name): raise WSDLError( 'Duplicate portType element: name' ) item = PortType(name, documentation) + if targetNamespace: + item.targetNamespace = targetNamespace self.portTypes[name] = item return item - def addBinding(self, name, type, documentation=''): + def addBinding(self, name, type, documentation='', targetNamespace=None): if self.bindings.has_key(name): raise WSDLError( 'Duplicate binding element: %s' % name ) item = Binding(name, type, documentation) + if targetNamespace: + item.targetNamespace = targetNamespace self.bindings[name] = item return item @@ -158,6 +166,7 @@ class WSDL: reader = SchemaReader(base_url=self.location) for element in DOM.getElements(definitions, None, None): + targetNamespace = DOM.getAttr(element, 'targetNamespace') localName = element.localName if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL): @@ -172,7 +181,7 @@ class WSDL: elif localName == 'message': name = DOM.getAttr(element, 'name') docs = GetDocumentation(element) - message = self.addMessage(name, docs) + message = self.addMessage(name, docs, targetNamespace) parts = DOM.getElements(element, 'part', NS_WSDL) message.load(parts) continue @@ -180,9 +189,10 @@ class WSDL: elif localName == 'portType': name = DOM.getAttr(element, 'name') docs = GetDocumentation(element) - ptype = self.addPortType(name, docs) - operations = DOM.getElements(element, 'operation', NS_WSDL) - ptype.load(operations) + ptype = self.addPortType(name, docs, targetNamespace) + #operations = DOM.getElements(element, 'operation', NS_WSDL) + #ptype.load(operations) + ptype.load(element) continue elif localName == 'binding': @@ -194,7 +204,7 @@ class WSDL: ) type = type.split(':', 1)[-1] docs = GetDocumentation(element) - binding = self.addBinding(name, type, docs) + binding = self.addBinding(name, type, docs, targetNamespace) operations = DOM.getElements(element, 'operation', NS_WSDL) binding.load(operations) binding.load_ex(GetExtensions(element)) @@ -203,7 +213,7 @@ class WSDL: elif localName == 'service': name = DOM.getAttr(element, 'name') docs = GetDocumentation(element) - service = self.addService(name, docs) + service = self.addService(name, docs, targetNamespace) ports = DOM.getElements(element, 'port', NS_WSDL) service.load(ports) service.load_ex(GetExtensions(element)) @@ -350,11 +360,37 @@ class MessagePart(Element): self.element = None self.type = None + def getWSDL(self): + """Return the WSDL object that contains this Message Part.""" + return self.parent().parent().parent().parent() + + def getTypeDefinition(self): + wsdl = self.getWSDL() + nsuri,name = self.type + schema = wsdl.types.get(nsuri, {}) + return schema.get(name) + + def getElementDeclaration(self): + wsdl = self.getWSDL() + nsuri,name = self.element + schema = wsdl.types.get(nsuri, {}) + return schema.get(name) + class PortType(Element): + '''PortType has a anyAttribute, thus must provide for an extensible + mechanism for supporting such attributes. + + Instance Data: + operations -- + attributes -- + + ''' + def __init__(self, name, documentation=''): Element.__init__(self, name, documentation) self.operations = Collection(self) + self.attributes = {} def getWSDL(self): return self.parent().parent() @@ -364,7 +400,13 @@ class PortType(Element): self.operations[name] = item return item - def load(self, elements): + def load(self, element): + self.name = DOM.getAttr(element, 'name') + self.documentation = GetDocumentation(element) + self.attributes.update(DOM.getAttrs(element)) + + NS_WSDL = DOM.GetWSDLUri(self.getWSDL().version) + elements = DOM.getElements(element, 'operation', NS_WSDL) for element in elements: name = DOM.getAttr(element, 'name') docs = GetDocumentation(element) @@ -424,7 +466,7 @@ class Operation(Element): wsdl = self.getPortType().getWSDL() return wsdl.messages[self.faults[name].message] - def addFault(self, name, message, documentation=''): + def addFault(self, message, name, documentation=''): if self.faults.has_key(name): raise WSDLError( 'Duplicate fault element: %s' % name