Beispiel #1
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
    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