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