def _append_projection_attribute_state( self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create new attribute context for the operation attr_ctx_op_fk_param = AttributeContextParameters() attr_ctx_op_fk_param._under = attr_ctx attr_ctx_op_fk_param._type = CdmAttributeContextType.OPERATION_REPLACE_AS_FOREIGN_KEY attr_ctx_op_fk_param._name = 'operation/index{}/operationReplaceAsForeignKey'.format( self._index) attr_ctx_op_fk = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_fk_param) # Create new attribute context for the AddedAttributeIdentity attr_ctx_fk_param = AttributeContextParameters() attr_ctx_fk_param._under = attr_ctx_op_fk attr_ctx_fk_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_IDENTITY attr_ctx_fk_param._name = '_foreignKey' attr_ctx_FK = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_fk_param) # get the added attribute and applied trait # the name here will be {m} and not {A}{o}{M} - should this map to the not projections approach and default to {A}{o}{M} - ??? sub_FK = self.replace_with add_trait = ['is.linkedEntity.identifier'] # Create new resolved attribute, set the new attribute as target, and apply "is.linkedEntity.identifier" trait res_attr_new_FK = self._create_new_resolved_attribute( proj_ctx, attr_ctx_FK, sub_FK, None, add_trait) outputFromOpPasSet = self._create_new_projection_attribute_state_set( proj_ctx, proj_output_set, res_attr_new_FK, self.reference) return outputFromOpPasSet
def _add_new_artifact_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> None: # Create a new attribute context for the operation attr_ctx_op_add_artifact_attr_param = AttributeContextParameters() attr_ctx_op_add_artifact_attr_param._under = attr_ctx attr_ctx_op_add_artifact_attr_param._type = CdmAttributeContextType.OPERATION_ADD_ARTIFACT_ATTRIBUTE attr_ctx_op_add_artifact_attr_param._name = 'operation/index{}/{}'.format(self._index, self.get_name()) attr_ctx_op_add_artifact_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_add_artifact_attr_param) from cdm.objectmodel import CdmTypeAttributeDefinition, CdmEntityAttributeDefinition, CdmAttributeGroupReference if isinstance(self.new_attribute, CdmTypeAttributeDefinition): # Create a new attribute context for the new artifact attribute we will create attr_ctx_new_attr_param = AttributeContextParameters() attr_ctx_new_attr_param._under = attr_ctx_op_add_artifact_attr attr_ctx_new_attr_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_NEW_ARTIFACT attr_ctx_new_attr_param._name = self.new_attribute.fetch_object_definition_name() attr_ctx_new_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_new_attr_param) new_res_attr = self._create_new_resolved_attribute(proj_ctx, attr_ctx_new_attr, self.new_attribute) # Create a new projection attribute state for the new artifact attribute and add it to the output set # There is no previous state for the newly created attribute new_PAS = ProjectionAttributeState(self.ctx) new_PAS._current_resolved_attribute = new_res_attr proj_output_set._add(new_PAS) elif isinstance(self.new_attribute, CdmEntityAttributeDefinition) or isinstance(self.new_attribute, CdmAttributeGroupReference): type_str = 'an entity attribute' if isinstance(self.new_attribute, CdmEntityAttributeDefinition) else 'an attribute group' logger.warning(self.ctx, self._TAG, CdmOperationAddArtifactAttribute._append_projection_attribute_state.__name__, self.at_corpus_path, CdmLogCode.WARN_PROJ_ADD_ARTIFACT_ATTR_NOT_SUPPORTED, type_str) else: logger.error(self.ctx, self._TAG, CdmOperationAddArtifactAttribute._append_projection_attribute_state.__name__, self.at_corpus_path, CdmLogCode.ERR_PROJ_UNSUPPORTED_SOURCE, str(self.new_attribute.object_type), self.get_name()) return proj_output_set
def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Pass through all the input projection attribute states if there are any for current_PAS in proj_ctx._current_attribute_state_set._states: proj_output_set._add(current_PAS) # Create a new attribute context for the operation attr_ctx_op_add_count_param = AttributeContextParameters() attr_ctx_op_add_count_param._under = attr_ctx attr_ctx_op_add_count_param._type = CdmAttributeContextType.OPERATION_ADD_COUNT_ATTRIBUTE attr_ctx_op_add_count_param._name = 'operation/index{}/operationAddCountAttribute'.format(self._index) attr_ctx_op_add_count = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_add_count_param) # Create a new attribute context for the Count attribute we will create attr_ctx_count_attr_param = AttributeContextParameters() attr_ctx_count_attr_param._under = attr_ctx_op_add_count attr_ctx_count_attr_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_EXPANSION_TOTAL attr_ctx_count_attr_param._name = self.count_attribute.name attr_ctx_count_attr = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_count_attr_param) # Create the Count attribute with the specified CountAttribute as its target and apply the trait "is.linkedEntity.array.count" to it add_trait = ['is.linkedEntity.array.count'] new_res_attr = self._create_new_resolved_attribute(proj_ctx, attr_ctx_count_attr, self.count_attribute, None, add_trait) # Create a new projection attribute state for the new Count attribute and add it to the output set # There is no previous state for the newly created Count attribute new_PAS = ProjectionAttributeState(proj_output_set._ctx) new_PAS._current_resolved_attribute = new_res_attr proj_output_set._add(new_PAS) return proj_output_set
def _append_projection_attribute_state( self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_exclude_attrs_param = AttributeContextParameters() attr_ctx_op_exclude_attrs_param._under = attr_ctx attr_ctx_op_exclude_attrs_param._type = CdmAttributeContextType.OPERATION_EXCLUDE_ATTRIBUTES attr_ctx_op_exclude_attrs_param._name = 'operation/index{}/operationExcludeAttributes'.format( self._index) attr_ctx_op_exclude_attrs = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_exclude_attrs_param) # Get the top-level attribute names of the attributes to exclude # We use the top-level names because the exclude list may contain a previous name our current resolved attributes had top_level_exclude_attribute_names = ProjectionResolutionCommonUtil._get_top_list( proj_ctx, self.exclude_attributes) # Iterate through all the projection attribute states generated from the source's resolved attributes # Each projection attribute state contains a resolved attribute that it is corresponding to for current_PAS in proj_ctx._current_attribute_state_set._values: # Check if the current projection attribute state's resolved attribute is in the list of attributes to exclude # If this attribute is not in the exclude list, then we are including it in the output if current_PAS._current_resolved_attribute.resolved_name not in top_level_exclude_attribute_names: # Create a new attribute context for the attribute that we are including attr_ctx_added_attr_param = AttributeContextParameters() attr_ctx_added_attr_param._under = attr_ctx attr_ctx_added_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION attr_ctx_added_attr_param._name = current_PAS._current_resolved_attribute.resolved_name attr_ctx_added_attr = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_added_attr_param) # Create a projection attribute state for the included attribute # We only create projection attribute states for attributes that are not in the exclude list # Add the current projection attribute state as the previous state of the new projection attribute state new_PAS = ProjectionAttributeState(proj_output_set._ctx) new_PAS._current_resolved_attribute = current_PAS._current_resolved_attribute new_PAS._previous_state_list = [current_PAS] proj_output_set._add(new_PAS) else: # The current projection attribute state's resolved attribute is in the exclude list # Get the attribute name the way it appears in the exclude list # For our attribute context, we want to use the attribute name the attribute has in the exclude list rather than its current name exclude_attribute_name = top_level_exclude_attribute_names[ current_PAS._current_resolved_attribute.resolved_name] # Create a new attribute context for the excluded attribute attr_ctx_excluded_attr_param = AttributeContextParameters() attr_ctx_excluded_attr_param._under = attr_ctx_op_exclude_attrs attr_ctx_excluded_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION attr_ctx_excluded_attr_param._name = exclude_attribute_name attr_ctx_excluded_attr = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_excluded_attr_param) return proj_output_set
def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', \ attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_add_attr_group_param = AttributeContextParameters() attr_ctx_op_add_attr_group_param._under = attr_ctx attr_ctx_op_add_attr_group_param._type = CdmAttributeContextType.OPERATION_ADD_ATTRIBUTE_GROUP attr_ctx_op_add_attr_group_param._name = 'operation/index{}/{}'.format( self._index, self.get_name()) attr_ctx_op_add_attr_group = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_add_attr_group_param) # Create a new attribute context for the attribute group we will create attr_ctx_attr_group_param = AttributeContextParameters() attr_ctx_attr_group_param._under = attr_ctx_op_add_attr_group attr_ctx_attr_group_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION attr_ctx_attr_group_param._name = self.attribute_group_name attr_ctx_attr_group = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_attr_group_param) # Create a new resolve attribute set builder that will be used to combine all the attributes into one set rasb = ResolvedAttributeSetBuilder() # Iterate through all the projection attribute states generated from the source's resolved attributes # Each projection attribute state contains a resolved attribute that it is corresponding to for current_PAS in proj_ctx._current_attribute_state_set._states: # Create a copy of the resolved attribute resolved_attribute = current_PAS._current_resolved_attribute.copy( ) # type: ResolvedAttribute # Add the attribute to the resolved attribute set rasb._resolved_attribute_set.merge(resolved_attribute) # Add each attribute's attribute context to the resolved attribute set attribute context attr_param = AttributeContextParameters() attr_param._under = attr_ctx_attr_group attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION attr_param._name = resolved_attribute.resolved_name resolved_attribute.att_ctx = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_param) resolved_attribute.att_ctx._add_lineage( current_PAS._current_resolved_attribute.att_ctx) # Create a new resolved attribute that will hold the attribute set containing all the attributes res_attr_new = ResolvedAttribute( proj_ctx._projection_directive._res_opt, rasb._resolved_attribute_set, self.attribute_group_name, attr_ctx_attr_group) # Create a new projection attribute state pointing to the resolved attribute set that represents the attribute group new_PAS = ProjectionAttributeState(self.ctx) new_PAS._current_resolved_attribute = res_attr_new proj_output_set._add(new_PAS) return proj_output_set
def _append_projection_attribute_state( self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Pass through all the input projection attribute states if there are any for current_PAS in proj_ctx._current_attribute_state_set._states: proj_output_set._add(current_PAS) # Create a new attribute context for the operation attr_ctx_op_add_supporting_attr_param = AttributeContextParameters() attr_ctx_op_add_supporting_attr_param._under = attr_ctx attr_ctx_op_add_supporting_attr_param._type = CdmAttributeContextType.OPERATION_ADD_SUPPORTING_ATTRIBUTE attr_ctx_op_add_supporting_attr_param._name = 'operation/index{}/{}'.format( self._index, self.get_name()) attr_ctx_op_add_supporting_attr = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_add_supporting_attr_param) # Create a new attribute context for the supporting attribute we will create attr_ctx_supporting_attr_param = AttributeContextParameters() attr_ctx_supporting_attr_param._under = attr_ctx_op_add_supporting_attr attr_ctx_supporting_attr_param._type = CdmAttributeContextType.ADDED_ATTRIBUTE_SUPPORTING attr_ctx_supporting_attr_param._name = self.supporting_attribute.name attr_ctx_supporting_attr = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_supporting_attr_param) # TODO: this if statement keeps the functionality the same way it works currently in resolution guidance. # This should be changed to point to the foreign key attribute instead. # There has to be some design decisions about how this will work and will be done in the next release. if len(proj_ctx._current_attribute_state_set._states) > 0: last_state = proj_ctx._current_attribute_state_set._states[ -1] # type: ProjectionAttributeState in_support_of_trait = self.supporting_attribute.applied_traits.append( 'is.addedInSupportOf') # type: CdmTraitReferenceBase in_support_of_trait.arguments.append( 'inSupportOf', last_state._current_resolved_attribute.resolved_name) # Create the supporting attribute with the specified 'SupportingAttribute' property as its target and apply the trait 'is.virtual.attribute' to it add_trait = ['is.virtual.attribute'] new_res_attr = self._create_new_resolved_attribute( proj_ctx, attr_ctx_supporting_attr, self.supporting_attribute, added_simple_ref_traits=add_trait) # Create a new projection attribute state for the new supporting attribute and add it to the output set # There is no previous state for the newly created supporting attribute new_PAS = ProjectionAttributeState(proj_output_set._ctx) new_PAS._current_resolved_attribute = new_res_attr proj_output_set._add(new_PAS) return proj_output_set
def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_exclude_attrs_param = AttributeContextParameters() attr_ctx_op_exclude_attrs_param._under = attr_ctx attr_ctx_op_exclude_attrs_param._type = CdmAttributeContextType.OPERATION_EXCLUDE_ATTRIBUTES attr_ctx_op_exclude_attrs_param._name = 'operation/index{}/operationExcludeAttributes'.format(self._index) attr_ctx_op_exclude_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_exclude_attrs_param) # Get the top-level attribute names of the attributes to exclude # We use the top-level names because the exclude list may contain a previous name our current resolved attributes had top_level_exclude_attribute_names = ProjectionResolutionCommonUtil._get_top_list(proj_ctx, self.exclude_attributes) # Initialize a projection attribute context tree builder with the created attribute context for the operation attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_exclude_attrs) # Iterate through all the projection attribute states generated from the source's resolved attributes # Each projection attribute state contains a resolved attribute that it is corresponding to for current_PAS in proj_ctx._current_attribute_state_set._states: # Check if the current projection attribute state's resolved attribute is in the list of attributes to exclude # If this attribute is not in the exclude list, then we are including it in the output if current_PAS._current_resolved_attribute.resolved_name not in top_level_exclude_attribute_names: # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters( None, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION, current_PAS._current_resolved_attribute.att_ctx, # lineage is the included attribute None) # don't know who will point here yet # Create a projection attribute state for the included attribute by creating a copy of the current state # Copy() sets the current state as the previous state for the new one # We only create projection attribute states for attributes that are not in the exclude list new_PAS = current_PAS._copy() proj_output_set._add(new_PAS) else: # The current projection attribute state's resolved attribute is in the exclude list # Get the attribute name the way it appears in the exclude list exclude_attribute_name = top_level_exclude_attribute_names[current_PAS._current_resolved_attribute.resolved_name] # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters( exclude_attribute_name, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_EXCLUDED, current_PAS._current_resolved_attribute.att_ctx, # lineage is the included attribute None) # don't know who will point here yet, excluded, so... this could be the end for you. # Create all the attribute contexts and construct the tree attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx) return proj_output_set
def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_attr_state_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_include_attrs_param = AttributeContextParameters() # type: AttributeContextParameters attr_ctx_op_include_attrs_param._under = attr_ctx attr_ctx_op_include_attrs_param._type = CdmAttributeContextType.OPERATION_INCLUDE_ATTRIBUTES attr_ctx_op_include_attrs_param._name = 'operation/indexIndex{}/operationIncludeAttributes'.format(self._index) attr_ctx_op_include_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_include_attrs_param) # type: CdmAttributeContext # Get the top-level attribute names for each of the included attributes # Since the include operation allows providing either current state resolved attribute names # or the previous state resolved attribute names, we search for the name in the PAS tree # and fetch the top level resolved attribute names. top_level_include_attribute_names = ProjectionResolutionCommonUtil._get_top_list(proj_ctx, self.include_attributes) # type: Dict[str, str] # Initialize a projection attribute context tree builder with the created attribute context for the operation attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_include_attrs) # Iterate through all the PAS in the PASSet generated from the projection source's resolved attributes for current_PAS in proj_ctx._current_attribute_state_set._states: # Check if the current PASs RA is in the list of attributes to include. if current_PAS._current_resolved_attribute.resolved_name in top_level_include_attribute_names: # Get the attribute name the way it appears in the include list include_attribute_name = top_level_include_attribute_names[current_PAS._current_resolved_attribute.resolved_name] # type: str # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters(include_attribute_name, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION) # Create a projection attribute state for the included attribute by creating a copy of the current state # Copy() sets the current state as the previous state for the new one # We only create projection attribute states for attributes in the include list new_PAS = current_PAS._copy() proj_attr_state_set._add(new_PAS) else: # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters(None, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION) # Create all the attribute contexts and construct the tree attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx) return proj_attr_state_set
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
async def create_resolved_entity_async(self, new_ent_name: str, res_opt: Optional['ResolveOptions'] = None, folder: 'CdmFolderDefinition' = None, new_doc_name: str = None) -> 'CdmEntityDefinition': """Create a resolved copy of the entity.""" if not res_opt: res_opt = ResolveOptions(self, self.ctx.corpus.default_resolution_directives) if not res_opt.wrt_doc: logger.error(self._TAG, self.ctx, 'No WRT document was supplied.', self.create_resolved_entity_async.__name__) return None if not new_ent_name: logger.error(self._TAG, self.ctx, 'No Entity Name provided.', self.create_resolved_entity_async.__name__) return None folder = folder or self.in_document.folder file_name = new_doc_name or new_ent_name + PersistenceLayer.CDM_EXTENSION orig_doc = self.in_document.at_corpus_path # Don't overwite the source document. target_at_corpus_path = self.ctx.corpus.storage.create_absolute_corpus_path(folder.at_corpus_path, folder) + file_name if StringUtils.equals_with_ignore_case(target_at_corpus_path, orig_doc): logger.error(self._TAG, self.ctx, 'Attempting to replace source entity\'s document \'{}\''.format( target_at_corpus_path), self.create_resolved_entity_async.__name__) return None if not await res_opt.wrt_doc._index_if_needed(res_opt, True): logger.error(self._TAG, self.ctx, 'Couldn\'t index source document.', self.create_resolved_entity_async.__name__) return None # Make the top level attribute context for this entity. # for this whole section where we generate the attribute context tree and get resolved attributes # set the flag that keeps all of the parent changes and document dirty from from happening was_resolving = self.ctx.corpus._is_currently_resolving self.ctx.corpus._is_currently_resolving = True ent_name = new_ent_name ctx = self.ctx att_ctx_ent = ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_CONTEXT_DEF, ent_name, True) # type: CdmAttributeContext att_ctx_ent.ctx = ctx att_ctx_ent.in_document = self.in_document # Cheating a bit to put the paths in the right place. acp = AttributeContextParameters() acp._under = att_ctx_ent acp._type = CdmAttributeContextType.ATTRIBUTE_GROUP acp._name = 'attributeContext' att_ctx_ac = CdmAttributeContext._create_child_under(res_opt, acp) acp_ent = AttributeContextParameters() acp_ent._under = att_ctx_ac acp_ent._type = CdmAttributeContextType.ENTITY acp_ent._name = ent_name acp_ent._regarding = ctx.corpus.make_ref(CdmObjectType.ENTITY_REF, self.get_name(), True) # Use this whenever we need to keep references pointing at things that were already found. # Used when 'fixing' references by localizing to a new document. res_opt_copy = CdmObject._copy_resolve_options(res_opt) res_opt_copy._save_resolutions_on_copy = True # Resolve attributes with this context. the end result is that each resolved attribute points to the level of # the context where it was created. ras = self._fetch_resolved_attributes(res_opt_copy, acp_ent) if ras is None: self._resolving_attributes = False return None # Create a new copy of the attribute context for this entity. # TODO: all_att_ctx type ideally should be ordered set all_att_ctx = [] # type: List[CdmAttributeContext] new_node = cast('CdmAttributeContext', att_ctx_ent.copy_node(res_opt)) att_ctx_ent = att_ctx_ent._copy_attribute_context_tree(res_opt, new_node, ras, all_att_ctx, 'resolvedFrom') att_ctx = cast('CdmAttributeContext', cast('CdmAttributeContext', att_ctx_ent.contents[0]).contents[0]) self.ctx.corpus._is_currently_resolving = was_resolving # make a new document in given folder if provided or the same folder as the source entity folder.documents.remove(file_name) doc_res = folder.documents.append(file_name) # add a import of the source document orig_doc = self.ctx.corpus.storage.create_relative_corpus_path(orig_doc, doc_res) # just in case we missed the prefix doc_res.imports.append(orig_doc, "resolvedFrom") # make the empty entity ent_resolved = doc_res.definitions.append(ent_name) # set the context to the copy of the tree. fix the docs on the context nodes ent_resolved.attribute_context = att_ctx def visit_callback(obj: 'CdmObject', path: str) -> bool: obj.in_document = doc_res return False att_ctx.visit('{}/attributeContext/'.format(ent_name), visit_callback, None) # add the traits of the entity rts_ent = self._fetch_resolved_traits(res_opt) for rt in rts_ent.rt_set: trait_ref = CdmObject._resolved_trait_to_trait_ref(res_opt_copy, rt) ent_resolved.exhibits_traits.append(trait_ref) # The attributes have been named, shaped, etc. for this entity so now it is safe to go and make each attribute # context level point at these final versions of attributes. att_path_to_order = {} # type: Dict[str, int] def point_context_at_resolved_atts(ras_sub: 'ResolvedAttributeSet', path: str) -> None: for ra in ras_sub._set: ra_ctx_in_ent = [] # type: List[CdmAttributeContext] ra_ctx_set = ras_sub.rattr_to_attctxset.get(ra) # find the correct attctx for this copy, intersect these two sets # (interate over the shortest list) if len(all_att_ctx) < len(ra_ctx_set): for curr_att_ctx in all_att_ctx: if curr_att_ctx in ra_ctx_set: ra_ctx_in_ent.append(curr_att_ctx) else: for curr_att_ctx in ra_ctx_set: if curr_att_ctx in all_att_ctx: ra_ctx_in_ent.append(curr_att_ctx) for ra_ctx in ra_ctx_in_ent: refs = ra_ctx.contents # there might be more than one explanation for where and attribute came from when things get merges as they do # This won't work when I add the structured attributes to avoid name collisions. att_ref_path = path + ra.resolved_name if isinstance(ra.target, CdmObject): if not att_ref_path in att_path_to_order: att_ref = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_REF, att_ref_path, True) # type: CdmObjectReference # only need one explanation for this path to the insert order att_path_to_order[att_ref.named_reference] = ra.insert_order refs.append(att_ref) else: att_ref_path += '/members/' point_context_at_resolved_atts(cast('ResolvedAttributeSet', ra.target), att_ref_path) point_context_at_resolved_atts(ras, ent_name + '/hasAttributes/') # generated attribute structures may end up with 0 attributes after that. prune them def clean_sub_group(sub_item, under_generated) -> bool: if sub_item.object_type == CdmObjectType.ATTRIBUTE_REF: return True # not empty ac = sub_item # type: CdmAttributeContext if ac.type == CdmAttributeContextType.GENERATED_SET: under_generated = True if not ac.contents: return False # empty # look at all children, make a set to remove to_remove = [] # type: List[CdmAttributeContext] for sub_sub in ac.contents: if not clean_sub_group(sub_sub, under_generated): potential_target = under_generated if not potential_target: # cast is safe because we returned false meaning empty and not a attribute ref # so is this the set holder itself? potential_target = sub_sub.type == CdmAttributeContextType.GENERATED_SET if potential_target: to_remove.append(sub_sub) for to_die in to_remove: ac.contents.remove(to_die) return bool(ac.contents) clean_sub_group(att_ctx, False) # Create an all-up ordering of attributes at the leaves of this tree based on insert order. Sort the attributes # in each context by their creation order and mix that with the other sub-contexts that have been sorted. def get_order_num(item: 'CdmObject') -> int: if item.object_type == CdmObjectType.ATTRIBUTE_CONTEXT_DEF: return order_contents(item) if item.object_type == CdmObjectType.ATTRIBUTE_REF: return att_path_to_order[item.named_reference] # put the mystery item on top. return -1 def order_contents(under: 'CdmAttributeContext') -> int: if under._lowest_order is None: under._lowest_order = -1 if len(under.contents) == 1: under._lowest_order = get_order_num(under.contents[0]) else: def get_and_update_order(item1: 'CdmObject', item2: 'CdmObject'): left_order = get_order_num(item1) right_order = get_order_num(item2) if left_order != -1 and (under._lowest_order == -1 or left_order < under._lowest_order): under._lowest_order = left_order if right_order != -1 and (under._lowest_order == -1 or right_order < under._lowest_order): under._lowest_order = right_order return left_order - right_order under.contents.sort(key=functools.cmp_to_key(get_and_update_order)) return under._lowest_order order_contents(att_ctx) # Resolved attributes can gain traits that are applied to an entity when referenced since these traits are # described in the context, it is redundant and messy to list them in the attribute. So, remove them. Create and # cache a set of names to look for per context. There is actually a hierarchy to this. All attributes from the # base entity should have all traits applied independently of the sub-context they come from. Same is true of # attribute entities. So do this recursively top down. ctx_to_trait_names = {} # type: Dict[CdmAttributeContext, Set[str]] def collect_context_traits(sub_att_ctx: 'CdmAttributeContext', inherited_trait_names: Set[str]) -> None: trait_names_here = set(inherited_trait_names) traits_here = sub_att_ctx.exhibits_traits if traits_here: for tat in traits_here: trait_names_here.add(tat.named_reference) ctx_to_trait_names[sub_att_ctx] = trait_names_here for cr in sub_att_ctx.contents: if cr.object_type == CdmObjectType.ATTRIBUTE_CONTEXT_DEF: # do this for all types? collect_context_traits(cast('CdmAttributeContext', cr), trait_names_here) collect_context_traits(att_ctx, set()) # add the attributes, put them in attribute groups if structure needed. res_att_to_ref_path = {} # type: Dict[ResolvedAttribute, str] def add_attributes(ras_sub: 'ResolvedAttributeSet', container: 'Union[CdmEntityDefinition, CdmAttributeGroupDefinition]', path: str) -> None: for ra in ras_sub._set: att_path = path + ra.resolved_name # use the path of the context associated with this attribute to find the new context that matches on path. ra_ctx_set = ras_sub.rattr_to_attctxset[ra] # find the correct att_ctx for this copy. # ra_ctx = next((ac for ac in all_att_ctx if ac in ra_ctx_set), None) # type: CdmAttributeContext if len(all_att_ctx) < len(ra_ctx_set): for curr_att_ctx in all_att_ctx: if curr_att_ctx in ra_ctx_set: ra_ctx = curr_att_ctx break else: for curr_att_ctx in ra_ctx_set: if curr_att_ctx in all_att_ctx: ra_ctx = curr_att_ctx break if isinstance(ra.target, ResolvedAttributeSet) and cast('ResolvedAttributeSet', ra.target)._set: # this is a set of attributes. Make an attribute group to hold them. att_grp = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_GROUP_DEF, ra.resolved_name) # type: CdmAttributeGroupDefinition att_grp.attribute_context = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_CONTEXT_REF, ra_ctx.at_corpus_path, True) # take any traits from the set and make them look like traits exhibited by the group. avoid_set = ctx_to_trait_names[ra_ctx] rts_att = ra.resolved_traits for rt in rts_att.rt_set: if not rt.trait.ugly: # Don't mention your ugly traits. if not avoid_set or rt.trait_name not in avoid_set: # Avoid the ones from the context. trait_ref = CdmObject._resolved_trait_to_trait_ref(res_opt_copy, rt) cast('CdmObjectDefinition', att_grp).exhibits_traits.append(trait_ref, isinstance(trait_ref, str)) # wrap it in a reference and then recurse with this as the new container. att_grp_ref = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_GROUP_REF, None) # type: CdmAttributeGroupReference att_grp_ref.explicit_reference = att_grp container._add_attribute_def(att_grp_ref) # isn't this where ... add_attributes(cast('ResolvedAttributeSet', ra.target), att_grp, att_path + '/(object)/members/') else: att = self.ctx.corpus.make_object(CdmObjectType.TYPE_ATTRIBUTE_DEF, ra.resolved_name) # type: CdmTypeAttributeDefinition att.attribute_context = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_CONTEXT_REF, ra_ctx.at_corpus_path, True) avoid_set = ctx_to_trait_names[ra_ctx] rts_att = ra.resolved_traits for rt in rts_att.rt_set: if not rt.trait.ugly: # Don't mention your ugly traits. if not avoid_set or rt.trait_name not in avoid_set: # Avoid the ones from the context. trait_ref = CdmObject._resolved_trait_to_trait_ref(res_opt_copy, rt) cast('CdmTypeAttributeDefinition', att).applied_traits.append(trait_ref, isinstance(trait_ref, str)) # none of the dataformat traits have the bit set that will make them turn into a property # this is intentional so that the format traits make it into the resolved object # but, we still want a guess as the data format, so get it and set it. implied_data_format = att.data_format if implied_data_format: att.data_format = implied_data_format container._add_attribute_def(att) res_att_to_ref_path[ra] = att_path add_attributes(ras, ent_resolved, ent_name + '/hasAttributes/') # Any resolved traits that hold arguments with attribute refs should get 'fixed' here. def replace_trait_att_ref(tr: 'CdmTraitReference', entity_hint: str) -> None: if tr.arguments: for arg in tr.arguments: v = arg._unresolved_value or arg.value # Is this an attribute reference? if v and isinstance(v, CdmObject) and cast('CdmObject', v).object_type == CdmObjectType.ATTRIBUTE_REF: # Only try this if the reference has no path to it (only happens with intra-entity att refs). att_ref = cast('CdmAttributeReference', v) if att_ref.named_reference and att_ref.named_reference.find('/') == -1: if not arg._unresolved_value: arg._unresolved_value = arg.value # Give a promise that can be worked out later. # Assumption is that the attribute must come from this entity. new_att_ref = self.ctx.corpus.make_object(CdmObjectType.ATTRIBUTE_REF, entity_hint + '/(resolvedAttributes)/' + att_ref.named_reference, True) # inDocument is not propagated during resolution, so set it here new_att_ref.in_document = arg.in_document arg.set_value(new_att_ref) # Fix entity traits. if ent_resolved.exhibits_traits: for et in ent_resolved.exhibits_traits: replace_trait_att_ref(et, new_ent_name) # Fix context traits. def fix_context_traits(sub_att_ctx: 'CdmAttributeContext', entity_hint: str) -> None: traits_here = sub_att_ctx.exhibits_traits if traits_here: for tr in traits_here: replace_trait_att_ref(tr, entity_hint) for cr in sub_att_ctx.contents: if cr.object_type == CdmObjectType.ATTRIBUTE_CONTEXT_DEF: # If this is a new entity context, get the name to pass along. sub_sub_att_ctx = cast('CdmAttributeContext', cr) sub_entity_hint = entity_hint if sub_sub_att_ctx.type == CdmAttributeContextType.ENTITY: sub_entity_hint = sub_sub_att_ctx.definition.named_reference # Do this for all types. fix_context_traits(sub_sub_att_ctx, sub_entity_hint) fix_context_traits(att_ctx, new_ent_name) # And the attribute traits. ent_atts = ent_resolved.attributes if ent_atts: for attribute in ent_atts: att_traits = attribute.applied_traits if att_traits: for tr in att_traits: replace_trait_att_ref(tr, new_ent_name) # We are about to put this content created in the context of various documents (like references to attributes # from base entities, etc.) into one specific document. All of the borrowed refs need to work. so, re-write all # string references to work from this new document. The catch-22 is that the new document needs these fixes done # before it can be used to make these fixes. The fix needs to happen in the middle of the refresh trigger the # document to refresh current content into the resolved OM. cast('CdmAttributeContext', att_ctx).parent = None # Remove the fake parent that made the paths work. res_opt_new = CdmObject._copy_resolve_options(res_opt) res_opt_new.wrt_doc = doc_res res_opt_new._localize_references_for = doc_res if not await doc_res.refresh_async(res_opt_new): logger.error(self._TAG, self.ctx, 'Failed to index the resolved document.', self.create_resolved_entity_async.__name__) return None # Get a fresh ref. ent_resolved = cast('CdmEntityDefinition', doc_res._fetch_object_from_document_path(ent_name, res_opt_new)) self.ctx.corpus._res_ent_map[self.at_corpus_path] = ent_resolved.at_corpus_path return ent_resolved
def _construct_projection_context(self, proj_directive: 'ProjectionDirective', attr_ctx: 'CdmAttributeContext', ras: Optional['ResolvedAttributeSet'] = None) -> 'ProjectionContext': """ A function to construct projection context and populate the resolved attribute set that ExtractResolvedAttributes method can then extract This function is the entry point for projection resolution. This function is expected to do the following 3 things: - Create an condition expression tree & default if appropriate - Create and initialize Projection Context - Process operations """ proj_context = None condition = self.condition if self.condition else "(true)" # create an expression tree based on the condition tree = ExpressionTree() self._condition_expression_tree_root = tree._construct_expression_tree(condition) if attr_ctx: # Add projection to context tree acp_proj = AttributeContextParameters() acp_proj._under = attr_ctx acp_proj._type = CdmAttributeContextType.PROJECTION acp_proj._name = self.fetch_object_definition_name() acp_proj._regarding = proj_directive._owner_ref acp_proj._include_traits = False ac_proj = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_proj) acp_source = AttributeContextParameters() acp_source._under = ac_proj acp_source._type = CdmAttributeContextType.SOURCE acp_source._name = 'source' acp_source._regarding = None acp_source._include_traits = False ac_source = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_source) # Initialize the projection context ctx = proj_directive._owner.ctx if proj_directive._owner else None if self.source: source = self.source.fetch_object_definition(proj_directive._res_opt) if source.object_type == CdmObjectType.PROJECTION_DEF: # A Projection proj_context = self.source.explicit_reference._construct_projection_context(proj_directive, ac_source, ras) else: # An Entity Reference acp_source_projection = AttributeContextParameters() acp_source_projection._under = ac_source acp_source_projection._type = CdmAttributeContextType.ENTITY acp_source_projection._name = self.source.named_reference if self.source.named_reference else self.source.explicit_reference.get_name() acp_source_projection._regarding = self.source acp_source_projection._include_traits = False ras = self.source._fetch_resolved_attributes(proj_directive._res_opt, acp_source_projection) # If polymorphic keep original source as previous state poly_source_set = None if proj_directive._is_source_polymorphic: poly_source_set = ProjectionResolutionCommonUtil._get_polymorphic_source_set(proj_directive, ctx, self.source, acp_source_projection) # Now initialize projection attribute state pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set( proj_directive, ctx, ras, proj_directive._is_source_polymorphic, poly_source_set ) proj_context = ProjectionContext(proj_directive, ras.attribute_context) proj_context._current_attribute_state_set = pas_set else: # A type attribute # Initialize projection attribute state pas_set = ProjectionResolutionCommonUtil._initialize_projection_attribute_state_set( proj_directive, ctx, ras, is_source_polymorphic=False, polymorphic_set=None ) proj_context = ProjectionContext(proj_directive, ras.attribute_context) proj_context._current_attribute_state_set = pas_set is_condition_valid = False if self._condition_expression_tree_root: input = InputValues() input.no_max_depth = proj_directive._has_no_maximum_depth input.is_array = proj_directive._is_array input.reference_only = proj_directive._is_reference_only input.normalized = proj_directive._is_normalized input.structured = proj_directive._is_structured input.is_virtual = proj_directive._is_virtual current_depth = proj_directive._current_depth current_depth += 1 input.next_depth = current_depth proj_directive._current_depth = current_depth input.max_depth = proj_directive._maximum_depth input.min_cardinality = proj_directive._cardinality._minimum_number if proj_directive._cardinality else None input.max_cardinality = proj_directive._cardinality._maximum_number if proj_directive._cardinality else None is_condition_valid = ExpressionTree._evaluate_expression_tree(self._condition_expression_tree_root, input) if is_condition_valid and self.operations and len(self.operations) > 0: # Just in case operations were added programmatically, reindex operations for i in range(len(self.operations)): self.operations[i]._index = i + 1 # Operation acp_gen_attr_set = AttributeContextParameters() acp_gen_attr_set._under = attr_ctx acp_gen_attr_set._type = CdmAttributeContextType.GENERATED_SET acp_gen_attr_set._name = '_generatedAttributeSet' ac_gen_attr_set = CdmAttributeContext._create_child_under(proj_directive._res_opt, acp_gen_attr_set) # Start with an empty list for each projection pas_operations = ProjectionAttributeStateSet(proj_context._current_attribute_state_set._ctx) for operation in self.operations: # Evaluate projections and apply to empty state new_pas_operations = operation._append_projection_attribute_state(proj_context, pas_operations, ac_gen_attr_set) # If the operations fails or it is not implemented the projection cannot be evaluated so keep previous valid state. if new_pas_operations is not None: pas_operations = new_pas_operations # Finally update the current state to the projection context proj_context._current_attribute_state_set = pas_operations return proj_context
def _append_projection_attribute_state( self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_array_expansion_param = AttributeContextParameters() attr_ctx_op_array_expansion_param._under = attr_ctx attr_ctx_op_array_expansion_param._type = CdmAttributeContextType.OPERATION_ARRAY_EXPANSION attr_ctx_op_array_expansion_param._name = 'operation/index{}/operationArrayExpansion'.format( self._index) attr_ctx_op_array_expansion = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_array_expansion_param) # Expansion steps start at round 0 round = 0 proj_attr_states_from_rounds = [] # Ordinal validation if self.start_ordinal > self.end_ordinal: logger.warning( self._TAG, self.ctx, 'startOrdinal {} should not be greater than endOrdinal {}'. format(self.start_ordinal, self.end_ordinal)) else: # Ordinals should start at startOrdinal or 0, whichever is larger. starting_ordinal = max(0, self.start_ordinal) # Ordinals should end at endOrdinal or the maximum ordinal allowed (set in resolve options), whichever is smaller. if self.end_ordinal > proj_ctx._projection_directive._res_opt.max_ordinal_for_array_expansion: logger.warning( self._TAG, self.ctx, 'endOrdinal {} is greater than the maximum allowed ordinal of {}. Using the maximum allowed ordinal instead.' .format( self.end_ordinal, proj_ctx._projection_directive. _res_opt.max_ordinal_for_array_expansion)) ending_ordinal = min( proj_ctx._projection_directive._res_opt. max_ordinal_for_array_expansion, self.end_ordinal) # For each ordinal, create a copy of the input resolved attribute for i in range(starting_ordinal, ending_ordinal + 1): # Create a new attribute context for the round attr_ctx_round_param = AttributeContextParameters() attr_ctx_round_param._under = attr_ctx_op_array_expansion attr_ctx_round_param._type = CdmAttributeContextType.GENERATED_ROUND attr_ctx_round_param._name = '_generatedAttributeRound{}'.format( round) attr_ctx_round = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_round_param) # Iterate through all the projection attribute states generated from the source's resolved attributes # Each projection attribute state contains a resolved attribute that it is corresponding to for current_PAS in proj_ctx._current_attribute_state_set._states: # Create a new attribute context for the expanded attribute with the current ordinal attr_ctx_expanded_attr_param = AttributeContextParameters() attr_ctx_expanded_attr_param._under = attr_ctx_round attr_ctx_expanded_attr_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION attr_ctx_expanded_attr_param._name = '{}@{}'.format( current_PAS._current_resolved_attribute.resolved_name, i) attr_ctx_expanded_attr = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_expanded_attr_param) # Create a new resolved attribute for the expanded attribute new_res_attr = self._create_new_resolved_attribute( proj_ctx, attr_ctx_expanded_attr, current_PAS._current_resolved_attribute.target, current_PAS._current_resolved_attribute.resolved_name) # Create a projection attribute state for the expanded attribute new_PAS = ProjectionAttributeState(proj_output_set._ctx) new_PAS._current_resolved_attribute = new_res_attr new_PAS._previous_state_list = [current_PAS] new_PAS._ordinal = i proj_attr_states_from_rounds.append(new_PAS) if i == ending_ordinal: break # Increment the round round += 1 if len(proj_attr_states_from_rounds) == 0: # No rounds were produced from the array expansion - input passes through for pas in proj_ctx._current_attribute_state_set._states: proj_output_set._add(pas) else: # Add all the projection attribute states containing the expanded attributes to the output for pas in proj_attr_states_from_rounds: proj_output_set._add(pas) return proj_output_set
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
def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_attr_state_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_combine_attrs_param = AttributeContextParameters() # type: AttributeContextParameters attr_ctx_op_combine_attrs_param._under = attr_ctx attr_ctx_op_combine_attrs_param._type = CdmAttributeContextType.OPERATION_COMBINE_ATTRIBUTES attr_ctx_op_combine_attrs_param._name = 'operation/index{}/operationCombineAttributes'.format(self._index) attr_ctx_op_combine_attrs = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, attr_ctx_op_combine_attrs_param) # type: CdmAttributeContext # Initialize a projection attribute context tree builder with the created attribute context for the operation attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder(attr_ctx_op_combine_attrs) # Get all the leaf level PAS nodes from the tree for each selected attribute and cache to a dictionary leaf_level_combine_attribute_names = OrderedDict() # OrderedDict[str, ProjectionAttributeState[]]() # Also, create a single list of leaf level PAS leaf_level_merge_pas_list = [] # type: List[ProjectionAttributeState] for select in self.select: leaf_level_list_for_current_select = ProjectionResolutionCommonUtil._get_leaf_list(proj_ctx, select) # type: List[ProjectionAttributeState] if leaf_level_list_for_current_select is not None and len(leaf_level_list_for_current_select) > 0 and not(select in leaf_level_combine_attribute_names): leaf_level_combine_attribute_names.__setitem__(select, leaf_level_list_for_current_select) leaf_level_merge_pas_list.extend(leaf_level_list_for_current_select) # Create a list of top-level PAS objects that will be get merged based on the selected attributes pas_merge_list = [] # type: List[ProjectionAttributeState] # Run through the top-level PAS objects for current_pas in proj_ctx._current_attribute_state_set._states: if current_pas._current_resolved_attribute._resolved_name in leaf_level_combine_attribute_names: # Attribute to Merge if current_pas not in pas_merge_list: pas_merge_list.append(current_pas) else: # Attribute to Pass Through # Create a projection attribute state for the non-selected / pass-through attribute by creating a copy of the current state # Copy() sets the current state as the previous state for the new one new_pas = current_pas._copy() # type: ProjectionAttributeState proj_attr_state_set._add(new_pas) if len(pas_merge_list) > 0: merge_into_attribute = self.merge_into # type: CdmTypeAttributeDefinition # the merged attribute needs one new place to live, so here it is merged_attr_ctx_param = AttributeContextParameters() # type: AttributeContextParameters merged_attr_ctx_param._under = attr_ctx_op_combine_attrs merged_attr_ctx_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION merged_attr_ctx_param._name = merge_into_attribute.get_name() merged_attr_ctx = CdmAttributeContext._create_child_under(proj_ctx._projection_directive._res_opt, merged_attr_ctx_param) # Create new resolved attribute, set the new attribute as target ra_new_merge_into = self._create_new_resolved_attribute(proj_ctx, merged_attr_ctx, merge_into_attribute, None) # Create new output projection attribute state set new_merge_into_pas = ProjectionAttributeState(proj_attr_state_set._ctx) # type: ProjectionAttributeState new_merge_into_pas._current_resolved_attribute = ra_new_merge_into new_merge_into_pas._previous_state_list = pas_merge_list attributes_added_to_context = set() # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end for select in leaf_level_combine_attribute_names.keys(): if select in leaf_level_combine_attribute_names and leaf_level_combine_attribute_names[select] is not None and len(leaf_level_combine_attribute_names[select]) > 0: for leaf_level_for_select in leaf_level_combine_attribute_names[select]: # When dealing with a polymorphic entity, it is possible that multiple entities have an attribute with the same name # Only one attribute with each name should be added otherwise the attribute context will end up with duplicated nodes if leaf_level_for_select._current_resolved_attribute._resolved_name not in attributes_added_to_context: attributes_added_to_context.add(leaf_level_for_select._current_resolved_attribute._resolved_name) attr_ctx_tree_builder._create_and_store_attribute_context_parameters( select, leaf_level_for_select, new_merge_into_pas._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION, leaf_level_for_select._current_resolved_attribute.att_ctx, # lineage is the source att new_merge_into_pas._current_resolved_attribute.att_ctx) # merge into points back here proj_attr_state_set._add(new_merge_into_pas) # Create all the attribute contexts and construct the tree attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx) return proj_attr_state_set
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
def _create_and_store_attribute_context_parameters( self, search_for: str, found: 'ProjectionAttributeState', res_attr_from_action: 'ResolvedAttribute', attr_ctx_type: 'CdmAttributeContextType', lineage_out: 'CdmAttributeContext', lineage_in: 'CdmAttributeContext') -> None: """ Creates the attribute context parameters for the search_for, found, and action nodes and then stores them in different maps. The maps are used when constructing the actual attribute context tree. :param search_for: The 'search for' string :param found: The projection attribute state that contains the 'found' attribute :param res_attr_from_action: The resolved attribute that resulted from the action :param attr_ctx_type: The attribute context type to give the 'action' attribute context parameter """ # search_for is null when we have to construct attribute contexts for the excluded attributes in Include or the included attributes in Exclude, # as these attributes weren't searched for with a search_for name. # If search_for is null, just set it to have the same name as found so that it'll collapse in the final tree. if search_for is None: search_for = found._current_resolved_attribute.resolved_name # Create the attribute context parameter for the search_for node and store it in the map as [search_for name]:[attribute context parameter] search_for_attr_ctx_param = None if search_for not in self._search_for_to_search_for_attr_ctx_param: search_for_attr_ctx_param = AttributeContextParameters() search_for_attr_ctx_param._under = self._root search_for_attr_ctx_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION search_for_attr_ctx_param._name = search_for self._search_for_to_search_for_attr_ctx_param[ search_for] = search_for_attr_ctx_param else: search_for_attr_ctx_param = self._search_for_to_search_for_attr_ctx_param[ search_for] # Create the attribute context parameter for the found node found_attr_ctx_param = AttributeContextParameters() found_attr_ctx_param._under = self._root # Set this to be under the root for now, as we may end up collapsing this node found_attr_ctx_param._type = CdmAttributeContextType.ATTRIBUTE_DEFINITION found_attr_ctx_param._name = '{}{}'.format( found._current_resolved_attribute.resolved_name, '@' + str(found._ordinal) if found._ordinal is not None else '') # Store this in the map as [search_for attribute context parameter]:[found attribute context parameters] # We store it this way so that we can create the found nodes under their corresponding search_for nodes. if search_for_attr_ctx_param not in self._search_for_attr_ctx_param_to_found_attr_ctx_param: self._search_for_attr_ctx_param_to_found_attr_ctx_param[ search_for_attr_ctx_param] = [] found_attr_ctx_params = self._search_for_attr_ctx_param_to_found_attr_ctx_param[ search_for_attr_ctx_param] found_attr_ctx_params.append(found_attr_ctx_param) # Create the attribute context parameter for the action node action_attr_ctx_param = AttributeContextParameters() action_attr_ctx_param._under = self._root # Set this to be under the root for now, as we may end up collapsing this node action_attr_ctx_param._type = attr_ctx_type # This type will be updated once we implement the new attribute context types action_attr_ctx_param._name = res_attr_from_action.resolved_name # Store this in the map as [found attribute context parameter]:[action attribute context parameter] # We store it this way so that we can create the action nodes under their corresponding found nodes. self._found_attr_ctx_param_to_action_attr_ctx_param[ found_attr_ctx_param] = action_attr_ctx_param # Store the action attribute context parameter with the resolved attribute resulting out of the action. # This is so that we can point the action attribute context to the correct resolved attribute once the attribute context is created. self._action_attr_ctx_param_to_res_attr[ action_attr_ctx_param] = res_attr_from_action # Store the current resAtt as the lineage of the new one # of note, if no lineage is stored AND the resolved Att associated above holds an existing context? we will # Flip the lineage when we make a new context and point 'back' to this new node. this means this new node should # point 'back' to the context of the source attribute if lineage_out is not None: self._action_attr_ctx_param_to_lineage_out[ action_attr_ctx_param] = lineage_out if lineage_in is not None: self._action_attr_ctx_param_to_lineage_in[ action_attr_ctx_param] = lineage_in
def _append_projection_attribute_state(self, proj_ctx: 'ProjectionContext', proj_output_set: 'ProjectionAttributeStateSet', \ attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_rename_attrs_param = AttributeContextParameters() attr_ctx_op_rename_attrs_param._under = attr_ctx attr_ctx_op_rename_attrs_param._type = CdmAttributeContextType.OPERATION_RENAME_ATTRIBUTES attr_ctx_op_rename_attrs_param._name = 'operation/index{}/operationRenameAttributes'.format( self._index) attr_ctx_op_rename_attrs = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_rename_attrs_param) # type: CdmAttributeContext # Get the list of attributes that will be renamed rename_attributes = None # type: List[str] if self.apply_to is not None: rename_attributes = self.apply_to else: rename_attributes = [] for current_PAS in proj_ctx._current_attribute_state_set._states: rename_attributes.append( current_PAS._current_resolved_attribute._resolved_name) # Get the top-level attribute names of the attributes to rename # We use the top-level names because the rename list may contain a previous name our current resolved attributes had top_level_rename_attribute_names = ProjectionResolutionCommonUtil._get_top_list( proj_ctx, rename_attributes) # type: Dict[str, str] source_attribute_name = proj_ctx._projection_directive._original_source_entity_attribute_name # type: str # Initialize a projection attribute context tree builder with the created attribute context for the operation attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder( attr_ctx_op_rename_attrs) # Iterate through all the projection attribute states generated from the source's resolved attributes # Each projection attribute state contains a resolved attribute that it is corresponding to for current_PAS in proj_ctx._current_attribute_state_set._states: # Check if the current projection attribute state's resolved attribute is in the list of attributes to rename # If this attribute is not in the rename list, then we are including it in the output without changes if current_PAS._current_resolved_attribute.resolved_name in top_level_rename_attribute_names: if isinstance(current_PAS._current_resolved_attribute.target, CdmAttribute): # The current attribute should be renamed new_attribute_name = self._get_new_attribute_name( current_PAS, source_attribute_name) # type: str # Create new resolved attribute with the new name, set the new attribute as target res_attr_new = self._create_new_resolved_attribute( proj_ctx, None, current_PAS._current_resolved_attribute.target, new_attribute_name) # type: ResolvedAttribute # Get the attribute name the way it appears in the applyTo list apply_to_name = top_level_rename_attribute_names[ current_PAS._current_resolved_attribute.resolved_name] # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters( apply_to_name, current_PAS, res_attr_new, CdmAttributeContextType.ATTRIBUTE_DEFINITION, current_PAS._current_resolved_attribute. att_ctx, # lineage is the original attribute None) # don't know who will point here yet # Create a projection attribute state for the renamed attribute by creating a copy of the current state # Copy() sets the current state as the previous state for the new one # We only create projection attribute states for attributes that are in the rename list new_PAS = current_PAS._copy() # Update the resolved attribute to be the new renamed attribute we created new_PAS._current_resolved_attribute = res_attr_new proj_output_set._add(new_PAS) else: logger.warning( self._TAG, self.ctx, 'RenameAttributes is not supported on an attribute group yet.' ) # Add the attribute without changes proj_output_set._add(current_PAS) else: # Pass through proj_output_set._add(current_PAS) # Create all the attribute contexts and construct the tree attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx) return proj_output_set
def _append_projection_attribute_state( self, proj_ctx: 'ProjectionContext', proj_attr_state_set: 'ProjectionAttributeStateSet', attr_ctx: 'CdmAttributeContext') -> 'ProjectionAttributeStateSet': # Create a new attribute context for the operation attr_ctx_op_include_attrs_param = AttributeContextParameters( ) # type: AttributeContextParameters attr_ctx_op_include_attrs_param._under = attr_ctx attr_ctx_op_include_attrs_param._type = CdmAttributeContextType.OPERATION_INCLUDE_ATTRIBUTES attr_ctx_op_include_attrs_param._name = 'operation/index{}/operationIncludeAttributes'.format( self._index) attr_ctx_op_include_attrs = CdmAttributeContext._create_child_under( proj_ctx._projection_directive._res_opt, attr_ctx_op_include_attrs_param) # type: CdmAttributeContext # Get the top-level attribute names for each of the included attributes # Since the include operation allows providing either current state resolved attribute names # or the previous state resolved attribute names, we search for the name in the PAS tree # and fetch the top level resolved attribute names. top_level_include_attribute_names = ProjectionResolutionCommonUtil._get_top_list( proj_ctx, self.include_attributes) # type: Dict[str, str] # Initialize a projection attribute context tree builder with the created attribute context for the operation attr_ctx_tree_builder = ProjectionAttributeContextTreeBuilder( attr_ctx_op_include_attrs) # Index that holds the current attribute name as the key and the attribute as value top_level_include_attribute = { } # type: Dict[str, ProjectionAttributeState] # List of attributes that were not included on the final attribute list removed_attributes = [] # List[ProjectionAttributeState] # Iterate through all the PAS in the PASSet generated from the projection source's resolved attributes for current_PAS in proj_ctx._current_attribute_state_set._states: # Check if the current PASs RA is in the list of attributes to include. if current_PAS._current_resolved_attribute.resolved_name in top_level_include_attribute_names: top_level_include_attribute[ current_PAS._current_resolved_attribute. resolved_name] = current_PAS else: removed_attributes.append(current_PAS) # Loop through the list of attributes in the same order that was specified by the user for key, value in top_level_include_attribute_names.items(): current_PAS = top_level_include_attribute[key] # Get the attribute name the way it appears in the include list include_attribute_name = value # type: str # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters( include_attribute_name, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_DEFINITION, current_PAS._current_resolved_attribute. att_ctx, # lineage is the included attribute None) # don't know who will point here yet # Create a projection attribute state for the included attribute by creating a copy of the current state # Copy() sets the current state as the previous state for the new one # We only create projection attribute states for attributes in the include list new_PAS = current_PAS._copy() proj_attr_state_set._add(new_PAS) # Generate attribute context nodes for the attributes that were not included for current_PAS in removed_attributes: # Create the attribute context parameters and just store it in the builder for now # We will create the attribute contexts at the end attr_ctx_tree_builder._create_and_store_attribute_context_parameters( None, current_PAS, current_PAS._current_resolved_attribute, CdmAttributeContextType.ATTRIBUTE_EXCLUDED, current_PAS._current_resolved_attribute. att_ctx, # lineage is the excluded attribute None ) # don't know who will point here, probably nobody, I mean, we got excluded # Create all the attribute contexts and construct the tree attr_ctx_tree_builder._construct_attribute_context_tree(proj_ctx) return proj_attr_state_set