source: trunk/suds/xsd/sxbase.py @ 462

Revision 462, 15.5 KB checked in by jortel, 5 years ago (diff)

fix ticket 208, further enhance processing for document/literal bare

Line 
1# This program is free software; you can redistribute it and/or modify
2# it under the terms of the (LGPL) GNU Lesser General Public License as
3# published by the Free Software Foundation; either version 3 of the
4# License, or (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9# GNU Library Lesser General Public License for more details at
10# ( http://www.gnu.org/licenses/lgpl.html ).
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software
14# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15# written by: Jeff Ortel ( jortel@redhat.com )
16
17"""
18The I{sxbase} module provides I{base} classes that represent
19schema objects.
20"""
21
22from logging import getLogger
23from suds import *
24from suds.xsd import *
25from suds.sax.element import Element
26
27log = getLogger(__name__)
28
29
30class SchemaObject:
31    """
32    A schema object is an extension to object object with
33    with schema awareness.
34    @ivar root: The XML root element.
35    @type root: L{Element}
36    @ivar schema: The schema containing this object.
37    @type schema: L{schema.Schema}
38    @ivar form_qualified: A flag that inidcates that @elementFormDefault
39        has a value of I{qualified}.
40    @type form_qualified: boolean
41    @ivar nillable: A flag that inidcates that @nillable
42        has a value of I{true}.
43    @type nillable: boolean
44    @ivar rawchildren: A list raw of all children.
45    @type rawchildren: [L{SchemaObject},...]
46    """
47
48    @classmethod
49    def prepend(cls, d, s, filter=Filter()):
50        """
51        Prepend schema object's from B{s}ource list to
52        the B{d}estination list while applying the filter.
53        @param d: The destination list.
54        @type d: list
55        @param s: The source list.
56        @type s: list
57        @param filter: A filter that allows items to be prepended.
58        @type filter: L{Filter}
59        """
60        i = 0
61        for x in s:
62            if x in filter:
63                d.insert(i, x)
64                i += 1
65   
66    @classmethod
67    def append(cls, d, s, filter=Filter()):
68        """
69        Append schema object's from B{s}ource list to
70        the B{d}estination list while applying the filter.
71        @param d: The destination list.
72        @type d: list
73        @param s: The source list.
74        @type s: list
75        @param filter: A filter that allows items to be appended.
76        @type filter: L{Filter}
77        """
78        for item in s:
79            if item in filter:
80                d.append(item)
81
82    def __init__(self, schema, root):
83        """
84        @param schema: The containing schema.
85        @type schema: L{schema.Schema}
86        @param root: The xml root node.
87        @type root: L{Element}
88        """
89        self.schema = schema
90        self.root = root
91        self.id = objid(self)
92        self.name = root.get('name')
93        self.qname = (self.name, schema.tns[1])
94        self.type = root.get('type')
95        self.ref = root.get('ref')
96        self.form_qualified = schema.form_qualified
97        self.nillable = False
98        self.rawchildren = []
99        self.cache = {}
100       
101    def attributes(self, filter=Filter()):
102        """
103        Get only the attribute content.
104        @param filter: A filter to constrain the result.
105        @type filter: L{Filter}
106        @return: A list of tuples (attr, ancestry)
107        @rtype: [(L{SchemaObject}, [L{SchemaObject},..]),..]
108        """
109        result = []
110        for child, ancestry in self:
111            if child.isattr() and child in filter:
112                result.append((child, ancestry))
113        return result
114               
115    def children(self, filter=Filter()):
116        """
117        Get only the I{direct} or non-attribute content.
118        @param filter: A filter to constrain the result.
119        @type filter: L{Filter}
120        @return: A list tuples: (child, ancestry)
121        @rtype: [(L{SchemaObject}, [L{SchemaObject},..]),..]
122        """
123        result = []
124        for child, ancestry in self:
125            if not child.isattr() and child in filter:
126                result.append((child, ancestry))
127        return result
128               
129    def get_attribute(self, name):
130        """
131        Get (find) a I{non-attribute} attribute by name.
132        @param name: A attribute name.
133        @type name: str
134        @return: A tuple: the requested (attribute, ancestry).
135        @rtype: (L{SchemaObject}, [L{SchemaObject},..])
136        """
137        for child, ancestry in self.attributes():
138            if child.name == name:
139                return (child, ancestry)
140        return (None, [])
141               
142    def get_child(self, name):
143        """
144        Get (find) a I{non-attribute} child by name.
145        @param name: A child name.
146        @type name: str
147        @return: A tuple: the requested (child, ancestry).
148        @rtype: (L{SchemaObject}, [L{SchemaObject},..])
149        """
150        for child, ancestry in self.children():
151            if child.any() or child.name == name:
152                return (child, ancestry)
153        return (None, [])
154
155    def namespace(self, prefix=None):
156        """
157        Get this properties namespace
158        @param prefix: The default prefix.
159        @type prefix: str
160        @return: The schema's target namespace
161        @rtype: (I{prefix},I{URI})
162        """
163        ns = self.schema.tns
164        if ns[0] is None:
165            ns = (prefix, ns[1])
166        return ns
167   
168    def default_namespace(self):
169        return self.root.defaultNamespace()
170   
171    def unbounded(self):
172        """
173        Get whether this node is unbounded I{(a collection)}
174        @return: True if unbounded, else False.
175        @rtype: boolean
176        """
177        return False
178   
179    def optional(self):
180        """
181        Get whether this type is optional.
182        @return: True if optional, else False
183        @rtype: boolean
184        """
185        return False
186   
187    def resolve(self, nobuiltin=False):
188        """
189        Resolve and return the nodes true self.
190        @param nobuiltin: Flag indicates that resolution must
191            not continue to include xsd builtins.
192        @return: The resolved (true) type.
193        @rtype: L{SchemaObject}
194        """
195        return self.cache.get(nobuiltin, self)
196   
197    def sequence(self):
198        """
199        Get whether this is an <xs:sequence/>
200        @return: True if any, else False
201        @rtype: boolean
202        """
203        return False
204   
205    def all(self):
206        """
207        Get whether this is an <xs:all/>
208        @return: True if any, else False
209        @rtype: boolean
210        """
211        return False
212   
213    def choice(self):
214        """
215        Get whether this is n <xs:choice/>
216        @return: True if any, else False
217        @rtype: boolean
218        """
219        return False
220       
221    def any(self):
222        """
223        Get whether this is an <xs:any/>
224        @return: True if any, else False
225        @rtype: boolean
226        """
227        return False
228   
229    def builtin(self):
230        """
231        Get whether this is a schema-instance (xs) type.
232        @return: True if any, else False
233        @rtype: boolean
234        """
235        return False
236   
237    def enum(self):
238        """
239        Get whether this is a simple-type containing an enumeration.
240        @return: True if any, else False
241        @rtype: boolean
242        """
243        return False
244   
245    def isattr(self):
246        """
247        Get whether the object is a schema I{attribute} definition.
248        @return: True if an attribute, else False.
249        @rtype: boolean
250        """
251        return False
252   
253    def extension(self):
254        """
255        Get whether the object is an extension/restriction
256        @return: True if an extension/restriction, else False.
257        @rtype: boolean
258        """
259        return False
260       
261    def find(self, qref, classes=()):
262        """
263        Find a referenced type in self or children.
264        @param qref: A qualified reference.
265        @type qref: qref
266        @param classes: A list of classes used to qualify the match.
267        @type classes: [I{class},...]
268        @return: The referenced type.
269        @rtype: L{SchemaObject}
270        @see: L{qualify()}
271        """
272        if not len(classes):
273            classes = (self.__class__,)
274        if self.qname == qref and self.__class__ in classes:
275            return self
276        for c in self.rawchildren:
277            p = c.find(qref, classes)
278            if p is not None:
279                return p
280        return None
281
282    def translate(self, value, topython=True):
283        """
284        Translate a value (type) to/from a python type.
285        @param value: A value to translate.
286        @return: The converted I{language} type.
287        """
288        return value
289   
290    def childtags(self):
291        """
292        Get a list of valid child tag names.
293        @return: A list of child tag names.
294        @rtype: [str,...]
295        """
296        return ()
297   
298    def dependencies(self):
299        """
300        Get a list of dependancies for dereferencing.
301        @return: A merge dependancy index and a list of dependancies.
302        @rtype: (int, [L{SchemaObject},...])
303        """
304        return (None, [])
305           
306    def merge(self, other):
307        """
308        Merge another object as needed.
309        """
310        pass
311           
312    def content(self, collection=None, filter=Filter(), history=None):
313        """
314        Get a I{flattened} list of this nodes contents.
315        @param collection: A list to fill.
316        @type collection: list
317        @param filter: A filter used to constrain the result.
318        @type filter: L{Filter}
319        @param history: The history list used to prevent cyclic dependency.
320        @type history: list
321        @return: The filled list.
322        @rtype: list
323        """
324        if collection is None:
325            collection = []
326        if history is None:
327            history = []
328        if self in history:
329            return collection
330        history.append(self)
331        if self in filter:
332            collection.append(self)
333        for c in self.rawchildren:
334            c.content(collection, filter, history[:])
335        return collection
336   
337    def str(self, indent=0, history=None):
338        """
339        Get a string representation of this object.
340        @param indent: The indent.
341        @type indent: int
342        @return: A string.
343        @rtype: str
344        """
345        if history is None: 
346            history = []
347        if self in history:
348            return '%s ...' % Repr(self)
349        history.append(self)
350        tab = '%*s'%(indent*3, '')
351        result  = []
352        result.append('%s<%s' % (tab, self.id))
353        for n in self.description():
354            if not hasattr(self, n):
355                continue
356            v = getattr(self, n)
357            if v is None:
358                continue
359            result.append(' %s="%s"' % (n, v))
360        if len(self):
361            result.append('>')
362            for c in self.rawchildren:
363                result.append('\n')
364                result.append(c.str(indent+1, history[:]))
365                if c.isattr():
366                    result.append('@')
367            result.append('\n%s' % tab)
368            result.append('</%s>' % self.__class__.__name__)
369        else:
370            result.append(' />')
371        return ''.join(result)
372   
373    def description(self):
374        """
375        Get the names used for str() and repr() description.
376        @return:  A dictionary of relavent attributes.
377        @rtype: [str,...]
378        """
379        return ()
380       
381    def __str__(self):
382        return unicode(self).encode('utf-8')
383           
384    def __unicode__(self):
385        return unicode(self.str())
386   
387    def __repr__(self):
388        s = []
389        s.append('<%s' % self.id)
390        for n in self.description():
391            if not hasattr(self, n):
392                continue
393            v = getattr(self, n)
394            if v is None:
395                continue
396            s.append(' %s="%s"' % (n, v))
397        s.append(' />')
398        myrep = ''.join(s)
399        return myrep.encode('utf-8')
400   
401    def __len__(self):
402        n = 0
403        for x in self: n += 1
404        return n
405   
406    def __iter__(self):
407        return Iter(self)
408   
409    def __getitem__(self, index):
410        i = 0
411        for c in self:
412            if i == index:
413                return c
414
415
416class Iter:
417    """
418    The content iterator - used to iterate the L{Content} children.  The iterator
419    provides a I{view} of the children that is free of container elements
420    such as <sequence/> and <choice/>.
421    @ivar stack: A stack used to control nesting.
422    @type stack: list
423    """
424   
425    class Frame:
426        """ A content iterator frame. """
427       
428        def __init__(self, sx):
429            """
430            @param sx: A schema object.
431            @type sx: L{SchemaObject}
432            """
433            self.sx = sx
434            self.items = sx.rawchildren
435            self.index = 0
436           
437        def next(self):
438            """
439            Get the I{next} item in the frame's collection.
440            @return: The next item or None
441            @rtype: L{SchemaObject}
442            """
443            if self.index < len(self.items):
444                result = self.items[self.index]
445                self.index += 1
446                return result
447   
448    def __init__(self, sx):
449        """
450        @param sx: A schema object.
451        @type sx: L{SchemaObject}
452        """
453        self.stack = []
454        self.push(sx)
455       
456    def push(self, sx):
457        """
458        Create a frame and push the specified object.
459        @param sx: A schema object to push.
460        @type sx: L{SchemaObject}
461        """
462        self.stack.append(Iter.Frame(sx))
463       
464    def pop(self):
465        """
466        Pop the I{top} frame.
467        @return: The popped frame.
468        @rtype: L{Frame}
469        @raise StopIteration: when stack is empty.
470        """
471        if len(self.stack):
472            return self.stack.pop()
473        else:
474            raise StopIteration()
475       
476    def top(self):
477        """
478        Get the I{top} frame.
479        @return: The top frame.
480        @rtype: L{Frame}
481        @raise StopIteration: when stack is empty.
482        """
483        if len(self.stack):
484            return self.stack[-1]
485        else:
486            raise StopIteration()
487   
488    def next(self):
489        """
490        Get the next item.
491        @return: A tuple: the next (child, ancestry).
492        @rtype: (L{SchemaObject}, [L{SchemaObject},..])
493        @raise StopIteration: A the end.
494        """
495        frame = self.top()
496        while True:
497            result = frame.next()
498            if result is None:
499                self.pop()
500                return self.next()
501            if isinstance(result, Content):
502                ancestry = [f.sx for f in self.stack]
503                return (result, ancestry)
504            self.push(result)
505            return self.next()
506   
507    def __iter__(self):
508        return self
509
510
511class XBuiltin(SchemaObject):
512    """
513    Represents an (xsd) schema <xs:*/> node
514    """
515   
516    def __init__(self, schema, name):
517        """
518        @param schema: The containing schema.
519        @type schema: L{schema.Schema}
520        """
521        root = Element(name)
522        SchemaObject.__init__(self, schema, root)
523        self.name = name
524        self.nillable = True
525           
526    def namespace(self, prefix=None):
527        return Namespace.xsdns
528   
529    def builtin(self):
530        return True
531   
532    def resolve(self, nobuiltin=False):
533        return self
534
535
536class Content(SchemaObject):
537    """
538    This class represents those schema objects that represent
539    real XML document content.
540    """
541    pass
Note: See TracBrowser for help on using the repository browser.