Exemple #1
0
    def _fetch_all_parameters(self, res_opt: 'ResolveOptions') -> ParameterCollection:
        if self._all_parameters:
            return self._all_parameters

        # Get parameters from base if there is one
        prior = None
        if self.extends_trait:
            prior = self.extends_trait.fetch_object_definition(res_opt)._fetch_all_parameters(res_opt)

        self._all_parameters = ParameterCollection(prior)
        if self.parameters:
            for element in self.parameters:
                self._all_parameters.add(element)

        return self._all_parameters
class CdmTraitDefinition(CdmObjectDefinition):
    """Help in expressing semantic meaning and structural guidance."""
    def __init__(self,
                 ctx: 'CdmCorpusContext',
                 name: str,
                 extends_trait: Optional['CdmTraitReference'] = None) -> None:
        super().__init__(ctx)

        # the trait associated properties.
        self.associated_properties = []  # type: List[str]

        # the trait elevated.
        self.elevated = None  # type: Optional[bool]

        # the trait extended by this trait.
        self.extends_trait = extends_trait  # type: Optional[CdmTraitReference]

        # the trait name.
        self.trait_name = name  # type: str

        # if trait is user facing or not.
        self.ugly = None  # type: Optional[bool]

        # internal

        self._this_is_known_to_have_parameters = None
        self._base_is_known_to_have_parameters = None
        self._has_set_flags = False
        self._all_parameters = None
        self._parameters = None

        self._TAG = CdmTraitDefinition.__name__

    @property
    def parameters(self) -> 'CdmCollection[CdmParameterDefinition]':
        if self._parameters is None:
            self._parameters = CdmCollection(self.ctx, self,
                                             CdmObjectType.PARAMETER_DEF)

        return self._parameters

    @property
    def object_type(self) -> CdmObjectType:
        return CdmObjectType.TRAIT_DEF

    def _construct_resolved_traits(self, rtsb: 'ResolvedTraitSetBuilder',
                                   res_opt: 'ResolveOptions') -> None:
        # Traits don't have traits.
        pass

    def _construct_resolved_attributes(
            self,
            res_opt: 'ResolveOptions',
            under: 'CdmAttributeContext' = None) -> None:
        return None

    def copy(
            self,
            res_opt: Optional['ResolveOptions'] = None,
            host: Optional['CdmTraitDefinition'] = None
    ) -> 'CdmTraitDefinition':
        if not res_opt:
            res_opt = ResolveOptions(wrt_doc=self)

        if not host:
            copy = CdmTraitDefinition(self.ctx, self.trait_name, None)
        else:
            copy = host
            copy.ctx = self.ctx
            copy.trait_name = self.trait_name

        if self.extends_trait:
            copy.extends_trait = self.extends_trait.copy(res_opt)

        copy._all_parameters = None
        copy.elevated = self.elevated
        copy.ugly = self.ugly
        copy.associated_properties = self.associated_properties

        self._copy_def(res_opt, copy)
        return copy

    def get_name(self):
        return self.trait_name

    def _fetch_resolved_traits(
            self, res_opt: 'ResolveOptions') -> 'ResolvedTraitSet':
        from cdm.resolvedmodel import ResolvedTrait, ResolvedTraitSet
        from cdm.utilities import SymbolSet

        from .cdm_corpus_def import CdmCorpusDefinition

        kind = 'rtsb'
        ctx = self.ctx
        # this may happen 0, 1 or 2 times. so make it fast
        base_trait = None  # type: CdmTraitDefinition
        base_values = None  # type: List[CdmArgumentValue]

        def get_base_info(base_trait, base_values):
            if self.extends_trait:
                base_trait = self.extends_trait.fetch_object_definition(
                    res_opt)
                if base_trait:
                    base_rts = self.extends_trait._fetch_resolved_traits(
                        res_opt)
                    if base_rts and base_rts.size == 1:
                        base_pv = base_rts.get(
                            base_trait).parameter_values if base_rts.get(
                                base_trait) else None
                        if base_pv:
                            base_values = base_pv.values

            return base_trait, base_values

        # see if one is already cached
        # if this trait has parameters, then the base trait found through the reference might be a different reference
        # because trait references are unique per argument value set. so use the base as a part of the cache tag
        # since it is expensive to figure out the extra tag, cache that too!
        if self._base_is_known_to_have_parameters is None:
            base_trait, base_values = get_base_info(base_trait, base_values)
            # is a cache tag needed? then make one
            self._base_is_known_to_have_parameters = False
            if base_values:
                self._base_is_known_to_have_parameters = True

        cache_tag_extra = ''
        if self._base_is_known_to_have_parameters:
            cache_tag_extra = str(self.extends_trait.id)

        cache_tag = ctx.corpus._fetch_definition_cache_tag(
            res_opt, self, kind, cache_tag_extra)
        rts_result = ctx._cache.get(cache_tag) if cache_tag else None

        # store the previous reference symbol set, we will need to add it with
        # children found from the _construct_resolved_traits call
        curr_sym_ref_set = res_opt._symbol_ref_set or SymbolSet()
        res_opt._symbol_ref_set = SymbolSet()

        # if not, then make one and save it
        if not rts_result:
            base_trait, base_values = get_base_info(base_trait, base_values)
            if base_trait:
                # get the resolution of the base class and use the values as a starting point for this trait's values
                if not self._has_set_flags:
                    # inherit these flags
                    if self.elevated is None:
                        self.elevated = base_trait.elevated
                    if self.ugly is None:
                        self.ugly = base_trait.ugly
                    if self.associated_properties is None:
                        self.associated_properties = base_trait.associated_properties

            self._has_set_flags = True
            pc = self._fetch_all_parameters(res_opt)
            av = []  # type: List[CdmArgumentValue]
            was_set = []  # type: List[bool]

            self._this_is_known_to_have_parameters = bool(pc.sequence)
            for i in range(len(pc.sequence)):
                # either use the default value or (higher precidence) the value taken from the base reference
                value = pc.sequence[i].default_value
                if base_values and i < len(base_values):
                    base_value = base_values[i]
                    if base_value:
                        value = base_value
                av.append(value)
                was_set.append(False)

            # save it
            res_trait = ResolvedTrait(self, pc, av, was_set)
            rts_result = ResolvedTraitSet(res_opt)
            rts_result.merge(res_trait, False)

            # register set of possible symbols
            ctx.corpus._register_definition_reference_symbols(
                self.fetch_object_definition(res_opt), kind,
                res_opt._symbol_ref_set)
            # get the new cache tag now that we have the list of docs
            cache_tag = ctx.corpus._fetch_definition_cache_tag(
                res_opt, self, kind, cache_tag_extra)
            if cache_tag:
                ctx._cache[cache_tag] = rts_result
        else:
            # cache found
            # get the SymbolSet for this cached object
            key = CdmCorpusDefinition._fetch_cache_key_from_object(self, kind)
            res_opt._symbol_ref_set = ctx.corpus._definition_reference_symbols.get(
                key)

        # merge child document set with current
        curr_sym_ref_set._merge(res_opt._symbol_ref_set)
        res_opt._symbol_ref_set = curr_sym_ref_set

        return rts_result

    def is_derived_from(self,
                        base: str,
                        res_opt: Optional['ResolveOptions'] = None) -> bool:
        if base == self.trait_name:
            return True
        return self._is_derived_from_def(res_opt, self.extends_trait,
                                         self.trait_name, base)

    def validate(self) -> bool:
        if not bool(self.trait_name):
            logger.error(
                self._TAG, self.ctx,
                Errors.validate_error_string(self.at_corpus_path,
                                             ['trait_name']))
            return False
        return True

    def visit(self, path_from: str, pre_children: 'VisitCallback',
              post_children: 'VisitCallback') -> bool:
        path = ''
        if self.ctx.corpus._block_declared_path_changes is False:
            path = self._declared_path
            if not path:
                path = path_from + self.trait_name
                self._declared_path = path

        if pre_children and pre_children(self, path):
            return False

        if self.extends_trait and self.extends_trait.visit(
                '{}/extendsTrait/'.format(path), pre_children, post_children):
            return True

        if self.parameters and self.parameters._visit_array(
                '{}/hasParameters/'.format(path), pre_children, post_children):
            return True

        if post_children and post_children(self, path):
            return True

        return False

    def _fetch_all_parameters(
            self, res_opt: 'ResolveOptions') -> ParameterCollection:
        if self._all_parameters:
            return self._all_parameters

        # Get parameters from base if there is one
        prior = None
        if self.extends_trait:
            prior = self.extends_trait.fetch_object_definition(
                res_opt)._fetch_all_parameters(res_opt)

        self._all_parameters = ParameterCollection(prior)
        if self.parameters:
            for element in self.parameters:
                self._all_parameters.add(element)

        return self._all_parameters