[profile gen] Move more code to the parent adapter
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 06 Jul 2016 18:38:52 +0200
changeset 1340 a6fd38685740
parent 1339 70bf9bb74735
child 1341 a68075226224
[profile gen] Move more code to the parent adapter
entities/profile_generation.py
--- a/entities/profile_generation.py	Wed Jul 06 18:17:26 2016 +0200
+++ b/entities/profile_generation.py	Wed Jul 06 18:38:52 2016 +0200
@@ -186,6 +186,34 @@
         callback = getattr(self, 'element_' + occ.target.__class__.__name__.lower())
         callback(occ, profile_element, target_value, to_process, card_entity)
 
+    def _dump(self, root):
+        entity = self.entity
+        xselement = XSDM_MAPPING.root_xselement
+        transfer_element = self.init_transfer_element(xselement, root, entity)
+        to_process = defaultdict(list)
+        to_process[xselement].append((entity, transfer_element))
+        # first round to ensure we have necessary basic structure
+        for xselement, etype, child_defs in XSDM_MAPPING:
+            # print 'PROCESS', getattr(xselement, 'local_name', xselement.__class__.__name__), etype
+            for entity, profile_element in to_process.pop(xselement, ()):
+                assert etype == entity.cw_etype
+                self._process(entity, profile_element, child_defs, to_process)
+        # then process remaining elements
+        # print 'STARTING ROUND 2'
+        while to_process:
+            xselement = next(iter(to_process))
+            entities_profiles = to_process.pop(xselement, ())
+            if entities_profiles:
+                try:
+                    etype, child_defs = XSDM_MAPPING[xselement]
+                except KeyError:
+                    continue  # XXX explain or fix me
+                for entity, profile_element in entities_profiles:
+                    assert etype == entity.cw_etype
+                    self._process(entity, profile_element, child_defs, to_process)
+
+        assert not to_process, to_process
+
     def _process(self, entity, profile_element, child_defs, to_process):
         for occ, path in child_defs:
             # print '  child', getattr(occ.target, 'local_name', occ.target.__class__.__name__), \
@@ -216,15 +244,21 @@
                 # later processing
                 self.dispatch_occ(profile_element, occ, None, to_process,
                                   card_entity=attrdict({'user_cardinality': cardinality}))
-                to_process[occ.target].append((entity, self._jumped_element(profile_element)))
+                to_process[occ.target].append((entity, self.jumped_element(profile_element)))
             else:
                 # print '  values', _path_target_values(entity, path)
                 for card_entity, target_value in _path_target_values(entity, path):
                     self.dispatch_occ(profile_element, occ, target_value, to_process,
                                       card_entity=card_entity)
 
-    def _jumped_element(self, profile_element):
-        """Return the last generated element, for insertion of its content;"""
+    def init_transfer_element(self, xselement, root, entity):
+        """Initialize and return the XML element holding the ArchiveTransfer definition, as well as
+        any other necessary global definitions.
+        """
+        raise NotImplementedError()
+
+    def jumped_element(self, profile_element):
+        """Return the last generated element, for insertion of its content."""
         raise NotImplementedError()
 
 
@@ -259,42 +293,21 @@
         xsd_cleanup_etree(root)
         return root
 
-    def _dump(self, root):
-        entity = self.entity
-        xselement = XSDM_MAPPING.root_xselement
+    def init_transfer_element(self, xselement, root, entity):
         profile_element = self.element('xsd:element', root,
                                        {'name': xselement.local_name,
                                         'documentation': entity.user_annotation})
         self.element('xsd:sequence', self.element('xsd:complexType', profile_element))
-        to_process = defaultdict(list)
-        to_process[xselement].append((entity, profile_element))
-        # first round to ensure we have necessary basic structure
-        for xselement, etype, child_defs in XSDM_MAPPING:
-            # print 'PROCESS', getattr(xselement, 'local_name', xselement.__class__.__name__), etype
-            for entity, profile_element in to_process.pop(xselement, ()):
-                assert etype == entity.cw_etype
-                self._process(entity, profile_element, child_defs, to_process)
-        # then process remaining elements
-        # print 'STARTING ROUND 2'
-        while to_process:
-            xselement = next(iter(to_process))
-            entities_profiles = to_process.pop(xselement, ())
-            if entities_profiles:
-                try:
-                    etype, child_defs = XSDM_MAPPING[xselement]
-                except KeyError:
-                    continue  # XXX explain or fix me
-                for entity, profile_element in entities_profiles:
-                    assert etype == entity.cw_etype
-                    self._process(entity, profile_element, child_defs, to_process)
-
-        assert not to_process, to_process
         open_type = self.element('xsd:complexType', root, {'name': 'OpenType', 'abstract': 'true'})
         open_type_seq = self.element('xsd:sequence', open_type)
         self.element('xsd:attribute', open_type, {'ref': 'xml:id', 'use': 'optional'})
         self.element('xsd:attribute', open_type, {'ref': 'xlink:href', 'use': 'optional'})
         self.element('xsd:any', open_type_seq, {'namespace': '##other', 'processContents': 'lax',
                                                 'minOccurs': '0'})
+        return profile_element
+
+    def jumped_element(self, profile_element):
+        return self._parent_element(profile_element)[-1]
 
     def element_alternative(self, occ, profile_element, target_value, to_process, card_entity):
         attrs = xsd_element_cardinality(occ, card_entity)
@@ -424,10 +437,6 @@
         attrs['name'] = xselement.local_name
         return self.element('xsd:element', parent_element, attrs)
 
-    def _jumped_element(self, profile_element):
-        """Return the last generated element, for insertion of its content;"""
-        return self._parent_element(profile_element)[-1]
-
     def xsd_content_type(self, content_type):
         """Return XSD content type from pyxst `textual_content_type` that may be None, a set or a string
         value.
@@ -484,9 +493,7 @@
             self.element('rng:text', element)
         return root
 
-    def _dump(self, root):
-        entity = self.entity
-        xselement = XSDM_MAPPING.root_xselement
+    def init_transfer_element(self, xselement, root, entity):
         transfer_element = self.element('rng:element', root,
                                         {'name': xselement.local_name,
                                          'documentation': entity.user_annotation})
@@ -494,63 +501,15 @@
             transfer_element, ['rng:zeroOrMore', 'rng:attribute', 'rng:anyName', 'rng:except'])
         self.element('rng:nsName', exc)
         self.element('rng:nsName', exc, {'ns': ''})
-        to_process = defaultdict(list)
-        to_process[xselement].append((entity, transfer_element))
-        # first round to ensure we have necessary basic structure
-        for xselement, etype, child_defs in XSDM_MAPPING:
-            # print 'PROCESS', getattr(xselement, 'local_name', xselement.__class__.__name__), etype
-            for entity, profile_element in to_process.pop(xselement, ()):
-                assert etype == entity.cw_etype
-                self._process(entity, profile_element, child_defs, to_process)
-        # then process remaining elements
-        # print 'STARTING ROUND 2'
-        while to_process:
-            xselement = next(iter(to_process))
-            entities_profiles = to_process.pop(xselement, ())
-            if entities_profiles:
-                try:
-                    etype, child_defs = XSDM_MAPPING[xselement]
-                except KeyError:
-                    continue  # XXX explain or fix me
-                for entity, profile_element in entities_profiles:
-                    assert etype == entity.cw_etype
-                    self._process(entity, profile_element, child_defs, to_process)
-
-        assert not to_process, to_process
+        return transfer_element
 
-    def _rng_element_parent(self, occ, card_entity, profile_element):
-        minimum, maximum = element_minmax_cardinality(occ, card_entity)
-        if minimum == 1 and maximum == 1:
-            return profile_element
-        elif minimum == 0 and maximum == 1:
-            return self.element('rng:optional', profile_element)
-        elif minimum == 0 and maximum == graph_nodes.INFINITY:
-            return self.element('rng:zeroOrMore', profile_element)
-        elif minimum == 1 and maximum == graph_nodes.INFINITY:
-            return self.element('rng:oneOrMore', profile_element)
-        else:
-            assert False, ('unexpected min/max cardinality:', minimum, maximum)
-
-    def _rng_attribute_parent(self, occ, card_entity, profile_element):
-        if attribute_minimum_cardinality(occ, card_entity) == 1:
-            return profile_element
-        else:
-            return self.element('rng:optional', profile_element)
-
-    def _rng_attribute(self, xselement, parent_element, value=None):
-        xstypes = content_types(xselement.textual_content_type)
-        if len(xstypes) > 1:
-            parent_element = self.element('rng:choice', parent_element)
-        for xstype in xstypes:
-            attr_element = self.element('rng:attribute', parent_element,
-                                        {'name': xselement.local_name})
-            if value is not None:
-                if xselement.local_name == 'id':
-                    attr_element.attrib[self.qname('seda:profid')] = value
-                else:
-                    self.element('rng:value', attr_element, {'type': xstype}, text=value)
-            else:
-                self.element('rng:data', attr_element, {'type': xstype})
+    def jumped_element(self, profile_element):
+        element = profile_element[-1]
+        if element.tag != '{http://relaxng.org/ns/structure/1.0}element':
+            # optional, zeroOrMore, etc.: should pick their child element
+            element = element[-1]
+            assert element.tag == '{http://relaxng.org/ns/structure/1.0}element', element
+        return element
 
     def element_alternative(self, occ, profile_element, target_value, to_process, card_entity):
         parent_element = self._rng_element_parent(occ, card_entity, profile_element)
@@ -653,13 +612,39 @@
         if value:
             self.element('rng:value', target_element, text=value.absolute_url())
 
-    def _jumped_element(self, profile_element):
-        element = profile_element[-1]
-        if element.tag != '{http://relaxng.org/ns/structure/1.0}element':
-            # optional, zeroOrMore, etc.: should pick their child element
-            element = element[-1]
-            assert element.tag == '{http://relaxng.org/ns/structure/1.0}element', element
-        return element
+    def _rng_element_parent(self, occ, card_entity, profile_element):
+        minimum, maximum = element_minmax_cardinality(occ, card_entity)
+        if minimum == 1 and maximum == 1:
+            return profile_element
+        elif minimum == 0 and maximum == 1:
+            return self.element('rng:optional', profile_element)
+        elif minimum == 0 and maximum == graph_nodes.INFINITY:
+            return self.element('rng:zeroOrMore', profile_element)
+        elif minimum == 1 and maximum == graph_nodes.INFINITY:
+            return self.element('rng:oneOrMore', profile_element)
+        else:
+            assert False, ('unexpected min/max cardinality:', minimum, maximum)
+
+    def _rng_attribute_parent(self, occ, card_entity, profile_element):
+        if attribute_minimum_cardinality(occ, card_entity) == 1:
+            return profile_element
+        else:
+            return self.element('rng:optional', profile_element)
+
+    def _rng_attribute(self, xselement, parent_element, value=None):
+        xstypes = content_types(xselement.textual_content_type)
+        if len(xstypes) > 1:
+            parent_element = self.element('rng:choice', parent_element)
+        for xstype in xstypes:
+            attr_element = self.element('rng:attribute', parent_element,
+                                        {'name': xselement.local_name})
+            if value is not None:
+                if xselement.local_name == 'id':
+                    attr_element.attrib[self.qname('seda:profid')] = value
+                else:
+                    self.element('rng:value', attr_element, {'type': xstype}, text=value)
+            else:
+                self.element('rng:data', attr_element, {'type': xstype})
 
     def _create_hierarchy(self, parent, tags):
         for tag in tags: