def own_one(self, ra: 'ResolvedAttribute') -> None: # Save the current context. att_ctx = self.ras.attribute_context self.take_reference(ResolvedAttributeSet()) self.ras.merge(ra, ra.att_ctx) # Reapply the old attribute context. self.ras.attribute_context = att_ctx
def generate_applier_attributes(self, arc: 'AttributeResolutionContext', apply_traits_to_new: bool) -> None: if not arc or not arc.applier_caps: return if not self.ras: self.take_reference(ResolvedAttributeSet()) # make sure all of the 'source' attributes know about this context. resolved_set = self.ras._set if resolved_set is not None: for ra in resolved_set: ra.arc = arc # the resolution guidance may be asking for a one time 'take' or avoid of attributes from the source # this also can re-order the attributes if arc.res_guide and arc.res_guide.selects_sub_attribute and \ arc.res_guide.selects_sub_attribute.selects == 'some' and \ (arc.res_guide.selects_sub_attribute.selects_some_take_names or arc.res_guide.selects_sub_attribute.selects_some_avoid_names): # we will make a new resolved attribute set from the 'take' list take_set = [] # type: List[ResolvedAttribute] selects_some_take_names = arc.res_guide.selects_sub_attribute.selects_some_take_names # type: List[str] selects_some_avoid_names = arc.res_guide.selects_sub_attribute.selects_some_avoid_names # type: List[str] if selects_some_take_names and not selects_some_avoid_names: # make an index that goes from name to insertion order inverted = {} # type: Dictionary[str, int] for index, resolved_attribute in enumerate(resolved_set): inverted[resolved_attribute._resolved_name] = index for name in selects_some_take_names: # if in the original set of attributes, take it in the new order if name in inverted: take_set.append(resolved_set[inverted[name]]) if selects_some_avoid_names: # make a quick look up of avoid names avoid = set(selects_some_avoid_names) # type: Set[str] for resolved_attribute in resolved_set: # only take the ones not in avoid the list given if resolved_attribute._resolved_name not in avoid: take_set.append(resolved_attribute) # replace the guts of the resolvedAttributeSet with this self.ras.alter_set_order_and_scope(take_set) # get the new atts and then add them one at a time into this set. new_atts = self._get_applier_generated_attributes( arc, True, apply_traits_to_new) if new_atts: ras = self.ras for new_att in new_atts: # here we want the context that was created in the appliers. ras = ras.merge(new_att, new_att.att_ctx) self.take_reference(ras)
def generate_applier_attributes(self, arc: 'AttributeResolutionContext', apply_traits_to_new: bool) -> None: if not arc or not arc.applier_caps: return if not self.ras: self.take_reference(ResolvedAttributeSet()) # Make sure all of the 'source' attributes know about this context. if self.ras.set: for ra in self.ras.set: ra.arc = arc # Get the new atts and then add them one at a time into this set. new_atts = self._get_applier_generated_attributes(arc, True, apply_traits_to_new) if new_atts: ras = self.ras for new_att in new_atts: # Here we want the context that was created in the appliers. ras = ras.merge(new_att, new_att.att_ctx) self.take_reference(ras)
def __init__(self): self.ras = ResolvedAttributeSet() # type: Optional[ResolvedAttributeSet] self.inherited_mark = 0 # type: int
class ResolvedAttributeSetBuilder: def __init__(self): self.ras = ResolvedAttributeSet() # type: Optional[ResolvedAttributeSet] self.inherited_mark = 0 # type: int def merge_attributes(self, ras_new: 'ResolvedAttributeSet') -> None: if ras_new: self.take_reference(self.ras.merge_set(ras_new)) def take_reference(self, ras_new: 'ResolvedAttributeSet') -> None: if self.ras != ras_new: if ras_new: ras_new._add_ref() if self.ras: self.ras._release() self.ras = ras_new def give_reference(self) -> Optional['ResolvedAttributeSet']: ras_ref = self.ras if self.ras: self.ras._release() if self.ras._ref_cnt == 0: self.ras = None return ras_ref def own_one(self, ra: 'ResolvedAttribute') -> None: # Save the current context. att_ctx = self.ras.attribute_context self.take_reference(ResolvedAttributeSet()) self.ras.merge(ra, ra.att_ctx) # Reapply the old attribute context. self.ras.attribute_context = att_ctx def apply_traits(self, arc: 'AttributeResolutionContext') -> None: if self.ras and arc and arc.traits_to_apply: self.take_reference(self.ras.apply_traits(arc.traits_to_apply, arc.res_opt, arc.res_guide, arc.actions_modify)) def generate_applier_attributes(self, arc: 'AttributeResolutionContext', apply_traits_to_new: bool) -> None: if not arc or not arc.applier_caps: return if not self.ras: self.take_reference(ResolvedAttributeSet()) # make sure all of the 'source' attributes know about this context. resolved_set = self.ras._set if resolved_set is not None: for ra in resolved_set: ra.arc = arc # the resolution guidance may be asking for a one time 'take' or avoid of attributes from the source # this also can re-order the attributes if arc.res_guide and arc.res_guide.selects_sub_attribute and \ arc.res_guide.selects_sub_attribute.selects == 'some' and \ (arc.res_guide.selects_sub_attribute.selects_some_take_names or arc.res_guide.selects_sub_attribute.selects_some_avoid_names): # we will make a new resolved attribute set from the 'take' list take_set = [] # type: List[ResolvedAttribute] selects_some_take_names = arc.res_guide.selects_sub_attribute.selects_some_take_names # type: List[str] selects_some_avoid_names = arc.res_guide.selects_sub_attribute.selects_some_avoid_names # type: List[str] if selects_some_take_names and not selects_some_avoid_names: # make an index that goes from name to insertion order inverted = {} # type: Dictionary[str, int] for index, resolved_attribute in enumerate(resolved_set): inverted[resolved_attribute._resolved_name] = index for name in selects_some_take_names: # if in the original set of attributes, take it in the new order if name in inverted: take_set.append(resolved_set[inverted[name]]) if selects_some_avoid_names: # make a quick look up of avoid names avoid = set(selects_some_avoid_names) # type: Set[str] for resolved_attribute in resolved_set: # only take the ones not in avoid the list given if resolved_attribute._resolved_name not in avoid: take_set.append(resolved_attribute) # replace the guts of the resolvedAttributeSet with this self.ras.alter_set_order_and_scope(take_set) # get the new atts and then add them one at a time into this set. new_atts = self._get_applier_generated_attributes(arc, True, apply_traits_to_new) if new_atts: ras = self.ras for new_att in new_atts: # here we want the context that was created in the appliers. ras = ras.merge(new_att, new_att.att_ctx) self.take_reference(ras) def remove_requested_atts(self) -> None: if self.ras: marker = (0, self.inherited_mark) # type: Tuple[int, int] self.take_reference(self.ras.remove_requested_atts(marker)) self.inherited_mark = marker[1] def mark_inherited(self) -> None: if not self.ras or not self.ras._set: self.inherited_mark = 0 return def count_set(ras_sub: 'ResolvedAttributeSet', offset: int) -> int: last = offset if ras_sub and ras_sub._set: for ra in ras_sub._set: if isinstance(ra.target, ResolvedAttributeSet) and ra.target._set: last = count_set(cast('ResolvedAttributeSet', ra.target), last) else: last += 1 return last self.inherited_mark = count_set(self.ras, 0) def mark_order(self) -> None: def mark_set(ras_sub: 'ResolvedAttributeSet', inherited_mark: int, offset: int) -> int: last = offset if ras_sub and ras_sub._set: ras_sub.insert_order = last for ra in ras_sub._set: if isinstance(ra.target, ResolvedAttributeSet) and ra.target._set: last = mark_set(cast('ResolvedAttributeSet', ra.target), inherited_mark, last) else: if last >= inherited_mark: ra.insert_order = last last += 1 return last mark_set(self.ras, self.inherited_mark, 0) def _get_applier_generated_attributes(self, arc: 'AttributeResolutionContext', clear_state: bool, apply_modifiers: bool) \ -> Optional[List['ResolvedAttribute']]: if not self.ras or self.ras._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.ras._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.ras.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.ras.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) 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.ras._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