in objectModel/Python/cdm/resolvedmodel/resolved_attribute_set_builder.py [0:0]
def _get_applier_generated_attributes(self, arc: 'AttributeResolutionContext', clear_state: bool, apply_modifiers: bool) \
-> Optional[List['ResolvedAttribute']]:
if not self._resolved_attribute_set or self._resolved_attribute_set._set is None or not arc or not arc.applier_caps:
return None
caps = arc.applier_caps
if not caps.can_attribute_add and not caps.can_group_add and not caps.can_round_add:
return None
from cdm.objectmodel import CdmAttributeContext
res_att_out = [] # type: List[ResolvedAttribute]
# This function constructs a 'plan' for building up the resolved attributes that get generated from a set of
# traits being applied to a set of attributes. It manifests the plan into an array of resolved attributes there
# are a few levels of hierarchy to consider.
# 1. Once per set of attributes, the traits may want to generate attributes. This is an attribute that is somehow
# descriptive of the whole set, even if it has repeating patterns, like the count for an expanded array.
# 2. It is possible that some traits (like the array expander) want to keep generating new attributes for some run.
# Each time they do this is considered a 'round'the traits are given a chance to generate attributes once per round.
# Every set gets at least one round, so these should be the attributes that describe the set of other attributes.
# For example, the foreign key of a relationship or the 'class' of a polymorphic type, etc.
# 3. For each round, there are new attributes created based on the resolved attributes from the previous round
# (or the starting atts for this set) the previous round attribute need to be 'done'.
# Having traits applied before they are used as sources for the current round.
# The goal here is to process each attribute completely before moving on to the next one
# That may need to start out clean.
if clear_state:
for ra in self._resolved_attribute_set._set:
ra.applier_state = None
# make an attribute context to hold attributes that are generated from appliers
# there is a context for the entire set and one for each 'round' of applications that happen
att_ctx_container_group = self._resolved_attribute_set.attribute_context # type: CdmAttributeContext
if att_ctx_container_group:
acp = AttributeContextParameters(
under=att_ctx_container_group,
type=CdmAttributeContextType.GENERATED_SET,
name='_generatedAttributeSet')
att_ctx_container_group = CdmAttributeContext._create_child_under(arc.res_opt, acp)
att_ctx_container = att_ctx_container_group # type: CdmAttributeContext
def make_resolved_attribute(res_att_source: 'ResolvedAttribute', action: 'AttributeResolutionApplier',
query_add: 'ApplierQuery', do_add: 'ApplierAction', state: str) -> 'ApplierContext':
app_ctx = ApplierContext()
app_ctx.state = state
app_ctx.res_opt = arc.res_opt
app_ctx.att_ctx = att_ctx_container
app_ctx.res_att_source = res_att_source
app_ctx.res_guide = arc.res_guide
if res_att_source and isinstance(res_att_source.target, ResolvedAttributeSet) and cast('ResolvedAttributeSet', res_att_source.target)._set:
return app_ctx # Makes no sense for a group.
# Will something add?
if query_add(app_ctx):
# May want to make a new attribute group.
# make the 'new' attribute look like any source attribute for the duration of this call to make a context. there could be state needed
app_ctx.res_att_new = res_att_source
if self._resolved_attribute_set.attribute_context and action._will_create_context and action._will_create_context(app_ctx):
action._do_create_context(app_ctx)
# Make a new resolved attribute as a place to hold results.
app_ctx.res_att_new = ResolvedAttribute(app_ctx.res_opt, None, None, app_ctx.att_ctx)
# Copy state from source.
if res_att_source and res_att_source.applier_state:
app_ctx.res_att_new.applier_state = res_att_source.applier_state._copy()
else:
app_ctx.res_att_new.applier_state = ApplierState()
# If applying traits, then add the sets traits as a staring point.
if apply_modifiers:
app_ctx.res_att_new.resolved_traits = arc.traits_to_apply.deep_copy()
# Make it
do_add(app_ctx)
# Combine resolution guidance for this set with anything new from the new attribute.
app_ctx.res_guide_new = app_ctx.res_guide._combine_resolution_guidance(app_ctx.res_guide_new)
app_ctx.res_att_new.arc = AttributeResolutionContext(arc.res_opt, app_ctx.res_guide_new, app_ctx.res_att_new.resolved_traits)
if apply_modifiers:
# Add the sets traits back in to this newly added one.
app_ctx.res_att_new.resolved_traits = app_ctx.res_att_new.resolved_traits.merge_set(arc.traits_to_apply)
# Be sure to use the new arc, the new attribute may have added actions. For now, only modify and
# remove will get acted on because recursion. Do all of the modify traits.
if app_ctx.res_att_new.arc.applier_caps.can_attribute_modify:
# Modify acts on the source and we should be done with it.
app_ctx.res_att_source = app_ctx.res_att_new
for mod_act in app_ctx.res_att_new.arc.actions_modify:
if mod_act._will_attribute_modify(app_ctx):
mod_act._do_attribute_modify(app_ctx)
app_ctx.res_att_new.complete_context(app_ctx.res_opt)
# tie this new resolved att to the source via lineage
if app_ctx.res_att_new.att_ctx and res_att_source and res_att_source.att_ctx \
and (not res_att_source.applier_state or not res_att_source.applier_state._flex_remove):
if res_att_source.att_ctx.lineage is not None and len(res_att_source.att_ctx.lineage) > 0:
for lineage in res_att_source.att_ctx.lineage:
app_ctx.res_att_source.att_ctx._add_lineage(lineage)
else:
app_ctx.res_att_new.att_ctx._add_lineage(res_att_source.att_ctx)
return app_ctx
# Get the one time atts.
if caps.can_group_add and arc.actions_group_add:
for action in arc.actions_group_add:
app_ctx = make_resolved_attribute(None, action, action._will_group_add, action._do_group_add, 'group')
# Save it.
if app_ctx and app_ctx.res_att_new:
res_att_out.append(app_ctx.res_att_new)
# Now starts a repeating pattern of rounds. First step is to get attribute that are descriptions of the round.
# Do this once and then use them as the first entries in the first set of 'previous' atts for the loop.
# make an attribute context to hold attributes that are generated from appliers in this round
round_num = 0
if att_ctx_container_group:
acp = AttributeContextParameters(
under=att_ctx_container_group,
type=CdmAttributeContextType.GENERATED_ROUND,
name='_generatedAttributeRound0')
att_ctx_container = CdmAttributeContext._create_child_under(arc.res_opt, acp)
res_atts_last_round = [] # type: List[ResolvedAttribute]
if caps.can_round_add and arc.actions_round_add:
for action in arc.actions_round_add:
app_ctx = make_resolved_attribute(None, action, action._will_round_add, action._do_round_add, 'round')
# Save it.
if app_ctx and app_ctx.res_att_new:
# Overall list.
res_att_out.append(app_ctx.res_att_new)
# Previous list.
res_atts_last_round.append(app_ctx.res_att_new)
# The first per-round set of attributes is the set owned by this object.
res_atts_last_round += self._resolved_attribute_set._set
# Now loop over all of the previous atts until they all say 'stop'.
if res_atts_last_round:
continues = 1
while continues:
continues = 0
res_att_this_round = [] # type: List[ResolvedAttribute]
if caps.can_attribute_add:
for att in res_atts_last_round:
if arc.actions_attribute_add:
for action in arc.actions_attribute_add:
app_ctx = make_resolved_attribute(att, action, action._will_attribute_add, action._do_attribute_add, 'detail')
# Save it
if app_ctx and app_ctx.res_att_new:
# Overall list.
res_att_out.append(app_ctx.res_att_new)
res_att_this_round.append(app_ctx.res_att_new)
if app_ctx.is_continue:
continues += 1
res_atts_last_round = res_att_this_round
round_num += 1
if att_ctx_container_group:
acp = AttributeContextParameters(
under=att_ctx_container_group,
type=CdmAttributeContextType.GENERATED_ROUND,
name='_generatedAttributeRound{}'.format(round_num))
att_ctx_container = CdmAttributeContext._create_child_under(arc.res_opt, acp)
return res_att_out