def _construct_resolved_attributes( self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext'] ) -> 'ResolvedAttributeSetBuilder': # find and cache the complete set of attributes # attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): # the datatype used as an attribute, traits applied to that datatype, # the purpose of the attribute, any traits applied to the attribute. from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition rasb = ResolvedAttributeSetBuilder() rasb._resolved_attribute_set.attribute_context = under # add this attribute to the set # make a new one and apply any traits new_att = ResolvedAttribute(res_opt, self, self.name, under) rasb.own_one(new_att) rts = self._fetch_resolved_traits(res_opt) if self.owner and self.owner.object_type == CdmObjectType.ENTITY_DEF: rasb._resolved_attribute_set.set_target_owner(self.owner) if self.projection: rasb._resolved_attribute_set.apply_traits(rts) proj_directive = ProjectionDirective(res_opt, self) proj_ctx = self.projection._construct_projection_context( proj_directive, under, rasb._resolved_attribute_set) ras = self.projection._extract_resolved_attributes(proj_ctx, under) rasb._resolved_attribute_set = ras else: # using resolution guidance # this context object holds all of the info about what needs to happen to resolve these attributes. # make a copy and add defaults if missing res_guide_with_default = None if self.resolution_guidance is not None: res_opt._used_resolution_guidance = True res_guide_with_default = self.resolution_guidance.copy(res_opt) else: res_guide_with_default = CdmAttributeResolutionGuidanceDefinition( self.ctx) # rename_format is not currently supported for type attributes res_guide_with_default.rename_format = None res_guide_with_default._update_attribute_defaults(None, self) arc = AttributeResolutionContext(res_opt, res_guide_with_default, rts) # from the traits of the datatype, purpose and applied here, see if new attributes get generated rasb.apply_traits(arc) rasb.generate_applier_attributes( arc, False) # false = don't apply these traits to added things # this may have added symbols to the dependencies, so merge them res_opt._symbol_ref_set._merge(arc.res_opt._symbol_ref_set) return rasb
def _fetch_att_res_context(self, res_opt): from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition from cdm.resolvedmodel.resolved_attribute_set_builder import AttributeResolutionContext rts_this_att = self._fetch_resolved_traits(res_opt) # this context object holds all of the info about what needs to happen to resolve these attributes. # make a copy and add defaults if missing res_guide_with_default = None if self.resolution_guidance is not None: res_guide_with_default = self.resolution_guidance.copy(res_opt) else: res_guide_with_default = CdmAttributeResolutionGuidanceDefinition(self.ctx) res_guide_with_default._update_attribute_defaults(self.name) return AttributeResolutionContext(res_opt, res_guide_with_default, rts_this_att)
def _construct_resolved_attributes( self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext'] ) -> 'ResolvedAttributeSetBuilder': # find and cache the complete set of attributes # attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): # the datatype used as an attribute, traits applied to that datatype, # the purpose of the attribute, any traits applied to the attribute. from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition rasb = ResolvedAttributeSetBuilder() rasb.ras.attribute_context = under # add this attribute to the set # make a new one and apply any traits new_att = ResolvedAttribute(res_opt, self, self.name, under) rasb.own_one(new_att) rts = self._fetch_resolved_traits(res_opt) # this context object holds all of the info about what needs to happen to resolve these attributes. # make a copy and add defaults if missing res_guide_with_default = None if self.resolution_guidance is not None: res_guide_with_default = self.resolution_guidance.copy(res_opt) else: res_guide_with_default = CdmAttributeResolutionGuidanceDefinition( self.ctx) res_guide_with_default._update_attribute_defaults(None) arc = AttributeResolutionContext(res_opt, res_guide_with_default, rts) # from the traits of the datatype, purpose and applied here, see if new attributes get generated rasb.apply_traits(arc) rasb.generate_applier_attributes( arc, False) # false = don't apply these traits to added things # this may have added symbols to the dependencies, so merge them res_opt.symbol_ref_set.merge(arc.res_opt.symbol_ref_set) return rasb
def fetch_resolved_entity_references( self, res_opt: Optional['ResolveOptions'] = None ) -> 'ResolvedEntityReferenceSet': res_opt = res_opt if res_opt is not None else ResolveOptions( wrt_doc=self) from cdm.resolvedmodel import AttributeResolutionContext, ResolvedEntityReference, ResolvedEntityReferenceSide, ResolvedEntityReferenceSet from cdm.utilities import ResolveOptions from .cdm_object import CdmObject rts_this_att = self._fetch_resolved_traits(res_opt) res_guide = self.resolution_guidance # this context object holds all of the info about what needs to happen to resolve these attributes arc = AttributeResolutionContext(res_opt, res_guide, rts_this_att) rel_info = self._get_relationship_info(res_opt, arc) if not rel_info.is_by_ref or rel_info.is_array: return None # only place this is used, so logic here instead of encapsulated. # make a set and the one ref it will hold rers = ResolvedEntityReferenceSet(res_opt) rer = ResolvedEntityReference() # referencing attribute(s) come from this attribute rer.referencing._rasb.merge_attributes( self._fetch_resolved_attributes(res_opt)) def resolve_side( ent_ref: 'CdmEntityReference') -> ResolvedEntityReferenceSide: side_other = ResolvedEntityReferenceSide() if ent_ref: # reference to the other entity, hard part is the attribue name. # by convention, this is held in a trait that identifies the key side_other.entity = ent_ref.fetch_object_definition(res_opt) if side_other.entity: other_attribute = None other_opts = ResolveOptions(wrt_doc=res_opt.wrt_doc, directives=res_opt.directives) trait = ent_ref._fetch_resolved_traits(other_opts).find( other_opts, 'is.identifiedBy') if trait and trait.parameter_values and trait.parameter_values.length: other_ref = trait.parameter_values.fetch_parameter_value( 'attribute').value if isinstance(other_ref, CdmObject): other_attribute = other_ref.fetch_object_definition( other_opts) if other_attribute: side_other._rasb.own_one( side_other.entity. _fetch_resolved_attributes(other_opts).get( other_attribute.get_name()).copy()) return side_other # either several or one entity # for now, a sub for the 'select one' idea if self.entity.explicit_reference: ent_pick_from = self.entity.fetch_object_definition(res_opt) atts_pick = ent_pick_from.attributes if ent_pick_from and atts_pick: for attribute in atts_pick: if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF: entity = attribute.entity rer.referenced.append(resolve_side(entity)) else: rer.referenced.append(resolve_side(self.entity)) rers.rer_set.append(rer) return rers
def _construct_resolved_attributes( self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext'] = None ) -> 'ResolvedAttributeSetBuilder': from cdm.resolvedmodel import AttributeResolutionContext, ResolvedAttribute, ResolvedAttributeSetBuilder, ResolvedTrait from cdm.utilities import AttributeContextParameters, AttributeResolutionDirectiveSet from .cdm_attribute_resolution_guidance_def import CdmAttributeResolutionGuidanceDefinition from .cdm_object import CdmObject rasb = ResolvedAttributeSetBuilder() ctx_ent = self.entity under_att = under acp_ent = None if under_att: # make a context for this attribute that holds the attributes that come up from the entity acp_ent = AttributeContextParameters( under=under_att, type=CdmAttributeContextType.ENTITY, name=ctx_ent.fetch_object_definition_name(), regarding=ctx_ent, include_traits=True) rts_this_att = self._fetch_resolved_traits(res_opt) # this context object holds all of the info about what needs to happen to resolve these attributes. # make a copy and add defaults if missing res_guide_with_default = None if self.resolution_guidance is not None: res_guide_with_default = self.resolution_guidance.copy(res_opt) else: res_guide_with_default = CdmAttributeResolutionGuidanceDefinition( self.ctx) res_guide_with_default._update_attribute_defaults(self.name) arc = AttributeResolutionContext(res_opt, res_guide_with_default, rts_this_att) # complete cheating but is faster. # this purpose will remove all of the attributes that get collected here, so dumb and slow to go get them rel_info = self._get_relationship_info(arc.res_opt, arc) if rel_info.is_by_ref: # make the entity context that a real recursion would have give us if under: under = rasb.ras.create_attribute_context(res_opt, acp_ent) # if selecting from one of many attributes, then make a context for each one if under and rel_info.selects_one: # the right way to do this is to get a resolved entity from the embedded entity and then # look through the attribute context hierarchy for non-nested entityReferenceAsAttribute nodes # that seems like a disaster waiting to happen given endless looping, etc. # for now, just insist that only the top level entity attributes declared in the ref entity will work ent_pick_from = self.entity.fetch_object_definition(res_opt) atts_pick = ent_pick_from.attributes if ent_pick_from and atts_pick: for attribute in atts_pick: if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF: # a table within a table. as expected with a selects_one attribute # since this is by ref, we won't get the atts from the table, but we do need the traits that hold the key # these are the same contexts that would get created if we recursed # first this attribute acp_ent_att = AttributeContextParameters( under=under, type=CdmAttributeContextType. ATTRIBUTE_DEFINITION, name=attribute.fetch_object_definition_name(), regarding=attribute, include_traits=True) pick_under = rasb.ras.create_attribute_context( res_opt, acp_ent_att) # and the entity under that attribute pick_ent = attribute.entity acp_ent_att_ent = AttributeContextParameters( under=pick_under, type=CdmAttributeContextType.ENTITY, name=pick_ent.fetch_object_definition_name(), regarding=pick_ent, include_traits=True) rasb.ras.create_attribute_context( res_opt, acp_ent_att_ent) # if we got here because of the max depth, need to impose the directives to make the trait work as expected if rel_info.max_depth_exceeded: if not arc.res_opt.directives: arc.res_opt.directives = AttributeResolutionDirectiveSet() arc.res_opt.directives.add('referenceOnly') else: res_link = CdmObject._copy_resolve_options(res_opt) res_link.symbol_ref_set = res_opt.symbol_ref_set res_link._relationship_depth = rel_info.next_depth rasb.merge_attributes( self.entity._fetch_resolved_attributes(res_link, acp_ent)) # from the traits of purpose and applied here, see if new attributes get generated rasb.ras.attribute_context = under_att rasb.apply_traits(arc) rasb.generate_applier_attributes( arc, True) # True = apply the prepared traits to new atts # this may have added symbols to the dependencies, so merge them res_opt.symbol_ref_set.merge(arc.res_opt.symbol_ref_set) # use the traits for linked entity identifiers to record the actual foreign key links if rasb.ras and rasb.ras.set and rel_info.is_by_ref: for att in rasb.ras.set: reqd_trait = att.resolved_traits.find( res_opt, 'is.linkedEntity.identifier') if not reqd_trait: continue if not reqd_trait.parameter_values: logger.warning( self._TAG, self.ctx, 'is.linkedEntity.identifier does not support arguments' ) continue ent_references = [] att_references = [] def add_entity_reference(entity_ref: 'CdmEntityReference', namespace: str): ent_def = entity_ref.fetch_object_definition(res_opt) required_trait = entity_ref._fetch_resolved_traits( res_opt).find(res_opt, 'is.identifiedBy') if required_trait and ent_def: att_ref = required_trait.parameter_values.fetch_parameter_value( 'attribute').value att_name = att_ref.named_reference.split('/')[-1] # path should be absolute and without a namespace relative_ent_path = self.ctx.corpus.storage.create_absolute_corpus_path( ent_def.at_corpus_path, ent_def.in_document) if relative_ent_path.startswith(namespace + ':'): relative_ent_path = relative_ent_path[len(namespace ) + 1:] ent_references.append(relative_ent_path) att_references.append(att_name) if rel_info.selects_one: ent_pick_from = self.entity.fetch_object_definition( res_opt) atts_pick = ent_pick_from.attributes if ent_pick_from else None if atts_pick: for attribute in atts_pick: if attribute.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF: add_entity_reference( attribute.entity, self.in_document.namespace) else: add_entity_reference(self.entity, self.in_document.namespace) c_ent = self.ctx.corpus.make_object( CdmObjectType.CONSTANT_ENTITY_DEF) c_ent.entity_shape = self.ctx.corpus.make_ref( CdmObjectType.ENTITY_REF, 'entityGroupSet', True) c_ent.constant_values = [[ entity_ref, att_references[idx] ] for idx, entity_ref in enumerate(ent_references)] param = self.ctx.corpus.make_ref(CdmObjectType.ENTITY_REF, c_ent, False) reqd_trait.parameter_values.update_parameter_value( res_opt, 'entityReferences', param) # a 'structured' directive wants to keep all entity attributes together in a group if arc and arc.res_opt.directives and arc.res_opt.directives.has( 'structured'): ra_sub = ResolvedAttribute(rts_this_att.res_opt, rasb.ras, self.name, rasb.ras.attribute_context) if rel_info.is_array: # put a resolved trait on this att group, yuck, # hope I never need to do this again and then need to make a function for this tr = self.ctx.corpus.make_object(CdmObjectType.TRAIT_REF, 'is.linkedEntity.array', True) t = tr.fetch_object_definition(res_opt) rt = ResolvedTrait(t, None, [], []) ra_sub.resolved_traits = ra_sub.resolved_traits.merge(rt, True) rasb = ResolvedAttributeSetBuilder() rasb.own_one(ra_sub) return rasb
def _construct_resolved_attributes(self, res_opt: 'ResolveOptions', under: Optional['CdmAttributeContext'] = None) -> 'ResolvedAttributeSetBuilder': # find and cache the complete set of attributes # attributes definitions originate from and then get modified by subsequent re-defintions from (in this order): # an extended entity, traits applied to extended entity, exhibited traits of main entity, # the (datatype or entity) used as an attribute, traits applied to that datatype or entity, # the relationsip of the attribute, the attribute definition itself and included attribute groups, # any traits applied to the attribute. from cdm.resolvedmodel import ResolvedAttributeSetBuilder from cdm.utilities import AttributeContextParameters self._rasb = ResolvedAttributeSetBuilder() self._rasb.ras.attribute_context = under if self.extends_entity: ext_ref = self.extends_entity extends_ref_under = None acp_ext_ent = None if under: acp_ext = AttributeContextParameters( under=under, type=CdmAttributeContextType.ENTITY_REFERENCE_EXTENDS, name='extends', regarding=None, include_traits=False) extends_ref_under = self._rasb.ras.create_attribute_context(res_opt, acp_ext) if ext_ref.explicit_reference and ext_ref.fetch_object_definition(res_opt).object_type == CdmObjectType.PROJECTION_DEF: # A Projection ext_ref_obj_def = ext_ref.fetch_object_definition(res_opt) if extends_ref_under: acp_ext_ent = AttributeContextParameters( under=extends_ref_under, type=CdmAttributeContextType.PROJECTION, name=ext_ref_obj_def.get_name(), regarding=ext_ref, include_traits=False ) proj_directive = ProjectionDirective(res_opt, self, ext_ref) proj_def = ext_ref_obj_def proj_ctx = proj_def._construct_projection_context(proj_directive, extends_ref_under) self._rasb.ras = proj_def._extract_resolved_attributes(proj_ctx) else: # An Entity Reference if extends_ref_under: acp_ext_ent = AttributeContextParameters( under=extends_ref_under, type=CdmAttributeContextType.ENTITY, name=ext_ref.named_reference if ext_ref.named_reference else ext_ref.explicit_reference.get_name(), regarding=ext_ref, include_traits=False ) # save moniker, extended entity may attach a different moniker that we do not # want to pass along to getting this entities attributes old_moniker = res_opt._from_moniker self._rasb.merge_attributes(self.extends_entity._fetch_resolved_attributes(res_opt, acp_ext_ent)) if not res_opt._check_attribute_count(self._rasb.ras._resolved_attribute_count): logger.error(self._TAG, self.ctx, 'Maximum number of resolved attributes reached for the entity: {}.'.format(self.entity_name)) return None if self.extends_entity_resolution_guidance: # some guidance was given on how to integrate the base attributes into the set. apply that guidance rts_base = self._fetch_resolved_traits(res_opt) # type: ResolvedTraitSet # this context object holds all of the info about what needs to happen to resolve these attributes. # make a copy and set defaults if needed res_guide = self.extends_entity_resolution_guidance.copy(res_opt) # CdmAttributeResolutionGuidanceDefinition res_guide._update_attribute_defaults(res_guide.fetch_object_definition_name()) # holds all the info needed by the resolver code arc = AttributeResolutionContext(res_opt, res_guide, rts_base) # type: AttributeResolutionContext self._rasb.generate_applier_attributes(arc, False) # true = apply the prepared traits to new atts # reset to the old moniker res_opt._from_moniker = old_moniker self._rasb.mark_inherited() self._rasb.ras.attribute_context = under if self.attributes: for att in self.attributes: acp_att = None if under: acp_att = AttributeContextParameters( under=under, type=CdmAttributeContextType.ATTRIBUTE_DEFINITION, name=att.fetch_object_definition_name(), regarding=att, include_traits=False) self._rasb.merge_attributes(att._fetch_resolved_attributes(res_opt, acp_att)) if not res_opt._check_attribute_count(self._rasb.ras._resolved_attribute_count): logger.error(self._TAG, self.ctx, 'Maximum number of resolved attributes reached for the entity: {}.'.format(self.entity_name)) return None self._rasb.mark_order() self._rasb.ras.attribute_context = under # things that need to go away self._rasb.remove_requested_atts() return self._rasb