Do write the model file at the end
This commit is contained in:
parent
c60391c280
commit
b0ff5f7e8c
@ -9,16 +9,6 @@ from gaphor.UML.properties import (
|
||||
redefine,
|
||||
)
|
||||
|
||||
# class 'ValueSpecification' has been stereotyped as 'SimpleAttribute'
|
||||
# class 'InstanceValue' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'Expression' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralSpecification' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralUnlimitedNatural' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralBoolean' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralInteger' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralString' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralNull' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'OpaqueExpression' has been stereotyped as 'SimpleAttribute' too
|
||||
# 14: override Element
|
||||
from gaphor.UML.element import Element
|
||||
|
||||
@ -544,6 +534,16 @@ class Reception(BehavioralFeature):
|
||||
pass
|
||||
|
||||
|
||||
# class 'ValueSpecification' has been stereotyped as 'SimpleAttribute'
|
||||
# class 'InstanceValue' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'Expression' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralSpecification' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralUnlimitedNatural' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralBoolean' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralInteger' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralString' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'LiteralNull' has been stereotyped as 'SimpleAttribute' too
|
||||
# class 'OpaqueExpression' has been stereotyped as 'SimpleAttribute' too
|
||||
Extension.isRequired = attribute("isRequired", int)
|
||||
Feature.isStatic = attribute("isStatic", int, default=False)
|
||||
RedefinableElement.isLeaf = attribute("isLeaf", int, default=True)
|
||||
@ -575,6 +575,7 @@ NamedElement.qualifiedName = property(
|
||||
)
|
||||
|
||||
del namedelement_qualifiedname
|
||||
|
||||
Component.isIndirectlyInstantiated = attribute(
|
||||
"isIndirectlyInstantiated", int, default=True
|
||||
)
|
||||
@ -628,6 +629,7 @@ Property.navigability = property(
|
||||
)
|
||||
|
||||
del property_navigability
|
||||
|
||||
Behavior.isReentrant = attribute("isReentrant", int)
|
||||
BehavioralFeature.isAbstract = attribute("isAbstract", int)
|
||||
Action.effect = attribute("effect", str)
|
||||
@ -1147,16 +1149,19 @@ MultiplicityElement.lower = derived(
|
||||
)
|
||||
MultiplicityElement.lower.filter = lambda obj: [obj.lowerValue]
|
||||
# MultiplicityElement.lower = MultiplicityElement.lowerValue
|
||||
|
||||
# 31: override MultiplicityElement.upper derives MultiplicityElement.upperValue
|
||||
MultiplicityElement.upper = derived(
|
||||
"upper", object, 0, 1, MultiplicityElement.upperValue
|
||||
)
|
||||
MultiplicityElement.upper.filter = lambda obj: [obj.upperValue]
|
||||
# MultiplicityElement.upper = MultiplicityElement.upperValue
|
||||
|
||||
# 132: override Property.isComposite derives Property.aggregation
|
||||
# Property.isComposite = property(lambda self: self.aggregation == 'composite')
|
||||
Property.isComposite = derivedunion("isComposite", bool, 0, 1, Property.aggregation)
|
||||
Property.isComposite.filter = lambda obj: [obj.aggregation == "composite"]
|
||||
|
||||
RedefinableElement.redefinedElement = derivedunion(
|
||||
"redefinedElement",
|
||||
RedefinableElement,
|
||||
@ -1232,6 +1237,7 @@ Property.opposite = property(property_opposite, doc=property_opposite.__doc__)
|
||||
|
||||
del property_opposite
|
||||
|
||||
|
||||
BehavioralFeature.parameter = derivedunion(
|
||||
"parameter",
|
||||
Parameter,
|
||||
@ -1334,6 +1340,7 @@ Classifier.general = property(
|
||||
""",
|
||||
)
|
||||
del classifier_general
|
||||
|
||||
# 51: override Association.endType derives Association.memberEnd Property.type
|
||||
|
||||
# References the classifiers that are used as types of the ends of the
|
||||
@ -1344,6 +1351,7 @@ Association.endType = derived(
|
||||
)
|
||||
Association.endType.filter = lambda self: [end.type for end in self.memberEnd if end]
|
||||
|
||||
|
||||
Classifier.attribute = derivedunion(
|
||||
"attribute",
|
||||
Property,
|
||||
@ -1358,8 +1366,10 @@ Classifier.attribute = derivedunion(
|
||||
)
|
||||
# 137: override Constraint.context
|
||||
Constraint.context = derivedunion("context", Namespace, 0, 1)
|
||||
|
||||
# 165: override Operation.type
|
||||
Operation.type = derivedunion("type", DataType, 0, 1)
|
||||
|
||||
# 76: override Extension.metaclass derives Extension.ownedEnd Association.memberEnd
|
||||
# See https://www.omg.org/spec/UML/2.5/PDF, section 12.4.1.5, page 271
|
||||
def extension_metaclass(self):
|
||||
@ -1379,6 +1389,7 @@ property is derived from the type of the memberEnd that is not the
|
||||
ownedEnd.""",
|
||||
)
|
||||
del extension_metaclass
|
||||
|
||||
# 60: override Class.extension derives Extension.metaclass
|
||||
# See https://www.omg.org/spec/UML/2.5/PDF, section 11.8.3.6, page 219
|
||||
# It defines `Extension.allInstances()`, which basically means we have to query the element factory.
|
||||
@ -1399,6 +1410,7 @@ metaclass. The property is derived from the extensions whose memberEnds
|
||||
are typed by the Class.""",
|
||||
)
|
||||
del class_extension
|
||||
|
||||
DirectedRelationship.target = derivedunion(
|
||||
"target",
|
||||
Element,
|
||||
@ -1442,6 +1454,7 @@ ActivityGroup.subgroup = derivedunion(
|
||||
)
|
||||
# 93: override Classifier.inheritedMember
|
||||
Classifier.inheritedMember = derivedunion("inheritedMember", NamedElement, 0, "*")
|
||||
|
||||
StructuredClassifier.role = derivedunion(
|
||||
"role",
|
||||
ConnectableElement,
|
||||
@ -1479,8 +1492,10 @@ Component.required = property(
|
||||
""",
|
||||
)
|
||||
del component_required
|
||||
|
||||
# 108: override Namespace.importedMember
|
||||
Namespace.importedMember = derivedunion("importedMember", PackageableElement, 0, "*")
|
||||
|
||||
Action.input = derivedunion("input", InputPin, 0, "*", SendSignalAction.target)
|
||||
# 194: override Component.provided
|
||||
import itertools
|
||||
@ -1534,8 +1549,10 @@ Component.provided = property(
|
||||
""",
|
||||
)
|
||||
del component_provided
|
||||
|
||||
# 93: override Classifier.inheritedMember
|
||||
Classifier.inheritedMember = derivedunion("inheritedMember", NamedElement, 0, "*")
|
||||
|
||||
Element.owner = derivedunion(
|
||||
"owner",
|
||||
Element,
|
||||
@ -1605,9 +1622,11 @@ StructuredClassifier.part = property(
|
||||
""",
|
||||
)
|
||||
del structuredclassifier_part
|
||||
|
||||
Transition.redefintionContext = derivedunion("redefintionContext", Classifier, 1, 1)
|
||||
# 105: override Class.superClass
|
||||
Class.superClass = Classifier.general
|
||||
|
||||
ActivityNode.redefinedElement = redefine(
|
||||
ActivityNode, "redefinedElement", ActivityNode, RedefinableElement.redefinedElement
|
||||
)
|
||||
@ -1652,6 +1671,7 @@ from gaphor.UML.umllex import parse_lifeline
|
||||
|
||||
Lifeline.parse = parse_lifeline
|
||||
del parse_lifeline
|
||||
|
||||
# 173: override Lifeline.render
|
||||
from gaphor.UML.umllex import render_lifeline
|
||||
|
||||
|
@ -28,12 +28,13 @@ def test_loading():
|
||||
GENERATED = """# This file is generated by build_uml.py. DO NOT EDIT!
|
||||
|
||||
from gaphor.UML.properties import association, attribute, enumeration, derived, derivedunion, redefine
|
||||
# class 'ValSpec' has been stereotyped as 'SimpleAttribute'
|
||||
# class 'ShouldNotShowUp' has been stereotyped as 'SimpleAttribute' too
|
||||
class Element: pass
|
||||
class SubClass(Element): pass
|
||||
class C: pass
|
||||
class D(C): pass
|
||||
|
||||
# class 'ValSpec' has been stereotyped as 'SimpleAttribute'
|
||||
# class 'ShouldNotShowUp' has been stereotyped as 'SimpleAttribute' too
|
||||
C.attr = attribute('attr', str)
|
||||
C.name1 = association('name1', SubClass, opposite='name2')
|
||||
SubClass.name2 = association('name2', C, opposite='name1')
|
||||
|
@ -15,6 +15,7 @@ attributes and traverse them to generate the data model.
|
||||
|
||||
import sys
|
||||
import ast
|
||||
from collections import OrderedDict
|
||||
|
||||
from gaphor.storage.parser import parse, base, element
|
||||
from utils.model import override
|
||||
@ -67,20 +68,32 @@ def msg(s):
|
||||
|
||||
|
||||
class Writer:
|
||||
def __init__(self, filename, overrides=None):
|
||||
def __init__(self, overrides=None):
|
||||
self.overrides = overrides
|
||||
self.classdefs = OrderedDict()
|
||||
self.features = []
|
||||
|
||||
def write(self, filename, header):
|
||||
if filename:
|
||||
self.out = hasattr(filename, "write") and filename or open(filename, "w")
|
||||
out = hasattr(filename, "write") and filename or open(filename, "w")
|
||||
else:
|
||||
self.out = sys.stdout
|
||||
out = sys.stdout
|
||||
|
||||
def write(self, data):
|
||||
self.out.write(data)
|
||||
try:
|
||||
out.write(header)
|
||||
for d in self.classdefs.values():
|
||||
out.write(d)
|
||||
out.write("\n")
|
||||
|
||||
def close(self):
|
||||
self.out.close()
|
||||
out.write("\n")
|
||||
|
||||
def write_classdef(self, clazz):
|
||||
for d in self.features:
|
||||
out.write(d)
|
||||
out.write("\n")
|
||||
finally:
|
||||
out.close()
|
||||
|
||||
def add_classdef(self, clazz):
|
||||
"""
|
||||
Write a class definition (class xx(x): pass).
|
||||
First the parent classes are examined. After that its own definition
|
||||
@ -90,27 +103,34 @@ class Writer:
|
||||
if not clazz.written:
|
||||
s = ""
|
||||
for g in clazz.generalization:
|
||||
self.write_classdef(g)
|
||||
self.add_classdef(g)
|
||||
if s:
|
||||
s += ", "
|
||||
s = s + g["name"]
|
||||
if not self.overrides.write_override(self, clazz["name"]):
|
||||
self.write("class {}".format(clazz["name"]))
|
||||
override = self.overrides.get_override(clazz["name"])
|
||||
if override:
|
||||
self.classdefs[clazz["name"]] = override
|
||||
else:
|
||||
line = f"class {clazz['name']}"
|
||||
if s:
|
||||
self.write(f"({s})")
|
||||
self.write(": pass\n")
|
||||
line += f"({s})"
|
||||
line += ": pass"
|
||||
self.classdefs[clazz["name"]] = line
|
||||
clazz.written = True
|
||||
|
||||
def write_property(self, full_name, value):
|
||||
def add_comment(self, line):
|
||||
self.features.append(f"# {line}")
|
||||
|
||||
def add_property(self, class_name, name, value, type):
|
||||
"""
|
||||
Write a property to the file. If the property is overridden, use the
|
||||
overridden value. full_name should be like Class.attribute. value is
|
||||
overridden value. value is
|
||||
free format text.
|
||||
"""
|
||||
if not self.overrides.write_override(self, full_name):
|
||||
self.write(f"{full_name} = {value}\n")
|
||||
override = self.overrides.get_override(f"{class_name}.{name}")
|
||||
self.features.append(override or f"{class_name}.{name} = {value}")
|
||||
|
||||
def write_attribute(self, a, enumerations={}):
|
||||
def add_attribute(self, a, enumerations={}):
|
||||
"""
|
||||
Write a definition for attribute a. Enumerations may be a dict
|
||||
of enumerations, indexed by ID. These are used to identify enums.
|
||||
@ -150,7 +170,12 @@ class Writer:
|
||||
|
||||
full_name = f"{a.class_name}.{a.name}"
|
||||
if self.overrides.has_override(full_name):
|
||||
self.overrides.write_override(self, full_name)
|
||||
self.add_property(
|
||||
a.class_name,
|
||||
a.name,
|
||||
self.overrides.get_override(full_name),
|
||||
self.overrides.get_type(full_name),
|
||||
)
|
||||
elif ast.literal_eval(a.isDerived or "0"):
|
||||
msg(
|
||||
"ignoring derived attribute %s.%s: no definition"
|
||||
@ -160,10 +185,11 @@ class Writer:
|
||||
e = list(filter(lambda e: e["name"] == type, list(enumerations.values())))[
|
||||
0
|
||||
]
|
||||
self.write_property(
|
||||
f"{a.class_name}.{a.name}",
|
||||
"enumeration('%s', %s, '%s')"
|
||||
% (a.name, e.enumerates, default or e.enumerates[0]),
|
||||
self.add_property(
|
||||
a.class_name,
|
||||
a.name,
|
||||
f"enumeration('{a.name}', {e.enumerates}, '{default or e.enumerates[0]}')",
|
||||
type="enumeration",
|
||||
)
|
||||
else:
|
||||
if params:
|
||||
@ -172,12 +198,17 @@ class Writer:
|
||||
)
|
||||
else:
|
||||
attribute = f"attribute('{a.name}', {type})"
|
||||
self.write_property(f"{a.class_name}.{a.name}", attribute)
|
||||
self.add_property(a.class_name, a.name, attribute, type="attribute")
|
||||
|
||||
def write_operation(self, o):
|
||||
def add_operation(self, o):
|
||||
full_name = f"{o.class_name}.{o.name}"
|
||||
if self.overrides.has_override(full_name):
|
||||
self.overrides.write_override(self, full_name)
|
||||
self.add_property(
|
||||
o.class_name,
|
||||
o.name,
|
||||
self.overrides.get_override(full_name),
|
||||
self.overrides.get_type(full_name),
|
||||
)
|
||||
else:
|
||||
msg(f"No override for operation {full_name}")
|
||||
|
||||
@ -219,9 +250,9 @@ class Writer:
|
||||
), "One end is derived, the other end not ???"
|
||||
a += f", opposite='{o_name}'"
|
||||
|
||||
self.write_property(f"{head.class_name}.{head.name}", a + ")")
|
||||
self.add_property(head.class_name, head.name, a + ")", type="association")
|
||||
|
||||
def write_derivedunion(self, d):
|
||||
def add_derivedunion(self, d):
|
||||
"""
|
||||
Write a derived union. If there are no subsets a warning
|
||||
is issued. The derivedunion is still created though.
|
||||
@ -232,13 +263,14 @@ class Writer:
|
||||
subs = ""
|
||||
for u in d.union:
|
||||
if u.derived and not u.written:
|
||||
self.write_derivedunion(u)
|
||||
self.add_derivedunion(u)
|
||||
if subs:
|
||||
subs += ", "
|
||||
subs += f"{u.class_name}.{u.name}"
|
||||
if subs:
|
||||
self.write_property(
|
||||
f"{d.class_name}.{d.name}",
|
||||
self.add_property(
|
||||
d.class_name,
|
||||
d.name,
|
||||
"derivedunion('%s', %s, %s, %s, %s)"
|
||||
% (
|
||||
d.name,
|
||||
@ -247,6 +279,7 @@ class Writer:
|
||||
d.upper == "*" and "'*'" or d.upper,
|
||||
subs,
|
||||
),
|
||||
type="derivedunion",
|
||||
)
|
||||
else:
|
||||
if not self.overrides.has_override(f"{d.class_name}.{d.name}"):
|
||||
@ -254,8 +287,9 @@ class Writer:
|
||||
"no subsets for derived union: %s.%s[%s..%s]"
|
||||
% (d.class_name, d.name, d.lower, d.upper)
|
||||
)
|
||||
self.write_property(
|
||||
f"{d.class_name}.{d.name}",
|
||||
self.add_property(
|
||||
d.class_name,
|
||||
d.name,
|
||||
"derivedunion('%s', %s, %s, %s)"
|
||||
% (
|
||||
d.name,
|
||||
@ -263,18 +297,21 @@ class Writer:
|
||||
d.lower,
|
||||
d.upper == "*" and "'*'" or d.upper,
|
||||
),
|
||||
type="derivedunion",
|
||||
)
|
||||
d.written = True
|
||||
|
||||
def write_redefine(self, r):
|
||||
def add_redefine(self, r):
|
||||
"""
|
||||
Redefines may be created for associations that were returned
|
||||
False by write_association().
|
||||
"""
|
||||
self.write_property(
|
||||
f"{r.class_name}.{r.name}",
|
||||
self.add_property(
|
||||
r.class_name,
|
||||
r.name,
|
||||
"redefine(%s, '%s', %s, %s)"
|
||||
% (r.class_name, r.name, r.opposite_class_name, r.redefines),
|
||||
type="redefine",
|
||||
)
|
||||
|
||||
|
||||
@ -378,7 +415,7 @@ def generate(filename, outfile=None, overridesfile=None):
|
||||
val.references[attr] = all_elements[refs]
|
||||
|
||||
overrides = override.Overrides(overridesfile)
|
||||
writer = Writer(outfile, overrides)
|
||||
writer = Writer(overrides)
|
||||
|
||||
# extract usable elements from all_elements. Some elements are given
|
||||
# some extra attributes.
|
||||
@ -456,9 +493,6 @@ def generate(filename, outfile=None, overridesfile=None):
|
||||
if ends:
|
||||
del classes[e.memberEnd[0].type.id]
|
||||
|
||||
# create file header
|
||||
writer.write(header)
|
||||
|
||||
# Tag classes with appliedStereotype
|
||||
for c in list(classes.values()):
|
||||
if c.get("appliedStereotype"):
|
||||
@ -467,17 +501,15 @@ def generate(filename, outfile=None, overridesfile=None):
|
||||
instSpec = all_elements[c.appliedStereotype[0]]
|
||||
sType = all_elements[instSpec.classifier[0]]
|
||||
c.stereotypeName = sType.name
|
||||
writer.write(
|
||||
"# class '%s' has been stereotyped as '%s'\n"
|
||||
% (c.name, c.stereotypeName)
|
||||
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.write(
|
||||
"# class '%s' has been stereotyped as '%s' too\n"
|
||||
% (child.name, child.stereotypeName)
|
||||
writer.add_comment(
|
||||
f"class '{child.name}' has been stereotyped as '{child.stereotypeName}' too"
|
||||
)
|
||||
# child.written = True
|
||||
tag_children(child)
|
||||
@ -491,20 +523,20 @@ def generate(filename, outfile=None, overridesfile=None):
|
||||
if c.stereotypeName == "SimpleAttribute":
|
||||
ignored_classes.add(c)
|
||||
else:
|
||||
writer.write_classdef(c)
|
||||
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 write_attribute depends on it
|
||||
# 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.write_attribute(a, enumerations)
|
||||
writer.add_attribute(a, enumerations)
|
||||
|
||||
# create associations, derivedunions are held back
|
||||
derivedunions = {} # indexed by name in stead of id
|
||||
@ -531,13 +563,13 @@ def generate(filename, outfile=None, overridesfile=None):
|
||||
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.write(
|
||||
f"# '{e2.type.name}.{e1.name}' is a simple attribute\n"
|
||||
writer.add_comment(
|
||||
f"'{e2.type.name}.{e1.name}' is a simple attribute"
|
||||
)
|
||||
e1.class_name = e2.type.name
|
||||
e1.typeValue = "str"
|
||||
|
||||
writer.write_attribute(e1, enumerations)
|
||||
writer.add_attribute(e1, enumerations)
|
||||
e1.written = True
|
||||
e2.written = True
|
||||
elif e1.redefines:
|
||||
@ -566,23 +598,23 @@ def generate(filename, outfile=None, overridesfile=None):
|
||||
# may depend on other derived attributes or associations.
|
||||
|
||||
for d in list(derivedattributes.values()):
|
||||
writer.write_attribute(d)
|
||||
writer.add_attribute(d)
|
||||
|
||||
for d in list(derivedunions.values()):
|
||||
writer.write_derivedunion(d)
|
||||
writer.add_derivedunion(d)
|
||||
|
||||
for r in redefines or ():
|
||||
msg(f"redefining {r.redefines} -> {r.class_name}.{r.name}")
|
||||
writer.write_redefine(r)
|
||||
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.write_operation(o)
|
||||
writer.add_operation(o)
|
||||
|
||||
writer.close()
|
||||
writer.write(outfile, header)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -68,16 +68,16 @@ class Overrides:
|
||||
def has_override(self, key):
|
||||
return bool(self.overrides.get(key))
|
||||
|
||||
def derives(self, key):
|
||||
return self.overrides.get(key, ((), None))[0]
|
||||
|
||||
def write_override(self, fp, key):
|
||||
def get_override(self, key):
|
||||
"""Write override data for 'key' to a file refered to by 'fp'."""
|
||||
deps, data, line = self.overrides.get(key, ((), None, None))
|
||||
if not data:
|
||||
return False
|
||||
return None
|
||||
|
||||
fp.write("# ")
|
||||
fp.write(line)
|
||||
fp.write(data)
|
||||
return True
|
||||
return f"# {line}{data}"
|
||||
|
||||
def get_type(self, key):
|
||||
return "property"
|
||||
|
||||
def derives(self, key):
|
||||
return self.overrides.get(key, ((), None))[0]
|
||||
|
Loading…
x
Reference in New Issue
Block a user