diff options
Diffstat (limited to 'suds/xsd/sxbase.py')
-rw-r--r-- | suds/xsd/sxbase.py | 341 |
1 files changed, 198 insertions, 143 deletions
diff --git a/suds/xsd/sxbase.py b/suds/xsd/sxbase.py index 9abefd8..634b98e 100644 --- a/suds/xsd/sxbase.py +++ b/suds/xsd/sxbase.py @@ -23,7 +23,6 @@ from logging import getLogger from suds import * from suds.xsd import * from suds.sax.element import Element -from copy import copy, deepcopy log = getLogger(__name__) @@ -42,17 +41,15 @@ class SchemaObject: @ivar nillable: A flag that inidcates that @nillable has a value of I{true}. @type nillable: boolean - @ivar children: A list of child xsd I{(non-attribute)} nodes - @type children: [L{SchemaObject},...] - @ivar attributes: A list of child xsd I{(attribute)} nodes - @type attributes: [L{SchemaObject},...] + @ivar rawchildren: A list raw of all children. + @type rawchildren: [L{SchemaObject},...] @ivar container: The <sequence/>,<all/> or <choice/> containing this object. @type container: L{SchemaObject} """ @classmethod - def prepend(cls, d, s, filter=None): + def prepend(cls, d, s, filter=Filter()): """ Prepend schema object's from B{s}ource list to the B{d}estination list while applying the filter. @@ -61,18 +58,16 @@ class SchemaObject: @param s: The source list. @type s: list @param filter: A filter that allows items to be prepended. - @type filter: L{ListFilter} + @type filter: L{Filter} """ - if filter is None: - filter = ListFilter() i = 0 for x in s: - if filter.permit(x): + if x in filter: d.insert(i, x) i += 1 @classmethod - def append(cls, d, s, filter=None): + def append(cls, d, s, filter=Filter()): """ Append schema object's from B{s}ource list to the B{d}estination list while applying the filter. @@ -81,12 +76,10 @@ class SchemaObject: @param s: The source list. @type s: list @param filter: A filter that allows items to be appended. - @type filter: L{ListFilter} + @type filter: L{Filter} """ - if filter is None: - filter = ListFilter() for item in s: - if filter.permit(item): + if item in filter: d.append(item) def __init__(self, schema, root): @@ -102,16 +95,61 @@ class SchemaObject: self.name = root.get('name') self.qname = (self.name, schema.tns[1]) self.type = root.get('type') - self.ref = [root.get('ref'), False] - self.ref[1] = ( self.ref[0] is not None ) + self.ref = root.get('ref') self.form_qualified = schema.form_qualified self.nillable = False self.inherited = False - self.children = [] - self.attributes = [] + self.rawchildren = [] self.container = None self.cache = {} - self.flattened = False + + def attributes(self, filter=Filter()): + """ + Get only the attribute content. + @param filter: A filter to constrain the result. + @type filter: L{Filter} + @return: A list attributes + @rtype: list + """ + for c in self: + if c.isattr() and c in filter: + yield c + + def children(self, filter=Filter()): + """ + Get only the I{direct} or non-attribute content. + @param filter: A filter to constrain the result. + @type filter: L{Filter} + @return: A list attributes + @rtype: list + """ + for c in self: + if not c.isattr() and c in filter: + yield c + + def get_attribute(self, name): + """ + Get (find) a I{non-attribute} attribute by name. + @param name: A attribute name. + @type name: str + @return: The requested child. + @rtype: L{SchemaObject} + """ + for child in self: + if child.isattr() and child.name == name: + return child + + def get_child(self, name): + """ + Get (find) a I{non-attribute} child by name. + @param name: A child name. + @type name: str + @return: The requested child. + @rtype: L{SchemaObject} + """ + for child in self.children(): + if child.any() or child.name == name: + return child def namespace(self): """ @@ -150,32 +188,6 @@ class SchemaObject: """ return self.cache.get(nobuiltin, self) - def get_child(self, name): - """ - Get (find) a I{non-attribute} child by name. - @param name: A child name. - @type name: str - @return: The requested child. - @rtype: L{SchemaObject} - """ - for child in self.children: - if child.any() or child.name == name: - return child - return None - - def get_attribute(self, name): - """ - Get (find) a I{non-attribute} attribute by name. - @param name: A attribute name. - @type name: str - @return: The requested child. - @rtype: L{SchemaObject} - """ - for child in self.attributes: - if child.name == name: - return child - return None - def sequence(self): """ Get whether this is an <xs:sequence/> @@ -264,7 +276,7 @@ class SchemaObject: classes = (self.__class__,) if self.qname == qref and self.__class__ in classes: return self - for c in self.children: + for c in self.rawchildren: p = c.find(qref, classes) if p is not None: return p @@ -286,58 +298,17 @@ class SchemaObject: """ return () - def flatten(self, parent=None): - """ - Walk the tree and invoke promote() on each node. This gives each - node the opportunity to flatten the tree as needed to remote - uninteresting nodes. Nodes that don't directly contribute to the - structure of the data are omitted. - """ - pa, pc = [],[] - if not self.flattened: - self.flattened = True - log.debug(Repr(self)) - for c in self.children: - a, c = c.flatten(self) - pa += a - pc += c - if parent is None: - self.attributes += pa - self.children = pc - else: - self.promote(pa, pc) - return (pa, pc) - - def promote(self, pa, pc): - """ - Promote children during the flattening proess. The object's - attributes and children are added to the B{p}romoted B{a}ttributes - and B{p}romoted B{c}hildren lists as they see fit. - @param pa: List of attributes to promote. - @type pa: [L{SchemaObject}] - @param pc: List of children to promote. - @type pc: [L{SchemaObject}] - """ - log.debug(Repr(self)) - filter = PromoteFilter() - self.prepend(pa, self.attributes) - self.prepend(pc, self.children, filter) - - def dereference(self): + def dependencies(self): """ - Walk the tree and invoke mutate() on each node. This gives each - node the opportunity to resolve references to other types - and mutate as needed. + Get a list of dependancies for dereferencing. + @return: A merge dependancy index and a list of dependancies. + @rtype: (int, [L{SchemaObject},...]) """ - if not self.ref[1]: return - log.debug(Repr(self)) - self.ref[1] = False - self.mutate() + return (None, []) - def mutate(self): + def merge(self, other): """ - Mutate into a I{true} type as defined by a reference to - another object. + Merge another object as needed. """ pass @@ -346,25 +317,35 @@ class SchemaObject: Mark this branch in the tree as inherited = true. """ self.inherited = True - for c in self.children: + for c in self: c.mark_inherited() - def contents(self, collection): + def content(self, collection=None, filter=Filter(), history=None): """ Get a I{flattened} list of this nodes contents. @param collection: A list to fill. @type collection: list + @param filter: A filter used to constrain the result. + @type filter: L{Filter} + @param history: The history list used to prevent cyclic dependency. + @type history: list @return: The filled list. @rtype: list """ - collection.append(self) - for a in self.attributes: - collection.append(a) - for c in self.children: - c.contents(collection) + if collection is None: + collection = [] + if history is None: + history = [] + if self in history: + return collection + history.append(self) + if self in filter: + collection.append(self) + for c in self.rawchildren: + c.content(collection, filter, history[:]) return collection - def str(self, indent=0): + def str(self, indent=0, history=None): """ Get a string representation of this object. @param indent: The indent. @@ -372,6 +353,11 @@ class SchemaObject: @return: A string. @rtype: str """ + if history is None: + history = [] + if self in history: + return '%s ...' % Repr(self) + history.append(self) tab = '%*s'%(indent*3, '') result = [] result.append('%s<%s' % (tab, self.id)) @@ -384,13 +370,11 @@ class SchemaObject: result.append(' %s="%s"' % (n, v)) if len(self): result.append('>') - for c in self.attributes: - result.append('\n') - result.append(c.str(indent+1)) - result.append('@') - for c in self.children: + for c in self.rawchildren: result.append('\n') - result.append(c.str(indent+1)) + result.append(c.str(indent+1, history[:])) + if c.isattr(): + result.append('@') result.append('\n%s' % tab) result.append('</%s>' % self.__class__.__name__) else: @@ -426,18 +410,112 @@ class SchemaObject: return myrep.encode('utf-8') def __len__(self): - return len(self.children)+len(self.attributes) + n = 0 + for x in self: n += 1 + return n + + def __iter__(self): + return Iter(self) def __getitem__(self, index): - return self.children[index] + i = 0 + for c in self: + if i == index: + return c - def __deepcopy__(self, memo={}): - clone = copy(self) - clone.attributes = deepcopy(self.attributes) - clone.children = deepcopy(self.children) - return clone +class Iter: + """ + The content iterator - used to iterate the L{Content} children. The iterator + provides a I{view} of the children that is free of container elements + such as <sequence/> and <choice/>. + @ivar stack: A stack used to control nesting. + @type stack: list + """ + + class Frame: + """ A content iterator frame. """ + + def __init__(self, sx): + """ + @param sx: A schema object. + @type sx: L{SchemaObject} + """ + self.items = sx.rawchildren + self.index = 0 + + def next(self): + """ + Get the I{next} item in the frame's collection. + @return: The next item or None + @rtype: L{SchemaObject} + """ + if self.index < len(self.items): + result = self.items[self.index] + self.index += 1 + return result + + def __init__(self, sx): + """ + @param sx: A schema object. + @type sx: L{SchemaObject} + """ + self.stack = [] + self.push(sx) + + def push(self, sx): + """ + Create a frame and push the specified object. + @param sx: A schema object to push. + @type sx: L{SchemaObject} + """ + self.stack.append(Iter.Frame(sx)) + + def pop(self): + """ + Pop the I{top} frame. + @return: The popped frame. + @rtype: L{Frame} + @raise StopIteration: when stack is empty. + """ + if len(self.stack): + return self.stack.pop() + else: + raise StopIteration() + + def top(self): + """ + Get the I{top} frame. + @return: The top frame. + @rtype: L{Frame} + @raise StopIteration: when stack is empty. + """ + if len(self.stack): + return self.stack[-1] + else: + raise StopIteration() + + def next(self): + """ + Get the next item. + @return: The next item being iterated. + @rtype: L{SchemaObject} + """ + frame = self.top() + while True: + result = frame.next() + if result is None: + self.pop() + return self.next() + if isinstance(result, Content): + return result + self.push(result) + return self.next() + def __iter__(self): + return self + + class XBuiltin(SchemaObject): """ Represents an (xsd) schema <xs:*/> node @@ -463,32 +541,9 @@ class XBuiltin(SchemaObject): return self -class Promotable(SchemaObject): +class Content(SchemaObject): """ - Represents I{promotable} schema objects. They are objects that - should be promoted during the flattening process. + This class represents those schema objects that represent + real XML document content. """ - - def __init__(self, schema, root): - """ - @param schema: The containing schema. - @type schema: L{schema.Schema} - @param root: The xml root node. - @type root: L{Element} - """ - SchemaObject.__init__(self, schema, root) - - -class ListFilter: - def permit(self, x): - return True - -class PromoteFilter(ListFilter): - def permit(self, x): - return isinstance(x, Promotable) - -class UniqueFilter(ListFilter): - def __init__(self, d): - self.ids = [m.id for m in d] - def permit(self, x): - return ( x.id not in self.ids )
\ No newline at end of file + pass |