Esempio n. 1
0
    def _create_new_resolved_attribute(
        proj_ctx: 'ProjectionContext',
        attr_ctx_under: 'CdmAttributeContext',
        target_attr: 'CdmAttribute',
        override_default_name: Optional[str] = None,
        added_simple_ref_traits: Optional[List[str]] = None
    ) -> 'ResolvedAttribute':
        """
        Projections require a new resolved attribute to be created multiple times
        This function allows us to create new resolved attributes based on a input attribute
        """
        new_res_attr = ResolvedAttribute(
            proj_ctx._projection_directive._res_opt, target_attr,
            override_default_name if override_default_name else
            target_attr.get_name(), attr_ctx_under)

        if added_simple_ref_traits is not None:
            for trait in added_simple_ref_traits:
                if target_attr.applied_traits.item(trait) == None:
                    target_attr.applied_traits.append(trait, True)

        res_trait_set = target_attr._fetch_resolved_traits(
            proj_ctx._projection_directive._res_opt)

        # Create deep a copy of traits to avoid conflicts in case of parameters
        if res_trait_set is not None:
            new_res_attr.resolved_traits = res_trait_set.deep_copy()

        return new_res_attr
Esempio n. 2
0
    def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        # find and cache the complete set of attributes
        # attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
        # the datatype used as an attribute, traits applied to that datatype,
        # the purpose of the attribute, any traits applied to the attribute.
        from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder

        from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition

        rasb = ResolvedAttributeSetBuilder()
        rasb._resolved_attribute_set.attribute_context = under

        # add this attribute to the set
        # make a new one and apply any traits
        new_att = ResolvedAttribute(res_opt, self, self.name, under)
        rasb.own_one(new_att)
        rts = self._fetch_resolved_traits(res_opt)

        if self.owner and self.owner.object_type == CdmObjectType.ENTITY_DEF:
            rasb._resolved_attribute_set.set_target_owner(self.owner)

        if self.projection:
            rasb._resolved_attribute_set.apply_traits(rts)

            proj_directive = ProjectionDirective(res_opt, self)
            proj_ctx = self.projection._construct_projection_context(
                proj_directive, under, rasb._resolved_attribute_set)

            ras = self.projection._extract_resolved_attributes(proj_ctx, under)
            rasb._resolved_attribute_set = ras
        else:
            # using resolution guidance

            # this context object holds all of the info about what needs to happen to resolve these attributes.
            # make a copy and add defaults if missing
            res_guide_with_default = None
            if self.resolution_guidance is not None:
                res_opt._used_resolution_guidance = True
                res_guide_with_default = self.resolution_guidance.copy(res_opt)
            else:
                res_guide_with_default = CdmAttributeResolutionGuidanceDefinition(
                    self.ctx)

            # rename_format is not currently supported for type attributes
            res_guide_with_default.rename_format = None

            res_guide_with_default._update_attribute_defaults(None, self)
            arc = AttributeResolutionContext(res_opt, res_guide_with_default,
                                             rts)

            # from the traits of the datatype, purpose and applied here, see if new attributes get generated
            rasb.apply_traits(arc)
            rasb.generate_applier_attributes(
                arc, False)  # false = don't apply these traits to added things
            # this may have added symbols to the dependencies, so merge them
            res_opt._symbol_ref_set._merge(arc.res_opt._symbol_ref_set)

        return rasb
Esempio n. 3
0
    def _create_new_resolved_attribute(
        proj_ctx: 'ProjectionContext',
        attr_ctx_under: 'CdmAttributeContext',
        target_attr_or_resolved_attr: Union['CdmAttribute',
                                            'ResolvedAttribute'],
        override_default_name: Optional[str] = None,
        added_simple_ref_traits: Optional[List[str]] = None
    ) -> 'ResolvedAttribute':
        """
        Projections require a new resolved attribute to be created multiple times
        This function allows us to create new resolved attributes based on a input attribute
        """
        from cdm.objectmodel import CdmAttribute
        target_attr = target_attr_or_resolved_attr.copy() \
            if isinstance(target_attr_or_resolved_attr, CdmAttribute) else target_attr_or_resolved_attr.target.copy()

        new_res_attr = ResolvedAttribute(
            proj_ctx._projection_directive._res_opt, target_attr,
            override_default_name if override_default_name else
            target_attr.get_name(), attr_ctx_under)

        target_attr.in_document = proj_ctx._projection_directive._owner.in_document

        if isinstance(target_attr_or_resolved_attr, CdmAttribute):
            if added_simple_ref_traits is not None:
                for trait in added_simple_ref_traits:
                    if target_attr.applied_traits.item(trait) == None:
                        target_attr.applied_traits.append(trait, True)

            res_trait_set = target_attr._fetch_resolved_traits(
                proj_ctx._projection_directive._res_opt)

            # Create deep a copy of traits to avoid conflicts in case of parameters
            if res_trait_set is not None:
                new_res_attr.resolved_traits = res_trait_set.deep_copy()
        else:
            new_res_attr.resolved_traits = target_attr_or_resolved_attr.resolved_traits.deep_copy(
            )

            if added_simple_ref_traits is not None:
                for trait in added_simple_ref_traits:
                    tr = CdmTraitReference(target_attr.ctx, trait, True, False)
                    new_res_attr.resolved_traits = new_res_attr.resolved_traits.merge_set(
                        tr._fetch_resolved_traits())

        return new_res_attr
Esempio n. 4
0
    def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', \
                                           attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_add_attr_group_param = AttributeContextParameters()
        attr_ctx_op_add_attr_group_param._under = attr_ctx
        attr_ctx_op_add_attr_group_param._type = CdmAttributeContextType.OPERATION_ADD_ATTRIBUTE_GROUP
        attr_ctx_op_add_attr_group_param._name = 'operation/index{}/{}'.format(
            self._index, self.get_name())
        attr_ctx_op_add_attr_group = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_op_add_attr_group_param)

        # Create a new attribute context for the attribute group we will create
        attr_ctx_attr_group_param = AttributeContextParameters()
        attr_ctx_attr_group_param._under = attr_ctx_op_add_attr_group
        attr_ctx_attr_group_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
        attr_ctx_attr_group_param._name = self.attribute_group_name
        attr_ctx_attr_group = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt, attr_ctx_attr_group_param)

        # Create a new resolve attribute set builder that will be used to combine all the attributes into one set
        rasb = ResolvedAttributeSetBuilder()

        # Iterate through all the projection attribute states generated from the source's resolved attributes
        # Each projection attribute state contains a resolved attribute that it is corresponding to
        for current_PAS in proj_ctx._current_attribute_state_set._states:
            # Create a copy of the resolved attribute
            resolved_attribute = current_PAS._current_resolved_attribute.copy(
            )  # type: ResolvedAttribute

            # Add the attribute to the resolved attribute set
            rasb._resolved_attribute_set.merge(resolved_attribute)

            # Add each attribute's attribute context to the resolved attribute set attribute context
            attr_param = AttributeContextParameters()
            attr_param._under = attr_ctx_attr_group
            attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
            attr_param._name = resolved_attribute.resolved_name
            resolved_attribute.att_ctx = CdmAttributeContext._create_child_under(
                proj_ctx._projection_directive._res_opt, attr_param)
            resolved_attribute.att_ctx._add_lineage(
                current_PAS._current_resolved_attribute.att_ctx)

        # Create a new resolved attribute that will hold the attribute set containing all the attributes
        res_attr_new = ResolvedAttribute(
            proj_ctx._projection_directive._res_opt,
            rasb._resolved_attribute_set, self.attribute_group_name,
            attr_ctx_attr_group)

        # Create a new projection attribute state pointing to the resolved attribute set that represents the attribute group
        new_PAS = ProjectionAttributeState(self.ctx)
        new_PAS._current_resolved_attribute = res_attr_new
        proj_output_set._add(new_PAS)

        return proj_output_set
Esempio n. 5
0
    def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        # find and cache the complete set of attributes
        # attributes definitions originate from and then get modified by subsequent re-defintions from (in this order):
        # the datatype used as an attribute, traits applied to that datatype,
        # the purpose of the attribute, any traits applied to the attribute.
        from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder

        from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition

        rasb = ResolvedAttributeSetBuilder()
        rasb.ras.attribute_context = under

        # add this attribute to the set
        # make a new one and apply any traits
        new_att = ResolvedAttribute(res_opt, self, self.name, under)
        rasb.own_one(new_att)
        rts = self._fetch_resolved_traits(res_opt)

        # this context object holds all of the info about what needs to happen to resolve these attributes.
        # make a copy and add defaults if missing
        res_guide_with_default = None
        if self.resolution_guidance is not None:
            res_guide_with_default = self.resolution_guidance.copy(res_opt)
        else:
            res_guide_with_default = CdmAttributeResolutionGuidanceDefinition(
                self.ctx)

        res_guide_with_default._update_attribute_defaults(None)
        arc = AttributeResolutionContext(res_opt, res_guide_with_default, rts)

        # from the traits of the datatype, purpose and applied here, see if new attributes get generated
        rasb.apply_traits(arc)
        rasb.generate_applier_attributes(
            arc, False)  # false = don't apply these traits to added things
        # this may have added symbols to the dependencies, so merge them
        res_opt.symbol_ref_set.merge(arc.res_opt.symbol_ref_set)

        return rasb
Esempio n. 6
0
    def _construct_resolved_attributes(
        self,
        res_opt: 'ResolveOptions',
        under: Optional['CdmAttributeContext'] = None
    ) -> 'ResolvedAttributeSetBuilder':
        from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder, ResolvedTrait
        from cdm.utilities import AttributeContextParameters, AttributeResolutionDirectiveSet

        from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition
        from .cdm_object import CdmObject

        rasb = ResolvedAttributeSetBuilder()
        ctx_ent = self.entity
        under_att = under
        acp_ent = None
        if under_att:
            # make a context for this attribute that holds the attributes that come up from the entity
            acp_ent = AttributeContextParameters(
                under=under_att,
                type=CdmAttributeContextType.ENTITY,
                name=ctx_ent.fetch_object_definition_name(),
                regarding=ctx_ent,
                include_traits=True)

        rts_this_att = self._fetch_resolved_traits(res_opt)

        # this context object holds all of the info about what needs to happen to resolve these attributes.
        # make a copy and add defaults if missing
        res_guide_with_default = None
        if self.resolution_guidance is not None:
            res_guide_with_default = self.resolution_guidance.copy(res_opt)
        else:
            res_guide_with_default = CdmAttributeResolutionGuidanceDefinition(
                self.ctx)

        res_guide_with_default._update_attribute_defaults(self.name)

        arc = AttributeResolutionContext(res_opt, res_guide_with_default,
                                         rts_this_att)

        # complete cheating but is faster.
        # this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them
        rel_info = self._get_relationship_info(arc.res_opt, arc)

        if rel_info.is_by_ref:
            # make the entity context that a real recursion would have give us
            if under:
                under = rasb.ras.create_attribute_context(res_opt, acp_ent)

            # if selecting from one of many attributes, then make a context for each one
            if under and rel_info.selects_one:
                # the right way to do this is to get a resolved entity from the embedded entity and then
                # look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes
                # that seems like a disaster waiting to happen given endless looping, etc.
                # for now, just insist that only the top level entity attributes declared in the ref entity will work
                ent_pick_from = self.entity.fetch_object_definition(res_opt)
                atts_pick = ent_pick_from.attributes
                if ent_pick_from and atts_pick:
                    for attribute in atts_pick:
                        if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF:
                            # a table within a table. as expected with a selects_one attribute
                            # since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key
                            # these are the same contexts that would get created if we recursed
                            # first this attribute
                            acp_ent_att = AttributeContextParameters(
                                under=under,
                                type=CdmAttributeContextType.
                                ATTRIBUTE_DEFINITION,
                                name=attribute.fetch_object_definition_name(),
                                regarding=attribute,
                                include_traits=True)
                            pick_under = rasb.ras.create_attribute_context(
                                res_opt, acp_ent_att)
                            # and the entity under that attribute
                            pick_ent = attribute.entity
                            acp_ent_att_ent = AttributeContextParameters(
                                under=pick_under,
                                type=CdmAttributeContextType.ENTITY,
                                name=pick_ent.fetch_object_definition_name(),
                                regarding=pick_ent,
                                include_traits=True)
                            rasb.ras.create_attribute_context(
                                res_opt, acp_ent_att_ent)

            # if we got here because of the max depth, need to impose the directives to make the trait work as expected
            if rel_info.max_depth_exceeded:
                if not arc.res_opt.directives:
                    arc.res_opt.directives = AttributeResolutionDirectiveSet()
                arc.res_opt.directives.add('referenceOnly')
        else:
            res_link = CdmObject._copy_resolve_options(res_opt)
            res_link.symbol_ref_set = res_opt.symbol_ref_set
            res_link._relationship_depth = rel_info.next_depth
            rasb.merge_attributes(
                self.entity._fetch_resolved_attributes(res_link, acp_ent))

        # from the traits of purpose and applied here, see if new attributes get generated
        rasb.ras.attribute_context = under_att
        rasb.apply_traits(arc)
        rasb.generate_applier_attributes(
            arc, True)  # True = apply the prepared traits to new atts
        # this may have added symbols to the dependencies, so merge them
        res_opt.symbol_ref_set.merge(arc.res_opt.symbol_ref_set)

        # use the traits for linked entity identifiers to record the actual foreign key links
        if rasb.ras and rasb.ras.set and rel_info.is_by_ref:
            for att in rasb.ras.set:
                reqd_trait = att.resolved_traits.find(
                    res_opt, 'is.linkedEntity.identifier')
                if not reqd_trait:
                    continue

                if not reqd_trait.parameter_values:
                    logger.warning(
                        self._TAG, self.ctx,
                        'is.linkedEntity.identifier does not support arguments'
                    )
                    continue

                ent_references = []
                att_references = []

                def add_entity_reference(entity_ref: 'CdmEntityReference',
                                         namespace: str):
                    ent_def = entity_ref.fetch_object_definition(res_opt)
                    required_trait = entity_ref._fetch_resolved_traits(
                        res_opt).find(res_opt, 'is.identifiedBy')
                    if required_trait and ent_def:
                        att_ref = required_trait.parameter_values.fetch_parameter_value(
                            'attribute').value
                        att_name = att_ref.named_reference.split('/')[-1]
                        # path should be absolute and without a namespace
                        relative_ent_path = self.ctx.corpus.storage.create_absolute_corpus_path(
                            ent_def.at_corpus_path, ent_def.in_document)
                        if relative_ent_path.startswith(namespace + ':'):
                            relative_ent_path = relative_ent_path[len(namespace
                                                                      ) + 1:]
                        ent_references.append(relative_ent_path)
                        att_references.append(att_name)

                if rel_info.selects_one:
                    ent_pick_from = self.entity.fetch_object_definition(
                        res_opt)
                    atts_pick = ent_pick_from.attributes if ent_pick_from else None
                    if atts_pick:
                        for attribute in atts_pick:
                            if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF:
                                add_entity_reference(
                                    attribute.entity,
                                    self.in_document.namespace)
                else:
                    add_entity_reference(self.entity,
                                         self.in_document.namespace)

                c_ent = self.ctx.corpus.make_object(
                    CdmObjectType.CONSTANT_ENTITY_DEF)
                c_ent.entity_shape = self.ctx.corpus.make_ref(
                    CdmObjectType.ENTITY_REF, 'entityGroupSet', True)
                c_ent.constant_values = [[
                    entity_ref, att_references[idx]
                ] for idx, entity_ref in enumerate(ent_references)]
                param = self.ctx.corpus.make_ref(CdmObjectType.ENTITY_REF,
                                                 c_ent, False)
                reqd_trait.parameter_values.update_parameter_value(
                    res_opt, 'entityReferences', param)

        # a 'structured' directive wants to keep all entity attributes together in a group
        if arc and arc.res_opt.directives and arc.res_opt.directives.has(
                'structured'):
            ra_sub = ResolvedAttribute(rts_this_att.res_opt, rasb.ras,
                                       self.name, rasb.ras.attribute_context)
            if rel_info.is_array:
                # put a resolved trait on this att group, yuck,
                #  hope I never need to do this again and then need to make a function for this
                tr = self.ctx.corpus.make_object(CdmObjectType.TRAIT_REF,
                                                 'is.linkedEntity.array', True)
                t = tr.fetch_object_definition(res_opt)
                rt = ResolvedTrait(t, None, [], [])
                ra_sub.resolved_traits = ra_sub.resolved_traits.merge(rt, True)
            rasb = ResolvedAttributeSetBuilder()
            rasb.own_one(ra_sub)

        return rasb
Esempio n. 7
0
    def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet',
                                           attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_alter_traits_param = AttributeContextParameters()
        attr_ctx_op_alter_traits_param._under = attr_ctx
        attr_ctx_op_alter_traits_param._type = CdmAttributeContextType.OPERATION_ALTER_TRAITS
        attr_ctx_op_alter_traits_param._name = 'operation/index{}/{}'.format(self._index, self.get_name())
        attr_ctx_op_alter_traits = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_alter_traits_param)

        # Get the top-level attribute names of the selected attributes to apply
        # We use the top-level names because the applyTo list may contain a previous name our current resolved attributes had
        top_level_selected_attribute_names = ProjectionResolutionCommonUtil._get_top_list(
            proj_ctx, self.apply_to) if self.apply_to is not None else None  # type: Dict[str, str]

        # Iterate through all the PAS in the PASSet generated from the projection source's resolved attributes
        for current_PAS in proj_ctx._current_attribute_state_set._states:
            # Check if the current projection attribute state's resolved attribute is in the list of selected attributes
            # If this attribute is not in the list, then we are including it in the output without changes
            if top_level_selected_attribute_names is None or current_PAS._current_resolved_attribute.resolved_name in top_level_selected_attribute_names:

                # Create a new attribute context for the new artifact attribute we will create
                attr_ctx_new_attr_param = AttributeContextParameters()
                attr_ctx_new_attr_param._under = attr_ctx_op_alter_traits
                attr_ctx_new_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
                attr_ctx_new_attr_param._name = current_PAS._current_resolved_attribute.resolved_name
                attr_ctx_new_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_new_attr_param)

                new_res_attr = None

                if isinstance(current_PAS._current_resolved_attribute.target, ResolvedAttributeSet):
                    # Attribute group
                    # Create a copy of resolved attribute set
                    res_attr_new_copy = current_PAS._current_resolved_attribute.target.copy()
                    new_res_attr = ResolvedAttribute(proj_ctx._projection_directive._res_opt, res_attr_new_copy,
                                                     current_PAS._current_resolved_attribute.resolved_name, attr_ctx_new_attr)

                    # the resolved attribute group obtained from previous projection operation may have a different set of traits comparing to the resolved attribute target.
                    #  We would want to take the set of traits from the resolved attribute.
                    new_res_attr.resolved_traits = current_PAS._current_resolved_attribute.resolved_traits.deep_copy()

                elif isinstance(current_PAS._current_resolved_attribute.target, CdmAttribute):
                    # Entity Attribute or Type Attribute
                    new_res_attr = self._create_new_resolved_attribute(
                        proj_ctx, attr_ctx_new_attr, current_PAS._current_resolved_attribute, current_PAS._current_resolved_attribute.resolved_name)

                else:
                    logger.error(self.ctx, self._TAG, CdmOperationAlterTraits._append_projection_attribute_state.__name__,
                                 self.at_corpus_path, CdmLogCode.ERR_PROJ_UNSUPPORTED_SOURCE, str(current_PAS._current_resolved_attribute.object_type), self.get_name())
                    proj_output_set._add(current_PAS)
                    break

                new_res_attr.resolved_traits = new_res_attr.resolved_traits.merge_set(self._resolved_new_traits(proj_ctx, current_PAS))
                self._remove_traits_in_new_attribute(proj_ctx._projection_directive._res_opt, new_res_attr)

                # Create a projection attribute state for the new attribute with new applied traits by creating a copy of the current state
                # Copy() sets the current state as the previous state for the new one
                new_PAS = current_PAS._copy()

                # Update the resolved attribute to be the new attribute we created
                new_PAS._current_resolved_attribute = new_res_attr

                proj_output_set._add(new_PAS)

            else:
                # Pass through
                proj_output_set._add(current_PAS)

        return proj_output_set
    def _build_fake_tree(self,
                         corpus: 'CdmCorpusDefinition') -> 'ProjectionContext':
        proj_dir = ProjectionDirective(ResolveOptions(), None)
        pc = ProjectionContext(proj_dir, None)

        p1 = ProjectionAttributeState(corpus.ctx)
        p1._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '1', '1', None)
        p2 = ProjectionAttributeState(corpus.ctx)
        p2._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '2', '2', None)
        p4 = ProjectionAttributeState(corpus.ctx)
        p4._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '4', '4', None)
        p5 = ProjectionAttributeState(corpus.ctx)
        p5._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '5', '5', None)
        p6 = ProjectionAttributeState(corpus.ctx)
        p6._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '6', '6', None)
        p7 = ProjectionAttributeState(corpus.ctx)
        p7._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '7', '7', None)
        p8 = ProjectionAttributeState(corpus.ctx)
        p8._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '8', '8', None)
        p9 = ProjectionAttributeState(corpus.ctx)
        p9._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '9', '9', None)
        p10 = ProjectionAttributeState(corpus.ctx)
        p10._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '10', '10', None)
        p11 = ProjectionAttributeState(corpus.ctx)
        p11._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '11', '11', None)
        p12 = ProjectionAttributeState(corpus.ctx)
        p12._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '12', '12', None)
        p13 = ProjectionAttributeState(corpus.ctx)
        p13._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '13', '13', None)
        p14 = ProjectionAttributeState(corpus.ctx)
        p14._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '14', '14', None)
        p15 = ProjectionAttributeState(corpus.ctx)
        p15._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '15', '15', None)
        p16 = ProjectionAttributeState(corpus.ctx)
        p16._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '16', '16', None)
        p17 = ProjectionAttributeState(corpus.ctx)
        p17._current_resolved_attribute = ResolvedAttribute(
            proj_dir._res_opt, '17', '17', None)

        p1._previous_state_list = []
        p2._previous_state_list = []
        p4._previous_state_list = []
        p5._previous_state_list = []
        p6._previous_state_list = []
        p7._previous_state_list = []
        p8._previous_state_list = []
        p9._previous_state_list = []
        p10._previous_state_list = []
        p11._previous_state_list = []
        p12._previous_state_list = []
        p13._previous_state_list = []
        p14._previous_state_list = []
        p15._previous_state_list = []
        p16._previous_state_list = []
        p17._previous_state_list = []

        p11._previous_state_list.append(p7)
        p7._previous_state_list.append(p2)
        p2._previous_state_list.append(p1)
        p12._previous_state_list.append(p8)
        p8._previous_state_list.append(p1)
        p13._previous_state_list.append(p9)
        p9._previous_state_list.append(p4)
        p9._previous_state_list.append(p5)
        p13._previous_state_list.append(p10)
        p10._previous_state_list.append(p6)
        p14._previous_state_list.append(p16)
        p16._previous_state_list.append(p1)
        p15._previous_state_list.append(p7)
        p17._previous_state_list.append(p8)

        pc._current_attribute_state_set._add(p11)
        pc._current_attribute_state_set._add(p12)
        pc._current_attribute_state_set._add(p13)
        pc._current_attribute_state_set._add(p14)
        pc._current_attribute_state_set._add(p15)
        pc._current_attribute_state_set._add(p17)

        return pc
Esempio n. 9
0
    def _construct_resolved_attributes(
        self,
        res_opt: 'ResolveOptions',
        under: Optional['CdmAttributeContext'] = None
    ) -> 'ResolvedAttributeSetBuilder':
        from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder, ResolvedTrait
        from cdm.utilities import AttributeContextParameters, AttributeResolutionDirectiveSet

        from .cdm_object import CdmObject

        rasb = ResolvedAttributeSetBuilder()
        ctx_ent = self.entity
        under_att = under
        acp_ent = None

        if not res_opt._in_circular_reference:
            arc = self._fetch_att_res_context(res_opt)

            # complete cheating but is faster.
            # this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them
            rel_info = self._get_relationship_info(arc.res_opt, arc)
            res_opt._depth_info.current_depth = rel_info.next_depth
            res_opt._depth_info.max_depth_exceeded = rel_info.max_depth_exceeded
            res_opt._depth_info.max_depth = rel_info.max_depth

            ctx_ent_obj_def = ctx_ent.fetch_object_definition(res_opt)

            if ctx_ent_obj_def and ctx_ent_obj_def.object_type == CdmObjectType.PROJECTION_DEF:
                # A Projection

                # if the max depth is exceeded it should not try to execute the projection
                if not res_opt._depth_info.max_depth_exceeded:
                    proj_directive = ProjectionDirective(
                        res_opt, self, ctx_ent)
                    proj_def = ctx_ent_obj_def
                    proj_ctx = proj_def._construct_projection_context(
                        proj_directive, under)
                    rasb._resolved_attribute_set = proj_def._extract_resolved_attributes(
                        proj_ctx, under_att)
            else:
                # An Entity Reference

                if under_att:
                    # make a context for this attribute that holds the attributes that come up from the entity
                    acp_ent = AttributeContextParameters(
                        under=under_att,
                        type=CdmAttributeContextType.ENTITY,
                        name=ctx_ent.fetch_object_definition_name(),
                        regarding=ctx_ent,
                        include_traits=True)

                if rel_info.is_by_ref:
                    # make the entity context that a real recursion would have give us
                    if under:
                        under = rasb._resolved_attribute_set.create_attribute_context(
                            res_opt, acp_ent)

                    # if selecting from one of many attributes, then make a context for each one
                    if under and rel_info.selects_one:
                        # the right way to do this is to get a resolved entity from the embedded entity and then
                        # look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes
                        # that seems like a disaster waiting to happen given endless looping, etc.
                        # for now, just insist that only the top level entity attributes declared in the ref entity will work
                        ent_pick_from = self.entity.fetch_object_definition(
                            res_opt)
                        atts_pick = ent_pick_from.attributes
                        if ent_pick_from and atts_pick:
                            for attribute in atts_pick:
                                if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF:
                                    # a table within a table. as expected with a selects_one attribute
                                    # since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key
                                    # these are the same contexts that would get created if we recursed
                                    # first this attribute
                                    acp_ent_att = AttributeContextParameters(
                                        under=under,
                                        type=CdmAttributeContextType.
                                        ATTRIBUTE_DEFINITION,
                                        name=attribute.
                                        fetch_object_definition_name(),
                                        regarding=attribute,
                                        include_traits=True)
                                    pick_under = rasb._resolved_attribute_set.create_attribute_context(
                                        res_opt, acp_ent_att)
                                    # and the entity under that attribute
                                    pick_ent = attribute.entity
                                    pick_ent_type = CdmAttributeContextType.PROJECTION if pick_ent.fetch_object_definition(
                                        res_opt
                                    ).object_type == CdmObjectType.PROJECTION_DEF else CdmAttributeContextType.ENTITY
                                    acp_ent_att_ent = AttributeContextParameters(
                                        under=pick_under,
                                        type=pick_ent_type,
                                        name=pick_ent.
                                        fetch_object_definition_name(),
                                        regarding=pick_ent,
                                        include_traits=True)
                                    rasb._resolved_attribute_set.create_attribute_context(
                                        res_opt, acp_ent_att_ent)

                    # if we got here because of the max depth, need to impose the directives to make the trait work as expected
                    if rel_info.max_depth_exceeded:
                        if not arc.res_opt.directives:
                            arc.res_opt.directives = AttributeResolutionDirectiveSet(
                            )
                        arc.res_opt.directives.add('referenceOnly')
                else:
                    res_link = res_opt.copy()
                    res_link._symbol_ref_set = res_opt._symbol_ref_set
                    rasb.merge_attributes(
                        self.entity._fetch_resolved_attributes(
                            res_link, acp_ent))

                    # need to pass up max_depth_exceeded if it was hit
                    if res_link._depth_info.max_depth_exceeded:
                        res_opt._depth_info = res_link._depth_info._copy()

                # from the traits of purpose and applied here, see if new attributes get generated
                rasb._resolved_attribute_set.attribute_context = under_att
                rasb.apply_traits(arc)
                rasb.generate_applier_attributes(
                    arc, True)  # True = apply the prepared traits to new atts
                # this may have added symbols to the dependencies, so merge them
                res_opt._symbol_ref_set._merge(arc.res_opt._symbol_ref_set)

                # use the traits for linked entity identifiers to record the actual foreign key links
                if rasb._resolved_attribute_set and rasb._resolved_attribute_set._set and rel_info.is_by_ref:
                    for att in rasb._resolved_attribute_set._set:
                        reqd_trait = att.resolved_traits.find(
                            res_opt, 'is.linkedEntity.identifier')
                        if not reqd_trait:
                            continue

                        if not reqd_trait.parameter_values:
                            logger.warning(
                                self._ctx, self._TAG,
                                CdmEntityAttributeDefinition.
                                _construct_resolved_attributes.__name__,
                                self.at_corpus_path, CdmLogCode.
                                WARN_IDENTIFIER_ARGUMENTS_NOT_SUPPORTED)
                            continue

                        ent_references = []
                        att_references = []

                        def add_entity_reference(
                                entity_ref: 'CdmEntityReference',
                                namespace: str):
                            ent_def = entity_ref.fetch_object_definition(
                                res_opt)
                            required_trait = entity_ref._fetch_resolved_traits(
                                res_opt).find(res_opt, 'is.identifiedBy')
                            if required_trait and ent_def:
                                att_ref = required_trait.parameter_values.fetch_parameter_value(
                                    'attribute').value
                                att_name = att_ref.named_reference.split(
                                    '/')[-1]
                                absolute_ent_path = self.ctx.corpus.storage.create_absolute_corpus_path(
                                    ent_def.at_corpus_path,
                                    ent_def.in_document)
                                ent_references.append(absolute_ent_path)
                                att_references.append(att_name)

                        if rel_info.selects_one:
                            ent_pick_from = self.entity.fetch_object_definition(
                                res_opt)
                            atts_pick = ent_pick_from.attributes if ent_pick_from else None
                            if atts_pick:
                                for attribute in atts_pick:
                                    if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF:
                                        add_entity_reference(
                                            attribute.entity,
                                            self.in_document.namespace)
                        else:
                            add_entity_reference(self.entity,
                                                 self.in_document.namespace)

                        c_ent = self.ctx.corpus.make_object(
                            CdmObjectType.CONSTANT_ENTITY_DEF)
                        c_ent.entity_shape = self.ctx.corpus.make_ref(
                            CdmObjectType.ENTITY_REF, 'entityGroupSet', True)
                        c_ent.constant_values = [[
                            entity_ref, att_references[idx]
                        ] for idx, entity_ref in enumerate(ent_references)]
                        param = self.ctx.corpus.make_ref(
                            CdmObjectType.ENTITY_REF, c_ent, False)
                        reqd_trait.parameter_values.update_parameter_value(
                            res_opt, 'entityReferences', param)

                # a 'structured' directive wants to keep all entity attributes together in a group
                if arc and arc.res_opt.directives and arc.res_opt.directives.has(
                        'structured'):
                    # make one resolved attribute with a name from this entityAttribute that contains the set
                    # of atts we just put together.
                    ra_sub = ResolvedAttribute(arc.traits_to_apply.res_opt,
                                               rasb._resolved_attribute_set,
                                               self.name, under_att)
                    if rel_info.is_array:
                        # put a resolved trait on this att group
                        # hope I never need to do this again and then need to make a function for this
                        tr = self.ctx.corpus.make_object(
                            CdmObjectType.TRAIT_REF, 'is.linkedEntity.array',
                            True)
                        t = tr.fetch_object_definition(res_opt)
                        rt = ResolvedTrait(t, None, [], [])
                        ra_sub.resolved_traits = ra_sub.resolved_traits.merge(
                            rt, True)
                    depth = rasb._resolved_attribute_set._depth_traveled
                    rasb = ResolvedAttributeSetBuilder()
                    rasb._resolved_attribute_set.attribute_context = ra_sub.att_ctx  # this got set to null with the new builder
                    rasb.own_one(ra_sub)
                    rasb._resolved_attribute_set._depth_traveled = depth

        # how ever they got here, mark every attribute from this entity attribute as now being 'owned' by this entityAtt
        rasb._resolved_attribute_set._set_attribute_ownership(self.name)
        rasb._resolved_attribute_set._depth_traveled += 1

        return rasb