예제 #1
0
 def _check_stereotype_can_be_added(self, stereotype):
     if stereotype in self.stereotypes_:
         raise CException(
             f"'{stereotype.name!s}' is already a stereotype instance on {self._get_element_name_string()!s}")
     if not stereotype.is_element_extended_by_stereotype_(self.element):
         raise CException(f"stereotype '{stereotype!s}' cannot be added to " +
                          f"{self._get_element_name_string()!s}: no extension by this stereotype found")
예제 #2
0
def get_common_classifier(objects):
    common_classifier = None
    for o in objects:
        if o is None or not is_cobject(o):
            raise CException(f"not an object: '{o!s}'")
        if common_classifier is None:
            common_classifier = o.classifier
        else:
            object_classifiers = {o.classifier
                                  }.union(o.classifier.all_superclasses)
            common_classifier_found = False
            if common_classifier in object_classifiers:
                common_classifier_found = True
            if not common_classifier_found and common_classifier in o.classifier.all_subclasses:
                common_classifier = o.classifier
                common_classifier_found = True
            if not common_classifier_found:
                for cl in common_classifier.all_superclasses:
                    if cl in object_classifiers:
                        common_classifier = cl
                        common_classifier_found = True
                        break
            if not common_classifier_found:
                if is_clink(o):
                    raise CException(
                        f"the link's association is missing a compatible classifier"
                    )
                else:
                    raise CException(
                        f"object '{o!s}' has an incompatible classifier")
    return common_classifier
예제 #3
0
 def stop_elements_exclusive(self, stop_elements_exclusive):
     if is_cbundlable(stop_elements_exclusive):
         stop_elements_exclusive = [stop_elements_exclusive]
     if not isinstance(stop_elements_exclusive, list):
         raise CException(
             f"expected a list of stop elements, but got: '{stop_elements_exclusive!s}'"
         )
     for e in stop_elements_exclusive:
         if not is_cbundlable(e):
             raise CException(
                 f"expected a list of stop elements, but got: '{stop_elements_exclusive!s}'"
                 + f" with element of wrong type: '{e!s}'")
     self._stop_elements_exclusive = stop_elements_exclusive
     self.all_stop_elements = self._stop_elements_inclusive + self._stop_elements_exclusive
예제 #4
0
    def __init__(self, name, **kwargs):
        """CNamedElement is the superclass for all named elements in Codeable Models, such as CClass, CObject, and
        so on. The class is usually not used directly.

        When it is reached in the inheritance hierarchy, it sets all keyword args contained in ``kwargs``.
        This is performed using the ``set_keyword_args()`` method, to be defined by subclasses.
        Calling it with keyword args that are not specified as legal keyword args (to be done in subclasses, in the
        ``legal_keyword_args`` list), causes an exception.

        For example, ``CClassifier`` adds ``superclasses`` and ``attributes`` to the legal keyword args, which
        can then be used on ``CClassifier`` and subclasses such as ``CClass``::

            CClass(domain_metaclass, "Item", attributes={
                "quantity": int,
                "price": float
            })

        Args:
           name (str): An optional name.
           **kwargs: Accepts keyword args defined as ``legal_keyword_args`` by subclasses.

        Attributes:
            name (str): The name of the entity. Can be ``None``.
        """
        self.name = name
        super().__init__()
        self.is_deleted = False
        if name is not None and not isinstance(name, str):
            raise CException(f"is not a name string: '{name!r}'")
        self._init_keyword_args(**kwargs)
예제 #5
0
 def add_object_(self, obj):
     if obj in self.objects_:
         raise CException(
             f"object '{obj!s}' is already an instance of the class '{self!s}'"
         )
     check_is_cobject(obj)
     self.objects_.append(obj)
예제 #6
0
def set_keyword_args(obj, allowed_values, **kwargs):
    for key in kwargs:
        if key in allowed_values:
            setattr(obj, key, kwargs[key])
        else:
            raise CException(
                f"unknown keyword argument '{key!s}', should be one of: {allowed_values!s}"
            )
예제 #7
0
 def values(self, values):
     if values is None:
         values = []
     if not isinstance(values, list):
         raise CException(
             f"an enum needs to be initialized with a list of values, but got: '{values!s}'"
         )
     self.values_ = values
예제 #8
0
 def _get_element_name_string(self):
     if is_cclass(self.element):
         return f"'{self.element.name!s}'"
     elif is_clink(self.element):
         return f"link from '{self.element.source!s}' to '{self.element.target!s}'"
     elif is_cassociation(self.element):
         return f"association from '{self.element.source!s}' to '{self.element.target!s}'"
     raise CException(f"unexpected element type: {self.element!r}")
예제 #9
0
 def _check_derived_association_has_same_aggregation_state(
         self, metaclass_association):
     if metaclass_association.aggregation and not self.aggregation:
         raise CException(
             f"metaclass association is an aggregation, but derived association is not"
         )
     if self.aggregation and not metaclass_association.aggregation:
         raise CException(
             f"derived association is an aggregation, but metaclass association is not"
         )
     if metaclass_association.composition and not self.composition:
         raise CException(
             f"metaclass association is a composition, but derived association is not"
         )
     if self.composition and not metaclass_association.composition:
         raise CException(
             f"derived association is a composition, but metaclass association is not"
         )
예제 #10
0
    def _check_association_class_derived_from_association_metaclass(
            class_, metaclass_, direction_string):
        try:
            check_is_cclass(class_)
        except CException:
            raise CException(
                "association 'derived_from' is called on is not a class-level association"
            )
        try:
            check_is_cmetaclass(metaclass_)
        except CException:
            raise CException(
                "association used as 'derived_from' parameter is not a metaclass-level association"
            )

        if not class_.metaclass.is_classifier_of_type(metaclass_):
            raise CException(
                f"association {direction_string!s} class '{class_!s}' is not "
                + f"derived {direction_string!s} metaclass '{metaclass_!s}'")
예제 #11
0
 def super_layer(self, layer):
     if layer is not None and not isinstance(layer, CLayer):
         raise CException(f"not a layer: {layer!s}")
     if self._super_layer is not None:
         self._super_layer._sub_layer = None
     self._super_layer = layer
     if layer is not None:
         if layer._sub_layer is not None:
             layer._sub_layer._super_layer = None
         layer._sub_layer = self
예제 #12
0
 def tagged_values(self):
     """dict[str, value]: Getter for getting all tagged values of the association using a dict, and setter of
     setting all tagged values of the association based on a dict. The dict uses key/value pairs.
     The value types must conform to the types defined for the attributes.
     """
     if self.is_deleted:
         raise CException("can't get tagged values on deleted link")
     return get_var_values(
         self.stereotype_instances_holder.get_stereotype_instance_path(),
         self.tagged_values_)
예제 #13
0
    def _eval_descriptor(self, descriptor):
        # handle name only if a ':' is found in the descriptor
        index = descriptor.find(":")
        if index != -1:
            name = descriptor[0:index]
            descriptor = descriptor[index + 1:]
            self.name = name.strip()

        # handle type of relation
        aggregation = False
        composition = False
        index = descriptor.find("->")
        length = 2
        if index == -1:
            index = descriptor.find("<>-")
            if index != -1:
                length = 3
                aggregation = True
            else:
                index = descriptor.find("<*>-")
                length = 4
                composition = True
                if index == -1:
                    raise CException("association descriptor malformed: '" +
                                     descriptor + "'")

        # handle role names and multiplicities
        source_str = descriptor[0:index]
        target_str = descriptor[index + length:]
        regexp_with_role_name = r'\s*\[([^\]]+)\]\s*(\S*)\s*'
        regexp_only_multiplicity = r'\s*(\S*)\s*'

        m = re.search(regexp_with_role_name, source_str)
        if m is not None:
            self.source_role_name = m.group(1)
            if m.group(2) != '':
                self.source_multiplicity = m.group(2)
        else:
            m = re.search(regexp_only_multiplicity, source_str)
            self.source_multiplicity = m.group(1)

        m = re.search(regexp_with_role_name, target_str)
        if m is not None:
            self.role_name = m.group(1)
            if m.group(2) != '':
                self.multiplicity = m.group(2)
        else:
            m = re.search(regexp_only_multiplicity, target_str)
            self.multiplicity = m.group(1)

        if aggregation:
            self.aggregation = True
        elif composition:
            self.composition = True
예제 #14
0
 def bundles(self, bundles):
     if bundles is None:
         bundles = []
     for b in self.bundles_:
         b.remove(self)
     self.bundles_ = []
     if is_cbundle(bundles):
         bundles = [bundles]
     elif not isinstance(bundles, list):
         raise CException(
             f"bundles requires a list of bundles or a bundle as input")
     for b in bundles:
         if not is_cbundle(b):
             raise CException(
                 f"bundles requires a list of bundles or a bundle as input")
         check_named_element_is_not_deleted(b)
         if b in self.bundles_:
             raise CException(
                 f"'{b.name!s}' is already a bundle of '{self.name!s}'")
         self.bundles_.append(b)
         b.elements_.append(self)
예제 #15
0
def get_links(linked_elements):
    result = []
    for linked_ in linked_elements:
        linked = linked_
        if not is_cobject(linked) and not is_clink(linked):
            if is_cclass(linked):
                linked_ = linked.class_object
            else:
                raise CException(
                    f"'{linked!s}' is not an object, class, or link")
        result.extend(linked_.links)
    return result
예제 #16
0
    def add(self, elt):
        """
        Add an element to the bundle.

        Args:
            elt (CBundlable): Element to add to the bundle

        Returns:
            None

        """
        if elt is not None:
            if elt in self.elements_:
                raise CException(
                    f"element '{elt!s}' cannot be added to bundle: element is already in bundle"
                )
            if isinstance(elt, CBundlable):
                self.elements_.append(elt)
                elt.bundles_.append(self)
                return
        raise CException(f"can't add '{elt!s}': not an element")
예제 #17
0
    def _set_multiplicity(self, multiplicity, is_target_multiplicity):
        if not isinstance(multiplicity, str):
            raise CException("multiplicity must be provided as a string")

        try:
            dots_pos = multiplicity.find("..")
            if dots_pos != -1:
                lower_match = multiplicity[:dots_pos]
                upper_match = multiplicity[dots_pos + 2:]
                lower = int(lower_match)
                if lower < 0:
                    raise CException(
                        f"negative multiplicity in '{multiplicity!s}'")
                if upper_match.strip() == "*":
                    upper = self.STAR_MULTIPLICITY
                else:
                    upper = int(upper_match)
                    if lower < 0 or upper < 0:
                        raise CException(
                            f"negative multiplicity in '{multiplicity!s}'")
            elif multiplicity.strip() == "*":
                lower = 0
                upper = self.STAR_MULTIPLICITY
            else:
                lower = int(multiplicity)
                if lower < 0:
                    raise CException(
                        f"negative multiplicity in '{multiplicity!s}'")
                upper = lower
        except Exception as e:
            if isinstance(e, CException):
                raise e
            raise CException(f"malformed multiplicity: '{multiplicity!s}'")

        if is_target_multiplicity:
            self.upper_multiplicity = upper
            self.lower_multiplicity = lower
        else:
            self.source_upper_multiplicity = upper
            self.source_lower_multiplicity = lower
예제 #18
0
 def elements(self, elements):
     if elements is None:
         elements = []
     for e in self.elements_:
         e._bundle = None
     self.elements_ = []
     if is_cnamedelement(elements):
         elements = [elements]
     elif not isinstance(elements, list):
         raise CException(
             f"elements requires a list or a named element as input")
     for e in elements:
         if e is not None:
             check_named_element_is_not_deleted(e)
         else:
             raise CException(f"'None' cannot be an element of bundle")
         is_cnamedelement(e)
         if e not in self.elements_:
             # if it is already in the bundle, do not add it twice
             self.elements_.append(e)
             # noinspection PyUnresolvedReferences
             e.bundles_.append(self)
예제 #19
0
def get_common_metaclasses(classes_or_links):
    common_metaclasses = None
    for classifier in classes_or_links:
        if is_clink(classifier):
            link_classifiers = [
                link_cl for link_cl in classifier.association.all_superclasses
                if is_cmetaclass(link_cl)
            ]
            if not link_classifiers:
                raise CException(
                    f"the metaclass link's association is missing a compatible classifier"
                )
            if common_metaclasses is None:
                common_metaclasses = link_classifiers
            else:
                common_metaclasses = update_common_metaclasses(
                    common_metaclasses, link_classifiers)
                if len(common_metaclasses) == 0:
                    break
        elif classifier is None or not is_cclass(classifier):
            raise CException(f"not a class or link: '{classifier!s}'")
        else:
            if common_metaclasses is None:
                common_metaclasses = classifier.metaclass.class_path
            else:
                common_metaclasses = update_common_metaclasses(
                    common_metaclasses, [classifier.metaclass])
                if len(common_metaclasses) == 0:
                    break
    if common_metaclasses is None:
        return [None]
    if len(common_metaclasses) == 0:
        raise CException(f"no common metaclass for classes or links found")
    # if some superclasses and their subclasses are in the list, take only the subclasses
    # and remove duplicates form the list
    common_metaclasses = _remove_superclasses_and_duplicates(
        common_metaclasses)
    return common_metaclasses
예제 #20
0
    def remove_class(self, cl):
        """Remove the class ``cl`` from the classes of this meta-class. Raises an exception, if ``cl`` is
        not a class derived from  this meta-class.

        Args:
            cl (CClass): A class to remove.

        Returns:
            None

        """
        if cl not in self.classes_:
            raise CException(
                f"can't remove class instance '{cl!s}' from metaclass '{self!s}': not a class instance"
            )
        self.classes_.remove(cl)
예제 #21
0
    def remove(self, element):
        """
        Remove an element from the bundle.

        Args:
            element (CBundlable): Element to remove from the bundle.

        Returns:
            None

        """
        if (element is None or (not isinstance(element, CBundlable))
                or (self not in element.bundles)):
            raise CException(f"'{element!s}' is not an element of the bundle")
        self.elements_.remove(element)
        element.bundles_.remove(self)
예제 #22
0
    def add_class(self, cl):
        """Add the class ``cl`` to the classes of this meta-class.

        Args:
            cl (CClass): A class to add.

        Returns:
            None

        """
        check_is_cclass(cl)
        if cl in self.classes_:
            raise CException(
                f"class '{cl!s}' is already a class of the metaclass '{self!s}'"
            )
        self.classes_.append(cl)
예제 #23
0
 def _set_stereotypes(self, elements):
     if elements is None:
         elements = []
     self._remove_from_stereotype()
     self.stereotypes_ = []
     if is_cstereotype(elements):
         elements = [elements]
     elif not isinstance(elements, list):
         raise CException(f"a list or a stereotype is required as input")
     for s in elements:
         check_is_cstereotype(s)
         if s is not None:
             check_named_element_is_not_deleted(s)
             self._check_stereotype_can_be_added(s)
             self.stereotypes_.append(s)
             # noinspection PyTypeChecker
             self._append_to_stereotype(s)
             self._init_extended_element(s)
예제 #24
0
    def association(self, target, descriptor=None, **kwargs):
        """Method used to create associations on this class. See documentation of method ``association``
        on :py:class:`.CClassifier` for details.

        Args:
            target: The association target classifier.
            descriptor: An optional descriptor making it easier to define associations with a simple string.
            **kwargs: Accepts all keyword arguments acceptable to :py:class:`.CAssociation` to define associations.

        Returns:
            CAssociation: The created association.

        """
        if not isinstance(target, CClass):
            raise CException(
                f"class '{self!s}' is not compatible with association target '{target!s}'"
            )
        return super(CClass, self).association(target, descriptor, **kwargs)
예제 #25
0
    def get_opposite_classifier(self, classifier):
        """Given a classifier, this method returns the opposite in the association,
        i.e. the source if ``classifier`` is the
        target, and vice versa. Raises an exception if ``classifier`` is neither source nor target.

        Args:
            classifier: The classifier from which we want to get the opposite in the association.

        Returns:
            CClassifier: The opposite classifier.

        """
        if classifier == self.source:
            return self.target
        elif classifier == self.target:
            return self.source
        else:
            raise CException("can only get opposite if either source or target classifier is provided")
예제 #26
0
    def check_multiplicity_(self, obj, actual_length, actual_opposite_length, check_target_multiplicity):
        if check_target_multiplicity:
            upper = self.upper_multiplicity
            lower = self.lower_multiplicity
            other_side_lower = self.source_lower_multiplicity
            multiplicity_string = self.multiplicity_
        else:
            upper = self.source_upper_multiplicity
            lower = self.source_lower_multiplicity
            other_side_lower = self.lower_multiplicity
            multiplicity_string = self.source_multiplicity_

        if (upper != CAssociation.STAR_MULTIPLICITY and actual_length > upper) or actual_length < lower:
            # if there is actually no link as actualOppositeLength is zero, this is ok, if the otherLower 
            # including zero:
            if not (actual_opposite_length == 0 and other_side_lower == 0):
                raise CException(f"links of object '{obj}' have wrong multiplicity " +
                                 f"'{actual_length!s}': should be '{multiplicity_string!s}'")
예제 #27
0
    def delete_tagged_value(self, name, stereotype=None):
        """Delete tagged value of a stereotype attribute with the given ``name``.  Optionally the stereotype
        to consider can be specified. This is needed, if one or more attributes of the same name are defined
        on the inheritance hierarchy. Then a shadowed attribute can be accessed by specifying its stereotype.

        Args:
            name: The name of the attribute.
            stereotype: The optional stereotype on which the attribute is defined.

        Returns:
            Supported Attribute Types: Value of the attribute.
        """
        if self.is_deleted:
            raise CException(
                f"can't delete tagged value '{name!s}' on deleted link")
        return delete_var_value(
            self,
            self.stereotype_instances_holder.get_stereotype_instance_path(),
            self.tagged_values_, name, VarValueKind.TAGGED_VALUE, stereotype)
예제 #28
0
    def derived_from(self, metaclass_association):
        if metaclass_association is not None:
            if not is_cassociation(metaclass_association):
                raise CException(
                    f"'{metaclass_association!s}' is not an association")
            self._check_association_class_derived_from_association_metaclass(
                self.source, metaclass_association.source, "source")
            self._check_association_class_derived_from_association_metaclass(
                self.target, metaclass_association.target, "target")

            self.check_derived_association_multiplicities_(
                metaclass_association)
            self._check_derived_association_has_same_aggregation_state(
                metaclass_association)

        if self.derived_from_ is not None:
            self.derived_from_.derived_associations_.remove(self)
            self.derived_from_ = None

        self.derived_from_ = metaclass_association
        if metaclass_association is not None:
            metaclass_association.derived_associations_.append(self)
예제 #29
0
    def get_elements(self, **kwargs):
        """
        Get specific elements from the bundle. Per default returns all elements.

        Args:
            **kwargs: Used to specify in more detail which elements to get from the bundle. Accepts the
                following arguments:

                - ``type``: Return only elements which are instances of the specified type.
                - ``name``: Return only elements with the specified name.

        Returns:
            List[CBundlable]: List of elements.

        """
        type_ = None
        name = None
        # use this as name can also be provided as None
        name_specified = False
        for key in kwargs:
            if key == "type":
                type_ = kwargs["type"]
            elif key == "name":
                name = kwargs["name"]
                name_specified = True
            else:
                raise CException(f"unknown argument to getElements: '{key!s}'")
        elements = []
        for elt in self.elements_:
            append = True
            if name_specified and elt.name != name:
                append = False
            # noinspection PyTypeHints
            if type_ is not None and not isinstance(elt, type_):
                append = False
            if append:
                elements.append(elt)
        return elements
예제 #30
0
 def _check_stereotype_can_be_added(self, stereotype):
     if stereotype in self.stereotypes_:
         raise CException(
             f"'{stereotype.name!s}' is already a stereotype of '{self.element.name!s}'"
         )