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
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()
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)
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)