def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        from cdm.resolvedmodel import ResolvedAttributeSetBuilder
        from cdm.utilities import AttributeContextParameters

        rasb = ResolvedAttributeSetBuilder()

        if under:
            acp_att_grp = AttributeContextParameters(
                under=under,
                type=CdmAttributeContextType.ATTRIBUTE_GROUP,
                name=self.get_name(),
                regarding=self,
                include_traits=False)
            under = rasb.ras.create_attribute_context(res_opt, acp_att_grp)

        if self.members:
            for att in self.members:
                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)
                rasb.merge_attributes(
                    att._fetch_resolved_attributes(res_opt, acp_att))
        rasb.ras.attribute_context = under

        # things that need to go away
        rasb.remove_requested_atts()

        return rasb
Exemplo n.º 2
0
    def _add_new_artifact_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> None:
        # Create a new attribute context for the operation
        attr_ctx_op_add_artifact_attr_param = AttributeContextParameters()
        attr_ctx_op_add_artifact_attr_param._under = attr_ctx
        attr_ctx_op_add_artifact_attr_param._type = CdmAttributeContextType.OPERATION_ADD_ARTIFACT_ATTRIBUTE
        attr_ctx_op_add_artifact_attr_param._name = 'operation/index{}/{}'.format(self._index, self.get_name())
        attr_ctx_op_add_artifact_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_add_artifact_attr_param)

        from cdm.objectmodel import CdmTypeAttributeDefinition, CdmEntityAttributeDefinition, CdmAttributeGroupReference
        if isinstance(self.new_attribute, CdmTypeAttributeDefinition):
            # 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_add_artifact_attr
            attr_ctx_new_attr_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_NEW_ARTIFACT
            attr_ctx_new_attr_param._name = self.new_attribute.fetch_object_definition_name()
            attr_ctx_new_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_new_attr_param)
            new_res_attr = self._create_new_resolved_attribute(proj_ctx, attr_ctx_new_attr, self.new_attribute)

            # Create a new projection attribute state for the new artifact attribute and add it to the output set
            # There is no previous state for the newly created attribute
            new_PAS = ProjectionAttributeState(self.ctx)
            new_PAS._current_resolved_attribute = new_res_attr
            proj_output_set._add(new_PAS)

        elif isinstance(self.new_attribute, CdmEntityAttributeDefinition) or isinstance(self.new_attribute, CdmAttributeGroupReference):
            type_str = 'an entity attribute' if isinstance(self.new_attribute, CdmEntityAttributeDefinition) else 'an attribute group'
            logger.warning(self.ctx, self._TAG, CdmOperationAddArtifactAttribute._append_projection_attribute_state.__name__,
                           self.at_corpus_path, CdmLogCode.WARN_PROJ_ADD_ARTIFACT_ATTR_NOT_SUPPORTED, type_str)

        else:
            logger.error(self.ctx, self._TAG, CdmOperationAddArtifactAttribute._append_projection_attribute_state.__name__,
                         self.at_corpus_path, CdmLogCode.ERR_PROJ_UNSUPPORTED_SOURCE, str(self.new_attribute.object_type), self.get_name())

        return proj_output_set
Exemplo n.º 3
0
    def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Pass through all the input projection attribute states if there are any
        for current_PAS in proj_ctx._current_attribute_state_set._states:
            proj_output_set._add(current_PAS)

        # Create a new attribute context for the operation
        attr_ctx_op_add_count_param = AttributeContextParameters()
        attr_ctx_op_add_count_param._under = attr_ctx
        attr_ctx_op_add_count_param._type = CdmAttributeContextType.OPERATION_ADD_COUNT_ATTRIBUTE
        attr_ctx_op_add_count_param._name = 'operation/index{}/operationAddCountAttribute'.format(self._index)
        attr_ctx_op_add_count = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_add_count_param)

        # Create a new attribute context for the Count attribute we will create
        attr_ctx_count_attr_param = AttributeContextParameters()
        attr_ctx_count_attr_param._under = attr_ctx_op_add_count
        attr_ctx_count_attr_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_EXPANSION_TOTAL
        attr_ctx_count_attr_param._name = self.count_attribute.name
        attr_ctx_count_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_count_attr_param)

        # Create the Count attribute with the specified CountAttribute as its target and apply the trait "is.linkedEntity.array.count" to it
        add_trait = ['is.linkedEntity.array.count']
        new_res_attr = self._create_new_resolved_attribute(proj_ctx, attr_ctx_count_attr, self.count_attribute, None, add_trait)

        # Create a new projection attribute state for the new Count attribute and add it to the output set
        # There is no previous state for the newly created Count attribute
        new_PAS = ProjectionAttributeState(proj_output_set._ctx)
        new_PAS._current_resolved_attribute = new_res_attr

        proj_output_set._add(new_PAS)

        return proj_output_set
    def _append_projection_attribute_state(
            self, proj_ctx: 'ProjectionContext',
            proj_output_set: 'ProjectionAttributeStateSet',
            attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create new attribute context for the operation
        attr_ctx_op_fk_param = AttributeContextParameters()
        attr_ctx_op_fk_param._under = attr_ctx
        attr_ctx_op_fk_param._type = CdmAttributeContextType.OPERATION_REPLACE_AS_FOREIGN_KEY
        attr_ctx_op_fk_param._name = 'operation/index{}/operationReplaceAsForeignKey'.format(
            self._index)
        attr_ctx_op_fk = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt, attr_ctx_op_fk_param)

        # Create new attribute context for the AddedAttributeIdentity
        attr_ctx_fk_param = AttributeContextParameters()
        attr_ctx_fk_param._under = attr_ctx_op_fk
        attr_ctx_fk_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_IDENTITY
        attr_ctx_fk_param._name = '_foreignKey'
        attr_ctx_FK = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt, attr_ctx_fk_param)

        # get the added attribute and applied trait
        # the name here will be {m} and not {A}{o}{M} - should this map to the not projections approach and default to {A}{o}{M} - ???
        sub_FK = self.replace_with
        add_trait = ['is.linkedEntity.identifier']

        # Create new resolved attribute, set the new attribute as target, and apply "is.linkedEntity.identifier" trait
        res_attr_new_FK = self._create_new_resolved_attribute(
            proj_ctx, attr_ctx_FK, sub_FK, None, add_trait)

        outputFromOpPasSet = self._create_new_projection_attribute_state_set(
            proj_ctx, proj_output_set, res_attr_new_FK, self.reference)

        return outputFromOpPasSet
Exemplo n.º 5
0
    def _append_projection_attribute_state(
            self, proj_ctx: 'ProjectionContext',
            proj_output_set: 'ProjectionAttributeStateSet',
            attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Pass through all the input projection attribute states if there are any
        for current_PAS in proj_ctx._current_attribute_state_set._states:
            proj_output_set._add(current_PAS)

        # Create a new attribute context for the operation
        attr_ctx_op_add_supporting_attr_param = AttributeContextParameters()
        attr_ctx_op_add_supporting_attr_param._under = attr_ctx
        attr_ctx_op_add_supporting_attr_param._type = CdmAttributeContextType.OPERATION_ADD_SUPPORTING_ATTRIBUTE
        attr_ctx_op_add_supporting_attr_param._name = 'operation/index{}/{}'.format(
            self._index, self.get_name())
        attr_ctx_op_add_supporting_attr = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_op_add_supporting_attr_param)

        # Create a new attribute context for the supporting attribute we will create
        attr_ctx_supporting_attr_param = AttributeContextParameters()
        attr_ctx_supporting_attr_param._under = attr_ctx_op_add_supporting_attr
        attr_ctx_supporting_attr_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_SUPPORTING
        attr_ctx_supporting_attr_param._name = self.supporting_attribute.name
        attr_ctx_supporting_attr = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_supporting_attr_param)

        # TODO: this if statement keeps the functionality the same way it works currently in resolution guidance.
        # This should be changed to point to the foreign key attribute instead.
        # There has to be some design decisions about how this will work and will be done in the next release.
        if len(proj_ctx._current_attribute_state_set._states) > 0:
            last_state = proj_ctx._current_attribute_state_set._states[
                -1]  # type: ProjectionAttributeState
            in_support_of_trait = self.supporting_attribute.applied_traits.append(
                'is.addedInSupportOf')  # type: CdmTraitReferenceBase
            in_support_of_trait.arguments.append(
                'inSupportOf',
                last_state._current_resolved_attribute.resolved_name)

        # Create the supporting attribute with the specified 'SupportingAttribute' property as its target and apply the trait 'is.virtual.attribute' to it
        add_trait = ['is.virtual.attribute']
        new_res_attr = self._create_new_resolved_attribute(
            proj_ctx,
            attr_ctx_supporting_attr,
            self.supporting_attribute,
            added_simple_ref_traits=add_trait)

        # Create a new projection attribute state for the new supporting attribute and add it to the output set
        # There is no previous state for the newly created supporting attribute
        new_PAS = ProjectionAttributeState(proj_output_set._ctx)
        new_PAS._current_resolved_attribute = new_res_attr

        proj_output_set._add(new_PAS)

        return proj_output_set
    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_exclude_attrs_param = AttributeContextParameters()
        attr_ctx_op_exclude_attrs_param._under = attr_ctx
        attr_ctx_op_exclude_attrs_param._type = CdmAttributeContextType.OPERATION_EXCLUDE_ATTRIBUTES
        attr_ctx_op_exclude_attrs_param._name = 'operation/index{}/operationExcludeAttributes'.format(self._index)
        attr_ctx_op_exclude_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_exclude_attrs_param)

        # Get the top-level attribute names of the attributes to exclude
        # We use the top-level names because the exclude list may contain a previous name our current resolved attributes had
        top_level_exclude_attribute_names = ProjectionResolutionCommonUtil._get_top_list(proj_ctx, self.exclude_attributes)

        # Initialize a projection attribute context tree builder with the created attribute context for the operation
        attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_exclude_attrs)

        # 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:
            # Check if the current projection attribute state's resolved attribute is in the list of attributes to exclude
            # If this attribute is not in the exclude list, then we are including it in the output
            if current_PAS._current_resolved_attribute.resolved_name not in top_level_exclude_attribute_names:
                # Create the attribute context parameters and just store it in the builder for now
                # We will create the attribute contexts at the end
                attr_ctx_tree_builder._create_and_store_attribute_context_parameters(
                    None, current_PAS, current_PAS._current_resolved_attribute,
                    CdmAttributeContextType.ATTRIBUTE_DEFINITION,
                    current_PAS._current_resolved_attribute.att_ctx,  # lineage is the included attribute
                    None)  # don't know who will point here yet

                # Create a projection attribute state for the included attribute by creating a copy of the current state
                # Copy() sets the current state as the previous state for the new one
                # We only create projection attribute states for attributes that are not in the exclude list
                new_PAS = current_PAS._copy()

                proj_output_set._add(new_PAS)
            else:
                # The current projection attribute state's resolved attribute is in the exclude list

                # Get the attribute name the way it appears in the exclude list
                exclude_attribute_name = top_level_exclude_attribute_names[current_PAS._current_resolved_attribute.resolved_name]

                # Create the attribute context parameters and just store it in the builder for now
                # We will create the attribute contexts at the end
                attr_ctx_tree_builder._create_and_store_attribute_context_parameters(
                    exclude_attribute_name, current_PAS, current_PAS._current_resolved_attribute,
                    CdmAttributeContextType.ATTRIBUTE_EXCLUDED,
                    current_PAS._current_resolved_attribute.att_ctx,  # lineage is the included attribute
                    None)  # don't know who will point here yet, excluded, so... this could be the end for you.

        # Create all the attribute contexts and construct the tree
        attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx)

        return proj_output_set
    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 an attribute set build that owns one resolved attribute
            attribute_rasb = ResolvedAttributeSetBuilder()
            attribute_rasb.own_one(current_PAS._current_resolved_attribute)

            # Merge the attribute set containing one attribute with the one holding all the attributes
            rasb.merge_attributes(attribute_rasb.ras)

            # Add each attribute's attribute context to the resolved attribute set attribute context
            attr_ctx_attr_group.contents.append(
                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.ras,
            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
Exemplo n.º 8
0
    def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        from cdm.resolvedmodel import ResolvedAttributeSetBuilder
        from cdm.utilities import AttributeContextParameters

        rasb = ResolvedAttributeSetBuilder()
        rasb._resolved_attribute_set.attribute_context = under
        acp_ent = None
        if under:
            acp_ent = AttributeContextParameters(
                under=under,
                type=CdmAttributeContextType.ENTITY,
                name=self.entity_shape.fetch_object_definition_name(),
                regarding=self.entity_shape,
                include_traits=True)

        if self.entity_shape:
            rasb.merge_attributes(
                self.entity_shape._fetch_resolved_attributes(res_opt, acp_ent))

        # things that need to go away
        rasb.remove_requested_atts()

        return rasb
Exemplo n.º 9
0
    def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        # find and cache the complete set of attributes
        from cdm.resolvedmodel import ResolvedAttributeSetBuilder
        from cdm.utilities import AttributeContextParameters

        rasb = ResolvedAttributeSetBuilder()
        rasb.ras.attribute_context = under
        definition = self.fetch_object_definition(res_opt)
        if definition:
            acp_ref = None
            if under:
                # ask for a 'pass through' context, that is, no new context at this level
                acp_ref = AttributeContextParameters(
                    under=under, type=CdmAttributeContextType.PASS_THROUGH)
            res_atts = definition._fetch_resolved_attributes(res_opt, acp_ref)
            if res_atts and res_atts._set:
                res_atts = res_atts.copy()
                rasb.merge_attributes(res_atts)
                rasb.remove_requested_atts()
        else:
            def_name = self.fetch_object_definition_name()
            logger.warning(
                self._TAG, self.ctx,
                'unable to resolve an object from the reference \'{}\''.format(
                    def_name))

        return rasb
Exemplo n.º 10
0
    def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        # find and cache the complete set of attributes
        from cdm.resolvedmodel import ResolvedAttributeSetBuilder
        from cdm.utilities import AttributeContextParameters
        from cdm.objectmodel import CdmEntityDefinition

        rasb = ResolvedAttributeSetBuilder()
        rasb._resolved_attribute_set.attribute_context = under
        definition = self.fetch_object_definition(res_opt)
        if definition:
            acp_ref = None
            if under:
                # ask for a 'pass through' context, that is, no new context at this level
                acp_ref = AttributeContextParameters(
                    under=under, type=CdmAttributeContextType.PASS_THROUGH)
            res_atts = definition._fetch_resolved_attributes(res_opt, acp_ref)
            if res_atts and res_atts._set:
                # res_atts = res_atts.copy()  should not need this copy now that we copy from the cache. lets try!
                rasb.merge_attributes(res_atts)
                rasb.remove_requested_atts()
        else:
            logger.warning(
                self.ctx, self._TAG,
                CdmObjectReference._construct_resolved_traits.__name__,
                self.at_corpus_path, CdmLogCode.WARN_RESOLVE_OBJECT_FAILED,
                self.fetch_object_definition_name())

        return rasb
Exemplo n.º 11
0
    def _construct_resolved_attributes(
        self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext']
    ) -> 'ResolvedAttributeSetBuilder':
        from cdm.resolvedmodel import ResolvedAttributeSetBuilder
        from cdm.utilities import AttributeContextParameters

        rasb = ResolvedAttributeSetBuilder()
        all_under = under  # type: CdmAttributeContext

        if under:
            acp_att_grp = AttributeContextParameters(
                under=under,
                type=CdmAttributeContextType.ATTRIBUTE_GROUP,
                name=self.get_name(),
                regarding=self,
                include_traits=False)
            under = rasb._resolved_attribute_set.create_attribute_context(
                res_opt, acp_att_grp)

        if self.members:
            for att in self.members:
                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)
                ras_from_att = att._fetch_resolved_attributes(
                    res_opt, acp_att)  # type: ResolvedAttributeSet
                # before we just merge, need to handle the case of 'attribute restatement' AKA an entity with an attribute having the same name as an attribute
                # from a base entity. thing might come out with different names, if they do, then any attributes owned by a similar named attribute before
                # that didn't just pop out of that same named attribute now need to go away.
                # mark any attributes formerly from this named attribute that don't show again as orphans
                rasb._resolved_attribute_set.mark_orphans_for_removal(
                    cast(CdmAttributeItem, att).fetch_object_definition_name,
                    ras_from_att)
                rasb.merge_attributes(ras_from_att)

        rasb._resolved_attribute_set.attribute_context = all_under  # context must be the one expected from the caller's pov.

        # things that need to go away
        rasb.remove_requested_atts()

        return rasb
Exemplo n.º 12
0
    def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_attr_state_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_include_attrs_param = AttributeContextParameters()  # type: AttributeContextParameters
        attr_ctx_op_include_attrs_param._under = attr_ctx
        attr_ctx_op_include_attrs_param._type = CdmAttributeContextType.OPERATION_INCLUDE_ATTRIBUTES
        attr_ctx_op_include_attrs_param._name = 'operation/indexIndex{}/operationIncludeAttributes'.format(self._index)

        attr_ctx_op_include_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_include_attrs_param)  # type: CdmAttributeContext

        # Get the top-level attribute names for each of the included attributes
        # Since the include operation allows providing either current state resolved attribute names
        #   or the previous state resolved attribute names, we search for the name in the PAS tree
        #   and fetch the top level resolved attribute names.
        top_level_include_attribute_names = ProjectionResolutionCommonUtil._get_top_list(proj_ctx, self.include_attributes)  # type: Dict[str, str]

        # Initialize a projection attribute context tree builder with the created attribute context for the operation
        attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_include_attrs)

        # 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 PASs RA is in the list of attributes to include.
            if current_PAS._current_resolved_attribute.resolved_name in top_level_include_attribute_names:
                # Get the attribute name the way it appears in the include list
                include_attribute_name = top_level_include_attribute_names[current_PAS._current_resolved_attribute.resolved_name]  # type: str

                # Create the attribute context parameters and just store it in the builder for now
                # We will create the attribute contexts at the end
                attr_ctx_tree_builder._create_and_store_attribute_context_parameters(include_attribute_name, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION)

                # Create a projection attribute state for the included attribute by creating a copy of the current state
                # Copy() sets the current state as the previous state for the new one
                # We only create projection attribute states for attributes in the include list
                new_PAS = current_PAS._copy()

                proj_attr_state_set._add(new_PAS)
            else:
                # Create the attribute context parameters and just store it in the builder for now
                # We will create the attribute contexts at the end
                attr_ctx_tree_builder._create_and_store_attribute_context_parameters(None, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION)

        # Create all the attribute contexts and construct the tree
        attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx)

        return proj_attr_state_set
Exemplo n.º 13
0
    def _append_projection_attribute_state(
            self, proj_ctx: 'ProjectionContext',
            proj_attr_state_set: 'ProjectionAttributeStateSet',
            attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_include_attrs_param = AttributeContextParameters(
        )  # type: AttributeContextParameters
        attr_ctx_op_include_attrs_param._under = attr_ctx
        attr_ctx_op_include_attrs_param._type = CdmAttributeContextType.OPERATION_INCLUDE_ATTRIBUTES
        attr_ctx_op_include_attrs_param._name = 'operation/index{}/operationIncludeAttributes'.format(
            self._index)

        attr_ctx_op_include_attrs = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_op_include_attrs_param)  # type: CdmAttributeContext

        # Get the top-level attribute names for each of the included attributes
        # Since the include operation allows providing either current state resolved attribute names
        #   or the previous state resolved attribute names, we search for the name in the PAS tree
        #   and fetch the top level resolved attribute names.
        top_level_include_attribute_names = ProjectionResolutionCommonUtil._get_top_list(
            proj_ctx, self.include_attributes)  # type: Dict[str, str]

        # Initialize a projection attribute context tree builder with the created attribute context for the operation
        attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(
            attr_ctx_op_include_attrs)

        # Index that holds the current attribute name as the key and the attribute as value
        top_level_include_attribute = {
        }  # type: Dict[str, ProjectionAttributeState]

        # List of attributes that were not included on the final attribute list
        removed_attributes = []  # List[ProjectionAttributeState]

        # 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 PASs RA is in the list of attributes to include.
            if current_PAS._current_resolved_attribute.resolved_name in top_level_include_attribute_names:
                top_level_include_attribute[
                    current_PAS._current_resolved_attribute.
                    resolved_name] = current_PAS
            else:
                removed_attributes.append(current_PAS)

        # Loop through the list of attributes in the same order that was specified by the user
        for key, value in top_level_include_attribute_names.items():
            current_PAS = top_level_include_attribute[key]

            # Get the attribute name the way it appears in the include list
            include_attribute_name = value  # type: str

            # Create the attribute context parameters and just store it in the builder for now
            # We will create the attribute contexts at the end
            attr_ctx_tree_builder._create_and_store_attribute_context_parameters(
                include_attribute_name,
                current_PAS,
                current_PAS._current_resolved_attribute,
                CdmAttributeContextType.ATTRIBUTE_DEFINITION,
                current_PAS._current_resolved_attribute.
                att_ctx,  # lineage is the included attribute
                None)  # don't know who will point here yet

            # Create a projection attribute state for the included attribute by creating a copy of the current state
            # Copy() sets the current state as the previous state for the new one
            # We only create projection attribute states for attributes in the include list
            new_PAS = current_PAS._copy()

            proj_attr_state_set._add(new_PAS)

        # Generate attribute context nodes for the attributes that were not included
        for current_PAS in removed_attributes:
            # Create the attribute context parameters and just store it in the builder for now
            # We will create the attribute contexts at the end
            attr_ctx_tree_builder._create_and_store_attribute_context_parameters(
                None,
                current_PAS,
                current_PAS._current_resolved_attribute,
                CdmAttributeContextType.ATTRIBUTE_EXCLUDED,
                current_PAS._current_resolved_attribute.
                att_ctx,  # lineage is the excluded attribute
                None
            )  # don't know who will point here, probably nobody, I mean, we got excluded

        # Create all the attribute contexts and construct the tree
        attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx)

        return proj_attr_state_set
Exemplo n.º 14
0
    def _construct_projection_context(
            self,
            proj_directive: 'ProjectionDirective',
            attr_ctx: 'CdmAttributeContext',
            ras: Optional['ResolvedAttributeSet'] = None
    ) -> 'ProjectionContext':
        """
        A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract
        This function is the entry point for projection resolution.
        This function is expected to do the following 3 things:
        - Create an condition expression tree & default if appropriate
        - Create and initialize Projection Context
        - Process operations
        """
        if not attr_ctx:
            return None

        proj_context = None

        # Add projection to context tree
        acp_proj = AttributeContextParameters()
        acp_proj._under = attr_ctx
        acp_proj._type = CdmAttributeContextType.PROJECTION
        acp_proj._name = self.fetch_object_definition_name()
        acp_proj._regarding = proj_directive._owner_ref
        acp_proj._include_traits = False

        ac_proj = CdmAttributeContext._create_child_under(
            proj_directive._res_opt, acp_proj)

        acp_source = AttributeContextParameters()
        acp_source._under = ac_proj
        acp_source._type = CdmAttributeContextType.SOURCE
        acp_source._name = 'source'
        acp_source._regarding = None
        acp_source._include_traits = False

        ac_source = CdmAttributeContext._create_child_under(
            proj_directive._res_opt, acp_source)

        # Initialize the projection context
        ctx = proj_directive._owner.ctx if proj_directive._owner else None

        if self.source:
            source = self.source.fetch_object_definition(
                proj_directive._res_opt)
            if source.object_type == CdmObjectType.PROJECTION_DEF:
                # A Projection

                proj_context = source._construct_projection_context(
                    proj_directive, ac_source, ras)
            else:
                # An Entity Reference

                acp_source_projection = AttributeContextParameters()
                acp_source_projection._under = ac_source
                acp_source_projection._type = CdmAttributeContextType.ENTITY
                acp_source_projection._name = self.source.named_reference if self.source.named_reference else self.source.explicit_reference.get_name(
                )
                acp_source_projection._regarding = self.source
                acp_source_projection._include_traits = False

                ras = self.source._fetch_resolved_attributes(
                    proj_directive._res_opt,
                    acp_source_projection)  # type: ResolvedAttributeSet
                # clean up the context tree, it was left in a bad state on purpose in this call
                ras.attribute_context._finalize_attribute_context(
                    proj_directive._res_opt, ac_source.at_corpus_path,
                    self.in_document, self.in_document, None, False)

                # if polymorphic keep original source as previous state
                poly_source_set = None
                if proj_directive._is_source_polymorphic:
                    poly_source_set = ProjectionResolutionCommonUtil._get_polymorphic_source_set(
                        proj_directive, ctx, self.source, ras)

                # Now initialize projection attribute state
                pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set(
                    proj_directive, ctx, ras,
                    proj_directive._is_source_polymorphic, poly_source_set)

                proj_context = ProjectionContext(proj_directive,
                                                 ras.attribute_context)
                proj_context._current_attribute_state_set = pas_set
        else:
            # A type attribute

            # Initialize projection attribute state
            pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set(
                proj_directive,
                ctx,
                ras,
                is_source_polymorphic=False,
                polymorphic_set=None)

            proj_context = ProjectionContext(proj_directive,
                                             ras.attribute_context)
            proj_context._current_attribute_state_set = pas_set

        is_condition_valid = False
        input_values = InputValues(proj_directive)
        is_condition_valid = ExpressionTree._evaluate_condition(
            self.condition, input_values)

        if is_condition_valid and self.operations and len(self.operations) > 0:
            # Just in case operations were added programmatically, reindex operations
            for i in range(len(self.operations)):
                self.operations[i]._index = i + 1

            # Operation

            acp_gen_attr_set = AttributeContextParameters()
            acp_gen_attr_set._under = attr_ctx
            acp_gen_attr_set._type = CdmAttributeContextType.GENERATED_SET
            acp_gen_attr_set._name = '_generatedAttributeSet'

            ac_gen_attr_set = CdmAttributeContext._create_child_under(
                proj_directive._res_opt, acp_gen_attr_set)

            # Start with an empty list for each projection
            pas_operations = ProjectionAttributeStateSet(
                proj_context._current_attribute_state_set._ctx)

            # The attribute set that the operation will execute on
            operation_working_attribute_set = None  # type: ProjectionAttributeStateSet

            # The attribute set containing the attributes from the source
            source_attribute_set = proj_context._current_attribute_state_set  # type: ProjectionAttributeStateSet

            # Specifies if the operation is the first on the list to run
            first_operation_to_run = True
            for operation in self.operations:
                operation_condition = ExpressionTree._evaluate_condition(
                    operation.condition, input_values)

                if not operation_condition:
                    # Skip this operation if the condition does not evaluate to true
                    continue

                # If run_sequentially is not true then all the operations will receive the source input
                # Unless the operation overwrites this behavior using the source_input property
                source_input = operation.source_input if operation.source_input is not None else not self.run_sequentially

                # If this is the first operation to run it will get the source attribute set since the operations attribute set starts empty
                if source_input or first_operation_to_run:
                    proj_context._current_attribute_state_set = source_attribute_set
                    operation_working_attribute_set = pas_operations
                else:
                    # Needs to create a copy since this set can be modified by the operation
                    proj_context._current_attribute_state_set = pas_operations._copy(
                    )
                    operation_working_attribute_set = ProjectionAttributeStateSet(
                        proj_context._current_attribute_state_set._ctx)

                # Evaluate projections and apply to empty state
                new_pas_operations = operation._append_projection_attribute_state(
                    proj_context, operation_working_attribute_set,
                    ac_gen_attr_set)

                # If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state.
                if new_pas_operations is not None:
                    first_operation_to_run = False
                    pas_operations = new_pas_operations

            # If no operation ran successfully pas_operations will be empty
            if not first_operation_to_run:
                # Finally update the current state to the projection context
                proj_context._current_attribute_state_set = pas_operations

        return proj_context
Exemplo n.º 15
0
    async def create_resolved_entity_async(self, new_ent_name: str, res_opt: Optional['ResolveOptions'] = None, folder: 'CdmFolderDefinition' = None,
                                           new_doc_name: str = None) -> 'CdmEntityDefinition':
        """Create a resolved copy of the entity."""

        if not res_opt:
            res_opt = ResolveOptions(self, self.ctx.corpus.default_resolution_directives)

        if not res_opt.wrt_doc:
            logger.error(self._TAG, self.ctx, 'No WRT document was supplied.', self.create_resolved_entity_async.__name__)
            return None

        if not new_ent_name:
            logger.error(self._TAG, self.ctx, 'No Entity Name provided.', self.create_resolved_entity_async.__name__)
            return None

        folder = folder or self.in_document.folder
        file_name = new_doc_name or new_ent_name + PersistenceLayer.CDM_EXTENSION
        orig_doc = self.in_document.at_corpus_path

        # Don't overwite the source document.
        target_at_corpus_path = self.ctx.corpus.storage.create_absolute_corpus_path(folder.at_corpus_path, folder) + file_name
        if StringUtils.equals_with_ignore_case(target_at_corpus_path, orig_doc):
            logger.error(self._TAG, self.ctx, 'Attempting to replace source entity\'s document \'{}\''.format(
                target_at_corpus_path), self.create_resolved_entity_async.__name__)
            return None

        if not await res_opt.wrt_doc._index_if_needed(res_opt, True):
            logger.error(self._TAG, self.ctx, 'Couldn\'t index source document.', self.create_resolved_entity_async.__name__)
            return None

        # Make the top level attribute context for this entity.
        # for this whole section where we generate the attribute context tree and get resolved attributes
        # set the flag that keeps all of the parent changes and document dirty from from happening
        was_resolving = self.ctx.corpus._is_currently_resolving
        self.ctx.corpus._is_currently_resolving = True
        ent_name = new_ent_name
        ctx = self.ctx
        att_ctx_ent = ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_CONTEXT_DEF, ent_name, True)  # type: CdmAttributeContext
        att_ctx_ent.ctx = ctx
        att_ctx_ent.in_document = self.in_document

        # Cheating a bit to put the paths in the right place.
        acp = AttributeContextParameters()
        acp._under = att_ctx_ent
        acp._type = CdmAttributeContextType.ATTRIBUTE_GROUP
        acp._name = 'attributeContext'

        att_ctx_ac = CdmAttributeContext._create_child_under(res_opt, acp)
        acp_ent = AttributeContextParameters()
        acp_ent._under = att_ctx_ac
        acp_ent._type = CdmAttributeContextType.ENTITY
        acp_ent._name = ent_name
        acp_ent._regarding = ctx.corpus.make_ref(CdmObjectType.ENTITY_REF, self.get_name(), True)

        # Use this whenever we need to keep references pointing at things that were already found.
        # Used when 'fixing' references by localizing to a new document.
        res_opt_copy = CdmObject._copy_resolve_options(res_opt)
        res_opt_copy._save_resolutions_on_copy = True

        # Resolve attributes with this context. the end result is that each resolved attribute points to the level of
        # the context where it was created.
        ras = self._fetch_resolved_attributes(res_opt_copy, acp_ent)

        if ras is None:
            self._resolving_attributes = False
            return None

        # Create a new copy of the attribute context for this entity.
        # TODO: all_att_ctx type ideally should be ordered set
        all_att_ctx = []  # type: List[CdmAttributeContext]
        new_node = cast('CdmAttributeContext', att_ctx_ent.copy_node(res_opt))
        att_ctx_ent = att_ctx_ent._copy_attribute_context_tree(res_opt, new_node, ras, all_att_ctx, 'resolvedFrom')
        att_ctx = cast('CdmAttributeContext', cast('CdmAttributeContext', att_ctx_ent.contents[0]).contents[0])

        self.ctx.corpus._is_currently_resolving = was_resolving

        # make a new document in given folder if provided or the same folder as the source entity
        folder.documents.remove(file_name)
        doc_res = folder.documents.append(file_name)
        # add a import of the source document
        orig_doc = self.ctx.corpus.storage.create_relative_corpus_path(orig_doc, doc_res)  # just in case we missed the prefix
        doc_res.imports.append(orig_doc, "resolvedFrom")

        # make the empty entity
        ent_resolved = doc_res.definitions.append(ent_name)
        # set the context to the copy of the tree. fix the docs on the context nodes
        ent_resolved.attribute_context = att_ctx

        def visit_callback(obj: 'CdmObject', path: str) -> bool:
            obj.in_document = doc_res
            return False

        att_ctx.visit('{}/attributeContext/'.format(ent_name), visit_callback, None)

        # add the traits of the entity
        rts_ent = self._fetch_resolved_traits(res_opt)
        for rt in rts_ent.rt_set:
            trait_ref = CdmObject._resolved_trait_to_trait_ref(res_opt_copy, rt)
            ent_resolved.exhibits_traits.append(trait_ref)

        # The attributes have been named, shaped, etc. for this entity so now it is safe to go and make each attribute
        # context level point at these final versions of attributes.
        att_path_to_order = {}  # type: Dict[str, int]

        def point_context_at_resolved_atts(ras_sub: 'ResolvedAttributeSet', path: str) -> None:
            for ra in ras_sub._set:
                ra_ctx_in_ent = []  # type: List[CdmAttributeContext]
                ra_ctx_set = ras_sub.rattr_to_attctxset.get(ra)

                # find the correct attctx for this copy, intersect these two sets
                # (interate over the shortest list)
                if len(all_att_ctx) < len(ra_ctx_set):
                    for curr_att_ctx in all_att_ctx:
                        if curr_att_ctx in ra_ctx_set:
                            ra_ctx_in_ent.append(curr_att_ctx)
                else:
                    for curr_att_ctx in ra_ctx_set:
                        if curr_att_ctx in all_att_ctx:
                            ra_ctx_in_ent.append(curr_att_ctx)
                for ra_ctx in ra_ctx_in_ent:
                    refs = ra_ctx.contents

                    #  there might be more than one explanation for where and attribute came from when things get merges as they do
                    # This won't work when I add the structured attributes to avoid name collisions.
                    att_ref_path = path + ra.resolved_name
                    if isinstance(ra.target, CdmObject):
                        if not att_ref_path in att_path_to_order:
                            att_ref = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_REF, att_ref_path, True)  # type: CdmObjectReference
                            # only need one explanation for this path to the insert order
                            att_path_to_order[att_ref.named_reference] = ra.insert_order
                            refs.append(att_ref)
                    else:
                        att_ref_path += '/members/'
                        point_context_at_resolved_atts(cast('ResolvedAttributeSet', ra.target), att_ref_path)

        point_context_at_resolved_atts(ras, ent_name + '/hasAttributes/')

        # generated attribute structures may end up with 0 attributes after that. prune them
        def clean_sub_group(sub_item, under_generated) -> bool:
            if sub_item.object_type == CdmObjectType.ATTRIBUTE_REF:
                return True  # not empty

            ac = sub_item  # type: CdmAttributeContext

            if ac.type == CdmAttributeContextType.GENERATED_SET:
                under_generated = True
            if not ac.contents:
                return False  # empty

            # look at all children, make a set to remove
            to_remove = []  # type: List[CdmAttributeContext]
            for sub_sub in ac.contents:
                if not clean_sub_group(sub_sub, under_generated):
                    potential_target = under_generated
                    if not potential_target:
                        # cast is safe because we returned false meaning empty and not a attribute ref
                        # so is this the set holder itself?
                        potential_target = sub_sub.type == CdmAttributeContextType.GENERATED_SET
                    if potential_target:
                        to_remove.append(sub_sub)
            for to_die in to_remove:
                ac.contents.remove(to_die)
            return bool(ac.contents)

        clean_sub_group(att_ctx, False)

        # Create an all-up ordering of attributes at the leaves of this tree based on insert order. Sort the attributes
        # in each context by their creation order and mix that with the other sub-contexts that have been sorted.
        def get_order_num(item: 'CdmObject') -> int:
            if item.object_type == CdmObjectType.ATTRIBUTE_CONTEXT_DEF:
                return order_contents(item)
            if item.object_type == CdmObjectType.ATTRIBUTE_REF:
                return att_path_to_order[item.named_reference]
            # put the mystery item on top.
            return -1

        def order_contents(under: 'CdmAttributeContext') -> int:
            if under._lowest_order is None:
                under._lowest_order = -1
                if len(under.contents) == 1:
                    under._lowest_order = get_order_num(under.contents[0])
                else:
                    def get_and_update_order(item1: 'CdmObject', item2: 'CdmObject'):
                        left_order = get_order_num(item1)
                        right_order = get_order_num(item2)
                        if left_order != -1 and (under._lowest_order == -1 or left_order < under._lowest_order):
                            under._lowest_order = left_order
                        if right_order != -1 and (under._lowest_order == -1 or right_order < under._lowest_order):
                            under._lowest_order = right_order
                        return left_order - right_order
                    under.contents.sort(key=functools.cmp_to_key(get_and_update_order))
            return under._lowest_order

        order_contents(att_ctx)

        # Resolved attributes can gain traits that are applied to an entity when referenced since these traits are
        # described in the context, it is redundant and messy to list them in the attribute. So, remove them. Create and
        # cache a set of names to look for per context. There is actually a hierarchy to this. All attributes from the
        # base entity should have all traits applied independently of the sub-context they come from. Same is true of
        # attribute entities. So do this recursively top down.
        ctx_to_trait_names = {}  # type: Dict[CdmAttributeContext, Set[str]]

        def collect_context_traits(sub_att_ctx: 'CdmAttributeContext', inherited_trait_names: Set[str]) -> None:
            trait_names_here = set(inherited_trait_names)
            traits_here = sub_att_ctx.exhibits_traits
            if traits_here:
                for tat in traits_here:
                    trait_names_here.add(tat.named_reference)

            ctx_to_trait_names[sub_att_ctx] = trait_names_here
            for cr in sub_att_ctx.contents:
                if cr.object_type == CdmObjectType.ATTRIBUTE_CONTEXT_DEF:
                    # do this for all types?
                    collect_context_traits(cast('CdmAttributeContext', cr), trait_names_here)

        collect_context_traits(att_ctx, set())

        # add the attributes, put them in attribute groups if structure needed.
        res_att_to_ref_path = {}  # type: Dict[ResolvedAttribute, str]

        def add_attributes(ras_sub: 'ResolvedAttributeSet', container: 'Union[CdmEntityDefinition, CdmAttributeGroupDefinition]', path: str) -> None:
            for ra in ras_sub._set:
                att_path = path + ra.resolved_name
                # use the path of the context associated with this attribute to find the new context that matches on path.
                ra_ctx_set = ras_sub.rattr_to_attctxset[ra]
                # find the correct att_ctx for this copy.
                # ra_ctx = next((ac for ac in all_att_ctx if ac in ra_ctx_set), None) # type: CdmAttributeContext

                if len(all_att_ctx) < len(ra_ctx_set):
                    for curr_att_ctx in all_att_ctx:
                        if curr_att_ctx in ra_ctx_set:
                            ra_ctx = curr_att_ctx
                            break
                else:
                    for curr_att_ctx in ra_ctx_set:
                        if curr_att_ctx in all_att_ctx:
                            ra_ctx = curr_att_ctx
                            break

                if isinstance(ra.target, ResolvedAttributeSet) and cast('ResolvedAttributeSet', ra.target)._set:
                    # this is a set of attributes. Make an attribute group to hold them.
                    att_grp = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_GROUP_DEF, ra.resolved_name)  # type: CdmAttributeGroupDefinition
                    att_grp.attribute_context = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_CONTEXT_REF, ra_ctx.at_corpus_path, True)
                    # take any traits from the set and make them look like traits exhibited by the group.
                    avoid_set = ctx_to_trait_names[ra_ctx]
                    rts_att = ra.resolved_traits
                    for rt in rts_att.rt_set:
                        if not rt.trait.ugly:  # Don't mention your ugly traits.
                            if not avoid_set or rt.trait_name not in avoid_set:  # Avoid the ones from the context.
                                trait_ref = CdmObject._resolved_trait_to_trait_ref(res_opt_copy, rt)
                                cast('CdmObjectDefinition', att_grp).exhibits_traits.append(trait_ref, isinstance(trait_ref, str))

                    # wrap it in a reference and then recurse with this as the new container.
                    att_grp_ref = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_GROUP_REF, None)  # type: CdmAttributeGroupReference
                    att_grp_ref.explicit_reference = att_grp
                    container._add_attribute_def(att_grp_ref)
                    # isn't this where ...
                    add_attributes(cast('ResolvedAttributeSet', ra.target), att_grp, att_path + '/(object)/members/')
                else:
                    att = self.ctx.corpus.make_object(CdmObjectType.TYPE_ATTRIBUTE_DEF, ra.resolved_name)  # type: CdmTypeAttributeDefinition
                    att.attribute_context = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_CONTEXT_REF, ra_ctx.at_corpus_path, True)
                    avoid_set = ctx_to_trait_names[ra_ctx]
                    rts_att = ra.resolved_traits
                    for rt in rts_att.rt_set:
                        if not rt.trait.ugly:  # Don't mention your ugly traits.
                            if not avoid_set or rt.trait_name not in avoid_set:  # Avoid the ones from the context.
                                trait_ref = CdmObject._resolved_trait_to_trait_ref(res_opt_copy, rt)
                                cast('CdmTypeAttributeDefinition', att).applied_traits.append(trait_ref, isinstance(trait_ref, str))

                    # none of the dataformat traits have the bit set that will make them turn into a property
                    # this is intentional so that the format traits make it into the resolved object
                    # but, we still want a guess as the data format, so get it and set it.
                    implied_data_format = att.data_format
                    if implied_data_format:
                        att.data_format = implied_data_format

                    container._add_attribute_def(att)
                    res_att_to_ref_path[ra] = att_path

        add_attributes(ras, ent_resolved, ent_name + '/hasAttributes/')

        # Any resolved traits that hold arguments with attribute refs should get 'fixed' here.
        def replace_trait_att_ref(tr: 'CdmTraitReference', entity_hint: str) -> None:
            if tr.arguments:
                for arg in tr.arguments:
                    v = arg._unresolved_value or arg.value
                    # Is this an attribute reference?
                    if v and isinstance(v, CdmObject) and cast('CdmObject', v).object_type == CdmObjectType.ATTRIBUTE_REF:
                        # Only try this if the reference has no path to it (only happens with intra-entity att refs).
                        att_ref = cast('CdmAttributeReference', v)
                        if att_ref.named_reference and att_ref.named_reference.find('/') == -1:
                            if not arg._unresolved_value:
                                arg._unresolved_value = arg.value
                            # Give a promise that can be worked out later.
                            # Assumption is that the attribute must come from this entity.
                            new_att_ref = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_REF, entity_hint +
                                                                      '/(resolvedAttributes)/' + att_ref.named_reference, True)
                            # inDocument is not propagated during resolution, so set it here
                            new_att_ref.in_document = arg.in_document
                            arg.set_value(new_att_ref)

        # Fix entity traits.
        if ent_resolved.exhibits_traits:
            for et in ent_resolved.exhibits_traits:
                replace_trait_att_ref(et, new_ent_name)

        # Fix context traits.
        def fix_context_traits(sub_att_ctx: 'CdmAttributeContext', entity_hint: str) -> None:
            traits_here = sub_att_ctx.exhibits_traits
            if traits_here:
                for tr in traits_here:
                    replace_trait_att_ref(tr, entity_hint)

            for cr in sub_att_ctx.contents:
                if cr.object_type == CdmObjectType.ATTRIBUTE_CONTEXT_DEF:
                    # If this is a new entity context, get the name to pass along.
                    sub_sub_att_ctx = cast('CdmAttributeContext', cr)
                    sub_entity_hint = entity_hint
                    if sub_sub_att_ctx.type == CdmAttributeContextType.ENTITY:
                        sub_entity_hint = sub_sub_att_ctx.definition.named_reference
                    # Do this for all types.
                    fix_context_traits(sub_sub_att_ctx, sub_entity_hint)

        fix_context_traits(att_ctx, new_ent_name)

        # And the attribute traits.
        ent_atts = ent_resolved.attributes
        if ent_atts:
            for attribute in ent_atts:
                att_traits = attribute.applied_traits
                if att_traits:
                    for tr in att_traits:
                        replace_trait_att_ref(tr, new_ent_name)

        # We are about to put this content created in the context of various documents (like references to attributes
        # from base entities, etc.) into one specific document. All of the borrowed refs need to work. so, re-write all
        # string references to work from this new document. The catch-22 is that the new document needs these fixes done
        # before it can be used to make these fixes. The fix needs to happen in the middle of the refresh trigger the
        # document to refresh current content into the resolved OM.
        cast('CdmAttributeContext', att_ctx).parent = None  # Remove the fake parent that made the paths work.
        res_opt_new = CdmObject._copy_resolve_options(res_opt)
        res_opt_new.wrt_doc = doc_res
        res_opt_new._localize_references_for = doc_res
        if not await doc_res.refresh_async(res_opt_new):
            logger.error(self._TAG, self.ctx, 'Failed to index the resolved document.', self.create_resolved_entity_async.__name__)
            return None

        # Get a fresh ref.
        ent_resolved = cast('CdmEntityDefinition', doc_res._fetch_object_from_document_path(ent_name, res_opt_new))

        self.ctx.corpus._res_ent_map[self.at_corpus_path] = ent_resolved.at_corpus_path

        return ent_resolved
Exemplo n.º 16
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
Exemplo n.º 17
0
    def _create_and_store_attribute_context_parameters(
            self, search_for: str, found: 'ProjectionAttributeState',
            res_attr_from_action: 'ResolvedAttribute',
            attr_ctx_type: 'CdmAttributeContextType',
            lineage_out: 'CdmAttributeContext',
            lineage_in: 'CdmAttributeContext') -> None:
        """
        Creates the attribute context parameters for the search_for, found, and action nodes and then stores them in different maps.
        The maps are used when constructing the actual attribute context tree.
        :param search_for: The 'search for' string
        :param found: The projection attribute state that contains the 'found' attribute
        :param res_attr_from_action: The resolved attribute that resulted from the action
        :param attr_ctx_type: The attribute context type to give the 'action' attribute context parameter
        """
        # search_for is null when we have to construct attribute contexts for the excluded attributes in Include or the included attributes in Exclude,
        # as these attributes weren't searched for with a search_for name.
        # If search_for is null, just set it to have the same name as found so that it'll collapse in the final tree.
        if search_for is None:
            search_for = found._current_resolved_attribute.resolved_name

        # Create the attribute context parameter for the search_for node and store it in the map as [search_for name]:[attribute context parameter]
        search_for_attr_ctx_param = None
        if search_for not in self._search_for_to_search_for_attr_ctx_param:
            search_for_attr_ctx_param = AttributeContextParameters()
            search_for_attr_ctx_param._under = self._root
            search_for_attr_ctx_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
            search_for_attr_ctx_param._name = search_for

            self._search_for_to_search_for_attr_ctx_param[
                search_for] = search_for_attr_ctx_param
        else:
            search_for_attr_ctx_param = self._search_for_to_search_for_attr_ctx_param[
                search_for]

        # Create the attribute context parameter for the found node
        found_attr_ctx_param = AttributeContextParameters()
        found_attr_ctx_param._under = self._root  # Set this to be under the root for now, as we may end up collapsing this node
        found_attr_ctx_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
        found_attr_ctx_param._name = '{}{}'.format(
            found._current_resolved_attribute.resolved_name,
            '@' + str(found._ordinal) if found._ordinal is not None else '')

        # Store this in the map as [search_for attribute context parameter]:[found attribute context parameters]
        # We store it this way so that we can create the found nodes under their corresponding search_for nodes.
        if search_for_attr_ctx_param not in self._search_for_attr_ctx_param_to_found_attr_ctx_param:
            self._search_for_attr_ctx_param_to_found_attr_ctx_param[
                search_for_attr_ctx_param] = []

        found_attr_ctx_params = self._search_for_attr_ctx_param_to_found_attr_ctx_param[
            search_for_attr_ctx_param]
        found_attr_ctx_params.append(found_attr_ctx_param)

        # Create the attribute context parameter for the action node
        action_attr_ctx_param = AttributeContextParameters()
        action_attr_ctx_param._under = self._root  # Set this to be under the root for now, as we may end up collapsing this node
        action_attr_ctx_param._type = attr_ctx_type  # This type will be updated once we implement the new attribute context types
        action_attr_ctx_param._name = res_attr_from_action.resolved_name

        # Store this in the map as [found attribute context parameter]:[action attribute context parameter]
        # We store it this way so that we can create the action nodes under their corresponding found nodes.
        self._found_attr_ctx_param_to_action_attr_ctx_param[
            found_attr_ctx_param] = action_attr_ctx_param

        # Store the action attribute context parameter with the resolved attribute resulting out of the action.
        # This is so that we can point the action attribute context to the correct resolved attribute once the attribute context is created.
        self._action_attr_ctx_param_to_res_attr[
            action_attr_ctx_param] = res_attr_from_action

        # Store the current resAtt as the lineage of the new one
        # of note, if no lineage is stored AND the resolved Att associated above holds an existing context? we will
        # Flip the lineage when we make a new context and point 'back' to this new node. this means this new node should
        # point 'back' to the context of the source attribute
        if lineage_out is not None:
            self._action_attr_ctx_param_to_lineage_out[
                action_attr_ctx_param] = lineage_out

        if lineage_in is not None:
            self._action_attr_ctx_param_to_lineage_in[
                action_attr_ctx_param] = lineage_in
Exemplo n.º 18
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
Exemplo n.º 19
0
    def _construct_projection_context(self, proj_directive: 'ProjectionDirective', attr_ctx: 'CdmAttributeContext', ras: Optional['ResolvedAttributeSet'] = None) -> 'ProjectionContext':
        """
        A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract
        This function is the entry point for projection resolution.
        This function is expected to do the following 3 things:
        - Create an condition expression tree & default if appropriate
        - Create and initialize Projection Context
        - Process operations
        """
        proj_context = None

        condition =  self.condition if self.condition  else "(true)"

        # create an expression tree based on the condition
        tree = ExpressionTree()
        self._condition_expression_tree_root = tree._construct_expression_tree(condition)

        if attr_ctx:
            # Add projection to context tree
            acp_proj = AttributeContextParameters()
            acp_proj._under = attr_ctx
            acp_proj._type = CdmAttributeContextType.PROJECTION
            acp_proj._name = self.fetch_object_definition_name()
            acp_proj._regarding = proj_directive._owner_ref
            acp_proj._include_traits = False

            ac_proj = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_proj)

            acp_source = AttributeContextParameters()
            acp_source._under = ac_proj
            acp_source._type = CdmAttributeContextType.SOURCE
            acp_source._name = 'source'
            acp_source._regarding = None
            acp_source._include_traits = False

            ac_source = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_source)

            # Initialize the projection context
            ctx = proj_directive._owner.ctx if proj_directive._owner else None

            if self.source:
                source = self.source.fetch_object_definition(proj_directive._res_opt)
                if source.object_type == CdmObjectType.PROJECTION_DEF:
                    # A Projection

                    proj_context = self.source.explicit_reference._construct_projection_context(proj_directive, ac_source, ras)
                else:
                    # An Entity Reference

                    acp_source_projection = AttributeContextParameters()
                    acp_source_projection._under = ac_source
                    acp_source_projection._type = CdmAttributeContextType.ENTITY
                    acp_source_projection._name = self.source.named_reference if self.source.named_reference else self.source.explicit_reference.get_name()
                    acp_source_projection._regarding = self.source
                    acp_source_projection._include_traits = False

                    ras = self.source._fetch_resolved_attributes(proj_directive._res_opt, acp_source_projection)

                    # If polymorphic keep original source as previous state
                    poly_source_set = None
                    if proj_directive._is_source_polymorphic:
                        poly_source_set = ProjectionResolutionCommonUtil._get_polymorphic_source_set(proj_directive, ctx, self.source, acp_source_projection)

                    # Now initialize projection attribute state
                    pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set(
                        proj_directive,
                        ctx,
                        ras,
                        proj_directive._is_source_polymorphic,
                        poly_source_set
                    )

                    proj_context = ProjectionContext(proj_directive, ras.attribute_context)
                    proj_context._current_attribute_state_set = pas_set
            else:
                # A type attribute

                # Initialize projection attribute state
                pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set(
                    proj_directive,
                    ctx,
                    ras,
                    is_source_polymorphic=False,
                    polymorphic_set=None
                )

                proj_context = ProjectionContext(proj_directive, ras.attribute_context)
                proj_context._current_attribute_state_set = pas_set

            is_condition_valid = False
            if self._condition_expression_tree_root:
                input = InputValues()
                input.no_max_depth = proj_directive._has_no_maximum_depth
                input.is_array = proj_directive._is_array
                input.reference_only = proj_directive._is_reference_only
                input.normalized = proj_directive._is_normalized
                input.structured = proj_directive._is_structured
                input.is_virtual = proj_directive._is_virtual

                current_depth = proj_directive._current_depth
                current_depth += 1
                input.next_depth = current_depth
                proj_directive._current_depth = current_depth

                input.max_depth = proj_directive._maximum_depth
                input.min_cardinality = proj_directive._cardinality._minimum_number if proj_directive._cardinality else None
                input.max_cardinality = proj_directive._cardinality._maximum_number if proj_directive._cardinality else None

                is_condition_valid = ExpressionTree._evaluate_expression_tree(self._condition_expression_tree_root, input)

            if is_condition_valid and self.operations and len(self.operations) > 0:
                # Just in case operations were added programmatically, reindex operations
                for i in range(len(self.operations)):
                    self.operations[i]._index = i + 1

                # Operation

                acp_gen_attr_set = AttributeContextParameters()
                acp_gen_attr_set._under = attr_ctx
                acp_gen_attr_set._type = CdmAttributeContextType.GENERATED_SET
                acp_gen_attr_set._name = '_generatedAttributeSet'

                ac_gen_attr_set = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_gen_attr_set)

                # Start with an empty list for each projection
                pas_operations = ProjectionAttributeStateSet(proj_context._current_attribute_state_set._ctx)
                for operation in self.operations:
                    # Evaluate projections and apply to empty state
                    new_pas_operations = operation._append_projection_attribute_state(proj_context, pas_operations, ac_gen_attr_set)

                    # If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state.
                    if new_pas_operations is not None:
                        pas_operations = new_pas_operations

                # Finally update the current state to the projection context
                proj_context._current_attribute_state_set = pas_operations

        return proj_context
    def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_attr_state_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_combine_attrs_param = AttributeContextParameters()  # type: AttributeContextParameters
        attr_ctx_op_combine_attrs_param._under = attr_ctx
        attr_ctx_op_combine_attrs_param._type = CdmAttributeContextType.OPERATION_COMBINE_ATTRIBUTES
        attr_ctx_op_combine_attrs_param._name = 'operation/index{}/operationCombineAttributes'.format(self._index)

        attr_ctx_op_combine_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_combine_attrs_param)  # type: CdmAttributeContext

        # Initialize a projection attribute context tree builder with the created attribute context for the operation
        attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_combine_attrs)

        # Get all the leaf level PAS nodes from the tree for each selected attribute and cache to a dictionary
        leaf_level_combine_attribute_names =  OrderedDict() # OrderedDict[str, ProjectionAttributeState[]]()
        # Also, create a single list of leaf level PAS to add to the 'is.linkedEntity.identifier' trait parameter
        leaf_level_merge_pas_list = []  # type: List[ProjectionAttributeState]
        for select in self.select:
            leaf_level_list_for_current_select = ProjectionResolutionCommonUtil._get_leaf_list(proj_ctx, select)  # type: List[ProjectionAttributeState]
            if leaf_level_list_for_current_select is not None and len(leaf_level_list_for_current_select) > 0 and not(select in leaf_level_combine_attribute_names):
                leaf_level_combine_attribute_names.__setitem__(select, leaf_level_list_for_current_select)

                leaf_level_merge_pas_list.extend(leaf_level_list_for_current_select)

        # Create a List of top-level PAS objects that will be get merged based on the selected attributes
        pas_merge_list = []  # type: List[ProjectionAttributeState]

        # Run through the top-level PAS objects 
        for current_pas in proj_ctx._current_attribute_state_set._states:
            if (proj_ctx._projection_directive._owner_type is CdmObjectType.ENTITY_DEF or proj_ctx._projection_directive._is_source_polymorphic) and (current_pas._current_resolved_attribute._resolved_name in leaf_level_combine_attribute_names):
                # Attribute to Merge

                if not(pas_merge_list.__contains__(current_pas)):
                    pas_merge_list.append(current_pas)
            else:
                # Attribute to Pass Through

                # Create a projection attribute state for the non-selected / pass-through attribute 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()  # type: ProjectionAttributeState

                proj_attr_state_set._add(new_pas)

        if len(pas_merge_list) > 0:
            merge_into_attribute = self.merge_into  # type: CdmTypeAttributeDefinition
            add_trait = [ 'is.linkedEntity.identifier' ]

            # Create new resolved attribute, set the new attribute as target, and apply 'is.linkedEntity.identifier' trait
            ra_new_merge_into = self._create_new_resolved_attribute(proj_ctx, attr_ctx_op_combine_attrs, merge_into_attribute, None, add_trait)  # type: ResolvedAttribute

            # update the new foreign key resolved attribute with trait param with reference details
            reqd_trait = ra_new_merge_into.resolved_traits.find(proj_ctx._projection_directive._res_opt, 'is.linkedEntity.identifier')  # type: ResolvedTrait
            if reqd_trait is not None:
                trait_param_ent_ref = ProjectionResolutionCommonUtil._create_foreign_key_linked_entity_identifier_trait_parameter(proj_ctx._projection_directive, proj_attr_state_set._ctx.corpus, leaf_level_merge_pas_list)  # type: CdmEntityReference
                reqd_trait.parameter_values.update_parameter_value(proj_ctx._projection_directive._res_opt, 'entityReferences', trait_param_ent_ref)

            # Create new output projection attribute state set for FK and add prevPas as previous state set
            new_merge_into_pas = ProjectionAttributeState(proj_attr_state_set._ctx)  # type: ProjectionAttributeState
            new_merge_into_pas._current_resolved_attribute = ra_new_merge_into
            new_merge_into_pas._previous_state_list = pas_merge_list

            # Create the attribute context parameters and just store it in the builder for now
            # We will create the attribute contexts at the end
            for select in leaf_level_combine_attribute_names.keys():
                if select in leaf_level_combine_attribute_names and leaf_level_combine_attribute_names[select] is not None and len(leaf_level_combine_attribute_names[select]) > 0:
                    for leaf_level_for_select in leaf_level_combine_attribute_names[select]:
                        attr_ctx_tree_builder._create_and_store_attribute_context_parameters(select, leaf_level_for_select, new_merge_into_pas._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION)

            proj_attr_state_set._add(new_merge_into_pas)

        # Create all the attribute contexts and construct the tree
        attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx, True)

        return proj_attr_state_set
Exemplo n.º 21
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
Exemplo n.º 22
0
    def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_attr_state_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_combine_attrs_param = AttributeContextParameters()  # type: AttributeContextParameters
        attr_ctx_op_combine_attrs_param._under = attr_ctx
        attr_ctx_op_combine_attrs_param._type = CdmAttributeContextType.OPERATION_COMBINE_ATTRIBUTES
        attr_ctx_op_combine_attrs_param._name = 'operation/index{}/operationCombineAttributes'.format(self._index)

        attr_ctx_op_combine_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_combine_attrs_param)  # type: CdmAttributeContext

        # Initialize a projection attribute context tree builder with the created attribute context for the operation
        attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_combine_attrs)

        # Get all the leaf level PAS nodes from the tree for each selected attribute and cache to a dictionary
        leaf_level_combine_attribute_names = OrderedDict() # OrderedDict[str, ProjectionAttributeState[]]()
        # Also, create a single list of leaf level PAS
        leaf_level_merge_pas_list = []  # type: List[ProjectionAttributeState]
        for select in self.select:
            leaf_level_list_for_current_select = ProjectionResolutionCommonUtil._get_leaf_list(proj_ctx, select)  # type: List[ProjectionAttributeState]
            if leaf_level_list_for_current_select is not None and len(leaf_level_list_for_current_select) > 0 and not(select in leaf_level_combine_attribute_names):
                leaf_level_combine_attribute_names.__setitem__(select, leaf_level_list_for_current_select)

                leaf_level_merge_pas_list.extend(leaf_level_list_for_current_select)

        # Create a list of top-level PAS objects that will be get merged based on the selected attributes
        pas_merge_list = []  # type: List[ProjectionAttributeState]

        # Run through the top-level PAS objects 
        for current_pas in proj_ctx._current_attribute_state_set._states:
            if current_pas._current_resolved_attribute._resolved_name in leaf_level_combine_attribute_names:
                # Attribute to Merge

                if current_pas not in pas_merge_list:
                    pas_merge_list.append(current_pas)
            else:
                # Attribute to Pass Through

                # Create a projection attribute state for the non-selected / pass-through attribute 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()  # type: ProjectionAttributeState

                proj_attr_state_set._add(new_pas)

        if len(pas_merge_list) > 0:
            merge_into_attribute = self.merge_into  # type: CdmTypeAttributeDefinition

            # the merged attribute needs one new place to live, so here it is
            merged_attr_ctx_param = AttributeContextParameters()  # type: AttributeContextParameters
            merged_attr_ctx_param._under = attr_ctx_op_combine_attrs
            merged_attr_ctx_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
            merged_attr_ctx_param._name = merge_into_attribute.get_name()

            merged_attr_ctx = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, merged_attr_ctx_param)

            # Create new resolved attribute, set the new attribute as target
            ra_new_merge_into = self._create_new_resolved_attribute(proj_ctx, merged_attr_ctx, merge_into_attribute, None)

            # Create new output projection attribute state set
            new_merge_into_pas = ProjectionAttributeState(proj_attr_state_set._ctx)  # type: ProjectionAttributeState
            new_merge_into_pas._current_resolved_attribute = ra_new_merge_into
            new_merge_into_pas._previous_state_list = pas_merge_list

            attributes_added_to_context = set()

            # Create the attribute context parameters and just store it in the builder for now
            # We will create the attribute contexts at the end
            for select in leaf_level_combine_attribute_names.keys():
                if select in leaf_level_combine_attribute_names and leaf_level_combine_attribute_names[select] is not None and len(leaf_level_combine_attribute_names[select]) > 0:
                    for leaf_level_for_select in leaf_level_combine_attribute_names[select]:
                        # When dealing with a polymorphic entity, it is possible that multiple entities have an attribute with the same name
                        # Only one attribute with each name should be added otherwise the attribute context will end up with duplicated nodes
                        if leaf_level_for_select._current_resolved_attribute._resolved_name not in attributes_added_to_context:
                            attributes_added_to_context.add(leaf_level_for_select._current_resolved_attribute._resolved_name)
                            attr_ctx_tree_builder._create_and_store_attribute_context_parameters(
                                select, leaf_level_for_select, new_merge_into_pas._current_resolved_attribute,
                                CdmAttributeContextType.ATTRIBUTE_DEFINITION,
                                leaf_level_for_select._current_resolved_attribute.att_ctx,  # lineage is the source att
                                new_merge_into_pas._current_resolved_attribute.att_ctx)  # merge into points back here

            proj_attr_state_set._add(new_merge_into_pas)

        # Create all the attribute contexts and construct the tree
        attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx)

        return proj_attr_state_set
Exemplo n.º 23
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
Exemplo n.º 24
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_rename_attrs_param = AttributeContextParameters()
        attr_ctx_op_rename_attrs_param._under = attr_ctx
        attr_ctx_op_rename_attrs_param._type = CdmAttributeContextType.OPERATION_RENAME_ATTRIBUTES
        attr_ctx_op_rename_attrs_param._name = 'operation/index{}/operationRenameAttributes'.format(
            self._index)
        attr_ctx_op_rename_attrs = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_op_rename_attrs_param)  # type: CdmAttributeContext

        # Get the list of attributes that will be renamed
        rename_attributes = None  # type: List[str]
        if self.apply_to is not None:
            rename_attributes = self.apply_to
        else:
            rename_attributes = []
            for current_PAS in proj_ctx._current_attribute_state_set._states:
                rename_attributes.append(
                    current_PAS._current_resolved_attribute._resolved_name)

        # Get the top-level attribute names of the attributes to rename
        # We use the top-level names because the rename list may contain a previous name our current resolved attributes had
        top_level_rename_attribute_names = ProjectionResolutionCommonUtil._get_top_list(
            proj_ctx, rename_attributes)  # type: Dict[str, str]

        source_attribute_name = proj_ctx._projection_directive._original_source_entity_attribute_name  # type: str

        # Initialize a projection attribute context tree builder with the created attribute context for the operation
        attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(
            attr_ctx_op_rename_attrs)

        # 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:
            # Check if the current projection attribute state's resolved attribute is in the list of attributes to rename
            # If this attribute is not in the rename list, then we are including it in the output without changes
            if current_PAS._current_resolved_attribute.resolved_name in top_level_rename_attribute_names:
                if isinstance(current_PAS._current_resolved_attribute.target,
                              CdmAttribute):
                    # The current attribute should be renamed

                    new_attribute_name = self._get_new_attribute_name(
                        current_PAS, source_attribute_name)  # type: str

                    # Create new resolved attribute with the new name, set the new attribute as target
                    res_attr_new = self._create_new_resolved_attribute(
                        proj_ctx, None,
                        current_PAS._current_resolved_attribute.target,
                        new_attribute_name)  # type: ResolvedAttribute

                    # Get the attribute name the way it appears in the applyTo list
                    apply_to_name = top_level_rename_attribute_names[
                        current_PAS._current_resolved_attribute.resolved_name]

                    # Create the attribute context parameters and just store it in the builder for now
                    # We will create the attribute contexts at the end
                    attr_ctx_tree_builder._create_and_store_attribute_context_parameters(
                        apply_to_name,
                        current_PAS,
                        res_attr_new,
                        CdmAttributeContextType.ATTRIBUTE_DEFINITION,
                        current_PAS._current_resolved_attribute.
                        att_ctx,  # lineage is the original attribute
                        None)  # don't know who will point here yet

                    # Create a projection attribute state for the renamed attribute by creating a copy of the current state
                    # Copy() sets the current state as the previous state for the new one
                    # We only create projection attribute states for attributes that are in the rename list
                    new_PAS = current_PAS._copy()

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

                    proj_output_set._add(new_PAS)
                else:
                    logger.warning(
                        self._TAG, self.ctx,
                        'RenameAttributes is not supported on an attribute group yet.'
                    )
                    # Add the attribute without changes
                    proj_output_set._add(current_PAS)
            else:
                # Pass through
                proj_output_set._add(current_PAS)

        # Create all the attribute contexts and construct the tree
        attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx)

        return proj_output_set
    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_array_expansion_param = AttributeContextParameters()
        attr_ctx_op_array_expansion_param._under = attr_ctx
        attr_ctx_op_array_expansion_param._type = CdmAttributeContextType.OPERATION_ARRAY_EXPANSION
        attr_ctx_op_array_expansion_param._name = 'operation/index{}/operationArrayExpansion'.format(
            self._index)
        attr_ctx_op_array_expansion = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_op_array_expansion_param)

        # Expansion steps start at round 0
        round = 0
        proj_attr_states_from_rounds = []

        # Ordinal validation
        if self.start_ordinal > self.end_ordinal:
            logger.warning(
                self._TAG, self.ctx,
                'startOrdinal {} should not be greater than endOrdinal {}'.
                format(self.start_ordinal, self.end_ordinal))
        else:
            # Ordinals should start at startOrdinal or 0, whichever is larger.
            starting_ordinal = max(0, self.start_ordinal)

            # Ordinals should end at endOrdinal or the maximum ordinal allowed (set in resolve options), whichever is smaller.
            if self.end_ordinal > proj_ctx._projection_directive._res_opt.max_ordinal_for_array_expansion:
                logger.warning(
                    self._TAG, self.ctx,
                    'endOrdinal {} is greater than the maximum allowed ordinal of {}. Using the maximum allowed ordinal instead.'
                    .format(
                        self.end_ordinal, proj_ctx._projection_directive.
                        _res_opt.max_ordinal_for_array_expansion))

            ending_ordinal = min(
                proj_ctx._projection_directive._res_opt.
                max_ordinal_for_array_expansion, self.end_ordinal)

            # For each ordinal, create a copy of the input resolved attribute
            for i in range(starting_ordinal, ending_ordinal + 1):
                # Create a new attribute context for the round
                attr_ctx_round_param = AttributeContextParameters()
                attr_ctx_round_param._under = attr_ctx_op_array_expansion
                attr_ctx_round_param._type = CdmAttributeContextType.GENERATED_ROUND
                attr_ctx_round_param._name = '_generatedAttributeRound{}'.format(
                    round)
                attr_ctx_round = CdmAttributeContext._create_child_under(
                    proj_ctx._projection_directive._res_opt,
                    attr_ctx_round_param)

                # 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 new attribute context for the expanded attribute with the current ordinal
                    attr_ctx_expanded_attr_param = AttributeContextParameters()
                    attr_ctx_expanded_attr_param._under = attr_ctx_round
                    attr_ctx_expanded_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
                    attr_ctx_expanded_attr_param._name = '{}@{}'.format(
                        current_PAS._current_resolved_attribute.resolved_name,
                        i)
                    attr_ctx_expanded_attr = CdmAttributeContext._create_child_under(
                        proj_ctx._projection_directive._res_opt,
                        attr_ctx_expanded_attr_param)

                    # Create a new resolved attribute for the expanded attribute
                    new_res_attr = self._create_new_resolved_attribute(
                        proj_ctx, attr_ctx_expanded_attr,
                        current_PAS._current_resolved_attribute.target,
                        current_PAS._current_resolved_attribute.resolved_name)

                    # Create a projection attribute state for the expanded attribute
                    new_PAS = ProjectionAttributeState(proj_output_set._ctx)
                    new_PAS._current_resolved_attribute = new_res_attr
                    new_PAS._previous_state_list = [current_PAS]
                    new_PAS._ordinal = i

                    proj_attr_states_from_rounds.append(new_PAS)

                if i == ending_ordinal:
                    break

                # Increment the round
                round += 1

        if len(proj_attr_states_from_rounds) == 0:
            # No rounds were produced from the array expansion - input passes through
            for pas in proj_ctx._current_attribute_state_set._states:
                proj_output_set._add(pas)
        else:
            # Add all the projection attribute states containing the expanded attributes to the output
            for pas in proj_attr_states_from_rounds:
                proj_output_set._add(pas)

        return proj_output_set
Exemplo n.º 26
0
    def apply(
            self, traits: 'ResolvedTraitSet', res_opt: 'ResolveOptions',
            res_guide: 'CdmAttributeResolutionGuidanceDefinition',
            actions: List['AttributeResolutionApplier']
    ) -> 'ResolvedAttributeSet':
        from cdm.objectmodel import CdmAttributeContext

        if not traits and not actions:
            # nothing can change.
            return self

        # for every attribute in the set run any attribute appliers.
        applied_att_set = ResolvedAttributeSet()
        applied_att_set.attribute_context = self.attribute_context

        # check to see if we need to make a copy of the attributes
        # do this when building an attribute context and when we will modify the attributes (beyond traits)
        # see if any of the appliers want to modify
        making_copy = False
        if self._set and applied_att_set.attribute_context and actions:
            res_att_test = self._set[0]
            for trait_action in actions:
                ctx = ApplierContext(res_opt=res_opt,
                                     res_att_source=res_att_test,
                                     res_guide=res_guide)
                if trait_action._will_attribute_modify(ctx):
                    making_copy = True
                    break

        if making_copy:
            # fake up a generation round for these copies that are about to happen
            acp = AttributeContextParameters(
                under=applied_att_set.attribute_context,
                type=CdmAttributeContextType.GENERATED_SET,
                name='_generatedAttributeSet')
            applied_att_set.attribute_context = CdmAttributeContext._create_child_under(
                traits.res_opt, acp)
            acp = AttributeContextParameters(
                under=applied_att_set.attribute_context,
                type=CdmAttributeContextType.GENERATED_ROUND,
                name='_generatedAttributeRound0')

            applied_att_set.attribute_context = CdmAttributeContext._create_child_under(
                traits.res_opt, acp)

        for res_att in self._set:
            att_ctx_to_merge = res_att.att_ctx  # start with the current context for the resolved att, if a copy happens this will change

            if isinstance(res_att.target, ResolvedAttributeSet):
                sub_set = cast('ResolvedAttributeSet', res_att.target)

                if making_copy:
                    res_att = res_att.copy()
                    # making a copy of a subset (att group) also bring along the context tree for that whole group
                    att_ctx_to_merge = res_att.att_ctx

                # the set contains another set. Process those.
                res_att.target = sub_set.apply(traits, res_opt, res_guide,
                                               actions)
            else:
                rts_merge = res_att.resolved_traits.merge_set(traits)
                res_att.resolved_traits = rts_merge

                if actions:
                    for trait_action in actions:
                        ctx = ApplierContext()
                        ctx.res_opt = traits.res_opt
                        ctx.res_att_source = res_att
                        ctx.res_guide = res_guide

                        if trait_action._will_attribute_modify(ctx):
                            # make a context for this new copy
                            if making_copy:
                                acp = AttributeContextParameters(
                                    under=applied_att_set.attribute_context,
                                    type=CdmAttributeContextType.
                                    ATTRIBUTE_DEFINITION,
                                    name=res_att.resolved_name,
                                    regarding=res_att.target)
                                ctx.att_ctx = CdmAttributeContext._create_child_under(
                                    traits.res_opt, acp)
                                att_ctx_to_merge = ctx.att_ctx  # type: CdmAttributeContext

                            # make a copy of the resolved att
                            if making_copy:
                                res_att = res_att.copy()
                                att_ctx_to_merge._add_lineage(res_att.att_ctx)
                                res_att.att_ctx = att_ctx_to_merge

                            ctx.res_att_source = res_att

                            # modify it
                            trait_action._do_attribute_modify(ctx)

            applied_att_set.merge(res_att)

        applied_att_set.attribute_context = self.attribute_context

        return applied_att_set
    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_exclude_attrs_param = AttributeContextParameters()
        attr_ctx_op_exclude_attrs_param._under = attr_ctx
        attr_ctx_op_exclude_attrs_param._type = CdmAttributeContextType.OPERATION_EXCLUDE_ATTRIBUTES
        attr_ctx_op_exclude_attrs_param._name = 'operation/index{}/operationExcludeAttributes'.format(
            self._index)
        attr_ctx_op_exclude_attrs = CdmAttributeContext._create_child_under(
            proj_ctx._projection_directive._res_opt,
            attr_ctx_op_exclude_attrs_param)

        # Get the top-level attribute names of the attributes to exclude
        # We use the top-level names because the exclude list may contain a previous name our current resolved attributes had
        top_level_exclude_attribute_names = ProjectionResolutionCommonUtil._get_top_list(
            proj_ctx, self.exclude_attributes)

        # 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._values:
            # Check if the current projection attribute state's resolved attribute is in the list of attributes to exclude
            # If this attribute is not in the exclude list, then we are including it in the output
            if current_PAS._current_resolved_attribute.resolved_name not in top_level_exclude_attribute_names:
                # Create a new attribute context for the attribute that we are including
                attr_ctx_added_attr_param = AttributeContextParameters()
                attr_ctx_added_attr_param._under = attr_ctx
                attr_ctx_added_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
                attr_ctx_added_attr_param._name = current_PAS._current_resolved_attribute.resolved_name
                attr_ctx_added_attr = CdmAttributeContext._create_child_under(
                    proj_ctx._projection_directive._res_opt,
                    attr_ctx_added_attr_param)

                # Create a projection attribute state for the included attribute
                # We only create projection attribute states for attributes that are not in the exclude list
                # Add the current projection attribute state as the previous state of the new projection attribute state
                new_PAS = ProjectionAttributeState(proj_output_set._ctx)
                new_PAS._current_resolved_attribute = current_PAS._current_resolved_attribute
                new_PAS._previous_state_list = [current_PAS]

                proj_output_set._add(new_PAS)
            else:
                # The current projection attribute state's resolved attribute is in the exclude list

                # Get the attribute name the way it appears in the exclude list
                # For our attribute context, we want to use the attribute name the attribute has in the exclude list rather than its current name
                exclude_attribute_name = top_level_exclude_attribute_names[
                    current_PAS._current_resolved_attribute.resolved_name]

                # Create a new attribute context for the excluded attribute
                attr_ctx_excluded_attr_param = AttributeContextParameters()
                attr_ctx_excluded_attr_param._under = attr_ctx_op_exclude_attrs
                attr_ctx_excluded_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION
                attr_ctx_excluded_attr_param._name = exclude_attribute_name
                attr_ctx_excluded_attr = CdmAttributeContext._create_child_under(
                    proj_ctx._projection_directive._res_opt,
                    attr_ctx_excluded_attr_param)

        return proj_output_set