1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """ 
 18  The I{schema} module provides a intelligent representation of 
 19  an XSD schema.  The I{raw} model is the XML tree and the I{model} 
 20  is the denormalized, objectified and intelligent view of the schema. 
 21  Most of the I{value-add} provided by the model is centered around 
 22  tranparent referenced type resolution and targeted denormalization. 
 23  """ 
 24   
 25   
 26  import suds.metrics 
 27  from suds import * 
 28  from suds.xsd import * 
 29  from suds.xsd.sxbuiltin import * 
 30  from suds.xsd.sxbasic import Factory as BasicFactory 
 31  from suds.xsd.sxbuiltin import Factory as BuiltinFactory 
 32  from suds.xsd.sxbase import SchemaObject 
 33  from suds.xsd.deplist import DepList 
 34  from suds.sax.element import Element 
 35  from suds.sax import splitPrefix, Namespace 
 36  from logging import getLogger 
 37   
 38  log = getLogger(__name__) 
 39   
 40   
 42      """ 
 43      A collection of schema objects.  This class is needed because WSDLs  
 44      may contain more then one <schema/> node. 
 45      @ivar wsdl: A wsdl object. 
 46      @type wsdl: L{suds.wsdl.Definitions} 
 47      @ivar children: A list contained schemas. 
 48      @type children: [L{Schema},...] 
 49      @ivar namespaces: A dictionary of contained schemas by namespace. 
 50      @type namespaces: {str:L{Schema}} 
 51      """ 
 52       
 54          """ 
 55          @param wsdl: A wsdl object. 
 56          @type wsdl: L{suds.wsdl.Definitions} 
 57          """ 
 58          self.wsdl = wsdl 
 59          self.children = [] 
 60          self.namespaces = {} 
  61           
 62 -    def add(self, schema): 
  63          """ 
 64          Add a schema node to the collection.  Schema(s) within the same target 
 65          namespace are consolidated. 
 66          @param schema: A schema object. 
 67          @type schema: (L{Schema}) 
 68          """ 
 69          key = schema.tns[1] 
 70          existing = self.namespaces.get(key) 
 71          if existing is None: 
 72              self.children.append(schema) 
 73              self.namespaces[key] = schema 
 74          else: 
 75              existing.root.children += schema.root.children 
 76              existing.root.nsprefixes.update(schema.root.nsprefixes) 
  77           
 78 -    def load(self, options): 
  79          """ 
 80          Load the schema objects for the root nodes. 
 81              - de-references schemas 
 82              - merge schemas 
 83          @param options: An options dictionary. 
 84          @type options: L{options.Options} 
 85          @return: The merged schema. 
 86          @rtype: L{Schema} 
 87          """ 
 88          if options.autoblend: 
 89              self.autoblend() 
 90          for child in self.children: 
 91              child.build() 
 92          for child in self.children: 
 93              child.open_imports(options) 
 94          for child in self.children: 
 95              child.dereference() 
 96          log.debug('loaded:\n%s', self) 
 97          merged = self.merge() 
 98          log.debug('MERGED:\n%s', merged) 
 99          return merged 
 100           
102          """ 
103          Ensure that all schemas within the collection 
104          import each other which has a blending effect. 
105          @return: self 
106          @rtype: L{SchemaCollection} 
107          """ 
108          namespaces = self.namespaces.keys() 
109          for s in self.children: 
110              for ns in namespaces: 
111                  tns = s.root.get('targetNamespace') 
112                  if  tns == ns: 
113                      continue 
114                  for imp in s.root.getChildren('import'): 
115                      if imp.get('namespace') == ns: 
116                          continue 
117                  imp = Element('import', ns=Namespace.xsdns) 
118                  imp.set('namespace', ns) 
119                  s.root.append(imp) 
120          return self 
 121           
123          """ 
124          Find a schema by namespace.  Only the URI portion of 
125          the namespace is compared to each schema's I{targetNamespace} 
126          @param ns: A namespace. 
127          @type ns: (prefix,URI) 
128          @return: The schema matching the namesapce, else None. 
129          @rtype: L{Schema} 
130          """ 
131          return self.namespaces.get(ns[1]) 
 132       
134          """ 
135          Merge the contained schemas into one. 
136          @return: The merged schema. 
137          @rtype: L{Schema} 
138          """ 
139          if len(self): 
140              schema = self.children[0] 
141              for s in self.children[1:]: 
142                  schema.merge(s) 
143              return schema 
144          else: 
145              return None 
 146       
149       
151          return unicode(self).encode('utf-8') 
 152       
 158   
159   
161      """ 
162      The schema is an objectification of a <schema/> (xsd) definition. 
163      It provides inspection, lookup and type resolution. 
164      @ivar root: The root node. 
165      @type root: L{sax.element.Element} 
166      @ivar baseurl: The I{base} URL for this schema. 
167      @type baseurl: str 
168      @ivar container: A schema collection containing this schema. 
169      @type container: L{SchemaCollection} 
170      @ivar children: A list of direct top level children. 
171      @type children: [L{SchemaObject},...] 
172      @ivar all: A list of all (includes imported) top level children. 
173      @type all: [L{SchemaObject},...] 
174      @ivar types: A schema types cache. 
175      @type types: {name:L{SchemaObject}} 
176      @ivar imports: A list of import objects. 
177      @type imports: [L{SchemaObject},...] 
178      @ivar elements: A list of <element/> objects. 
179      @type elements: [L{SchemaObject},...] 
180      @ivar attributes: A list of <attribute/> objects. 
181      @type attributes: [L{SchemaObject},...] 
182      @ivar groups: A list of group objects. 
183      @type groups: [L{SchemaObject},...] 
184      @ivar agrps: A list of attribute group objects. 
185      @type agrps: [L{SchemaObject},...] 
186      @ivar form_qualified: The flag indicating: 
187          (@elementFormDefault). 
188      @type form_qualified: bool 
189      """ 
190       
191      Tag = 'schema' 
192       
193 -    def __init__(self, root, baseurl, options, container=None): 
 194          """ 
195          @param root: The xml root. 
196          @type root: L{sax.element.Element} 
197          @param baseurl: The base url used for importing. 
198          @type baseurl: basestring 
199          @param options: An options dictionary. 
200          @type options: L{options.Options} 
201          @param container: An optional container. 
202          @type container: L{SchemaCollection} 
203          """ 
204          self.root = root 
205          self.id = objid(self) 
206          self.tns = self.mktns() 
207          self.baseurl = baseurl 
208          self.container = container 
209          self.children = [] 
210          self.all = [] 
211          self.types = {} 
212          self.imports = [] 
213          self.elements = {} 
214          self.attributes = {} 
215          self.groups = {} 
216          self.agrps = {} 
217          if options.doctor is not None: 
218              options.doctor.examine(root) 
219          form = self.root.get('elementFormDefault') 
220          if form is None: 
221              self.form_qualified = False 
222          else: 
223              self.form_qualified = ( form == 'qualified' ) 
224          if container is None: 
225              self.build() 
226              self.open_imports(options) 
227              log.debug('built:\n%s', self) 
228              self.dereference() 
229              log.debug('dereferenced:\n%s', self) 
 230                   
232          """ 
233          Make the schema's target namespace. 
234          @return: The namespace representation of the schema's 
235              targetNamespace value. 
236          @rtype: (prefix, uri) 
237          """ 
238          tns = [None, self.root.get('targetNamespace')] 
239          if tns[1] is not None: 
240              tns[0] = self.root.findPrefix(tns[1]) 
241          return tuple(tns) 
 242                   
244          """ 
245          Build the schema (object graph) using the root node 
246          using the factory. 
247              - Build the graph. 
248              - Collate the children. 
249          """ 
250          self.children = BasicFactory.build(self.root, self) 
251          collated = BasicFactory.collate(self.children) 
252          self.children = collated[0] 
253          self.attributes = collated[2] 
254          self.imports = collated[1] 
255          self.elements = collated[3] 
256          self.types = collated[4] 
257          self.groups = collated[5] 
258          self.agrps = collated[6] 
 259           
260 -    def merge(self, schema): 
 261          """ 
262          Merge the contents from the schema.  Only objects not already contained 
263          in this schema's collections are merged.  This is to provide for bidirectional 
264          import which produce cyclic includes. 
265          @returns: self 
266          @rtype: L{Schema}  
267          """ 
268          for item in schema.attributes.items(): 
269              if item[0] in self.attributes: 
270                  continue 
271              self.all.append(item[1]) 
272              self.attributes[item[0]] = item[1] 
273          for item in schema.elements.items(): 
274              if item[0] in self.elements: 
275                  continue 
276              self.all.append(item[1]) 
277              self.elements[item[0]] = item[1] 
278          for item in schema.types.items(): 
279              if item[0] in self.types: 
280                  continue 
281              self.all.append(item[1]) 
282              self.types[item[0]] = item[1] 
283          for item in schema.groups.items(): 
284              if item[0] in self.groups: 
285                  continue 
286              self.all.append(item[1]) 
287              self.groups[item[0]] = item[1] 
288          for item in schema.agrps.items(): 
289              if item[0] in self.agrps: 
290                  continue 
291              self.all.append(item[1]) 
292              self.agrps[item[0]] = item[1] 
293          schema.merged = True 
294          return self 
 295           
297          """ 
298          Instruct all contained L{sxbasic.Import} children to import 
299          the schema's which they reference.  The contents of the 
300          imported schema are I{merged} in. 
301          @param options: An options dictionary. 
302          @type options: L{options.Options} 
303          """ 
304          for imp in self.imports: 
305              imported = imp.open(options) 
306              if imported is None: 
307                  continue 
308              imported.open_imports(options) 
309              log.debug('imported:\n%s', imported) 
310              self.merge(imported) 
 311               
313          """ 
314          Instruct all children to perform dereferencing. 
315          """ 
316          all = [] 
317          indexes = {} 
318          for child in self.children: 
319              child.content(all) 
320          deplist = DepList() 
321          for x in all: 
322              x.qualify() 
323              midx, deps = x.dependencies() 
324              item = (x, tuple(deps)) 
325              deplist.add(item) 
326              indexes[x] = midx 
327          for x, deps in deplist.sort(): 
328              midx = indexes.get(x) 
329              if midx is None: continue 
330              d = deps[midx] 
331              log.debug('(%s) merging %s <== %s', self.tns[1], Repr(x), Repr(d)) 
332              x.merge(d) 
 333           
335          """ 
336          Find a schema by namespace.  Only the URI portion of 
337          the namespace is compared to each schema's I{targetNamespace}. 
338          The request is passed to the container. 
339          @param ns: A namespace. 
340          @type ns: (prefix,URI) 
341          @return: The schema matching the namesapce, else None. 
342          @rtype: L{Schema} 
343          """ 
344          if self.container is not None: 
345              return self.container.locate(ns) 
346          else: 
347              return None 
 348   
349 -    def custom(self, ref, context=None): 
 350          """ 
351          Get whether the specified reference is B{not} an (xs) builtin. 
352          @param ref: A str or qref. 
353          @type ref: (str|qref) 
354          @return: True if B{not} a builtin, else False. 
355          @rtype: bool  
356          """ 
357          if ref is None: 
358              return True 
359          else: 
360              return ( not self.builtin(ref, context) ) 
 361       
362 -    def builtin(self, ref, context=None): 
 363          """ 
364          Get whether the specified reference is an (xs) builtin. 
365          @param ref: A str or qref. 
366          @type ref: (str|qref) 
367          @return: True if builtin, else False. 
368          @rtype: bool  
369          """ 
370          w3 = 'http://www.w3.org' 
371          try: 
372              if isqref(ref): 
373                  ns = ref[1] 
374                  return ( ref[0] in Factory.tags and ns.startswith(w3) ) 
375              if context is None: 
376                  context = self.root     
377              prefix = splitPrefix(ref)[0] 
378              prefixes = context.findPrefixes(w3, 'startswith') 
379              return ( prefix in prefixes and ref[0] in Factory.tags ) 
380          except: 
381              return False 
 382           
383 -    def instance(self, root, baseurl, options): 
 384          """ 
385          Create and return an new schema object using the 
386          specified I{root} and I{url}. 
387          @param root: A schema root node. 
388          @type root: L{sax.element.Element} 
389          @param baseurl: A base URL. 
390          @type baseurl: str 
391          @param options: An options dictionary. 
392          @type options: L{options.Options} 
393          @return: The newly created schema object. 
394          @rtype: L{Schema} 
395          @note: This is only used by Import children. 
396          """ 
397          return Schema(root, baseurl, options) 
 398   
399 -    def str(self, indent=0): 
 410           
412          myrep = '<%s tns="%s"/>' % (self.id, self.tns[1]) 
413          return myrep.encode('utf-8') 
 414       
416          return unicode(self).encode('utf-8') 
 417       
 420