예제 #1
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
예제 #2
0
    def _fetch_att_res_context(self, res_opt):
        from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition
        from cdm.resolvedmodel.resolved_attribute_set_builder import AttributeResolutionContext
        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)

        return AttributeResolutionContext(res_opt, res_guide_with_default, rts_this_att)
예제 #3
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
예제 #4
0
    def fetch_resolved_entity_references(
        self,
        res_opt: Optional['ResolveOptions'] = None
    ) -> 'ResolvedEntityReferenceSet':
        res_opt = res_opt if res_opt is not None else ResolveOptions(
            wrt_doc=self)

        from cdm.resolvedmodel import AttributeResolutionContext, ResolvedEntityReference, ResolvedEntityReferenceSide, ResolvedEntityReferenceSet
        from cdm.utilities import ResolveOptions

        from .cdm_object import CdmObject

        rts_this_att = self._fetch_resolved_traits(res_opt)
        res_guide = self.resolution_guidance

        # this context object holds all of the info about what needs to happen to resolve these attributes
        arc = AttributeResolutionContext(res_opt, res_guide, rts_this_att)

        rel_info = self._get_relationship_info(res_opt, arc)
        if not rel_info.is_by_ref or rel_info.is_array:
            return None

        # only place this is used, so logic here instead of encapsulated.
        # make a set and the one ref it will hold
        rers = ResolvedEntityReferenceSet(res_opt)
        rer = ResolvedEntityReference()
        # referencing attribute(s) come from this attribute
        rer.referencing._rasb.merge_attributes(
            self._fetch_resolved_attributes(res_opt))

        def resolve_side(
                ent_ref: 'CdmEntityReference') -> ResolvedEntityReferenceSide:
            side_other = ResolvedEntityReferenceSide()
            if ent_ref:
                # reference to the other entity, hard part is the attribue name.
                # by convention, this is held in a trait that identifies the key
                side_other.entity = ent_ref.fetch_object_definition(res_opt)
                if side_other.entity:
                    other_attribute = None
                    other_opts = ResolveOptions(wrt_doc=res_opt.wrt_doc,
                                                directives=res_opt.directives)
                    trait = ent_ref._fetch_resolved_traits(other_opts).find(
                        other_opts, 'is.identifiedBy')
                    if trait and trait.parameter_values and trait.parameter_values.length:
                        other_ref = trait.parameter_values.fetch_parameter_value(
                            'attribute').value
                        if isinstance(other_ref, CdmObject):
                            other_attribute = other_ref.fetch_object_definition(
                                other_opts)
                            if other_attribute:
                                side_other._rasb.own_one(
                                    side_other.entity.
                                    _fetch_resolved_attributes(other_opts).get(
                                        other_attribute.get_name()).copy())

            return side_other

        # either several or one entity
        # for now, a sub for the 'select one' idea
        if self.entity.explicit_reference:
            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:
                        entity = attribute.entity
                        rer.referenced.append(resolve_side(entity))
        else:
            rer.referenced.append(resolve_side(self.entity))

        rers.rer_set.append(rer)

        return rers
예제 #5
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
예제 #6
0
    def _construct_resolved_attributes(self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext'] = None) -> '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):
        # an extended entity, traits applied to extended entity, exhibited traits of main entity,
        # the (datatype or entity) used as an attribute, traits applied to that datatype or entity,
        # the relationsip of the attribute, the attribute definition itself and included attribute groups,
        #  any traits applied to the attribute.
        from cdm.resolvedmodel import ResolvedAttributeSetBuilder
        from cdm.utilities import AttributeContextParameters

        self._rasb = ResolvedAttributeSetBuilder()
        self._rasb.ras.attribute_context = under

        if self.extends_entity:
            ext_ref = self.extends_entity
            extends_ref_under = None
            acp_ext_ent = None

            if under:
                acp_ext = AttributeContextParameters(
                    under=under,
                    type=CdmAttributeContextType.ENTITY_REFERENCE_EXTENDS,
                    name='extends',
                    regarding=None,
                    include_traits=False)
                extends_ref_under = self._rasb.ras.create_attribute_context(res_opt, acp_ext)

            if ext_ref.explicit_reference and ext_ref.fetch_object_definition(res_opt).object_type == CdmObjectType.PROJECTION_DEF:
                # A Projection

                ext_ref_obj_def = ext_ref.fetch_object_definition(res_opt)
                if extends_ref_under:
                    acp_ext_ent = AttributeContextParameters(
                        under=extends_ref_under,
                        type=CdmAttributeContextType.PROJECTION,
                        name=ext_ref_obj_def.get_name(),
                        regarding=ext_ref,
                        include_traits=False
                    )

                proj_directive = ProjectionDirective(res_opt, self, ext_ref)
                proj_def = ext_ref_obj_def
                proj_ctx = proj_def._construct_projection_context(proj_directive, extends_ref_under)

                self._rasb.ras = proj_def._extract_resolved_attributes(proj_ctx)
            else:
                # An Entity Reference

                if extends_ref_under:
                    acp_ext_ent = AttributeContextParameters(
                        under=extends_ref_under,
                        type=CdmAttributeContextType.ENTITY,
                        name=ext_ref.named_reference if ext_ref.named_reference else ext_ref.explicit_reference.get_name(),
                        regarding=ext_ref,
                        include_traits=False
                    )

                # save moniker, extended entity may attach a different moniker that we do not
                # want to pass along to getting this entities attributes
                old_moniker = res_opt._from_moniker

                self._rasb.merge_attributes(self.extends_entity._fetch_resolved_attributes(res_opt, acp_ext_ent))

                if not res_opt._check_attribute_count(self._rasb.ras._resolved_attribute_count):
                    logger.error(self._TAG, self.ctx, 'Maximum number of resolved attributes reached for the entity: {}.'.format(self.entity_name))
                    return None

                if self.extends_entity_resolution_guidance:
                    # some guidance was given on how to integrate the base attributes into the set. apply that guidance
                    rts_base = self._fetch_resolved_traits(res_opt)  # type: ResolvedTraitSet

                    # this context object holds all of the info about what needs to happen to resolve these attributes.
                    # make a copy and set defaults if needed
                    res_guide = self.extends_entity_resolution_guidance.copy(res_opt)  # CdmAttributeResolutionGuidanceDefinition
                    res_guide._update_attribute_defaults(res_guide.fetch_object_definition_name())
                    # holds all the info needed by the resolver code
                    arc = AttributeResolutionContext(res_opt, res_guide, rts_base)  # type: AttributeResolutionContext

                    self._rasb.generate_applier_attributes(arc, False)  # true = apply the prepared traits to new atts

                # reset to the old moniker
                res_opt._from_moniker = old_moniker

        self._rasb.mark_inherited()
        self._rasb.ras.attribute_context = under

        if self.attributes:
            for att in self.attributes:
                acp_att = None
                if under:
                    acp_att = AttributeContextParameters(
                        under=under,
                        type=CdmAttributeContextType.ATTRIBUTE_DEFINITION,
                        name=att.fetch_object_definition_name(),
                        regarding=att,
                        include_traits=False)
                self._rasb.merge_attributes(att._fetch_resolved_attributes(res_opt, acp_att))

                if not res_opt._check_attribute_count(self._rasb.ras._resolved_attribute_count):
                    logger.error(self._TAG, self.ctx, 'Maximum number of resolved attributes reached for the entity: {}.'.format(self.entity_name))
                    return None

        self._rasb.mark_order()
        self._rasb.ras.attribute_context = under

        # things that need to go away
        self._rasb.remove_requested_atts()

        return self._rasb