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

        caps = arc.applier_caps

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

        from cdm.objectmodel import CdmAttributeContext

        res_att_out = []  # type: List[ResolvedAttribute]

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

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

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

        att_ctx_container = att_ctx_container_group  # type: CdmAttributeContext

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

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

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

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

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

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

                # Make it
                do_add(app_ctx)

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

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

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

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

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

            return app_ctx

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

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

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

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

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

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

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

        return res_att_out
コード例 #2
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._rename_attribute(
                        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
                    applyToName = 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(
                        applyToName, current_PAS, res_attr_new,
                        CdmAttributeContextType.ATTRIBUTE_DEFINITION)

                    # 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, True)

        return proj_output_set
コード例 #3
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 = None  # Optional[CdmAttributeContext]

            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()

                            ctx.res_att_source = res_att

                            # modify it
                            trait_action._do_attribute_modify(ctx)

            applied_att_set.merge(res_att, att_ctx_to_merge)

        applied_att_set.attribute_context = self.attribute_context

        if not making_copy:
            # didn't copy the attributes or make any new context, so just take the old ones
            applied_att_set.rattr_to_attctxset = self.rattr_to_attctxset
            applied_att_set.attctx_to_rattr = self.attctx_to_rattr
        return applied_att_set
コード例 #4
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)

        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 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):
            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:
                    #  there might be more than one explanation for where and attribute came from when things get merges as they do
                    refs = ra_ctx.contents

                    # 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):
                        att_ref = self.ctx.corpus.make_object(
                            CdmObjectType.ATTRIBUTE_REF, att_ref_path,
                            True)  # type: CdmObjectReference
                        if att_ref.named_reference not in att_path_to_order:
                            # 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)

                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 + '/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))
        return ent_resolved
コード例 #5
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

        if self.run_sequentially is not None:
            logger.error(
                self._TAG, self.ctx,
                'RunSequentially is not supported by this Object Model version.'
            )

        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)

        # 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,
                        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
            input.next_depth = proj_directive._res_opt._depth_info.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:
                if operation.condition is not None:
                    logger.error(
                        self._TAG, self.ctx,
                        'Condition on the operation level is not supported by this Object Model version.'
                    )

                if operation.source_input is not None:
                    logger.error(
                        self._TAG, self.ctx,
                        'SourceInput on the operation level is not supported by this Object Model version.'
                    )

                # 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
コード例 #6
0
ファイル: cdm_projection.py プロジェクト: manubust/CDM
    def _construct_projection_context(
            self, proj_directive: 'ProjectionDirective',
            attr_ctx: 'CdmAttributeContext') -> '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

        if not self.condition:
            # if no condition is provided, get default condition and persist
            self.condition = ConditionExpression._get_default_condition_expression(
                self.operations, self.owner)

        # create an expression tree based on the condition
        tree = ExpressionTree()
        self._condition_expression_tree_root = tree._construct_expression_tree(
            self.condition)
        if not self._condition_expression_tree_root:
            logger.info(
                self._TAG, self.ctx,
                'Optional expression missing. Implicit expression will automatically apply.',
                CdmProjection._construct_projection_context.__name__)

        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)

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

                proj_context = self.source.explicit_reference._construct_projection_context(
                    proj_directive, ac_source)
            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)

                # Initialize the projection context

                ctx = proj_directive._owner.ctx if proj_directive._owner else None

                pas_set = None

                # 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

            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

                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)

                acp_gen_attr_round0 = AttributeContextParameters()
                acp_gen_attr_round0._under = ac_gen_attr_set
                acp_gen_attr_round0._type = CdmAttributeContextType.GENERATED_ROUND
                acp_gen_attr_round0._name = '_generatedAttributeRound0'

                ac_gen_attr_round0 = CdmAttributeContext._create_child_under(
                    proj_directive._res_opt, acp_gen_attr_round0)

                # 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_round0)

                    # 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
コード例 #7
0
    def _append_projection_attribute_state(
            self, proj_ctx: 'ProjectionContext',
            proj_output_set: 'ProjectionAttributeStateSet',
            attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet':
        # Create a new attribute context for the operation
        attr_ctx_op_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.ctx, self._TAG, CdmOperationArrayExpansion.
                _append_projection_attribute_state.__name__,
                self.at_corpus_path,
                CdmLogCode.WARN_VALDN_ORDINAL_START_END_ORDER,
                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.ctx, self._TAG, CdmOperationArrayExpansion.
                    _append_projection_attribute_state.__name__,
                    self.at_corpus_path, CdmLogCode.WARN_VALDN_MAX_ORDINAL,
                    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)

                    if isinstance(
                            current_PAS._current_resolved_attribute.target,
                            ResolvedAttributeSet):
                        logger.error(
                            self.ctx, self._TAG,
                            '_append_projection_attribute_state',
                            self.at_corpus_path,
                            CdmLogCode.ERR_PROJ_UNSUPPORTED_ATTR_GROUPS)
                        proj_attr_states_from_rounds.clear()
                        break

                    # 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,
                        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
コード例 #8
0
def _create_child_under(
        res_opt: 'ResolveOptions',
        acp: 'AttributeContextParameters') -> 'CdmAttributeContext':
    from cdm.objectmodel.cdm_attribute_context import CdmAttributeContext

    return CdmAttributeContext._create_child_under(res_opt, acp)
コード例 #9
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)

        # 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,
                    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)
            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,
                    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
コード例 #10
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

            # 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,
                            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
コード例 #11
0
    def _construct_attribute_context_tree(
            self, proj_ctx: 'ProjectionContext') -> None:
        """
        Takes all the stored attribute context parameters, creates attribute contexts from them, and then constructs the tree.
        :param proj_ctx: The projection context
        """

        # Iterate over all the search_for attribute context parameters
        for search_for_attr_ctx_param in self._search_for_to_search_for_attr_ctx_param.values(
        ):
            search_for_attr_ctx = None

            # Fetch all the found attribute context parameters associated with this search_for
            found_attr_ctx_params = self._search_for_attr_ctx_param_to_found_attr_ctx_param[
                search_for_attr_ctx_param]

            # Iterate over all the found attribute context parameters
            for found_attr_ctx_param in found_attr_ctx_params:
                # We should only create the search_for node when search_for and found have different names. Else collapse the nodes together.
                if not StringUtils.equals_with_case(
                        search_for_attr_ctx_param._name,
                        found_attr_ctx_param._name):
                    # Create the attribute context for searchFor if it hasn't been created already and set it as the parent of found
                    if search_for_attr_ctx is None:
                        search_for_attr_ctx = CdmAttributeContext._create_child_under(
                            proj_ctx._projection_directive._res_opt,
                            search_for_attr_ctx_param)
                    found_attr_ctx_param._under = search_for_attr_ctx

                # Fetch the action attribute context parameter associated with this found
                action_attr_ctx_param = self._found_attr_ctx_param_to_action_attr_ctx_param[
                    found_attr_ctx_param]

                # We should only create the found node when found and action have different names. Else collapse the nodes together.
                if not StringUtils.equals_with_case(
                        found_attr_ctx_param._name,
                        action_attr_ctx_param._name):
                    # Create the attribute context for found and set it as the parent of action
                    found_attr_ctx = CdmAttributeContext._create_child_under(
                        proj_ctx._projection_directive._res_opt,
                        found_attr_ctx_param)
                    action_attr_ctx_param._under = found_attr_ctx

                # Create the attribute context for action
                action_attr_ctx = CdmAttributeContext._create_child_under(
                    proj_ctx._projection_directive._res_opt,
                    action_attr_ctx_param)

                # Fetch the resolved attribute that should now point at this action attribute context
                res_attr_from_action = self._action_attr_ctx_param_to_res_attr.get(
                    action_attr_ctx_param, None)

                # make sure the lineage of the attribute stays linked up
                # there can be either (or both) a lineageOut and a lineageIn.
                # out lineage is where this attribute came from
                # in lineage should be pointing back at this context as a source
                lineage_out = self._action_attr_ctx_param_to_lineage_out.get(
                    action_attr_ctx_param, None)  # type: CdmAttributeContext
                if lineage_out:
                    if action_attr_ctx:
                        action_attr_ctx._add_lineage(lineage_out)
                    res_attr_from_action.att_ctx = action_attr_ctx  # probably the right context for this resAtt, unless ...

                lineage_in = self._action_attr_ctx_param_to_lineage_in.get(
                    action_attr_ctx_param, None)  # type: CdmAttributeContext
                if lineage_in:
                    if action_attr_ctx:
                        lineage_in._add_lineage(action_attr_ctx)
                    res_attr_from_action.att_ctx = lineage_in  # if there is a lineageIn. it points to us as lineage, so it is best
コード例 #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_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
コード例 #13
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