def _get_applier_generated_attributes()

in objectModel/Python/cdm/resolvedmodel/resolved_attribute_set_builder.py [0:0]


    def _get_applier_generated_attributes(self, arc: 'AttributeResolutionContext', clear_state: bool, apply_modifiers: bool) \
            -> Optional[List['ResolvedAttribute']]:
        if not self._resolved_attribute_set or self._resolved_attribute_set._set is None or not arc or not arc.applier_caps:
            return None

        caps = arc.applier_caps

        if not caps.can_attribute_add and not caps.can_group_add and not caps.can_round_add:
            return None

        from cdm.objectmodel import CdmAttributeContext

        res_att_out = []  # type: List[ResolvedAttribute]

        # This function constructs a 'plan' for building up the resolved attributes that get generated from a set of
        # traits being applied to a set of attributes. It manifests the plan into an array of resolved attributes there
        # are a few levels of hierarchy to consider.
        # 1. Once per set of attributes, the traits may want to generate attributes. This is an attribute that is somehow
        #    descriptive of the whole set, even if it has repeating patterns, like the count for an expanded array.
        # 2. It is possible that some traits (like the array expander) want to keep generating new attributes for some run.
        #    Each time they do this is considered a 'round'the traits are given a chance to generate attributes once per round.
        #    Every set gets at least one round, so these should be the attributes that describe the set of other attributes.
        #    For example, the foreign key of a relationship or the 'class' of a polymorphic type, etc.
        # 3. For each round, there are new attributes created based on the resolved attributes from the previous round
        #    (or the starting atts for this set) the previous round attribute need to be 'done'.
        #    Having traits applied before they are used as sources for the current round.
        #    The goal here is to process each attribute completely before moving on to the next one

        # That may need to start out clean.
        if clear_state:
            for ra in self._resolved_attribute_set._set:
                ra.applier_state = None

        # make an attribute context to hold attributes that are generated from appliers
        # there is a context for the entire set and one for each 'round' of applications that happen
        att_ctx_container_group = self._resolved_attribute_set.attribute_context  # type: CdmAttributeContext
        if att_ctx_container_group:
            acp = AttributeContextParameters(
                under=att_ctx_container_group,
                type=CdmAttributeContextType.GENERATED_SET,
                name='_generatedAttributeSet')
            att_ctx_container_group = CdmAttributeContext._create_child_under(arc.res_opt, acp)

        att_ctx_container = att_ctx_container_group  # type: CdmAttributeContext

        def make_resolved_attribute(res_att_source: 'ResolvedAttribute', action: 'AttributeResolutionApplier',
                                    query_add: 'ApplierQuery', do_add: 'ApplierAction', state: str) -> 'ApplierContext':
            app_ctx = ApplierContext()
            app_ctx.state = state
            app_ctx.res_opt = arc.res_opt
            app_ctx.att_ctx = att_ctx_container
            app_ctx.res_att_source = res_att_source
            app_ctx.res_guide = arc.res_guide

            if res_att_source and isinstance(res_att_source.target, ResolvedAttributeSet) and cast('ResolvedAttributeSet', res_att_source.target)._set:
                return app_ctx  # Makes no sense for a group.

            # Will something add?
            if query_add(app_ctx):
                # May want to make a new attribute group.
                # make the 'new' attribute look like any source attribute for the duration of this call to make a context. there could be state needed
                app_ctx.res_att_new = res_att_source
                if self._resolved_attribute_set.attribute_context and action._will_create_context and action._will_create_context(app_ctx):
                    action._do_create_context(app_ctx)

                # Make a new resolved attribute as a place to hold results.
                app_ctx.res_att_new = ResolvedAttribute(app_ctx.res_opt, None, None, app_ctx.att_ctx)

                # Copy state from source.
                if res_att_source and res_att_source.applier_state:
                    app_ctx.res_att_new.applier_state = res_att_source.applier_state._copy()
                else:
                    app_ctx.res_att_new.applier_state = ApplierState()

                # If applying traits, then add the sets traits as a staring point.
                if apply_modifiers:
                    app_ctx.res_att_new.resolved_traits = arc.traits_to_apply.deep_copy()

                # Make it
                do_add(app_ctx)

                # Combine resolution guidance for this set with anything new from the new attribute.
                app_ctx.res_guide_new = app_ctx.res_guide._combine_resolution_guidance(app_ctx.res_guide_new)

                app_ctx.res_att_new.arc = AttributeResolutionContext(arc.res_opt, app_ctx.res_guide_new, app_ctx.res_att_new.resolved_traits)

                if apply_modifiers:
                    # Add the sets traits back in to this newly added one.
                    app_ctx.res_att_new.resolved_traits = app_ctx.res_att_new.resolved_traits.merge_set(arc.traits_to_apply)

                    # Be sure to use the new arc, the new attribute may have added actions. For now, only modify and
                    # remove will get acted on because recursion. Do all of the modify traits.
                    if app_ctx.res_att_new.arc.applier_caps.can_attribute_modify:
                        # Modify acts on the source and we should be done with it.
                        app_ctx.res_att_source = app_ctx.res_att_new

                        for mod_act in app_ctx.res_att_new.arc.actions_modify:
                            if mod_act._will_attribute_modify(app_ctx):
                                mod_act._do_attribute_modify(app_ctx)
                app_ctx.res_att_new.complete_context(app_ctx.res_opt)
                # tie this new resolved att to the source via lineage
                if app_ctx.res_att_new.att_ctx and res_att_source and res_att_source.att_ctx  \
                    and (not res_att_source.applier_state or not res_att_source.applier_state._flex_remove):
                    if res_att_source.att_ctx.lineage is not None and len(res_att_source.att_ctx.lineage) > 0:
                        for lineage in res_att_source.att_ctx.lineage:
                            app_ctx.res_att_source.att_ctx._add_lineage(lineage)
                    else:
                        app_ctx.res_att_new.att_ctx._add_lineage(res_att_source.att_ctx)

            return app_ctx

        # Get the one time atts.
        if caps.can_group_add and arc.actions_group_add:
            for action in arc.actions_group_add:
                app_ctx = make_resolved_attribute(None, action, action._will_group_add, action._do_group_add, 'group')
                # Save it.
                if app_ctx and app_ctx.res_att_new:
                    res_att_out.append(app_ctx.res_att_new)

        # Now starts a repeating pattern of rounds. First step is to get attribute that are descriptions of the round.
        # Do this once and then use them as the first entries in the first set of 'previous' atts for the loop.

        # make an attribute context to hold attributes that are generated from appliers in this round
        round_num = 0
        if att_ctx_container_group:
            acp = AttributeContextParameters(
                under=att_ctx_container_group,
                type=CdmAttributeContextType.GENERATED_ROUND,
                name='_generatedAttributeRound0')
            att_ctx_container = CdmAttributeContext._create_child_under(arc.res_opt, acp)

        res_atts_last_round = []  # type: List[ResolvedAttribute]
        if caps.can_round_add and arc.actions_round_add:
            for action in arc.actions_round_add:
                app_ctx = make_resolved_attribute(None, action, action._will_round_add, action._do_round_add, 'round')
                # Save it.
                if app_ctx and app_ctx.res_att_new:
                    # Overall list.
                    res_att_out.append(app_ctx.res_att_new)
                    # Previous list.
                    res_atts_last_round.append(app_ctx.res_att_new)

        # The first per-round set of attributes is the set owned by this object.
        res_atts_last_round += self._resolved_attribute_set._set

        # Now loop over all of the previous atts until they all say 'stop'.
        if res_atts_last_round:
            continues = 1
            while continues:
                continues = 0
                res_att_this_round = []  # type: List[ResolvedAttribute]
                if caps.can_attribute_add:
                    for att in res_atts_last_round:
                        if arc.actions_attribute_add:
                            for action in arc.actions_attribute_add:
                                app_ctx = make_resolved_attribute(att, action, action._will_attribute_add, action._do_attribute_add, 'detail')
                                # Save it
                                if app_ctx and app_ctx.res_att_new:
                                    # Overall list.
                                    res_att_out.append(app_ctx.res_att_new)
                                    res_att_this_round.append(app_ctx.res_att_new)
                                    if app_ctx.is_continue:
                                        continues += 1
                res_atts_last_round = res_att_this_round

                round_num += 1
                if att_ctx_container_group:
                    acp = AttributeContextParameters(
                        under=att_ctx_container_group,
                        type=CdmAttributeContextType.GENERATED_ROUND,
                        name='_generatedAttributeRound{}'.format(round_num))
                    att_ctx_container = CdmAttributeContext._create_child_under(arc.res_opt, acp)

        return res_att_out