# HG changeset patch
# User Mihai Ibanescu <https://issues.rpath.com/>
# Date 1251320159 14400
# Node ID c81eeab45d5f5d5351c716f06edfcde4239d8a4a
# Parent  716938e1d4bb56d9e1df4e65f2dec60da5cea03b
support for named element groups

diff -r 716938e1d4bb -r c81eeab45d5f generateDS.py
--- a/generateDS.py	Mon Aug 24 12:00:12 2009 -0400
+++ b/generateDS.py	Wed Aug 26 16:55:59 2009 -0400
@@ -163,6 +163,7 @@
 SubclassSuffix = 'Sub'
 RootElement = None
 AttributeGroups = {}
+ElementGroups = {}
 SubstitutionGroups = {}
 #
 # SubstitutionGroups can also include simple types that are
@@ -184,7 +185,7 @@
         PositiveIntegerType, NegativeIntegerType, \
         NonPositiveIntegerType, NonNegativeIntegerType, \
         BooleanType, FloatType, DoubleType, \
-        ElementType, ComplexTypeType, SequenceType, ChoiceType, \
+        ElementType, ComplexTypeType, GroupType, SequenceType, ChoiceType, \
         AttributeGroupType, AttributeType, SchemaType, \
         DateTimeType, DateType, \
         ComplexContentType, ExtensionType, \
@@ -199,6 +200,7 @@
     ChoiceType = nameSpace + 'choice'
     ComplexContentType = nameSpace + 'complexContent'
     ComplexTypeType = nameSpace + 'complexType'
+    GroupType = nameSpace + 'group'
     SimpleTypeType = nameSpace + 'simpleType'
     RestrictionType = nameSpace + 'restriction'
     WhiteSpaceType = nameSpace + 'whiteSpace'
@@ -425,6 +427,8 @@
         # We will add the attribute defintions in each of these groups
         #   to this element in annotate().
         self.attributeGroupNameList = []
+        # similar things as above, for groups of elements
+        self.elementGroup = None
         self.topLevel = 0
         # Does this element contain an anyAttribute?
         self.anyAttribute = 0
@@ -488,6 +492,8 @@
         self.attributeGroups[name] = attribute
     def setAttributeGroup(self, attributeGroup): self.attributeGroup = attributeGroup
     def getAttributeGroup(self): return self.attributeGroup
+    def setElementGroup(self, elementGroup): self.elementGroup = elementGroup
+    def getElementGroup(self): return self.elementGroup
     def setTopLevel(self, topLevel): self.topLevel = topLevel
     def getTopLevel(self): return self.topLevel
     def setAnyAttribute(self, anyAttribute): self.anyAttribute = anyAttribute
@@ -521,6 +527,11 @@
             child.show(outfile, level + 1)
 
     def annotate(self):
+        # resolve group references within groups
+        for grp in ElementGroups.values():
+            expandGroupReferences(grp)
+        # Recursively expand group references
+        self.expandGroupReferences_tree()
         self.collect_element_dict()
         #self.element_dict = {}
         #self.build_element_dict(self.element_dict)
@@ -530,6 +541,11 @@
         self.coerce_attr_types()
         self.checkMixedBases()
 
+    def expandGroupReferences_tree(self):
+        expandGroupReferences(self)
+        for child in self.children:
+            child.expandGroupReferences_tree()
+
     def collect_element_dict(self):
         base = self.getBase()
         if self.getTopLevel() or len(self.getChildren()) > 0 or \
@@ -853,7 +871,6 @@
             child.coerce_attr_types()
 # end class XschemaElement
 
-
 class XschemaAttributeGroup:
     def __init__(self, name='', group=None):
         self.name = name
@@ -882,6 +899,11 @@
             return 0
 # end class XschemaAttributeGroup
 
+class XschemaGroup:
+    def __init__(self, ref):
+        self.ref = ref
+# end class XschemaGroup
+
 class XschemaAttribute:
     def __init__(self, name, data_type='xs:string', use='optional', default=None):
         self.name = name
@@ -1013,6 +1035,11 @@
                 for key in attrs.keys():
                     parentDict[key] = attrs[key]
             self.inComplexType = 1
+        elif name == GroupType:
+            element = XschemaElement(attrs)
+            if len(self.stack) == 1:
+                element.setTopLevel(1)
+            self.stack.append(element)
         elif name == SequenceType:
             self.inSequence = 1
         elif name == ChoiceType:
@@ -1212,6 +1239,19 @@
                 # We have already added it to the list of attributeGroup names.
                 # Leave it.  We'll fill it in during annotate.
                 pass
+        elif name == GroupType:
+            element = self.stack.pop()
+            name = element.getAttrs()['name']
+            elementGroup = XschemaGroup(element.name)
+            ref = element.getAttrs().get('ref')
+            if len(self.stack) == 1 and ref is None:
+                # This is the definition
+                ElementGroups[name] = element
+            elif len(self.stack) > 1 and ref is not None:
+                # This is a reference. Add it to the parent's children. We
+                # need to preserve the order of elements.
+                element.setElementGroup(elementGroup)
+                self.stack[-1].addChild(element)
         elif name == SchemaType:
             self.inSchema = 0
             if len(self.stack) != 1:
@@ -4178,6 +4218,41 @@
     generate(outfileName, subclassFilename, behaviorFilename, 
         prefix, root, superModule)
 
+# Function that gets called recursively in order to expand nested references
+# to element groups
+def _expandGR(grp, visited):
+    # visited is used for loop detection
+    children = []
+    changed = False
+    for child in grp.children:
+        groupRef = child.getElementGroup()
+        if not groupRef:
+            children.append(child)
+            continue
+        ref = groupRef.ref
+        referencedGroup = ElementGroups.get(ref, None)
+        if referencedGroup is None:
+            ref = strip_namespace(ref)
+            referencedGroup = ElementGroups.get(ref, None)
+        if referencedGroup is None:
+            err_msg('*** Reference to unknown group %s' % groupRef.attrs['ref'])
+            continue
+        visited.add(id(grp))
+        if id(referencedGroup) in visited:
+            err_msg('*** Circular reference for %s' % groupRef.attrs['ref'])
+            continue
+        changed = True
+        _expandGR(referencedGroup, visited)
+        visited.remove(id(grp))
+        children.extend(referencedGroup.children)
+    if changed:
+        # Avoid replacing the list with a copy of the list
+        grp.children = children
+
+def expandGroupReferences(grp):
+    visited = set()
+    _expandGR(grp, visited)
+
 def debug_show_elements(root):
     #print 'ElementDict:', ElementDict
     print '=' * 50
