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 test_entity_string_reference(self): """Test evaluate_expression function""" input = InputValues() input.max_cardinality = 1 input.min_cardinality = 0 input.max_depth = 32 input.next_depth = 1 input.no_max_depth = True input.is_array = True input.normalized = False input.reference_only = True input.structured = True expr_and_expected_result_list = [] expr_and_expected_result_list.append( ('(cardinality.maximum > 1) && (!referenceOnly)', 'False')) expr_and_expected_result_list.append(('', 'False')) expr_and_expected_result_list.append((' ', 'False')) expr_and_expected_result_list.append(('always', 'True')) expr_and_expected_result_list.append(('!structured', 'False')) expr_and_expected_result_list.append( ('referenceOnly || (depth > 5)', 'True')) expr_and_expected_result_list.append(('!(referenceOnly)', 'False')) expr_and_expected_result_list.append( ('!(normalized && cardinality.maximum > 1)', 'True')) expr_and_expected_result_list.append(('true', 'True')) expr_and_expected_result_list.append(('(((true==true)))', 'True')) expr_and_expected_result_list.append( ('!(normalized && isArray) || noMaxDepth', 'False')) for item in expr_and_expected_result_list: tree = ExpressionTree() tree_top = tree._construct_expression_tree(item[0]) expected = item[1] actual = str( ExpressionTree._evaluate_expression_tree(tree_top, input)) self.assertEqual(expected, actual)
def _construct_projection_context(self, proj_directive: 'ProjectionDirective', attr_ctx: 'CdmAttributeContext', ras: Optional['ResolvedAttributeSet'] = None) -> 'ProjectionContext': """ A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract This function is the entry point for projection resolution. This function is expected to do the following 3 things: - Create an condition expression tree & default if appropriate - Create and initialize Projection Context - Process operations """ proj_context = None condition = self.condition if self.condition else "(true)" # create an expression tree based on the condition tree = ExpressionTree() self._condition_expression_tree_root = tree._construct_expression_tree(condition) if attr_ctx: # Add projection to context tree acp_proj = AttributeContextParameters() acp_proj._under = attr_ctx acp_proj._type = CdmAttributeContextType.PROJECTION acp_proj._name = self.fetch_object_definition_name() acp_proj._regarding = proj_directive._owner_ref acp_proj._include_traits = False ac_proj = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_proj) acp_source = AttributeContextParameters() acp_source._under = ac_proj acp_source._type = CdmAttributeContextType.SOURCE acp_source._name = 'source' acp_source._regarding = None acp_source._include_traits = False ac_source = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_source) # Initialize the projection context ctx = proj_directive._owner.ctx if proj_directive._owner else None if self.source: source = self.source.fetch_object_definition(proj_directive._res_opt) if source.object_type == CdmObjectType.PROJECTION_DEF: # A Projection proj_context = self.source.explicit_reference._construct_projection_context(proj_directive, ac_source, ras) else: # An Entity Reference acp_source_projection = AttributeContextParameters() acp_source_projection._under = ac_source acp_source_projection._type = CdmAttributeContextType.ENTITY acp_source_projection._name = self.source.named_reference if self.source.named_reference else self.source.explicit_reference.get_name() acp_source_projection._regarding = self.source acp_source_projection._include_traits = False ras = self.source._fetch_resolved_attributes(proj_directive._res_opt, acp_source_projection) # If polymorphic keep original source as previous state poly_source_set = None if proj_directive._is_source_polymorphic: poly_source_set = ProjectionResolutionCommonUtil._get_polymorphic_source_set(proj_directive, ctx, self.source, acp_source_projection) # Now initialize projection attribute state pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set( proj_directive, ctx, ras, proj_directive._is_source_polymorphic, poly_source_set ) proj_context = ProjectionContext(proj_directive, ras.attribute_context) proj_context._current_attribute_state_set = pas_set else: # A type attribute # Initialize projection attribute state pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set( proj_directive, ctx, ras, is_source_polymorphic=False, polymorphic_set=None ) proj_context = ProjectionContext(proj_directive, ras.attribute_context) proj_context._current_attribute_state_set = pas_set is_condition_valid = False if self._condition_expression_tree_root: input = InputValues() input.no_max_depth = proj_directive._has_no_maximum_depth input.is_array = proj_directive._is_array input.reference_only = proj_directive._is_reference_only input.normalized = proj_directive._is_normalized input.structured = proj_directive._is_structured input.is_virtual = proj_directive._is_virtual current_depth = proj_directive._current_depth current_depth += 1 input.next_depth = current_depth proj_directive._current_depth = current_depth input.max_depth = proj_directive._maximum_depth input.min_cardinality = proj_directive._cardinality._minimum_number if proj_directive._cardinality else None input.max_cardinality = proj_directive._cardinality._maximum_number if proj_directive._cardinality else None is_condition_valid = ExpressionTree._evaluate_expression_tree(self._condition_expression_tree_root, input) if is_condition_valid and self.operations and len(self.operations) > 0: # Just in case operations were added programmatically, reindex operations for i in range(len(self.operations)): self.operations[i]._index = i + 1 # Operation acp_gen_attr_set = AttributeContextParameters() acp_gen_attr_set._under = attr_ctx acp_gen_attr_set._type = CdmAttributeContextType.GENERATED_SET acp_gen_attr_set._name = '_generatedAttributeSet' ac_gen_attr_set = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_gen_attr_set) # Start with an empty list for each projection pas_operations = ProjectionAttributeStateSet(proj_context._current_attribute_state_set._ctx) for operation in self.operations: # Evaluate projections and apply to empty state new_pas_operations = operation._append_projection_attribute_state(proj_context, pas_operations, ac_gen_attr_set) # If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state. if new_pas_operations is not None: pas_operations = new_pas_operations # Finally update the current state to the projection context proj_context._current_attribute_state_set = pas_operations return proj_context
def test_entity_string_reference(self): """Test evaluate_expression function""" input_values = InputValues(None) input_values.max_cardinality = 1 input_values.min_cardinality = 0 input_values.max_depth = 32 input_values.next_depth = 1 input_values.no_max_depth = True input_values.is_array = True input_values.normalized = False input_values.reference_only = True input_values.structured = True expr_and_expected_result_list = [] expr_and_expected_result_list.append( ('(cardinality.maximum > 1) && (!referenceOnly)', False)) expr_and_expected_result_list.append(('', True)) expr_and_expected_result_list.append((' ', True)) expr_and_expected_result_list.append(('always', True)) expr_and_expected_result_list.append(('!structured', False)) expr_and_expected_result_list.append( ('referenceOnly || (depth > 5)', True)) expr_and_expected_result_list.append(('!(referenceOnly)', False)) expr_and_expected_result_list.append( ('!(normalized && cardinality.maximum > 1)', True)) expr_and_expected_result_list.append(('true', True)) expr_and_expected_result_list.append(('(((true==true)))', True)) expr_and_expected_result_list.append( ('!(normalized && isArray) || noMaxDepth', False)) for item in expr_and_expected_result_list: actual = ExpressionTree._evaluate_condition(item[0], input_values) expected = item[1] self.assertEqual(expected, actual)