def test_message_item_upgrade(element_factory, modeling_language):
    """"""
    path = distribution().locate_file("test-models/multiple-messages.gaphor")

    elements = parse(path)
    load_elements(elements, element_factory, modeling_language)

    diagram = element_factory.lselect(UML.Diagram)[0]
    items = diagram.canvas.get_root_items()
    message_items = [i for i in items if isinstance(i, diagramitems.MessageItem)]
    subjects = [m.subject for m in message_items]
    messages = element_factory.lselect(UML.Message)
    presentations = [m.presentation for m in messages]

    assert len(messages) == 10
    assert all(subjects), subjects
    assert len(message_items) == 10
    assert all(presentations), presentations
def test_message_item_upgrade(element_factory):
    """
    """
    dist = importlib_metadata.distribution("gaphor")
    path = dist.locate_file("test-diagrams/multiple-messages.gaphor")

    elements = parse(path)
    load_elements(elements, element_factory)

    diagram = element_factory.lselect(lambda e: e.isKindOf(UML.Diagram))[0]
    items = diagram.canvas.get_root_items()
    message_items = [
        i for i in items if isinstance(i, diagramitems.MessageItem)
    ]
    subjects = [m.subject for m in message_items]
    messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message))
    presentations = [m.presentation for m in messages]

    assert len(messages) == 10
    assert all(subjects), subjects
    assert len(message_items) == 10
    assert all(presentations), presentations
예제 #3
0
def generate(filename, outfile=None, overridesfile=None):
    # parse the file
    all_elements = parse(filename)

    def resolve(val, attr):
        """Resolve references.
        """
        try:
            refs = val.references[attr]
        except KeyError:
            val.references[attr] = None
            return

        if type(refs) is type([]):
            unrefs = []
            for r in refs:
                unrefs.append(all_elements[r])
            val.references[attr] = unrefs
        else:
            val.references[attr] = all_elements[refs]

    overrides = override.Overrides(overridesfile)
    writer = Writer(outfile, overrides)

    # extract usable elements from all_elements. Some elements are given
    # some extra attributes.
    classes = { }
    enumerations = { }
    generalizations = { }
    associations = { }
    properties = { }
    operations = { }
    extensions = { } # for identifying metaclasses
    for key, val in all_elements.items():
        # Find classes, *Kind (enumerations) are given special treatment
        if isinstance(val, element):
            if val.type == 'Class' and val.get('name'):
                if val['name'].endswith('Kind') or val['name'].endswith('Sort'):
                    enumerations[key] = val
                else:
                    # Metaclasses are removed later on (need to be checked
                    # via the Extension instances)
                    classes[key] = val
                    # Add extra properties for easy code generation:
                    val.specialization = []
                    val.generalization = []
                    val.stereotypeName = None
                    val.written = False
            elif val.type == 'Generalization':
                generalizations[key] = val
            elif val.type == 'Association':
                val.asAttribute = None
                associations[key] = val
            elif val.type == 'Property':
                properties[key] = val
                #resolve(val, 'typeValue')
                #resolve(val, 'defaultValue')
                #resolve(val, 'lowerValue')
                #resolve(val, 'upperValue')
                resolve(val, 'appliedStereotype')
                for st in val.appliedStereotype or []:
                    resolve(st, 'slot')
                    for slot in st.slot or []:
                        resolve(slot, 'value')
                        resolve(slot, 'definingFeature')
                val.written = False
            elif val.type == 'Operation':
                operations[key] = val
            elif val.type == 'Extension':
                extensions[key] = val

    # find inheritance relationships
    for g in generalizations.values():
        #assert g.specific and g.general
        specific = g['specific']
        general = g['general']
        classes[specific].generalization.append(classes[general])
        classes[general].specialization.append(classes[specific])

    # add values to enumerations:
    for e in enumerations.values():
        values = []
        for key in e['ownedAttribute']:
            values.append(str(properties[key]['name']))
        e.enumerates = tuple(values)

    # Remove metaclasses from classes dict
    # should check for Extension.memberEnd<Property>.type
    for e in extensions.values():
        ends = []
        for end in e.memberEnd:
            end = all_elements[end]
            if not end['type']:
                continue
            end.type = all_elements[end['type']]
            ends.append(end)
        e.memberEnd = ends
        if ends:
            del classes[e.memberEnd[0].type.id]


    # create file header
    writer.write(header)

    # Tag classes with appliedStereotype
    for c in classes.values():
        if c.get('appliedStereotype'):
            # Figure out stereotype name through
            # Class.appliedStereotype.classifier.name
            instSpec = all_elements[c.appliedStereotype[0]]
            sType = all_elements[instSpec.classifier[0]]
            c.stereotypeName = sType.name
            print "  class '%s' has been stereotyped as '%s'" % (c.name, c.stereotypeName)
            writer.write("# class '%s' has been stereotyped as '%s'\n" % (c.name, c.stereotypeName))
            #c.written = True
            def tag_children(me):
                for child in me.specialization:
                    child.stereotypeName = sType.name
                    print "  class '%s' has been stereotyped as '%s' too" % (child.name, child.stereotypeName)
                    writer.write("# class '%s' has been stereotyped as '%s' too\n" % (child.name, child.stereotypeName))
                    #child.written = True
                    tag_children(child)
            tag_children(c)

    ignored_classes = set()

    # create class definitions, not for SimpleAttribute
    for c in classes.values():
        if c.stereotypeName == 'SimpleAttribute':
            ignored_classes.add(c)
        else:
            writer.write_classdef(c)
 
    # create attributes and enumerations
    derivedattributes = { }
    for c in filter(lambda c: c not in ignored_classes, classes.values()):
        for p in c.get('ownedAttribute') or []:
            a = properties.get(p)
            # set class_name, since write_attribute depends on it
            a.class_name = c['name']
            if not a.get('association'):
                if overrides.derives('%s.%s' % (a.class_name, a.name)):
                    derivedattributes[a.name] = a
                else:
                    writer.write_attribute(a, enumerations)

    # create associations, derivedunions are held back
    derivedunions = { } # indexed by name in stead of id
    redefines = [ ]
    for a in associations.values():
        ends = []
        # Resolve some properties:
        for end in a.memberEnd:
            end = properties[end]
            end.type = classes[end['type']]
            end.class_ = end.get('class_') and classes[end['class_']] or None
            end.is_simple_attribute = False
            if end.type and end.type.stereotypeName == 'SimpleAttribute':
                end.is_simple_attribute = True
                a.asAttribute = end
            ends.append(end)

        for e1, e2 in ((ends[0], ends[1]), (ends[1], ends[0])):
            parse_association_end(e1, e2)

        for e1, e2 in ((ends[0], ends[1]), (ends[1], ends[0])):
            if a.asAttribute:
                if a.asAttribute is e1 and e1.navigable:
                   writer.write("# '%s.%s' is a simple attribute\n" % (e2.type.name, e1.name))
                   e1.class_name = e2.type.name
                   e1.typeValue = 'str'

                   writer.write_attribute(e1, enumerations)
                   e1.written = True
                   e2.written = True
            elif e1.redefines:
                redefines.append(e1)
            elif e1.derived or overrides.derives('%s.%s' % (e1.class_name, e1.name)):
                assert not derivedunions.get(e1.name), "%s.%s is already in derived union set in class %s" % (e1.class_name, e1.name, derivedunions.get(e1.name).class_name)
                derivedunions[e1.name] = e1
                e1.union = [ ]
                e1.written = False
            elif e1.navigable:
                writer.write_association(e1, e2)


    # create derived unions, first link the association ends to the d
    for a in (v for v in properties.values() if v.subsets):
        for s in a.subsets or ():
            try:
                if a.type not in ignored_classes:
                    derivedunions[s].union.append(a)
            except KeyError:
                msg('not a derived union: %s.%s' % (a.class_name, s))


    # TODO: We should do something smart here, since derived attributes (mostly)
    #       may depend on other derived attributes or associations.

    for d in derivedattributes.values():
        writer.write_attribute(d)

    for d in derivedunions.values():
        writer.write_derivedunion(d)

    for r in redefines or ():
        msg('redefining %s -> %s.%s' % (r.redefines, r.class_name, r.name))
        writer.write_redefine(r)

    # create operations
    for c in filter(lambda c: c not in ignored_classes, classes.values()):
        for p in c.get('ownedOperation') or ():
            o = operations.get(p)
            o.class_name = c['name']
            writer.write_operation(o)

    writer.close()
예제 #4
0
파일: uml_coder.py 프로젝트: tuxcell/gaphor
def generate(filename, outfile=None, overridesfile=None):  # noqa: C901
    # parse the file
    all_elements = parse(filename)
    overrides = override.Overrides(overridesfile)
    writer = Writer(overrides)

    def resolve(val, attr):
        """Resolve references."""
        try:
            refs = val.references[attr]
        except KeyError:
            val.references[attr] = None
            return

        if isinstance(refs, type([])):
            unrefs = []
            for r in refs:  # type: ignore
                unrefs.append(all_elements[r])
            val.references[attr] = unrefs
        else:
            val.references[attr] = all_elements[refs]

    # extract usable elements from all_elements. Some elements are given
    # some extra attributes.
    classes = {}
    enumerations = {}
    generalizations = {}
    associations = {}
    properties = {}
    operations = {}
    extensions = {}  # for identifying metaclasses
    for key, val in all_elements.items():
        # Find classes, *Kind (enumerations) are enumerations
        if isinstance(val, element):
            if val.type == "Class" and val.get("name"):
                if val["name"].endswith("Kind") or val["name"].endswith(
                        "Sort"):
                    enumerations[key] = val
                else:
                    # Metaclasses are removed later on (need to be checked
                    # via the Extension instances)
                    classes[key] = val
                    # Add extra properties for easy code generation:
                    val.specialization = []  # type: ignore
                    val.generalization = []  # type: ignore
                    val.stereotypeName = None  # type: ignore
                    val.written = False  # type: ignore
            elif val.type == "Generalization":
                generalizations[key] = val
            elif val.type == "Association":
                val.asAttribute = None  # type: ignore
                associations[key] = val
            elif val.type == "Property":
                properties[key] = val
                resolve(val, "appliedStereotype")
                for st in val.appliedStereotype or []:
                    resolve(st, "slot")
                    for slot in st.slot or []:
                        resolve(slot, "value")
                        resolve(slot, "definingFeature")
                val.written = False  # type: ignore
            elif val.type == "Operation":
                operations[key] = val
            elif val.type == "Extension":
                extensions[key] = val

    enumerations = enrich_enumerations_with_values(enumerations, properties)

    classes = enrich_classes_with_generalizations(classes, generalizations)
    classes = enrich_classes_with_stereotypes(classes, all_elements, writer)
    classes = filter_out_metaclasses(classes, extensions, all_elements)
    all_classes = classes
    classes = filter_out_simple_attributes(classes)

    for c in classes.values():
        writer.add_classdef(c)

    # create attributes and enumerations
    derivedattributes = {}
    for c in classes.values():
        for p in c.get("ownedAttribute") or []:
            a = properties.get(p)
            # set class_name, since add_attribute depends on it
            a.class_name = c["name"]  # type: ignore
            if not a.get("association"):  # type: ignore
                if overrides.derives(
                        f"{a.class_name}.{a.name}"):  # type: ignore
                    derivedattributes[a.name] = a  # type: ignore
                else:
                    writer.add_attribute(a, enumerations)

    # create associations, derivedunions are held back
    derivedunions: Dict[str, Optional[element]] = {
    }  # indexed by name in stead of id
    redefines = []
    for a in list(associations.values()):
        ends = get_association_ends(a, properties, all_classes)

        for e1, e2 in ((ends[0], ends[1]), (ends[1], ends[0])):
            parse_association_end(e1, e2)

        for e1, e2 in ((ends[0], ends[1]), (ends[1], ends[0])):
            if a.asAttribute is not None:
                if a.asAttribute is e1 and e1.navigable:
                    writer.add_comment(
                        f"'{e2.type.name}.{e1.name}' is a simple attribute")
                    e1.class_name = e2.type.name
                    e1.typeValue = "str"

                    writer.add_attribute(e1, enumerations)
                    e1.written = True
                    e2.written = True
            elif e1.redefines:
                redefines.append(e1)
            elif e1.derived or overrides.derives(
                    f"{e1.get('class_name')}.{e1.get('name')}"):
                assert not derivedunions.get(e1.name), (
                    "%s.%s is already in derived union set in class %s" %
                    (e1.class_name, e1.name, derivedunions.get(
                        e1.name).class_name)  # type: ignore
                )
                derivedunions[e1.name] = e1
                e1.union = []
                e1.written = False
            elif e1.navigable:
                writer.add_association(e1, e2)

    # create derived unions, first link the association ends to the d
    for a in properties.values():
        for s in a.subsets or ():
            try:
                if a["type"] in classes:
                    derivedunions[s].union.append(a)  # type: ignore
            except KeyError:
                msg(f"not a derived union: {a.class_name}.{s}")

    # TODO: We should do something smart here, since derived attributes (mostly)
    #       may depend on other derived attributes or associations.

    for d in list(derivedattributes.values()):
        writer.add_attribute(d)

    for d in list(derivedunions.values()):
        writer.add_derivedunion(d)

    for r in redefines or ():
        msg(f"redefining {r.redefines} -> {r.class_name}.{r.name}[{r.upper}]")
        writer.add_redefine(r)

    # create operations
    for c in classes.values():
        for p in c.get("ownedOperation") or ():
            o = operations.get(p)
            o.class_name = c["name"]  # type: ignore
            writer.add_operation(o)

    writer.write(outfile, header)
예제 #5
0
def generate(filename, outfile=None, overridesfile=None):
    # parse the file
    all_elements = parse(filename)

    def resolve(val, attr):
        """Resolve references.
        """
        try:
            refs = val.references[attr]
        except KeyError:
            val.references[attr] = None
            return

        if isinstance(refs, type([])):
            unrefs = []
            for r in refs:
                unrefs.append(all_elements[r])
            val.references[attr] = unrefs
        else:
            val.references[attr] = all_elements[refs]

    overrides = override.Overrides(overridesfile)
    writer = Writer(overrides)

    # extract usable elements from all_elements. Some elements are given
    # some extra attributes.
    classes = {}
    enumerations = {}
    generalizations = {}
    associations = {}
    properties = {}
    operations = {}
    extensions = {}  # for identifying metaclasses
    for key, val in list(all_elements.items()):
        # Find classes, *Kind (enumerations) are given special treatment
        if isinstance(val, element):
            if val.type == "Class" and val.get("name"):
                if val["name"].endswith("Kind") or val["name"].endswith(
                        "Sort"):
                    enumerations[key] = val
                else:
                    # Metaclasses are removed later on (need to be checked
                    # via the Extension instances)
                    classes[key] = val
                    # Add extra properties for easy code generation:
                    val.specialization = []
                    val.generalization = []
                    val.stereotypeName = None
                    val.written = False
            elif val.type == "Generalization":
                generalizations[key] = val
            elif val.type == "Association":
                val.asAttribute = None
                associations[key] = val
            elif val.type == "Property":
                properties[key] = val
                # resolve(val, 'typeValue')
                # resolve(val, 'defaultValue')
                # resolve(val, 'lowerValue')
                # resolve(val, 'upperValue')
                resolve(val, "appliedStereotype")
                for st in val.appliedStereotype or []:
                    resolve(st, "slot")
                    for slot in st.slot or []:
                        resolve(slot, "value")
                        resolve(slot, "definingFeature")
                val.written = False
            elif val.type == "Operation":
                operations[key] = val
            elif val.type == "Extension":
                extensions[key] = val

    # find inheritance relationships
    for g in list(generalizations.values()):
        # assert g.specific and g.general
        specific = g["specific"]
        general = g["general"]
        classes[specific].generalization.append(classes[general])
        classes[general].specialization.append(classes[specific])

    # add values to enumerations:
    for e in list(enumerations.values()):
        values = []
        for key in e["ownedAttribute"]:
            values.append(str(properties[key]["name"]))
        e.enumerates = tuple(values)

    # Remove metaclasses from classes dict
    # should check for Extension.memberEnd[Property].type
    for e in list(extensions.values()):
        ends = []
        for end in e.memberEnd:
            end = all_elements[end]
            if not end["type"]:
                continue
            end.type = all_elements[end["type"]]
            ends.append(end)
        e.memberEnd = ends
        if ends:
            del classes[e.memberEnd[0].type.id]

    # Tag classes with appliedStereotype
    for c in list(classes.values()):
        if c.get("appliedStereotype"):
            # Figure out stereotype name through
            # Class.appliedStereotype.classifier.name
            instSpec = all_elements[c.appliedStereotype[0]]
            sType = all_elements[instSpec.classifier[0]]
            c.stereotypeName = sType.name
            writer.add_comment(
                f"class '{c.name}' has been stereotyped as '{c.stereotypeName}'"
            )

            # c.written = True
            def tag_children(me):
                for child in me.specialization:
                    child.stereotypeName = sType.name
                    writer.add_comment(
                        f"class '{child.name}' has been stereotyped as '{child.stereotypeName}' too"
                    )
                    # child.written = True
                    tag_children(child)

            tag_children(c)

    ignored_classes = set()

    # create class definitions, not for SimpleAttribute
    for c in list(classes.values()):
        if c.stereotypeName == "SimpleAttribute":
            ignored_classes.add(c)
        else:
            writer.add_classdef(c)

    # create attributes and enumerations
    derivedattributes = {}
    for c in [c for c in list(classes.values()) if c not in ignored_classes]:
        for p in c.get("ownedAttribute") or []:
            a = properties.get(p)
            # set class_name, since add_attribute depends on it
            a.class_name = c["name"]
            if not a.get("association"):
                if overrides.derives(f"{a.class_name}.{a.name}"):
                    derivedattributes[a.name] = a
                else:
                    writer.add_attribute(a, enumerations)

    # create associations, derivedunions are held back
    derivedunions = {}  # indexed by name in stead of id
    redefines = []
    for a in list(associations.values()):
        ends = []
        # Resolve some properties:
        for end in a.memberEnd:
            end = properties[end]
            end.type = classes[end["type"]]
            if end.get("class_"):
                end.class_ = classes[end["class_"]]
            else:
                end.class_ = None
            end.is_simple_attribute = False
            if end.type is not None and end.type.stereotypeName == "SimpleAttribute":
                end.is_simple_attribute = True
                a.asAttribute = end
            ends.append(end)

        for e1, e2 in ((ends[0], ends[1]), (ends[1], ends[0])):
            parse_association_end(e1, e2)

        for e1, e2 in ((ends[0], ends[1]), (ends[1], ends[0])):
            if a.asAttribute is not None:
                if a.asAttribute is e1 and e1.navigable:
                    writer.add_comment(
                        f"'{e2.type.name}.{e1.name}' is a simple attribute")
                    e1.class_name = e2.type.name
                    e1.typeValue = "str"

                    writer.add_attribute(e1, enumerations)
                    e1.written = True
                    e2.written = True
            elif e1.redefines:
                redefines.append(e1)
            elif e1.derived or overrides.derives(
                    f"{e1.get('class_name')}.{e1.get('name')}"):
                assert not derivedunions.get(e1.name), (
                    "%s.%s is already in derived union set in class %s" %
                    (e1.class_name, e1.name, derivedunions.get(
                        e1.name).class_name))
                derivedunions[e1.name] = e1
                e1.union = []
                e1.written = False
            elif e1.navigable:
                writer.add_association(e1, e2)

    # create derived unions, first link the association ends to the d
    for a in (v for v in list(properties.values()) if v.subsets):
        for s in a.subsets or ():
            try:
                if a.type not in ignored_classes:
                    derivedunions[s].union.append(a)
            except KeyError:
                msg(f"not a derived union: {a.class_name}.{s}")

    # TODO: We should do something smart here, since derived attributes (mostly)
    #       may depend on other derived attributes or associations.

    for d in list(derivedattributes.values()):
        writer.add_attribute(d)

    for d in list(derivedunions.values()):
        writer.add_derivedunion(d)

    for r in redefines or ():
        msg(f"redefining {r.redefines} -> {r.class_name}.{r.name}")
        writer.add_redefine(r)

    # create operations
    for c in [c for c in list(classes.values()) if c not in ignored_classes]:
        for p in c.get("ownedOperation") or ():
            o = operations.get(p)
            o.class_name = c["name"]
            writer.add_operation(o)

    writer.write(outfile, header)