From ec7641f0d9bb7acb4bd6ffa459694852de58f387 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Fri, 21 Feb 2020 23:09:29 +0100 Subject: [PATCH 01/36] Update interaction base diagram for UML 2.5 --- gaphor/UML/uml2.gaphor | 1629 ++++++++++++++++++++++++++++++++++++++-- gaphor/UML/uml2.py | 12 +- 2 files changed, 1559 insertions(+), 82 deletions(-) diff --git a/gaphor/UML/uml2.gaphor b/gaphor/UML/uml2.gaphor index b90011aaa..0701e66f3 100755 --- a/gaphor/UML/uml2.gaphor +++ b/gaphor/UML/uml2.gaphor @@ -1,5 +1,5 @@ - + @@ -171,6 +171,9 @@ * + +* + public @@ -199,6 +202,9 @@ 1 + +1 + public @@ -264,6 +270,9 @@ 1 + +1 + public @@ -639,7 +648,7 @@ -(1.0, 0.0, 0.0, 1.0, 239.73051224928787, 380.6650717703349) +(1.0, 0.0, 0.0, 1.0, 239.73051224928787, 388.6650717703349) 0 @@ -648,7 +657,7 @@ 1 -[(0.0, 0.0), (-1.7305122492878695, 56.33492822966508)] +[(0.0, 0.0), (-1.7305122492878695, 48.33492822966508)] @@ -720,7 +729,7 @@ 166.475247525 -50.0 +58.0 0 @@ -887,6 +896,9 @@ 0 + +0 + abstraction @@ -899,6 +911,9 @@ 1 + +1 + public @@ -974,6 +989,9 @@ 0 + +0 + upperValue @@ -986,6 +1004,9 @@ 1 + +1 + public @@ -1040,6 +1061,9 @@ * + +* + public @@ -1461,6 +1485,9 @@ 0 + +0 + symbol @@ -1470,6 +1497,9 @@ 1 + +1 + @@ -1686,6 +1716,9 @@ 1 + +1 + public @@ -1717,6 +1750,9 @@ * + +* + @@ -1745,6 +1781,9 @@ * + +* + public @@ -1852,6 +1891,9 @@ * + +* + public @@ -1896,6 +1938,9 @@ * + +* + public @@ -2351,6 +2396,9 @@ * + +* + public @@ -2915,6 +2963,9 @@ 1 + +1 + public @@ -2934,6 +2985,9 @@ 0 + +0 + lower @@ -2943,6 +2997,9 @@ 1 + +1 + @@ -2984,6 +3041,9 @@ * + +* + public @@ -3015,6 +3075,9 @@ * + +* + public @@ -3091,6 +3154,9 @@ 1 + +1 + target @@ -3103,6 +3169,9 @@ * + +* + public @@ -3144,6 +3213,9 @@ BehavioredClassifier. 0 + +0 + ownerReturnParam @@ -3156,6 +3228,9 @@ BehavioredClassifier. 1 + +1 + @@ -3184,6 +3259,9 @@ BehavioredClassifier. * + +* + public @@ -3308,6 +3386,9 @@ BehavioredClassifier. 1 + +1 + public @@ -3418,6 +3499,9 @@ BehavioredClassifier. * + +* + @@ -3447,6 +3531,9 @@ BehavioredClassifier. 1 + +1 + extensionLocation @@ -3459,6 +3546,9 @@ BehavioredClassifier. * + +* + public @@ -3517,6 +3607,9 @@ BehavioredClassifier. 1 + +1 + @@ -3545,6 +3638,9 @@ BehavioredClassifier. * + +* + public @@ -3573,6 +3669,9 @@ BehavioredClassifier. * + +* + @@ -4483,6 +4582,9 @@ BehavioredClassifier. * + +* + public @@ -4511,6 +4613,9 @@ BehavioredClassifier. * + +* + @@ -4613,6 +4718,9 @@ BehavioredClassifier. * + +* + public @@ -4635,6 +4743,9 @@ BehavioredClassifier. 1 + +1 + endType @@ -4647,6 +4758,9 @@ BehavioredClassifier. * + +* + public @@ -4718,6 +4832,9 @@ BehavioredClassifier. * + +* + @@ -4746,6 +4863,9 @@ BehavioredClassifier. * + +* + public @@ -4762,6 +4882,9 @@ BehavioredClassifier. 0 + +0 + visibility @@ -4771,6 +4894,9 @@ BehavioredClassifier. 1 + +1 + public @@ -4790,6 +4916,9 @@ BehavioredClassifier. 0 + +0 + namespace_ @@ -4805,6 +4934,9 @@ BehavioredClassifier. 1 + +1 + public @@ -4849,6 +4981,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5027,6 +5162,9 @@ BehavioredClassifier. * + +* + public @@ -5055,6 +5193,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5113,6 +5254,9 @@ BehavioredClassifier. 0 + +0 + default @@ -5122,6 +5266,9 @@ BehavioredClassifier. 1 + +1 + @@ -5183,6 +5330,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5211,6 +5361,9 @@ BehavioredClassifier. * + +* + public @@ -5255,6 +5408,9 @@ BehavioredClassifier. * + +* + public @@ -5277,6 +5433,9 @@ BehavioredClassifier. 0 + +0 + lowerValue @@ -5289,6 +5448,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5354,6 +5516,9 @@ BehavioredClassifier. 1 + +1 + @@ -5373,6 +5538,9 @@ BehavioredClassifier. 0 + +0 + specification @@ -5385,6 +5553,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5500,6 +5671,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5617,6 +5791,9 @@ BehavioredClassifier. * + +* + public @@ -5783,6 +5960,9 @@ BehavioredClassifier. 0 + +0 + preContext @@ -5798,6 +5978,9 @@ BehavioredClassifier. 1 + +1 + public @@ -5812,6 +5995,9 @@ BehavioredClassifier. 0 + +0 + decisionInput @@ -5824,6 +6010,9 @@ BehavioredClassifier. 1 + +1 + @@ -6255,6 +6444,9 @@ BehavioredClassifier. 1 + +1 + @@ -6323,6 +6515,9 @@ BehavioredClassifier. * + +* + public @@ -6414,6 +6609,9 @@ BehavioredClassifier. * + +* + public @@ -6452,6 +6650,9 @@ BehavioredClassifier. 0 + +0 + postContext @@ -6467,6 +6668,9 @@ BehavioredClassifier. 1 + +1 + public @@ -6498,6 +6702,9 @@ BehavioredClassifier. * + +* + public @@ -6561,6 +6768,9 @@ BehavioredClassifier. 0 + +0 + interface_ @@ -6573,6 +6783,9 @@ BehavioredClassifier. 1 + +1 + @@ -6639,6 +6852,9 @@ BehavioredClassifier. 1 + +1 + public @@ -6684,6 +6900,9 @@ BehavioredClassifier. 0 + +0 + owner @@ -6696,6 +6915,9 @@ BehavioredClassifier. 1 + +1 + public @@ -6739,6 +6961,9 @@ BehavioredClassifier. 0 + +0 + context @@ -6751,6 +6976,9 @@ BehavioredClassifier. 1 + +1 + public @@ -6800,6 +7028,9 @@ BehavioredClassifier. * + +* + public @@ -6918,6 +7149,9 @@ BehavioredClassifier. * + +* + public @@ -7017,6 +7251,9 @@ BehavioredClassifier. * + +* + public @@ -7067,6 +7304,9 @@ BehavioredClassifier. * + +* + public @@ -7160,6 +7400,9 @@ BehavioredClassifier. 0 + +0 + defaultValue @@ -7172,6 +7415,9 @@ BehavioredClassifier. 1 + +1 + public @@ -7272,6 +7518,9 @@ BehavioredClassifier. * + +* + public @@ -7351,6 +7600,9 @@ BehavioredClassifier. * + +* + public @@ -7476,6 +7728,9 @@ BehavioredClassifier. * + +* + public @@ -7548,6 +7803,9 @@ BehavioredClassifier. * + +* + public @@ -7707,6 +7965,9 @@ BehavioredClassifier. * + +* + public @@ -7822,6 +8083,9 @@ BehavioredClassifier. 0 + +0 + defaultValue @@ -7834,6 +8098,9 @@ BehavioredClassifier. 1 + +1 + public @@ -7937,6 +8204,9 @@ BehavioredClassifier. * + +* + public @@ -7965,6 +8235,9 @@ BehavioredClassifier. 1 + +1 + public @@ -8732,6 +9005,9 @@ BehavioredClassifier. 1 + +1 + @@ -8791,6 +9067,9 @@ BehavioredClassifier. * + +* + public @@ -8980,6 +9259,9 @@ BehavioredClassifier. * + +* + public @@ -9674,6 +9956,9 @@ BehavioredClassifier. 1 + +1 + @@ -9774,6 +10059,9 @@ BehavioredClassifier. 0 + +0 + importingNamespace @@ -9786,6 +10074,9 @@ BehavioredClassifier. 1 + +1 + public @@ -9926,6 +10217,9 @@ BehavioredClassifier. * + +* + public @@ -9957,6 +10251,9 @@ BehavioredClassifier. * + +* + public @@ -10020,6 +10317,9 @@ BehavioredClassifier. 0 + +0 + class_ @@ -10032,6 +10332,9 @@ BehavioredClassifier. 1 + +1 + public @@ -10055,6 +10358,9 @@ BehavioredClassifier. * + +* + @@ -10083,6 +10389,9 @@ BehavioredClassifier. * + +* + @@ -10778,6 +11087,9 @@ BehavioredClassifier. 1 + +1 + public @@ -10880,6 +11192,9 @@ BehavioredClassifier. 0 + +0 + name @@ -10889,6 +11204,9 @@ BehavioredClassifier. 1 + +1 + public @@ -10917,6 +11235,9 @@ BehavioredClassifier. * + +* + public @@ -11012,6 +11333,9 @@ BehavioredClassifier. 0 + +0 + association @@ -11024,6 +11348,9 @@ BehavioredClassifier. 1 + +1 + public @@ -11188,6 +11515,9 @@ BehavioredClassifier. * + +* + public @@ -11290,6 +11620,9 @@ BehavioredClassifier. * + +* + public @@ -11318,6 +11651,9 @@ BehavioredClassifier. 1 + +1 + @@ -11346,6 +11682,9 @@ BehavioredClassifier. * + +* + public @@ -11377,6 +11716,9 @@ BehavioredClassifier. * + +* + public @@ -11465,6 +11807,9 @@ BehavioredClassifier. * + +* + @@ -11573,6 +11918,9 @@ BehavioredClassifier. 0 + +0 + context_ @@ -11585,6 +11933,9 @@ BehavioredClassifier. 1 + +1 + @@ -11636,6 +11987,9 @@ BehavioredClassifier. * + +* + public @@ -11664,6 +12018,9 @@ BehavioredClassifier. * + +* + public @@ -11732,6 +12089,9 @@ BehavioredClassifier. * + +* + public @@ -11751,6 +12111,9 @@ BehavioredClassifier. 0 + +0 + owningAssociation @@ -11763,6 +12126,9 @@ BehavioredClassifier. 1 + +1 + public @@ -12223,6 +12589,9 @@ BehavioredClassifier. * + +* + public @@ -12254,6 +12623,9 @@ BehavioredClassifier. * + +* + public @@ -12270,6 +12642,9 @@ BehavioredClassifier. 0 + +0 + bodyContext @@ -12285,6 +12660,9 @@ BehavioredClassifier. 1 + +1 + public @@ -12382,6 +12760,9 @@ BehavioredClassifier. 0 + +0 + language @@ -12391,6 +12772,9 @@ BehavioredClassifier. 1 + +1 + @@ -12440,6 +12824,9 @@ BehavioredClassifier. 0 + +0 + class_ @@ -12452,6 +12839,9 @@ BehavioredClassifier. 1 + +1 + public @@ -12494,6 +12884,9 @@ BehavioredClassifier. 0 + +0 + enumeration @@ -12506,6 +12899,9 @@ BehavioredClassifier. 1 + +1 + public @@ -12534,6 +12930,9 @@ BehavioredClassifier. * + +* + public @@ -12640,6 +13039,9 @@ BehavioredClassifier. * + +* + public @@ -12659,6 +13061,9 @@ BehavioredClassifier. 0 + +0 + package @@ -12671,6 +13076,9 @@ BehavioredClassifier. 1 + +1 + public @@ -12732,6 +13140,9 @@ BehavioredClassifier. 1 + +1 + public @@ -12801,6 +13212,9 @@ BehavioredClassifier. * + +* + public @@ -12863,6 +13277,9 @@ BehavioredClassifier. * + +* + public @@ -12997,6 +13414,9 @@ BehavioredClassifier. 1 + +1 + public @@ -13028,6 +13448,9 @@ BehavioredClassifier. * + +* + public @@ -13069,6 +13492,9 @@ BehavioredClassifier. * + +* + @@ -13111,6 +13537,9 @@ BehavioredClassifier. * + +* + public @@ -13247,6 +13676,9 @@ BehavioredClassifier. 1 + +1 + public @@ -13266,6 +13698,9 @@ BehavioredClassifier. 0 + +0 + activity @@ -13278,6 +13713,9 @@ BehavioredClassifier. 1 + +1 + @@ -13306,6 +13744,9 @@ BehavioredClassifier. * + +* + public @@ -13325,6 +13766,9 @@ BehavioredClassifier. 0 + +0 + @@ -13340,6 +13784,9 @@ BehavioredClassifier. 1 + +1 + @@ -13363,6 +13810,9 @@ BehavioredClassifier. * + +* + public @@ -13436,6 +13886,9 @@ BehavioredClassifier. 0 + +0 + classifier @@ -13448,6 +13901,9 @@ BehavioredClassifier. 1 + +1 + @@ -13467,6 +13923,9 @@ BehavioredClassifier. 2 + +2 + memberEnd @@ -13479,6 +13938,9 @@ BehavioredClassifier. * + +* + public @@ -14031,6 +14493,9 @@ BehavioredClassifier. 0 + +0 + superGroup @@ -14043,6 +14508,9 @@ BehavioredClassifier. 1 + +1 + @@ -14084,6 +14552,9 @@ BehavioredClassifier. 1 + +1 + @@ -14118,6 +14589,9 @@ BehavioredClassifier. 0 + +0 + class_ @@ -14133,6 +14607,9 @@ BehavioredClassifier. 1 + +1 + public @@ -14250,6 +14727,9 @@ BehavioredClassifier. 0 + +0 + activity @@ -14262,6 +14742,9 @@ BehavioredClassifier. 1 + +1 + @@ -14849,6 +15332,9 @@ BehavioredClassifier. * + +* + @@ -14894,6 +15380,9 @@ BehavioredClassifier. * + +* + public @@ -14913,6 +15402,9 @@ BehavioredClassifier. 0 + +0 + default @@ -14922,6 +15414,9 @@ BehavioredClassifier. 1 + +1 + @@ -15079,6 +15574,9 @@ BehavioredClassifier. * + +* + public @@ -15128,6 +15626,9 @@ BehavioredClassifier. * + +* + public @@ -15159,6 +15660,9 @@ BehavioredClassifier. * + +* + public @@ -15178,6 +15682,9 @@ BehavioredClassifier. 0 + +0 + operation @@ -15190,6 +15697,9 @@ BehavioredClassifier. 1 + +1 + public @@ -15216,6 +15726,9 @@ BehavioredClassifier. 1 + +1 + public @@ -15303,6 +15816,9 @@ BehavioredClassifier. 0 + +0 + subject @@ -15315,6 +15831,9 @@ BehavioredClassifier. 1 + +1 + public @@ -15407,6 +15926,9 @@ BehavioredClassifier. * + +* + public @@ -15442,6 +15964,9 @@ BehavioredClassifier. 0 + +0 + bodyCondition @@ -15454,6 +15979,9 @@ BehavioredClassifier. 1 + +1 + public @@ -15560,6 +16088,9 @@ BehavioredClassifier. * + +* + public @@ -16037,6 +16568,9 @@ BehavioredClassifier. 0 + +0 + alias @@ -16046,6 +16580,9 @@ BehavioredClassifier. 1 + +1 + @@ -16100,6 +16637,9 @@ BehavioredClassifier. 1 + +1 + public @@ -16168,6 +16708,9 @@ BehavioredClassifier. * + +* + public @@ -16631,6 +17174,9 @@ BehavioredClassifier. 0 + +0 + upper @@ -16640,6 +17186,9 @@ BehavioredClassifier. 1 + +1 + @@ -17296,6 +17845,9 @@ BehavioredClassifier. 0 + +0 + importingNamespace @@ -17308,6 +17860,9 @@ BehavioredClassifier. 1 + +1 + public @@ -17432,6 +17987,9 @@ BehavioredClassifier. 0 + +0 + datatype @@ -17444,6 +18002,9 @@ BehavioredClassifier. 1 + +1 + public @@ -17497,6 +18058,9 @@ BehavioredClassifier. 0 + +0 + value @@ -17509,6 +18073,9 @@ BehavioredClassifier. 1 + +1 + @@ -17548,6 +18115,9 @@ BehavioredClassifier. 0 + +0 + package @@ -17560,6 +18130,9 @@ BehavioredClassifier. 1 + +1 + public @@ -17627,6 +18200,9 @@ BehavioredClassifier. 1 + +1 + public @@ -17706,6 +18282,9 @@ BehavioredClassifier. 0 + +0 + lower @@ -17715,6 +18294,9 @@ BehavioredClassifier. 1 + +1 + @@ -17898,6 +18480,9 @@ BehavioredClassifier. 0 + +0 + constraint @@ -17910,6 +18495,9 @@ BehavioredClassifier. 1 + +1 + public @@ -18071,6 +18659,9 @@ BehavioredClassifier. * + +* + public @@ -18511,6 +19102,9 @@ BehavioredClassifier. 1 + +1 + @@ -18539,6 +19133,9 @@ BehavioredClassifier. * + +* + @@ -18574,6 +19171,9 @@ BehavioredClassifier. 1 + +1 + @@ -18644,6 +19244,9 @@ BehavioredClassifier. * + +* + @@ -18681,6 +19284,9 @@ BehavioredClassifier. 0 + +0 + datatype @@ -18693,6 +19299,9 @@ BehavioredClassifier. 1 + +1 + public @@ -18737,6 +19346,9 @@ BehavioredClassifier. * + +* + public @@ -18765,6 +19377,9 @@ BehavioredClassifier. * + +* + @@ -18824,6 +19439,9 @@ BehavioredClassifier. 1 + +1 + public @@ -18930,6 +19548,9 @@ BehavioredClassifier. * + +* + public @@ -18949,6 +19570,9 @@ BehavioredClassifier. 1 + +1 + supplier @@ -18961,6 +19585,9 @@ BehavioredClassifier. * + +* + public @@ -18984,6 +19611,9 @@ BehavioredClassifier. * + +* + @@ -19519,6 +20149,9 @@ BehavioredClassifier. 1 + +1 + public @@ -20109,6 +20742,9 @@ BehavioredClassifier. * + +* + public @@ -20204,6 +20840,9 @@ BehavioredClassifier. 0 + +0 + opposite @@ -20216,6 +20855,9 @@ BehavioredClassifier. 1 + +1 + public @@ -20552,6 +21194,9 @@ specially for Gaphor 0 + +0 + upper @@ -20561,6 +21206,9 @@ specially for Gaphor 1 + +1 + @@ -20610,6 +21258,9 @@ specially for Gaphor * + +* + public @@ -20651,6 +21302,9 @@ specially for Gaphor * + +* + @@ -20682,6 +21336,9 @@ specially for Gaphor * + +* + @@ -20701,6 +21358,9 @@ specially for Gaphor 0 + +0 + typeValue @@ -20713,6 +21373,9 @@ specially for Gaphor 1 + +1 + public @@ -22044,6 +22707,9 @@ specially for Gaphor * + +* + public @@ -22177,6 +22843,9 @@ specially for Gaphor * + +* + @@ -22284,6 +22953,9 @@ specially for Gaphor 1 + +1 + featuringClassifier @@ -22296,6 +22968,9 @@ specially for Gaphor * + +* + public @@ -22340,6 +23015,9 @@ specially for Gaphor * + +* + public @@ -22381,6 +23059,9 @@ specially for Gaphor * + +* + @@ -22877,7 +23558,7 @@ specially for Gaphor 220.33333333333337 -110.33333333333334 +117.0 1 @@ -22966,7 +23647,7 @@ specially for Gaphor -(1.0, 0.0, 0.0, 1.0, 376.0, 311.0) +(1.0, 0.0, 0.0, 1.0, 376.0, 317.66666666666663) 0 @@ -22975,7 +23656,7 @@ specially for Gaphor 1 -[(0.0, 0.0), (-102.78593272171258, 108.66666666666663)] +[(0.0, 0.0), (-102.78593272171258, 102.0)] @@ -23012,7 +23693,7 @@ specially for Gaphor -(1.0, 0.0, 0.0, 1.0, 519.0, 311.0) +(1.0, 0.0, 0.0, 1.0, 519.0, 317.66666666666663) 0 @@ -23021,7 +23702,7 @@ specially for Gaphor 1 -[(0.0, 0.0), (122.6666666666664, 110.33333333333337)] +[(0.0, 0.0), (122.6666666666664, 103.66666666666674)] @@ -23067,7 +23748,7 @@ specially for Gaphor -(1.0, 0.0, 0.0, 1.0, 561.0, 259.87913533368067) +(1.0, 0.0, 0.0, 1.0, 561.0, 263.45692800238237) 1 @@ -23076,7 +23757,7 @@ specially for Gaphor 1 -[(0.0, 0.0), (197.66666666666674, 0.0), (197.66666666666674, 184.9159716999162), (152.66666666666652, 184.9159716999162)] +[(0.0, 0.0), (197.66666666666674, 0.0), (197.66666666666674, 181.3381790312145), (152.66666666666652, 181.3381790312145)] @@ -23099,7 +23780,7 @@ specially for Gaphor -(1.0, 0.0, 0.0, 1.0, 561.0, 210.24515424515423) +(1.0, 0.0, 0.0, 1.0, 561.0, 210.8239148239148) 1 @@ -23108,7 +23789,7 @@ specially for Gaphor 1 -[(0.0, 0.0), (271.6666666666665, 0.0), (271.6666666666665, 284.82518214615004), (152.66666666666652, 284.82518214615004)] +[(0.0, 0.0), (271.6666666666665, 0.0), (271.6666666666665, 284.24642156738946), (152.66666666666652, 284.24642156738946)] @@ -23466,6 +24147,9 @@ specially for Gaphor 0 + +0 + body @@ -23475,6 +24159,9 @@ specially for Gaphor 1 + +1 + @@ -23598,6 +24285,9 @@ specially for Gaphor * + +* + public @@ -23626,6 +24316,9 @@ specially for Gaphor * + +* + @@ -23705,6 +24398,9 @@ specially for Gaphor 0 + +0 + qualifiedName @@ -23714,6 +24410,9 @@ specially for Gaphor 1 + +1 + public @@ -23745,6 +24444,9 @@ specially for Gaphor * + +* + public @@ -23795,6 +24497,9 @@ specially for Gaphor 0 + +0 + interface_ @@ -23807,6 +24512,9 @@ specially for Gaphor 1 + +1 + @@ -23815,6 +24523,9 @@ specially for Gaphor 0 + +0 + specification @@ -23830,6 +24541,9 @@ specially for Gaphor 1 + +1 + @@ -23873,6 +24587,9 @@ specially for Gaphor * + +* + @@ -23921,6 +24638,9 @@ specially for Gaphor 1 + +1 + @@ -23992,6 +24712,9 @@ specially for Gaphor 0 + +0 + type @@ -24004,6 +24727,9 @@ specially for Gaphor 1 + +1 + public @@ -24036,6 +24762,7 @@ specially for Gaphor + @@ -24091,6 +24818,9 @@ specially for Gaphor * + +* + public @@ -24774,6 +25504,9 @@ specially for Gaphor 0 + +0 + package @@ -24786,6 +25519,9 @@ specially for Gaphor 1 + +1 + public @@ -25454,6 +26190,9 @@ specially for Gaphor 0 + +0 + namespace @@ -25466,6 +26205,9 @@ specially for Gaphor 1 + +1 + public @@ -25553,6 +26295,9 @@ specially for Gaphor 1 + +1 + source @@ -25565,6 +26310,9 @@ specially for Gaphor * + +* + public @@ -25587,6 +26335,9 @@ specially for Gaphor 1 + +1 + relatedElement @@ -25599,6 +26350,9 @@ specially for Gaphor * + +* + public @@ -25618,6 +26372,9 @@ specially for Gaphor 0 + +0 + type @@ -25630,6 +26387,9 @@ specially for Gaphor 1 + +1 + public @@ -25661,6 +26421,9 @@ specially for Gaphor * + +* + @@ -26320,6 +27083,9 @@ specially for Gaphor * + +* + @@ -26344,6 +27110,9 @@ specially for Gaphor 0 + +0 + context @@ -26356,6 +27125,9 @@ specially for Gaphor 1 + +1 + @@ -26430,6 +27202,9 @@ specially for Gaphor * + +* + @@ -26446,6 +27221,9 @@ specially for Gaphor 0 + +0 + ownerFormalParam @@ -26458,6 +27236,9 @@ specially for Gaphor 1 + +1 + @@ -26531,6 +27312,9 @@ specially for Gaphor 1 + +1 + client @@ -26543,6 +27327,9 @@ specially for Gaphor * + +* + public @@ -26598,6 +27385,9 @@ specially for Gaphor * + +* + @@ -26646,6 +27436,9 @@ specially for Gaphor * + +* + @@ -27065,6 +27858,9 @@ some elements to have a stereotype * + +* + @@ -27084,6 +27880,9 @@ some elements to have a stereotype 0 + +0 + useCase @@ -27096,6 +27895,9 @@ some elements to have a stereotype 1 + +1 + @@ -27131,6 +27933,9 @@ some elements to have a stereotype 0 + +0 + actor @@ -27143,6 +27948,9 @@ some elements to have a stereotype 1 + +1 + @@ -27171,6 +27979,9 @@ some elements to have a stereotype * + +* + @@ -28327,13 +29138,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 134.0, 355.0) +(1.0, 0.0, 0.0, 1.0, 21.0, 356.0) -117.0 +226.5 -59.0 +57.0 1 @@ -28362,7 +29173,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (100.00000000000003, 119.0)] +[(0.0, 0.0), (1.9743589743590277, 120.0)] @@ -28385,7 +29196,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-158.0, 129.0)] +[(0.0, 0.0), (-140.5, 130.0)] @@ -28422,7 +29233,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 192.0, 355.0) +(1.0, 0.0, 0.0, 1.0, 206.5, 356.0) 1 @@ -28431,7 +29242,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (0.0, -161.0), (163.0, -161.0)] +[(0.0, 0.0), (0.0, -162.0), (148.5, -162.0)] @@ -28451,7 +29262,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 273.0, 355.0) +(1.0, 0.0, 0.0, 1.0, 505.0, 354.0) 192.0 @@ -28477,7 +29288,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 408.0, 226.0) +(1.0, 0.0, 0.0, 1.0, 482.5, 226.0) 0 @@ -28486,7 +29297,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-60.0, 129.0)] +[(0.0, 0.0), (97.5, 128.0)] @@ -28520,7 +29331,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 737.4353870738637, 469.00787353515625) +(1.0, 0.0, 0.0, 1.0, 738.4353870738637, 543.0078735351562) 112.0 @@ -28555,7 +29366,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-1.4373401988636942, 44.00787353515625)] +[(0.0, 0.0), (-0.43734019886369424, 118.00787353515625)] @@ -28598,7 +29409,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 477.5657142857143, 356.0) +(1.0, 0.0, 0.0, 1.0, 266.0, 356.0) 203.0 @@ -28633,7 +29444,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (92.3810436270316, 130.0)] +[(0.0, 0.0), (-119.1846706586827, 130.0)] @@ -28755,6 +29566,11 @@ some elements to have a stereotype + + + + + @@ -28764,6 +29580,9 @@ some elements to have a stereotype 0 + +0 + enclosingInteraction @@ -28776,6 +29595,9 @@ some elements to have a stereotype 1 + +1 + @@ -28801,6 +29623,9 @@ some elements to have a stereotype * + +* + @@ -28874,11 +29699,6 @@ some elements to have a stereotype - - - - - @@ -28889,18 +29709,38 @@ some elements to have a stereotype + + + + + - - - + + + + +0 + + +0 + + +stateInvariant + + +1 + + +1 + @@ -28929,6 +29769,9 @@ some elements to have a stereotype 1 + +1 + @@ -29292,6 +30135,9 @@ some elements to have a stereotype * + +* + @@ -29317,6 +30163,9 @@ some elements to have a stereotype 1 + +1 + @@ -29358,6 +30207,9 @@ some elements to have a stereotype 1 + +1 + @@ -29386,6 +30238,9 @@ some elements to have a stereotype * + +* + @@ -29453,6 +30308,9 @@ some elements to have a stereotype 0 + +0 + discriminator @@ -29465,6 +30323,9 @@ some elements to have a stereotype 1 + +1 + @@ -29497,7 +30358,7 @@ some elements to have a stereotype 180.0 -60.0 +71.0 0 @@ -29540,7 +30401,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 380.69375548070826, 110.00393676757812) +(1.0, 0.0, 0.0, 1.0, 380.69375548070826, 121.00393676757812) 0 @@ -29549,7 +30410,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-0.4118171547170846, 162.99606323242188)] +[(0.0, 0.0), (-0.4118171547170846, 151.99606323242188)] @@ -29716,7 +30577,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1157.0, 147.0) +(1.0, 0.0, 0.0, 1.0, 1222.5, 142.99612426757812) 191.0 @@ -29858,7 +30719,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1117.0, 290.0) +(1.0, 0.0, 0.0, 1.0, 1182.6148936170212, 287.5) 203.0 @@ -29907,7 +30768,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1204.8049029622064, 197.0) +(1.0, 0.0, 0.0, 1.0, 1291.578125, 192.99612426757812) 0 @@ -29916,7 +30777,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (19.165156918033745, 93.0)] +[(0.0, 0.0), (-1.9931715027387327, 94.50387573242188)] @@ -29930,7 +30791,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 485.9998779296875, 96.05044839548512) +(1.0, 0.0, 0.0, 1.0, 485.9998779296875, 104.4923088606014) 1 @@ -29939,7 +30800,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (394.99945068359375, 0.0), (394.99945068359375, 185.94955160451474)] +[(0.0, 0.0), (394.99945068359375, 0.0), (394.99945068359375, 177.50769113939845)] @@ -29950,13 +30811,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1482.0, 294.0) +(1.0, 0.0, 0.0, 1.0, 1549.078125, 282.0) -162.0 +160.421875 -56.0 +64.0 1 @@ -29976,7 +30837,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 485.9998779296875, 54.88765769781068) +(1.0, 0.0, 0.0, 1.0, 485.9998779296875, 55.783006535019986) 1 @@ -29985,7 +30846,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (1099.000122070313, 0.0), (1099.000122070313, 239.11234230218932)] +[(0.0, 0.0), (1165.074871298708, 0.0), (1165.074871298708, 226.21699346498002)] @@ -29999,7 +30860,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1348.0, 165.0) +(1.0, 0.0, 0.0, 1.0, 1413.5, 160.99612426757812) 1 @@ -30008,7 +30869,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (147.0, 0.0), (147.0, 129.0)] +[(0.0, 0.0), (148.45148533950623, 0.0), (148.45148533950623, 121.00387573242188)] @@ -30031,7 +30892,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1320.0, 299.0) +(1.0, 0.0, 0.0, 1.0, 1385.6148936170212, 296.5) 0 @@ -30040,7 +30901,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (162.0, 2.0)] +[(0.0, 0.0), (163.46323138297885, -0.5)] @@ -30063,7 +30924,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1320.0, 340.0) +(1.0, 0.0, 0.0, 1.0, 1385.6148936170212, 337.5) 0 @@ -30072,7 +30933,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (162.0, 0.0)] +[(0.0, 0.0), (163.46323138297885, -1.5)] @@ -30092,7 +30953,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1107.1148936170212, 456.0) +(1.0, 0.0, 0.0, 1.0, 1176.6148936170212, 456.0) 192.0 @@ -30118,7 +30979,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1136.4491017964071, 346.0) +(1.0, 0.0, 0.0, 1.0, 1202.0639954134283, 343.5) 0 @@ -30127,7 +30988,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-4.449101796407149, 110.0)] +[(0.0, 0.0), (-0.5639954134283016, 112.5)] @@ -30150,7 +31011,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1240.9880239520958, 346.0) +(1.0, 0.0, 0.0, 1.0, 1306.602917569117, 343.5) 0 @@ -30159,7 +31020,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-22.988023952095773, 110.0)] +[(0.0, 0.0), (0.47520743088307427, 112.5)] @@ -30179,7 +31040,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1113.1148936170212, 595.0) +(1.0, 0.0, 0.0, 1.0, 1176.6148936170212, 613.0) 139.0 @@ -30205,7 +31066,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1182.5514619163707, 595.0) +(1.0, 0.0, 0.0, 1.0, 1246.0514619163707, 613.0) 0 @@ -30214,7 +31075,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-5.551461916370954, -83.0)] +[(0.0, 0.0), (0.448538083629046, -101.0)] @@ -30283,7 +31144,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1117.0, 306.0) +(1.0, 0.0, 0.0, 1.0, 1182.6148936170212, 303.5) 1 @@ -30292,7 +31153,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-48.55949491613046, 0.0), (-48.55949491613046, 155.0)] +[(0.0, 0.0), (-114.17438853315161, 0.0), (-114.17438853315161, 157.5)] @@ -30458,6 +31319,9 @@ some elements to have a stereotype 0 + +0 + argument @@ -30470,6 +31334,9 @@ some elements to have a stereotype 1 + +1 + @@ -30781,6 +31648,9 @@ some elements to have a stereotype 0 + +0 + signature @@ -30793,6 +31663,9 @@ some elements to have a stereotype 1 + +1 + @@ -30858,6 +31731,9 @@ some elements to have a stereotype 0 + +0 + sendMessage @@ -30870,6 +31746,9 @@ some elements to have a stereotype 1 + +1 + @@ -30884,6 +31763,9 @@ some elements to have a stereotype 0 + +0 + sendEvent @@ -30896,6 +31778,9 @@ some elements to have a stereotype 1 + +1 + @@ -30923,6 +31808,9 @@ some elements to have a stereotype 0 + +0 + receiveMessage @@ -30935,6 +31823,9 @@ some elements to have a stereotype 1 + +1 + @@ -30949,6 +31840,9 @@ some elements to have a stereotype 0 + +0 + receiveEvent @@ -30961,6 +31855,9 @@ some elements to have a stereotype 1 + +1 + @@ -31035,6 +31932,9 @@ some elements to have a stereotype 1 + +1 + @@ -31063,6 +31963,9 @@ some elements to have a stereotype * + +* + @@ -31190,6 +32093,9 @@ some elements to have a stereotype * + +* + @@ -31226,6 +32132,9 @@ some elements to have a stereotype 1 + +1 + @@ -31246,6 +32155,9 @@ some elements to have a stereotype * + +* + @@ -31282,6 +32194,9 @@ some elements to have a stereotype 1 + +1 + @@ -31302,6 +32217,9 @@ some elements to have a stereotype * + +* + @@ -31338,6 +32256,9 @@ some elements to have a stereotype 1 + +1 + @@ -31358,6 +32279,9 @@ some elements to have a stereotype * + +* + @@ -31394,6 +32318,9 @@ some elements to have a stereotype 1 + +1 + @@ -31414,6 +32341,9 @@ some elements to have a stereotype * + +* + @@ -31455,6 +32385,9 @@ some elements to have a stereotype * + +* + @@ -32065,6 +32998,9 @@ some elements to have a stereotype * + +* + @@ -32093,6 +33029,9 @@ some elements to have a stereotype * + +* + @@ -32142,6 +33081,9 @@ some elements to have a stereotype * + +* + @@ -32155,6 +33097,9 @@ some elements to have a stereotype 0 + +0 + @@ -32167,6 +33112,9 @@ some elements to have a stereotype 1 + +1 + @@ -32213,6 +33161,9 @@ some elements to have a stereotype * + +* + @@ -32235,6 +33186,9 @@ some elements to have a stereotype * + +* + @@ -32760,6 +33714,9 @@ some elements to have a stereotype * + +* + @@ -32776,6 +33733,9 @@ some elements to have a stereotype 0 + +0 + type @@ -32788,6 +33748,9 @@ some elements to have a stereotype 1 + +1 + @@ -32870,6 +33833,9 @@ some elements to have a stereotype 2 + +2 + end @@ -32882,6 +33848,9 @@ some elements to have a stereotype * + +* + @@ -32904,6 +33873,9 @@ some elements to have a stereotype 1 + +1 + @@ -32963,6 +33935,9 @@ some elements to have a stereotype * + +* + @@ -32988,6 +33963,9 @@ some elements to have a stereotype * + +* + @@ -33060,6 +34038,9 @@ some elements to have a stereotype 0 + +0 + role @@ -33072,6 +34053,9 @@ some elements to have a stereotype 1 + +1 + @@ -33097,6 +34081,9 @@ some elements to have a stereotype * + +* + @@ -33153,6 +34140,9 @@ some elements to have a stereotype * + +* + @@ -33172,6 +34162,9 @@ some elements to have a stereotype 0 + +0 + definingEnd @@ -33184,6 +34177,9 @@ some elements to have a stereotype 1 + +1 + @@ -33231,6 +34227,9 @@ some elements to have a stereotype 0 + +0 + @@ -33243,6 +34242,9 @@ some elements to have a stereotype 1 + +1 + @@ -33271,6 +34273,9 @@ some elements to have a stereotype * + +* + @@ -33305,6 +34310,9 @@ some elements to have a stereotype 0 + +0 + @@ -33317,6 +34325,9 @@ some elements to have a stereotype 1 + +1 + @@ -33345,6 +34356,9 @@ some elements to have a stereotype * + +* + @@ -33871,6 +34885,9 @@ some elements to have a stereotype 0 + +0 + @@ -33883,6 +34900,9 @@ some elements to have a stereotype 1 + +1 + @@ -33911,6 +34931,9 @@ some elements to have a stereotype 1 + +1 + @@ -33954,6 +34977,9 @@ some elements to have a stereotype * + +* + @@ -33970,6 +34996,9 @@ some elements to have a stereotype 0 + +0 + selection @@ -33982,6 +35011,9 @@ some elements to have a stereotype 1 + +1 + @@ -34147,6 +35179,9 @@ some elements to have a stereotype 1 + +1 + @@ -35639,6 +36674,9 @@ some elements to have a stereotype 1 + +1 + region @@ -35651,6 +36689,9 @@ some elements to have a stereotype * + +* + @@ -35667,6 +36708,9 @@ some elements to have a stereotype 0 + +0 + stateMachine @@ -35679,6 +36723,9 @@ some elements to have a stereotype 1 + +1 + @@ -35783,6 +36830,9 @@ some elements to have a stereotype 1 + +1 + @@ -35888,6 +36938,9 @@ some elements to have a stereotype * + +* + @@ -35904,6 +36957,9 @@ some elements to have a stereotype 0 + +0 + container @@ -35916,6 +36972,9 @@ some elements to have a stereotype 1 + +1 + @@ -35965,6 +37024,9 @@ some elements to have a stereotype 1 + +1 + @@ -35985,6 +37047,9 @@ some elements to have a stereotype * + +* + @@ -36021,6 +37086,9 @@ some elements to have a stereotype 1 + +1 + @@ -36041,6 +37109,9 @@ some elements to have a stereotype * + +* + @@ -36157,6 +37228,9 @@ some elements to have a stereotype 0 + +0 + @@ -36169,6 +37243,9 @@ some elements to have a stereotype 1 + +1 + @@ -36189,6 +37266,9 @@ some elements to have a stereotype * + +* + @@ -36230,6 +37310,9 @@ some elements to have a stereotype * + +* + @@ -36238,6 +37321,9 @@ some elements to have a stereotype 0 + +0 + @@ -36250,6 +37336,9 @@ some elements to have a stereotype 1 + +1 + @@ -36287,6 +37376,9 @@ some elements to have a stereotype 0 + +0 + stateMachine @@ -36299,6 +37391,9 @@ some elements to have a stereotype 1 + +1 + @@ -36327,6 +37422,9 @@ some elements to have a stereotype * + +* + @@ -36437,6 +37535,9 @@ some elements to have a stereotype * + +* + @@ -36453,6 +37554,9 @@ some elements to have a stereotype 0 + +0 + state @@ -36465,6 +37569,9 @@ some elements to have a stereotype 1 + +1 + @@ -36502,6 +37609,9 @@ some elements to have a stereotype 0 + +0 + connectionPoint @@ -36517,6 +37627,9 @@ some elements to have a stereotype * + +* + @@ -36533,6 +37646,9 @@ some elements to have a stereotype 0 + +0 + state @@ -36545,6 +37661,9 @@ some elements to have a stereotype 1 + +1 + @@ -36594,6 +37713,9 @@ some elements to have a stereotype * + +* + @@ -36610,6 +37732,9 @@ some elements to have a stereotype 0 + +0 + state @@ -36622,6 +37747,9 @@ some elements to have a stereotype 1 + +1 + @@ -36738,6 +37866,9 @@ some elements to have a stereotype 0 + +0 + @@ -36750,6 +37881,9 @@ some elements to have a stereotype 1 + +1 + @@ -36769,6 +37903,9 @@ some elements to have a stereotype 0 + +0 + entry @@ -36781,6 +37918,9 @@ some elements to have a stereotype 1 + +1 + @@ -36810,6 +37950,9 @@ some elements to have a stereotype 0 + +0 + @@ -36822,6 +37965,9 @@ some elements to have a stereotype 1 + +1 + @@ -36841,6 +37987,9 @@ some elements to have a stereotype 0 + +0 + exit @@ -36853,6 +38002,9 @@ some elements to have a stereotype 1 + +1 + @@ -36882,6 +38034,9 @@ some elements to have a stereotype 0 + +0 + @@ -36894,6 +38049,9 @@ some elements to have a stereotype 1 + +1 + @@ -36913,6 +38071,9 @@ some elements to have a stereotype 0 + +0 + doActivity @@ -36925,6 +38086,9 @@ some elements to have a stereotype 1 + +1 + @@ -36965,6 +38129,9 @@ some elements to have a stereotype 0 + +0 + effect @@ -36977,6 +38144,9 @@ some elements to have a stereotype 1 + +1 + @@ -36985,6 +38155,9 @@ some elements to have a stereotype 0 + +0 + @@ -36997,6 +38170,9 @@ some elements to have a stereotype 1 + +1 + @@ -37032,6 +38208,9 @@ some elements to have a stereotype 0 + +0 + statevariant @@ -37044,6 +38223,9 @@ some elements to have a stereotype 1 + +1 + @@ -37055,6 +38237,9 @@ some elements to have a stereotype 0 + +0 + owningState @@ -37067,6 +38252,9 @@ some elements to have a stereotype 1 + +1 + @@ -37107,6 +38295,9 @@ some elements to have a stereotype 0 + +0 + guard @@ -37119,6 +38310,9 @@ some elements to have a stereotype 1 + +1 + @@ -37127,6 +38321,9 @@ some elements to have a stereotype 0 + +0 + @@ -37139,6 +38336,9 @@ some elements to have a stereotype 1 + +1 + @@ -37174,6 +38374,9 @@ some elements to have a stereotype 0 + +0 + submachine @@ -37186,6 +38389,9 @@ some elements to have a stereotype 1 + +1 + @@ -37206,6 +38412,9 @@ some elements to have a stereotype * + +* + @@ -37652,6 +38861,9 @@ some elements to have a stereotype 0 + +0 + @@ -37664,6 +38876,9 @@ some elements to have a stereotype 1 + +1 + @@ -37675,6 +38890,9 @@ some elements to have a stereotype 0 + +0 + extendedStateMachine @@ -37687,6 +38905,9 @@ some elements to have a stereotype 1 + +1 + @@ -37778,6 +38999,9 @@ some elements to have a stereotype * + +* + @@ -37795,6 +39019,9 @@ some elements to have a stereotype * + +* + @@ -37820,6 +39047,9 @@ some elements to have a stereotype * + +* + @@ -37849,6 +39079,9 @@ some elements to have a stereotype 0 + +0 + @@ -37861,6 +39094,9 @@ some elements to have a stereotype 1 + +1 + @@ -37912,6 +39148,9 @@ some elements to have a stereotype 0 + +0 + @@ -37924,6 +39163,9 @@ some elements to have a stereotype 1 + +1 + @@ -37973,6 +39215,9 @@ some elements to have a stereotype * + +* + @@ -37981,6 +39226,9 @@ some elements to have a stereotype 0 + +0 + @@ -37993,6 +39241,9 @@ some elements to have a stereotype 1 + +1 + @@ -38380,6 +39631,9 @@ some elements to have a stereotype 0 + +0 + @@ -38392,6 +39646,9 @@ some elements to have a stereotype * + +* + @@ -38403,6 +39660,9 @@ some elements to have a stereotype 0 + +0 + partWithPort @@ -38415,6 +39675,9 @@ some elements to have a stereotype 1 + +1 + @@ -38503,6 +39766,9 @@ some elements to have a stereotype 0 + +0 + @@ -38512,6 +39778,9 @@ some elements to have a stereotype 1 + +1 + @@ -38531,6 +39800,9 @@ some elements to have a stereotype 0 + +0 + ownedPort @@ -38543,6 +39815,9 @@ some elements to have a stereotype * + +* + @@ -38927,6 +40202,9 @@ some elements to have a stereotype * + +* + @@ -38935,6 +40213,9 @@ some elements to have a stereotype 0 + +0 + @@ -38947,6 +40228,9 @@ some elements to have a stereotype 1 + +1 + @@ -39143,7 +40427,7 @@ some elements to have a stereotype 134.0 -54.0 +71.0 0 @@ -39163,7 +40447,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 779.9142857142857, 96.0) +(1.0, 0.0, 0.0, 1.0, 779.9142857142857, 113.0) 0 @@ -39172,7 +40456,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (-1.0582251082252014, 48.0)] +[(0.0, 0.0), (-1.0582251082252014, 31.0)] @@ -39391,7 +40675,7 @@ some elements to have a stereotype 136.0 -54.0 +71.0 0 @@ -39411,7 +40695,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, -43.49751281738281, 144.0) +(1.0, 0.0, 0.0, 1.0, -43.49751281738281, 161.0) 0 @@ -39420,7 +40704,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (0.6259842519685037, 52.00701904296875)] +[(0.0, 0.0), (0.6259842519685037, 35.00701904296875)] @@ -39572,6 +40856,9 @@ some elements to have a stereotype 1 + +1 + @@ -39600,6 +40887,9 @@ some elements to have a stereotype * + +* + @@ -39638,6 +40928,9 @@ some elements to have a stereotype * + +* + @@ -39663,6 +40956,9 @@ some elements to have a stereotype * + +* + @@ -39898,7 +41194,7 @@ some elements to have a stereotype 134.0 -54.0 +71.0 0 @@ -39918,7 +41214,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 404.5913043478261, 176.0) +(1.0, 0.0, 0.0, 1.0, 404.5913043478261, 193.0) 0 @@ -39927,7 +41223,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (-15.389691444600317, 82.0)] +[(0.0, 0.0), (-15.389691444600317, 65.0)] @@ -40093,6 +41389,9 @@ some elements to have a stereotype * + +* + @@ -40118,6 +41417,9 @@ some elements to have a stereotype * + +* + @@ -40163,6 +41465,9 @@ some elements to have a stereotype 0 + +0 + represents @@ -40175,6 +41480,9 @@ some elements to have a stereotype 1 + +1 + @@ -40192,6 +41500,9 @@ some elements to have a stereotype * + +* + @@ -40238,6 +41549,9 @@ some elements to have a stereotype * + +* + @@ -40266,6 +41580,9 @@ some elements to have a stereotype * + +* + @@ -40307,6 +41624,9 @@ swimlanes 0 + +0 + @@ -40319,6 +41639,9 @@ swimlanes 1 + +1 + @@ -40344,6 +41667,9 @@ swimlanes * + +* + @@ -40618,6 +41944,8 @@ swimlanes + + @@ -45394,7 +46722,7 @@ swimlanes -ownedElement +ownedMember @@ -45422,7 +46750,7 @@ swimlanes -ownedMember +ownedElement @@ -48348,6 +49676,9 @@ to accomodate simpleAttribute * + +* + @@ -48418,6 +49749,9 @@ to accomodate simpleAttribute * + +* + @@ -48476,6 +49810,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -48531,6 +49868,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -48615,6 +49955,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -48664,6 +50007,9 @@ to accomodate simpleAttribute 0 + +0 + replyValue @@ -48676,6 +50022,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -48734,6 +50083,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -49420,6 +50772,9 @@ to accomodate simpleAttribute * + +* + @@ -49437,6 +50792,9 @@ to accomodate simpleAttribute * + +* + @@ -50103,7 +51461,7 @@ to accomodate simpleAttribute 134.0 -54.0 +71.0 0 @@ -50238,7 +51596,7 @@ to accomodate simpleAttribute -(1.0, 0.0, 0.0, 1.0, 387.9142857142857, 162.0) +(1.0, 0.0, 0.0, 1.0, 387.9142857142857, 179.0) 0 @@ -50247,7 +51605,7 @@ to accomodate simpleAttribute 0 -[(0.0, 0.0), (-14.914285714285711, 68.0)] +[(0.0, 0.0), (-14.914285714285711, 51.0)] @@ -50421,6 +51779,9 @@ to accomodate simpleAttribute 0 + +0 + @@ -50433,6 +51794,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -50461,6 +51825,9 @@ to accomodate simpleAttribute * + +* + @@ -50527,6 +51894,9 @@ to accomodate simpleAttribute * + +* + @@ -50547,6 +51917,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -50926,6 +52299,9 @@ to accomodate simpleAttribute 0 + +0 + owningSignal @@ -50941,6 +52317,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -50969,6 +52348,9 @@ to accomodate simpleAttribute * + +* + @@ -51001,6 +52383,9 @@ to accomodate simpleAttribute 0 + +0 + signal @@ -51013,6 +52398,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -51030,6 +52418,9 @@ to accomodate simpleAttribute * + +* + @@ -51312,6 +52703,9 @@ to accomodate simpleAttribute * + +* + @@ -51332,6 +52726,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -51370,6 +52767,9 @@ to accomodate simpleAttribute * + +* + @@ -51390,6 +52790,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -51428,6 +52831,9 @@ to accomodate simpleAttribute * + +* + @@ -51448,6 +52854,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -51486,6 +52895,9 @@ to accomodate simpleAttribute * + +* + @@ -51506,6 +52918,9 @@ to accomodate simpleAttribute 1 + +1 + @@ -51660,4 +53075,60 @@ to accomodate simpleAttribute + + + + + + + + + + + + + + + + + + + + + + + + + +namespace + + + + + + + + + + + + + + + + + + + + + + + + + + + +owner + + \ No newline at end of file diff --git a/gaphor/UML/uml2.py b/gaphor/UML/uml2.py index cd22efb1a..334cf9816 100644 --- a/gaphor/UML/uml2.py +++ b/gaphor/UML/uml2.py @@ -561,6 +561,7 @@ class ActivityGroup(Element): class Constraint(PackageableElement): constrainedElement: relation_many[Element] specification: attribute[str] + stateInvariant: relation_one[StateInvariant] owningState: relation_one[State] context: derivedunion[Namespace] @@ -1185,8 +1186,11 @@ InteractionFragment.enclosingInteraction = association( Interaction.fragment = association( "fragment", InteractionFragment, opposite="enclosingInteraction" ) +Constraint.stateInvariant = association( + "stateInvariant", StateInvariant, upper=1, opposite="invariant" +) StateInvariant.invariant = association( - "invariant", Constraint, lower=1, upper=1, composite=True + "invariant", Constraint, lower=1, upper=1, composite=True, opposite="stateInvariant" ) Lifeline.coveredBy = association("coveredBy", InteractionFragment, opposite="covered") InteractionFragment.covered = association( @@ -1463,6 +1467,7 @@ NamedElement.namespace = derivedunion( Parameter.ownerFormalParam, Property.useCase, Property.actor, + InteractionFragment.enclosingInteraction, Lifeline.interaction, Message.interaction, Region.stateMachine, @@ -1503,7 +1508,7 @@ Namespace.ownedMember = derivedunion( BehavioredClassifier.ownedBehavior, UseCase.ownedAttribute, Actor.ownedAttribute, - StateInvariant.invariant, + Interaction.fragment, Interaction.lifeline, Interaction.message, StateMachine.region, @@ -1664,6 +1669,7 @@ Element.owner = derivedunion( PackageImport.importingNamespace, PackageMerge.mergingPackage, NamedElement.namespace, + Constraint.stateInvariant, Pseudostate.state, ) Element.ownedElement = derivedunion( @@ -1687,7 +1693,7 @@ Element.ownedElement = derivedunion( Activity.edge, Activity.node, Action.output, - Interaction.fragment, + StateInvariant.invariant, InteractionFragment.generalOrdering, Connector.end, State.entry, From 65dc4dbed12d99e340989ec535b619359b14d20a Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 14:49:06 +0100 Subject: [PATCH 02/36] Let derived interface resemble association --- gaphor/UML/properties.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/gaphor/UML/properties.py b/gaphor/UML/properties.py index 693d04578..c0497edd6 100644 --- a/gaphor/UML/properties.py +++ b/gaphor/UML/properties.py @@ -381,7 +381,9 @@ class association(umlproperty): def _set_one(self, obj, value, from_opposite, do_notify) -> None: if not (isinstance(value, self.type) or (value is None)): - raise AttributeError(f"Value should be of type {self.type.__name__}") + raise AttributeError( + f"Value should be of type {self.type.__name__}, got a {type(value)} instead" + ) old = self._get(obj) @@ -841,7 +843,7 @@ class redefine(umlproperty): self.name = name self._name = "_" + name self.type = type - self.original = original + self.original: Union[association, derived] = original self.upper = original.upper self.lower = original.lower @@ -872,32 +874,20 @@ class redefine(umlproperty): def __str__(self) -> str: return f"" - def __get__(self, obj, class_=None): - # No longer needed - if not obj: - return self - return self.original.__get__(obj, class_) - - def __set__(self, obj, value: T) -> None: - # No longer needed - if not isinstance(value, self.type): - raise AttributeError(f"Value should be of type {self.type.__name__}") - self.original.__set__(obj, value) - - def __delete__(self, obj, value=None): - # No longer needed - self.original.__delete__(obj, value) - def _get(self, obj): return self.original._get(obj) - def _set(self, obj, value, from_opposite=False): + def _set(self, obj, value, from_opposite=False, do_notify=True): + if not (isinstance(value, self.type) or (self.upper == 1 and value is None)): + raise AttributeError( + f"Value should be of type {self.type.__name__}, got a {type(value)} instead" + ) assert isinstance(self.original, association) - return self.original._set(obj, value, from_opposite) + return self.original._set(obj, value, from_opposite, do_notify) - def _del(self, obj, value, from_opposite=False): + def _del(self, obj, value, from_opposite=False, do_notify=True): assert isinstance(self.original, association) - return self.original._del(obj, value, from_opposite) + return self.original._del(obj, value, from_opposite, do_notify) def propagate(self, event): if event.property is self.original and isinstance( From 675cc143f542875e26a81f21ef6eac48b9c09052 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 12:00:53 +0100 Subject: [PATCH 03/36] Upgrade Interactions model to UML 2.5 --- gaphor/UML/uml2.gaphor | 1160 ++++++++++++++++++++++++---------------- gaphor/UML/uml2.py | 19 +- 2 files changed, 710 insertions(+), 469 deletions(-) diff --git a/gaphor/UML/uml2.gaphor b/gaphor/UML/uml2.gaphor index 0701e66f3..c90fa8876 100755 --- a/gaphor/UML/uml2.gaphor +++ b/gaphor/UML/uml2.gaphor @@ -299,7 +299,6 @@ - @@ -7064,10 +7063,10 @@ BehavioredClassifier. - + @@ -11051,14 +11050,8 @@ BehavioredClassifier. - - - - - - @@ -11444,12 +11437,10 @@ BehavioredClassifier. - - @@ -23132,6 +23123,7 @@ specially for Gaphor + @@ -23144,6 +23136,7 @@ specially for Gaphor + @@ -28035,7 +28028,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 439.0, 161.0) +(1.0, 0.0, 0.0, 1.0, 437.0, 160.0) 132.0 @@ -28089,23 +28082,9 @@ some elements to have a stereotype - - -(1.0, 0.0, 0.0, 1.0, 446.0, 266.0) - - -155.0 - - -70.0 - - - - - -(1.0, 0.0, 0.0, 1.0, 39.0, 372.0) +(1.0, 0.0, 0.0, 1.0, 34.0, 372.0) 228.0 @@ -28119,7 +28098,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 448.0, 374.0) +(1.0, 0.0, 0.0, 1.0, 437.0, 372.0) 133.0 @@ -28145,6 +28124,20 @@ some elements to have a stereotype + + +(1.0, 0.0, 0.0, 1.0, 437.0, 265.0) + + +240.0 + + +70.0 + + + + + @@ -28742,182 +28735,9 @@ some elements to have a stereotype - - -Interactions - - - - - - - - - - - - - - - - - - - - - - - -interactions - - - - - - - -(1.0, 0.0, 0.0, 1.0, 185.0, 193.0) - - -195.0 - - -70.0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 202.0, 339.0) - - -143.0 - - -70.0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 188.0, 31.0) - - -179.0 - - -70.0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 263.99999999999994, 101.0) - - -0 - - -1 - - -[(0.0, 0.0), (5.684341886080802e-14, 92.0)] - - - - - - - - -1 - - - - -(1.0, 0.0, 0.0, 1.0, 15.0, 232.0) - - -173.0 - - -70.0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 266.0, 263.0) - - -0 - - -1 - - -[(0.0, 0.0), (0.0, 76.0)] - - - - - - - - -1 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 115.00000000000003, 302.0) - - -0 - - -1 - - -[(0.0, 0.0), (86.99999999999997, 73.00000000000006)] - - - - - - - - -1 - - - - - - - - - -BasicInteractions +Interactions (UML 2.5, Ch 17) @@ -28929,7 +28749,6 @@ some elements to have a stereotype - @@ -28960,6 +28779,10 @@ some elements to have a stereotype + + + + @@ -28971,97 +28794,17 @@ some elements to have a stereotype - + - - - - - - - - - - - - - - - - - -Fragments - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Interaction +Interactions @@ -29069,7 +28812,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, -10.0, 164.0) +(1.0, 0.0, 0.0, 1.0, 21.0, 97.0) 140.0 @@ -29092,13 +28835,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 365.0, 35.0) +(1.0, 0.0, 0.0, 1.0, 385.75, 30.5) -166.0 +166.5 -98.0 +66.5 0 @@ -29115,13 +28858,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 355.0, 167.0) +(1.0, 0.0, 0.0, 1.0, 355.0, 161.0) -191.0 +246.0 -59.0 +65.0 1 @@ -29164,7 +28907,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 50.0, 236.0) +(1.0, 0.0, 0.0, 1.0, 55.5, 169.0) 0 @@ -29173,7 +28916,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (1.9743589743590277, 120.0)] +[(0.0, 0.0), (-3.5256410256409723, 187.0)] @@ -29187,16 +28930,16 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 375.0, 226.0) +(1.0, 0.0, 0.0, 1.0, 380.7591623036649, 226.0) -0 +1 -1 +0 -[(0.0, 0.0), (-140.5, 130.0)] +[(0.0, 0.0), (0.0, 55.0), (-142.25916230366488, 55.0), (-142.25916230366488, 130.0)] @@ -29210,7 +28953,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 433.8843537414968, 133.0) +(1.0, 0.0, 0.0, 1.0, 467.5, 97.0) 0 @@ -29219,7 +28962,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (0.11564625850326138, 34.0)] +[(0.0, 0.0), (0.0, 64.0)] @@ -29242,7 +28985,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (0.0, -162.0), (148.5, -162.0)] +[(0.0, 0.0), (0.0, -165.25423728813558), (148.5, -165.25423728813558)] @@ -29288,7 +29031,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 482.5, 226.0) +(1.0, 0.0, 0.0, 1.0, 532.0, 226.0) 0 @@ -29297,7 +29040,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (97.5, 128.0)] +[(0.0, 0.0), (-1.0, 128.0)] @@ -29331,13 +29074,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 738.4353870738637, 543.0078735351562) +(1.0, 0.0, 0.0, 1.0, 740.5, 544.5078735351562) -112.0 +108.4353870738637 -62.0 +59.0 0 @@ -29366,7 +29109,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-0.43734019886369424, 118.00787353515625)] +[(0.0, 0.0), (0.11346957552382264, 119.50787353515625)] @@ -29389,16 +29132,16 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 546.0, 226.0) +(1.0, 0.0, 0.0, 1.0, 578.0, 226.0) -0 +1 -1 +0 -[(0.0, 0.0), (238.4363636363637, 129.0)] +[(0.0, 0.0), (0.0, 55.0), (206.4363636363637, 55.0), (206.4363636363637, 129.0)] @@ -29435,7 +29178,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 454.99999999999994, 226.0) +(1.0, 0.0, 0.0, 1.0, 439.0, 226.0) 0 @@ -29444,7 +29187,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-119.1846706586827, 130.0)] +[(0.0, 0.0), (-1.5, 130.0)] @@ -29453,6 +29196,58 @@ some elements to have a stereotype + + +(1.0, 0.0, 0.0, 1.0, 129.0, 544.5078735351562) + + +118.5 + + +59.0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 188.0, 413.0) + + +0 + + +0 + + +[(0.0, 0.0), (-1.5, 131.50787353515625)] + + + + + + + + +0 + + + + + + + + @@ -29497,6 +29292,7 @@ some elements to have a stereotype + @@ -29681,14 +29477,16 @@ some elements to have a stereotype + - + + @@ -29780,6 +29578,7 @@ some elements to have a stereotype + @@ -29788,7 +29587,7 @@ some elements to have a stereotype -Lifeline +Lifelines @@ -29796,7 +29595,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 228.00177001953125, 167.99993896484375) +(1.0, 0.0, 0.0, 1.0, 218.50177001953125, 160.5) 117.0 @@ -29819,13 +29618,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 219.0, 333.0) +(1.0, 0.0, 0.0, 1.0, -8.5, 327.5) -174.22662353515625 +1006.5013427734375 -95.0 +91.0 1 @@ -29842,7 +29641,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 597.9971923828125, 355.99609375) +(1.0, 0.0, 0.0, 1.0, 619.5013427734375, 5.01171875) 191.0 @@ -29868,16 +29667,16 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 597.9971923828125, 379.99609374999994) +(1.0, 0.0, 0.0, 1.0, 810.5013427734375, 33.015625) -0 +1 1 -[(0.0, 0.0), (-204.77056884765625, -0.30117849576271283)] +[(0.0, 0.0), (329.6690905143786, 0.0), (329.6690905143786, 339.21318855932196), (187.5, 339.21318855932196)] @@ -29900,7 +29699,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 284.00177001953136, 223.99993896484375) +(1.0, 0.0, 0.0, 1.0, 274.50177001953136, 216.5) 0 @@ -29909,7 +29708,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (0.4921797035383406, 109.00006103515625)] +[(0.0, 0.0), (0.9995727539061363, 111.0)] @@ -29929,13 +29728,13 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, -4.0, 337.0) +(1.0, 0.0, 0.0, 1.0, -8.5, 160.5) -166.0 +175.0013427734375 -98.0 +56.0 0 @@ -29955,7 +29754,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 162.0, 376.1999999999999) +(1.0, 0.0, 0.0, 1.0, 71.00134277343751, 216.5) 0 @@ -29964,7 +29763,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (57.0, -1.3355932203388647)] +[(0.0, 0.0), (2.498657226562486, 111.0)] @@ -29973,64 +29772,9 @@ some elements to have a stereotype - - -(1.0, 0.0, 0.0, 1.0, 326.0013427734375, 503.99951171875) - - -121.0 - - -76.0 - - -0 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 373.51485225928093, 428.0) - - -0 - - -1 - - -[(0.0, 0.0), (-0.9750479473818814, 75.99951171875)] - - - - - - - - -0 - - - - - - - - -(1.0, 0.0, 0.0, 1.0, -84.9986572265625, 512.003173828125) +(1.0, 0.0, 0.0, 1.0, -8.5, 495.503173828125) 384.0 @@ -30044,7 +29788,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 239.9834984549641, 428.0) +(1.0, 0.0, 0.0, 1.0, 112.72096464059666, 418.5) 0 @@ -30053,7 +29797,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-237.76725596805954, 84.003173828125)] +[(0.0, 0.0), (-34.006064927129614, 77.003173828125)] @@ -30062,6 +29806,156 @@ some elements to have a stereotype + + +(1.0, 0.0, 0.0, 1.0, 487.0013427734375, 160.5) + + +191.0 + + +56.0 + + +0 + + +0 + + + + + + + +(1.0, 0.0, 0.0, 1.0, 854.0013427734375, 160.5) + + +116.0 + + +56.0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 688.1704332878161, 61.01171875) + + +1 + + +0 + + +[(0.0, 0.0), (0.0, 55.98828125), (-111.16909051437858, 55.98828125), (-111.16909051437858, 99.48828125)] + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 733.6704332878161, 61.01171875) + + +1 + + +0 + + +[(0.0, 0.0), (0.0, 55.98828125), (176.33090948562142, 55.98828125), (176.33090948562142, 99.48828125)] + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 912.6704332878161, 216.5) + + +0 + + +0 + + +[(0.0, 0.0), (-1.0, 111.0)] + + + + + + + + +0 + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 577.1704332878161, 216.5) + + +0 + + +0 + + +[(0.0, 0.0), (1.5, 111.0)] + + + + + + + + +0 + + + + + + + + @@ -30076,7 +29970,6 @@ some elements to have a stereotype - @@ -30255,78 +30148,6 @@ some elements to have a stereotype - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -composite - - - - - - - - - - - - - -0 - - -0 - - -discriminator - - - - - - - - -1 - - -1 - - @@ -31877,6 +31698,7 @@ some elements to have a stereotype + @@ -31886,6 +31708,7 @@ some elements to have a stereotype + @@ -31975,6 +31798,7 @@ some elements to have a stereotype + @@ -41946,6 +41770,10 @@ swimlanes + + + + @@ -42673,6 +42501,8 @@ swimlanes + + @@ -46827,11 +46657,6 @@ swimlanes - - - - - @@ -53131,4 +52956,407 @@ to accomodate simpleAttribute owner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + +0 + + +interaction + + + + + + + + +1 + + +1 + + + + +composite + + + + + + + + + + + + + +action + + + + + + + + +* + + +* + + + + + + + + + + + + + + + + + + + + + + + + + + + +owner + + + + + + + + + + + + + + + + + + + + + + + + + + + +ownedElement + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +stateInvariant + + + + + + + + + + + +* + + +* + + + + + + + + + + + + + + + +covered + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +events + + + + + + + + + + + +* + + +* + + + + + + + + + + + + + + + +covered + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + +InteractionFragment.covered + + + + + + + + + + + + + + + + + + + + + + + + + + + +coveredBy + + + + + + + + + + + + + + + + + + + + + + + + + + + +InteractionFragment.covered + + + + + + + + + + + + + + + + + + + + + + + + + + + +coveredBy + + \ No newline at end of file diff --git a/gaphor/UML/uml2.py b/gaphor/UML/uml2.py index 334cf9816..926a92538 100644 --- a/gaphor/UML/uml2.py +++ b/gaphor/UML/uml2.py @@ -529,6 +529,7 @@ class ValuePin(InputPin): class Action(ExecutableNode): effect: attribute[str] + interaction: relation_one[Interaction] output: relation_many[OutputPin] context_: relation_one[Classifier] input: relation_many[InputPin] @@ -576,6 +577,7 @@ class Interaction(Behavior, InteractionFragment): fragment: relation_many[InteractionFragment] lifeline: relation_many[Lifeline] message: relation_many[Message] + action: relation_many[Action] class ExecutionOccurence(InteractionFragment): @@ -586,12 +588,12 @@ class ExecutionOccurence(InteractionFragment): class StateInvariant(InteractionFragment): invariant: relation_one[Constraint] + covered: relation_one[Lifeline] # type: ignore[assignment] class Lifeline(NamedElement): coveredBy: relation_many[InteractionFragment] interaction: relation_one[Interaction] - discriminator: attribute[str] parse: Callable[[Lifeline, str], None] render: Callable[[Lifeline], str] @@ -616,6 +618,7 @@ class OccurrenceSpecification(InteractionFragment): toBefore: relation_many[GeneralOrdering] finishExec: relation_many[ExecutionOccurence] startExec: relation_many[ExecutionOccurence] + covered: relation_one[Lifeline] # type: ignore[assignment] class GeneralOrdering(NamedElement): @@ -1202,8 +1205,6 @@ Lifeline.interaction = association( Interaction.lifeline = association( "lifeline", Lifeline, composite=True, opposite="interaction" ) -# 'Lifeline.discriminator' is a simple attribute -Lifeline.discriminator = attribute("discriminator", str) # 'Message.argument' is a simple attribute Message.argument = attribute("argument", str) Message.signature = association("signature", NamedElement, upper=1) @@ -1351,6 +1352,10 @@ SendOperationEvent.operation = association("operation", Operation, lower=1, uppe SendSignalEvent.signal = association("signal", Signal, lower=1, upper=1) ReceiveOperationEvent.operation = association("operation", Operation, lower=1, upper=1) ReceiveSignalEvent.signal = association("signal", Signal, lower=1, upper=1) +Action.interaction = association("interaction", Interaction, upper=1, opposite="action") +Interaction.action = association( + "action", Action, composite=True, opposite="interaction" +) # 96: override NamedElement.qualifiedName(NamedElement.namespace): derived[List[str]] # defined in uml2overrides.py @@ -1671,6 +1676,7 @@ Element.owner = derivedunion( NamedElement.namespace, Constraint.stateInvariant, Pseudostate.state, + Action.interaction, ) Element.ownedElement = derivedunion( Element, @@ -1703,6 +1709,7 @@ Element.ownedElement = derivedunion( State.statevariant, Transition.guard, DeploymentTarget.deployment, + Interaction.action, ) ConnectorEnd.definingEnd = derivedunion(ConnectorEnd, "definingEnd", Property, 0, 1) # 164: override StructuredClassifier.part: property @@ -1780,6 +1787,12 @@ Transition.redefinedTransition = redefine( "*", RedefinableElement.redefinedElement, ) +StateInvariant.covered = redefine( + StateInvariant, "covered", Lifeline, 1, InteractionFragment.covered +) +OccurrenceSpecification.covered = redefine( + OccurrenceSpecification, "covered", Lifeline, 1, InteractionFragment.covered +) # 149: override Lifeline.parse: Callable[[Lifeline, str], None] # defined in uml2overrides.py From 02dce5c86417a9e2020b06f4338a2b0b0970bb51 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 13:33:15 +0100 Subject: [PATCH 04/36] Remove events from UML model --- gaphor/UML/uml2.gaphor | 1166 ---------------------------------------- gaphor/UML/uml2.py | 36 -- 2 files changed, 1202 deletions(-) diff --git a/gaphor/UML/uml2.gaphor b/gaphor/UML/uml2.gaphor index c90fa8876..e5263d851 100755 --- a/gaphor/UML/uml2.gaphor +++ b/gaphor/UML/uml2.gaphor @@ -20903,8 +20903,6 @@ BehavioredClassifier. - - @@ -28767,18 +28765,6 @@ some elements to have a stereotype - - - - - - - - - - - - @@ -28790,7 +28776,6 @@ some elements to have a stereotype - @@ -50649,627 +50634,6 @@ to accomodate simpleAttribute role - - -Events - - - - - - - -(1.0, 0.0, 0.0, 1.0, 530.0, 101.0) - - -147.0 - - -54.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 222.00021362304688, 247.0) - - -135.0 - - -50.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 381.000244140625, 247.0) - - -126.0 - - -50.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 542.0, 247.0) - - -128.0 - - -50.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 718.0, 247.0) - - -149.0 - - -50.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 218.0, 352.0) - - -175.0 - - -50.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 396.0, 352.0) - - -148.0 - - -51.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 544.0, 351.0) - - -196.0 - - -51.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 735.0, 351.0) - - -169.0 - - -51.0 - - -0 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 530.0, 121.0) - - -0 - - -0 - - -[(0.0, 0.0), (-237.0, 0.0), (-236.679031659972, 126.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 530.0, 121.0) - - -0 - - -0 - - -[(0.0, 0.0), (-81.0, 0.0), (-82.21975585937503, 126.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 609.9473684210526, 155.0) - - -0 - - -0 - - -[(0.0, 0.0), (-1.3873684210526562, 92.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 677.0, 122.0) - - -0 - - -0 - - -[(0.0, 0.0), (119.220458984375, 0.2376708984375), (119.9572649572649, 125.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 602.16, 297.0) - - -0 - - -0 - - -[(0.0, 0.0), (-2.936550292968718, 27.230682373046875), (-291.9321252441406, 26.234619140625), (-290.82666666666665, 55.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 602.16, 297.0) - - -0 - - -0 - - -[(0.0, 0.0), (-1.9365502929687182, 27.230682373046875), (-132.15999999999997, 27.230682373046875), (-131.50513274336276, 55.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 602.16, 297.0) - - -0 - - -0 - - -[(0.0, 0.0), (-1.4231578947368462, 54.0)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 602.16, 297.0) - - -0 - - -0 - - -[(0.0, 0.0), (-1.9365502929687182, 29.23455810546875), (221.06003173828128, 26.234619140625), (221.24, 54.0)] - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 235.5, 453.0) - - -100.0 - - -54.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 570.0, 453.0) - - -100.0 - - -54.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 381.000244140625, 456.99603271484375) - - -147.0 - - -54.0 - - -0 - - -0 - - -0 - - - - - - - -(1.0, 0.0, 0.0, 1.0, 725.000244140625, 456.99603271484375) - - -147.0 - - -54.0 - - -0 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 284.0, 402.0) - - -0 - - -0 - - -[(0.0, 0.0), (0.0, 51.0)] - - - - - - - - -0 - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 451.0, 403.0) - - -0 - - -0 - - -[(0.0, 0.0), (2.2107704564144797, 53.99603271484375)] - - - - - - - - -0 - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 618.0, 402.0) - - -0 - - -0 - - -[(0.0, 0.0), (0.0, 51.0)] - - - - - - - - -0 - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 800.0, 402.0) - - -0 - - -0 - - -[(0.0, 0.0), (-1.499755859375, 54.99603271484375)] - - - - - - - - -0 - - - - - - - - - - Triggers @@ -51545,7 +50909,6 @@ to accomodate simpleAttribute - @@ -51746,277 +51109,6 @@ to accomodate simpleAttribute 1 - - - - - - - -ExecutionEvent - - - - - - - - - - - - - - - - - -CreationEvent - - - - - - - - - - - - - - - - - -1 - - -MessageEvent - - - - - - - - - - - - - - - - - -DestructionEvent - - - - - - - - - - - - - - - - - -SendOperationEvent - - - - - - - - - - - - - - - - - - - - - - -SendSignalEvent - - - - - - - - - - - - - - - - - - - - - - -ReceiveOperationEvent - - - - - - - - - - - - - - - - - - - - - - -ReceiveSignalEvent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -52037,8 +51129,6 @@ to accomodate simpleAttribute - - @@ -52491,262 +51581,6 @@ to accomodate simpleAttribute feature, ownedMember - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - - -* - - - - - - - - - - -operation - - - - - - - - -1 - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - - -* - - - - - - - - - - -signal - - - - - - - - -1 - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - - -* - - - - - - - - - - -operation - - - - - - - - -1 - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* - - -* - - - - - - - - - - -signal - - - - - - - - -1 - - -1 - - Inheritance relationship is implicit. diff --git a/gaphor/UML/uml2.py b/gaphor/UML/uml2.py index 926a92538..4efd5b03f 100644 --- a/gaphor/UML/uml2.py +++ b/gaphor/UML/uml2.py @@ -770,38 +770,6 @@ class Event(PackageableElement): pass -class ExecutionEvent(Event): - pass - - -class CreationEvent(Event): - pass - - -class MessageEvent(Event): - pass - - -class DestructionEvent(Event): - pass - - -class SendOperationEvent(MessageEvent): - operation: relation_one[Operation] - - -class SendSignalEvent(MessageEvent): - signal: relation_one[Signal] - - -class ReceiveOperationEvent(MessageEvent): - operation: relation_one[Operation] - - -class ReceiveSignalEvent(MessageEvent): - signal: relation_one[Signal] - - class Signal(Classifier): ownedAttribute: relation_many[Property] @@ -1348,10 +1316,6 @@ Signal.ownedAttribute = association("ownedAttribute", Property, composite=True) Reception.signal = association("signal", Signal, upper=1) Class.ownedReception = association("ownedReception", Reception, composite=True) Interface.ownedReception = association("ownedReception", Reception, composite=True) -SendOperationEvent.operation = association("operation", Operation, lower=1, upper=1) -SendSignalEvent.signal = association("signal", Signal, lower=1, upper=1) -ReceiveOperationEvent.operation = association("operation", Operation, lower=1, upper=1) -ReceiveSignalEvent.signal = association("signal", Signal, lower=1, upper=1) Action.interaction = association("interaction", Interaction, upper=1, opposite="action") Interaction.action = association( "action", Action, composite=True, opposite="interaction" From bac72dc7a26f32218f8d3ccefa2c73bd2b30ac04 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 14:31:15 +0100 Subject: [PATCH 05/36] Fix modelfactory test name --- gaphor/UML/tests/test_modelfactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gaphor/UML/tests/test_modelfactory.py b/gaphor/UML/tests/test_modelfactory.py index d08217778..e3d01b052 100644 --- a/gaphor/UML/tests/test_modelfactory.py +++ b/gaphor/UML/tests/test_modelfactory.py @@ -285,7 +285,7 @@ def test_realization(factory): # Tests for interaction messages. -def test_create(factory): +def test_interaction_messages_cloning(factory): """Test message creation.""" m = factory.create(UML.Message) send = factory.create(UML.MessageOccurrenceSpecification) From 685b4f3383c11695de0ad2172a66df58ca3f1b77 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 19:20:20 +0100 Subject: [PATCH 06/36] Mark load UML model file test as slow --- gaphor/storage/tests/test_storage.py | 3 +++ pytest.ini | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/gaphor/storage/tests/test_storage.py b/gaphor/storage/tests/test_storage.py index 220eb631d..39d62dd68 100644 --- a/gaphor/storage/tests/test_storage.py +++ b/gaphor/storage/tests/test_storage.py @@ -5,6 +5,8 @@ Unittest the storage and parser modules import re from io import StringIO +import pytest + from gaphor import UML from gaphor.application import distribution from gaphor.diagram.classes import AssociationItem, ClassItem, InterfaceItem @@ -147,6 +149,7 @@ class StorageTestCase(TestCase): assert len(elements) == 1, elements assert elements[0].name == difficult_name, elements[0].name + @pytest.mark.slow def test_load_uml_metamodel(self): """ Test if the meta model can be loaded. diff --git a/pytest.ini b/pytest.ini index 064125a05..ff47cc60a 100644 --- a/pytest.ini +++ b/pytest.ini @@ -9,4 +9,7 @@ python_files = test_*.py # Console tests are failing the GitHub Actions CI (seg fault) norecursedirs = gaphor/plugins/console/tests -junit_family=xunit1 \ No newline at end of file +junit_family=xunit1 + +markers = + slow: marks tests as slow (deselect with '-m "not slow"') \ No newline at end of file From 446a7afa5942b68d4e312e89eb371d6d960188bf Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 19:45:02 +0100 Subject: [PATCH 07/36] Add makefile target for running non-slow tests --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 90a8c391a..5e0a71755 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,12 @@ help: ## Show this help @echo "make , where is one of:" @grep -hP "\t##" $(MAKEFILE_LIST) | sed -e 's/^\([a-z]*\):.*## / \1\t/' | expand -t14 -dist: translate ## Build application distribution (requires Poetry) +dist: test translate ## Build application distribution (requires Poetry) poetry build +test: ## Run all but slow tests (requires PyTest) + pytest -m "not slow" + docs: ## Generate documentation (requirss Sphinx) $(MAKE) -C docs html @@ -20,4 +23,4 @@ model: gaphor/UML/uml2.py ## Generate Python model files from Gaphor models (req gaphor/UML/uml2.py: gaphor/UML/uml2.gaphor utils/model/gen_uml.py utils/model/override.py utils/model/writer.py utils/model/build_uml.py && black $@ -.PHONY: help dist docs icons translate model +.PHONY: help dist test docs icons translate model From aaa41a3ce8b39538e9f27f2444aa32eb0c83bbc5 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 21:27:15 +0100 Subject: [PATCH 08/36] Consolidate class and metaclass property pages So class related info is properly shown again. --- .../diagram/classes/classespropertypages.py | 62 +++++++++++++- .../tests/test_metaclasspropertypage.py | 6 +- gaphor/diagram/profiles/__init__.py | 1 - .../diagram/profiles/metaclasspropertypage.py | 82 ------------------- gaphor/ui/elementeditor.py | 10 +-- 5 files changed, 68 insertions(+), 93 deletions(-) rename gaphor/diagram/{profiles => classes}/tests/test_metaclasspropertypage.py (85%) delete mode 100644 gaphor/diagram/profiles/metaclasspropertypage.py diff --git a/gaphor/diagram/classes/classespropertypages.py b/gaphor/diagram/classes/classespropertypages.py index 66da762be..82db16ff6 100644 --- a/gaphor/diagram/classes/classespropertypages.py +++ b/gaphor/diagram/classes/classespropertypages.py @@ -1,4 +1,5 @@ import logging +from inspect import isclass from gaphas.decorators import AsyncIO from gi.repository import Gtk @@ -93,16 +94,36 @@ class ClassOperations(EditableTreeModel): return self._item.subject.ownedOperation.swap(o1, o2) +def _issubclass(c, b): + try: + return issubclass(c, b) + except TypeError: + return False + + @PropertyPages.register(UML.Class) class ClassPropertyPage(NamedElementPropertyPage): - """Adapter which shows a property page for a class view.""" + """Adapter which shows a property page for a class view. + Also handles metaclasses. + """ subject: UML.Class + CLASSES = list( + sorted( + c + for c in dir(UML) + if _issubclass(getattr(UML, c), UML.Element) and c != "Stereotype" + ) + ) + def __init__(self, subject): super().__init__(subject) def construct(self): + if UML.model.is_metaclass(self.subject): + return self.construct_metaclass() + page = super().construct() if not self.subject: @@ -127,6 +148,45 @@ class ClassPropertyPage(NamedElementPropertyPage): def _on_abstract_change(self, button): self.subject.isAbstract = button.get_active() + def construct_metaclass(self): + page = Gtk.VBox() + + subject = self.subject + if not subject: + return page + + hbox = create_hbox_label(self, page, gettext("Name")) + model = Gtk.ListStore(str) + for c in self.CLASSES: + model.append([c]) + + cb = Gtk.ComboBox.new_with_model_and_entry(model) + + completion = Gtk.EntryCompletion() + completion.set_model(model) + completion.set_minimum_key_length(1) + completion.set_text_column(0) + cb.get_child().set_completion(completion) + + entry = cb.get_child() + entry.set_text(subject and subject.name or "") + hbox.pack_start(cb, True, True, 0) + page.default = entry + + # monitor subject.name attribute + changed_id = entry.connect("changed", self._on_name_change) + + def handler(event): + if event.element is subject and event.new_value is not None: + entry.handler_block(changed_id) + entry.set_text(event.new_value) + entry.handler_unblock(changed_id) + + self.watcher.watch("name", handler).subscribe_all() + entry.connect("destroy", self.watcher.unsubscribe_all) + page.show_all() + return page + @PropertyPages.register(InterfaceItem) class InterfacePropertyPage(NamedItemPropertyPage): diff --git a/gaphor/diagram/profiles/tests/test_metaclasspropertypage.py b/gaphor/diagram/classes/tests/test_metaclasspropertypage.py similarity index 85% rename from gaphor/diagram/profiles/tests/test_metaclasspropertypage.py rename to gaphor/diagram/classes/tests/test_metaclasspropertypage.py index 8f3345509..050204f68 100644 --- a/gaphor/diagram/profiles/tests/test_metaclasspropertypage.py +++ b/gaphor/diagram/classes/tests/test_metaclasspropertypage.py @@ -1,7 +1,7 @@ from gi.repository import Gtk from gaphor import UML -from gaphor.diagram.profiles.metaclasspropertypage import MetaclassNamePropertyPage +from gaphor.diagram.classes.classespropertypages import ClassPropertyPage from gaphor.tests import TestCase @@ -10,7 +10,7 @@ class MetaclassPropertyPageTest(TestCase): class_ = self.element_factory.create(UML.Class) class_.name = "Class" - editor = MetaclassNamePropertyPage(class_) + editor = ClassPropertyPage(class_) page = editor.construct() assert page entry = page.get_children()[0].get_children()[1] @@ -28,7 +28,7 @@ class MetaclassPropertyPageTest(TestCase): stereotype.name = "NewStereotype" UML.model.create_extension(metaclass, stereotype) - editor = MetaclassNamePropertyPage(metaclass) + editor = ClassPropertyPage(metaclass) page = editor.construct() assert page combo = page.get_children()[0].get_children()[1] diff --git a/gaphor/diagram/profiles/__init__.py b/gaphor/diagram/profiles/__init__.py index 026af1bca..09c1db60f 100644 --- a/gaphor/diagram/profiles/__init__.py +++ b/gaphor/diagram/profiles/__init__.py @@ -4,7 +4,6 @@ from gaphor.diagram.profiles.extension import ExtensionItem def _load(): from gaphor.diagram.profiles import ( extensionconnect, - metaclasspropertypage, stereotypepage, ) diff --git a/gaphor/diagram/profiles/metaclasspropertypage.py b/gaphor/diagram/profiles/metaclasspropertypage.py deleted file mode 100644 index c2b930618..000000000 --- a/gaphor/diagram/profiles/metaclasspropertypage.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Metaclass item editors. -""" - -from gi.repository import Gtk - -from gaphor import UML -from gaphor.core import gettext -from gaphor.diagram.propertypages import ( - NamedElementPropertyPage, - PropertyPages, - create_hbox_label, -) - - -def _issubclass(c, b): - try: - return issubclass(c, b) - except TypeError: - return False - - -@PropertyPages.register(UML.Class) -class MetaclassNamePropertyPage(NamedElementPropertyPage): - """ - Metaclass name editor. Provides editable combo box entry with - predefined list of names of UML classes. - """ - - order = 10 - - NAME_LABEL = gettext("Name") - - CLASSES = list( - sorted( - n - for n in dir(UML) - if _issubclass(getattr(UML, n), UML.Element) and n != "Stereotype" - ) - ) - - def construct(self): - if not UML.model.is_metaclass(self.subject): - return super().construct() - - page = Gtk.VBox() - - subject = self.subject - if not subject: - return page - - hbox = create_hbox_label(self, page, self.NAME_LABEL) - model = Gtk.ListStore(str) - for c in self.CLASSES: - model.append([c]) - - cb = Gtk.ComboBox.new_with_model_and_entry(model) - - completion = Gtk.EntryCompletion() - completion.set_model(model) - completion.set_minimum_key_length(1) - completion.set_text_column(0) - cb.get_child().set_completion(completion) - - entry = cb.get_child() - entry.set_text(subject and subject.name or "") - hbox.pack_start(cb, True, True, 0) - page.default = entry - - # monitor subject.name attribute - changed_id = entry.connect("changed", self._on_name_change) - - def handler(event): - if event.element is subject and event.new_value is not None: - entry.handler_block(changed_id) - entry.set_text(event.new_value) - entry.handler_unblock(changed_id) - - self.watcher.watch("name", handler).subscribe_all() - entry.connect("destroy", self.watcher.unsubscribe_all) - page.show_all() - return page diff --git a/gaphor/ui/elementeditor.py b/gaphor/ui/elementeditor.py index afebd504a..1e46f4f60 100644 --- a/gaphor/ui/elementeditor.py +++ b/gaphor/ui/elementeditor.py @@ -167,16 +167,14 @@ class ElementEditor(UIComponent, ActionProvider): Return an ordered list of (order, name, adapter). """ adaptermap = {} - try: - if item.subject: - for adapter in PropertyPages(item.subject): - adaptermap[adapter.name] = (adapter.order, adapter.name, adapter) - except AttributeError: - pass + if item.subject: + for adapter in PropertyPages(item.subject): + adaptermap[adapter.name] = (adapter.order, adapter.name, adapter) for adapter in PropertyPages(item): adaptermap[adapter.name] = (adapter.order, adapter.name, adapter) adapters = sorted(adaptermap.values()) + print("adapters", adapters) return adapters def create_pages(self, item): From d535f98187c651c87eb9720a8052db1713f4e177 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Feb 2020 22:51:45 +0100 Subject: [PATCH 09/36] Add interactions Occurences diagram --- gaphor/UML/uml2.gaphor | 2452 ++++++++++++++++++++++++---------------- gaphor/UML/uml2.py | 82 +- 2 files changed, 1534 insertions(+), 1000 deletions(-) diff --git a/gaphor/UML/uml2.gaphor b/gaphor/UML/uml2.gaphor index e5263d851..2db5c2ae2 100755 --- a/gaphor/UML/uml2.gaphor +++ b/gaphor/UML/uml2.gaphor @@ -6817,13 +6817,13 @@ BehavioredClassifier. - + @@ -21851,11 +21851,11 @@ specially for Gaphor - + @@ -23135,6 +23135,7 @@ specially for Gaphor + @@ -28742,7 +28743,6 @@ some elements to have a stereotype - @@ -28751,24 +28751,30 @@ some elements to have a stereotype - - - - - - - + + + + + + + + + + + + + @@ -28776,6 +28782,7 @@ some elements to have a stereotype + @@ -28855,7 +28862,7 @@ some elements to have a stereotype 1 -1 +0 0 @@ -28878,7 +28885,7 @@ some elements to have a stereotype 1 -1 +0 0 @@ -28988,32 +28995,9 @@ some elements to have a stereotype - - -(1.0, 0.0, 0.0, 1.0, 505.0, 354.0) - - -192.0 - - -59.0 - - -1 - - -1 - - -0 - - - - - - + (1.0, 0.0, 0.0, 1.0, 532.0, 226.0) @@ -29025,30 +29009,30 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (-1.0, 128.0)] +[(0.0, 0.0), (0.0, 130.0)] - + -(1.0, 0.0, 0.0, 1.0, 728.4363636363637, 355.0) +(1.0, 0.0, 0.0, 1.0, 728.4363636363637, 356.0) -142.0 +145.5636363636363 -70.0 +57.0 1 -1 +0 0 @@ -29085,7 +29069,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 786.4363636363638, 425.0) +(1.0, 0.0, 0.0, 1.0, 787.8919334186942, 413.0) 0 @@ -29094,7 +29078,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (0.11346957552382264, 119.50787353515625)] +[(0.0, 0.0), (-1.342100206806549, 131.50787353515625)] @@ -29126,7 +29110,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (0.0, 55.0), (206.4363636363637, 55.0), (206.4363636363637, 129.0)] +[(0.0, 0.0), (0.0, 55.0), (207.84174135723435, 55.0), (207.84174135723435, 130.0)] @@ -29149,7 +29133,7 @@ some elements to have a stereotype 1 -1 +0 0 @@ -29233,6 +29217,23 @@ some elements to have a stereotype + + +(1.0, 0.0, 0.0, 1.0, 501.0, 356.0) + + +179.0 + + +57.0 + + +0 + + + + + @@ -29248,7 +29249,7 @@ some elements to have a stereotype - + @@ -29258,7 +29259,7 @@ some elements to have a stereotype - + @@ -29408,48 +29409,6 @@ some elements to have a stereotype * - - - - - - - -1 - - -ExecutionOccurence - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -30158,7 +30117,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 305.9998779296875, 50.003936767578125) +(1.0, 0.0, 0.0, 1.0, 654.9993286132812, 48.503936767578125) 180.0 @@ -30207,16 +30166,16 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 380.69375548070826, 121.00393676757812) +(1.0, 0.0, 0.0, 1.0, 729.6932061643021, 119.50393676757812) -0 +1 -1 +0 -[(0.0, 0.0), (-0.4118171547170846, 151.99606323242188)] +[(0.0, 0.0), (0.0, 82.49606323242188), (-205.19320616430207, 82.49606323242188), (-205.19320616430207, 153.49606323242188)] @@ -30227,7 +30186,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, -110.0, 282.0) +(1.0, 0.0, 0.0, 1.0, 308.054964747098, 472.5) 174.0 @@ -30253,7 +30212,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 64.0, 316.69642857142867) +(1.0, 0.0, 0.0, 1.0, 352.0, 472.5) 0 @@ -30262,7 +30221,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (252.0, -1.7538595073178271)] +[(0.0, 0.0), (-0.5, -100.27001953125)] @@ -30282,7 +30241,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 64.0, 415.0) +(1.0, 0.0, 0.0, 1.0, 75.0, 52.5) 131.22964477539062 @@ -30305,7 +30264,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 64.0, 554.0) +(1.0, 0.0, 0.0, 1.0, 75.0, 192.5) 149.0 @@ -30326,90 +30285,12 @@ some elements to have a stereotype - - -(1.0, 0.0, 0.0, 1.0, 384.999755859375, 456.0) - - -166.0 - - -98.0 - - -0 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 462.91812320631396, 456.0) - - -0 - - -1 - - -[(0.0, 0.0), (-0.3101936908955736, -83.77001953125)] - - - - - - - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1222.5, 142.99612426757812) - - -191.0 - - -50.0 - - -0 - - -0 - - -0 - - - - - -(1.0, 0.0, 0.0, 1.0, 506.53529893732923, 192.99612426757812) +(1.0, 0.0, 0.0, 1.0, 353.03529893732923, 152.99230886060138) 0 @@ -30418,7 +30299,7 @@ some elements to have a stereotype 1 -[(0.0, 0.0), (0.055009432714825834, 80.00387573242188)] +[(0.0, 0.0), (1.4647010626707697, 120.00769113939862)] @@ -30525,7 +30406,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1182.6148936170212, 287.5) +(1.0, 0.0, 0.0, 1.0, 1025.1148936170212, 282.0) 203.0 @@ -30534,7 +30415,7 @@ some elements to have a stereotype 56.0 -1 +0 0 @@ -30548,7 +30429,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 461.554964747098, 142.99612426757812) +(1.0, 0.0, 0.0, 1.0, 308.054964747098, 102.9923088606014) 117.0 @@ -30569,44 +30450,21 @@ some elements to have a stereotype - - - - - -(1.0, 0.0, 0.0, 1.0, 1291.578125, 192.99612426757812) - - -0 - - -1 - - -[(0.0, 0.0), (-1.9931715027387327, 94.50387573242188)] - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 485.9998779296875, 104.4923088606014) +(1.0, 0.0, 0.0, 1.0, 784.5, 119.50393676757812) 1 -1 +0 -[(0.0, 0.0), (394.99945068359375, 0.0), (394.99945068359375, 177.50769113939845)] +[(0.0, 0.0), (0.0, 82.49606323242188), (96.49932861328125, 82.49606323242188), (96.49932861328125, 162.4960632324217)] @@ -30615,293 +30473,9 @@ some elements to have a stereotype - - -(1.0, 0.0, 0.0, 1.0, 1549.078125, 282.0) - - -160.421875 - - -64.0 - - -1 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 485.9998779296875, 55.783006535019986) - - -1 - - -1 - - -[(0.0, 0.0), (1165.074871298708, 0.0), (1165.074871298708, 226.21699346498002)] - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1413.5, 160.99612426757812) - - -1 - - -1 - - -[(0.0, 0.0), (148.45148533950623, 0.0), (148.45148533950623, 121.00387573242188)] - - - - - - - - -0 - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1385.6148936170212, 296.5) - - -0 - - -1 - - -[(0.0, 0.0), (163.46323138297885, -0.5)] - - - - - - - - -0 - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1385.6148936170212, 337.5) - - -0 - - -1 - - -[(0.0, 0.0), (163.46323138297885, -1.5)] - - - - - - - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1176.6148936170212, 456.0) - - -192.0 - - -56.0 - - -0 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1202.0639954134283, 343.5) - - -0 - - -1 - - -[(0.0, 0.0), (-0.5639954134283016, 112.5)] - - - - - - - - -0 - - - - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1306.602917569117, 343.5) - - -0 - - -1 - - -[(0.0, 0.0), (0.47520743088307427, 112.5)] - - - - - - - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1176.6148936170212, 613.0) - - -139.0 - - -72.0 - - -0 - - -0 - - -0 - - - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 1246.0514619163707, 613.0) - - -0 - - -1 - - -[(0.0, 0.0), (0.448538083629046, -101.0)] - - - - - - - - -0 - - - - - - - - -(1.0, 0.0, 0.0, 1.0, 884.9993286132812, 461.0) +(1.0, 0.0, 0.0, 1.0, 880.9993286132812, 462.5) 269.0 @@ -30936,7 +30510,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (1.8382352941176805, 107.0)] +[(0.0, 0.0), (0.03192138671875, 108.5)] @@ -30950,16 +30524,16 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 1182.6148936170212, 303.5) +(1.0, 0.0, 0.0, 1.0, 1082.53125, 338.0) -1 +0 -1 +0 -[(0.0, 0.0), (-114.17438853315161, 0.0), (-114.17438853315161, 157.5)] +[(0.0, 0.0), (-2.5, 124.5)] @@ -30970,7 +30544,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 50.000579833984375, 197.0) +(1.0, 0.0, 0.0, 1.0, 34.0, 416.114990234375) 190.0 @@ -30984,7 +30558,7 @@ some elements to have a stereotype -(1.0, 0.0, 0.0, 1.0, 156.00057983398438, 275.0) +(1.0, 0.0, 0.0, 1.0, 224.0, 446.0) 0 @@ -30993,7 +30567,7 @@ some elements to have a stereotype 0 -[(0.0, 0.0), (-0.20408649422319058, 40.415142499639956)] +[(0.0, 0.0), (127.74573031644684, -24.491252272155975)] @@ -31002,6 +30576,38 @@ some elements to have a stereotype + + + + + +(1.0, 0.0, 0.0, 1.0, 480.5, 273.0) + + +1 + + +0 + + +[(0.0, 0.0), (0.0, -190.0), (174.49932861328125, -190.0)] + + + + + + + + +0 + + + + + + + + @@ -31018,10 +30624,10 @@ some elements to have a stereotype - + @@ -31423,70 +31029,6 @@ some elements to have a stereotype asynchSignal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -0 - - -0 - - -signature - - - - - - - - -1 - - -1 - - - - - - - - - - - - - - - - @@ -31671,18 +31213,11 @@ some elements to have a stereotype - -1 - OccurrenceSpecification - - - - @@ -31692,8 +31227,9 @@ some elements to have a stereotype - + + @@ -31782,7 +31318,6 @@ some elements to have a stereotype - @@ -31814,8 +31349,7 @@ some elements to have a stereotype - - + @@ -31823,7 +31357,7 @@ some elements to have a stereotype - + @@ -31833,385 +31367,13 @@ some elements to have a stereotype - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -composite - - - - - - - - - - - - - -generalOrdering - - - - - - - - -* - - -* - - - - - - - - - - - - - - - - - - - - - - - - - - -before - - - - - - - - -1 - - -1 - - - - - - - - - - -toAfter - - - - - - - - -* - - -* - - - - - - - - - - - - - - - - - - - - - - - - - - -after - - - - - - - - -1 - - -1 - - - - - - - - - - -toBefore - - - - - - - - -* - - -* - - - - - - - - - - - - - - - - - - - - - - - - - - -finish - - - - - - - - -1 - - -1 - - - - - - - - - - -finishExec - - - - - - - - -* - - -* - - - - - - - - - - - - - - - - - - - - - - - - - - -start - - - - - - - - -1 - - -1 - - - - - - - - - - -startExec - - - - - - - - -* - - -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -behavior - - - - - - - - -* - - -* - - - - - - - - - - - - - - - - @@ -41759,6 +40921,8 @@ swimlanes + + @@ -46857,11 +46021,6 @@ swimlanes - - - - - @@ -52193,4 +51352,1383 @@ to accomodate simpleAttribute coveredBy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + +0 + + +signature + + + + + + + + +1 + + +1 + + + + +Occurences + + + + + + + +(1.0, 0.0, 0.0, 1.0, 297.61489361702115, 259.5) + + +210.88510638297885 + + +276.5 + + +1 + + +0 + + +0 + + + + + + + +(1.0, 0.0, 0.0, 1.0, 671.5, 259.5) + + +153.5 + + +104.5 + + +1 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 508.5, 288.5) + + +0 + + +0 + + +[(0.0, 0.0), (163.0, -0.04216867469881436)] + + + + + + + + +0 + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 508.5, 334.5) + + +0 + + +0 + + +[(0.0, 0.0), (163.0, 0.9618473895582156)] + + + + + + + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 658.25, 73.00393676757812) + + +180.0 + + +71.0 + + +0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 742.5, 144.00393676757812) + + +0 + + +0 + + +[(0.0, 0.0), (-1.0, 115.49606323242188)] + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 957.5, 108.50393676757812) + + +162.0 + + +57.0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 825.0, 313.0) + + +1 + + +1 + + +[(0.0, 0.0), (206.5, 0.0), (206.5, -147.49606323242188)] + + + + + + + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 827.5, 397.75) + + +194.25 + + +264.25 + + +0 + + +0 + + + + + + + +(1.0, 0.0, 0.0, 1.0, 271.0574468085106, 605.0) + + +264.0 + + +57.0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 398.5, 536.0) + + +0 + + +0 + + +[(0.0, 0.0), (-1.0, 69.0)] + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 508.5, 424.5) + + +0 + + +0 + + +[(0.0, 0.0), (319.0, 1.5)] + + + + + + + + +0 + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 508.5, 489.0) + + +0 + + +0 + + +[(0.0, 0.0), (319.0, 1.5)] + + + + + + + + +0 + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 535.0574468085106, 634.5) + + +0 + + +0 + + +[(0.0, 0.0), (292.4425531914894, 0.0)] + + + + + + + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 487.25, 746.5) + + +226.0 + + +57.0 + + +0 + + +0 + + + + + + + +(1.0, 0.0, 0.0, 1.0, 775.75, 746.5) + + +246.0 + + +57.0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 880.0, 662.0) + + +1 + + +0 + + +[(0.0, 0.0), (0.0, 47.0), (-272.0, 47.0), (-272.0, 84.5)] + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 920.5, 662.0) + + +0 + + +0 + + +[(0.0, 0.0), (-1.0, 84.5)] + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 834.0, 917.5) + + +129.5 + + +54.0 + + +0 + + +0 + + + + + + + +(1.0, 0.0, 0.0, 1.0, 543.0, 917.5) + + +114.5 + + +54.0 + + +0 + + +0 + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 600.0, 803.5) + + +0 + + +0 + + +[(0.0, 0.0), (-1.5, 114.0)] + + + + + + + + +0 + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 899.0, 803.5) + + +0 + + +0 + + +[(0.0, 0.0), (-1.0, 114.0)] + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +before + + + + + + + + + + + +1 + + +1 + + + + + + + +toAfter + + + + + + + + + + + +* + + +* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +after + + + + + + + + + + + +1 + + +1 + + + + + + + +toBefore + + + + + + + + + + + +* + + +* + + + + + + + + + + + + + + + + + + + + +composite + + + + + + + + + + + + + +generalOrdering + + + + + + + + +* + + +* + + + + + + + + + + + + + + + +0 + + +0 + + +interactionFragment + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + +ownedElement + + + + + + + + + + + + + + + + + + + + + + + + + + + +owner + + + + + + + + + +ExecutionSpecification + + + + + + + + + + + + + + + + + + + + + + + + +ExecutionOccurrenceSpecification + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +start + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +finish + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + +0 + + +executionOccurenceSpecification + + + + + + + + + + + +2 + + +2 + + + + + + + + + + +execution + + + + + + + + +1 + + +1 + + + + + + + + + +ActionExecutionSpecification + + + + + + + + + + + + + + + + + + + + + + +BehaviorExecutionSpecification + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +actionExecutionSpecification + + + + + + + + + + + +* + + +* + + + + + + + + + + +action + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +actionExecutionSpecification + + + + + + + + + + + +* + + +* + + + + + + + + + + +0 + + +0 + + +behavior + + + + + + + + +1 + + +1 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gaphor/UML/uml2.py b/gaphor/UML/uml2.py index 4efd5b03f..a5e2f3d9e 100644 --- a/gaphor/UML/uml2.py +++ b/gaphor/UML/uml2.py @@ -580,12 +580,6 @@ class Interaction(Behavior, InteractionFragment): action: relation_many[Action] -class ExecutionOccurence(InteractionFragment): - finish: relation_one[OccurrenceSpecification] - start: relation_one[OccurrenceSpecification] - behavior: relation_many[Behavior] - - class StateInvariant(InteractionFragment): invariant: relation_one[Constraint] covered: relation_one[Lifeline] # type: ignore[assignment] @@ -602,10 +596,10 @@ class Message(NamedElement): messageKind: property messageSort: enumeration argument: attribute[str] - signature: relation_one[NamedElement] sendEvent: relation_one[MessageEnd] receiveEvent: relation_one[MessageEnd] interaction: relation_one[Interaction] + signature: relation_one[NamedElement] class MessageEnd(NamedElement): @@ -614,16 +608,11 @@ class MessageEnd(NamedElement): class OccurrenceSpecification(InteractionFragment): - toAfter: relation_many[GeneralOrdering] - toBefore: relation_many[GeneralOrdering] - finishExec: relation_many[ExecutionOccurence] - startExec: relation_many[ExecutionOccurence] covered: relation_one[Lifeline] # type: ignore[assignment] class GeneralOrdering(NamedElement): - before: relation_one[OccurrenceSpecification] - after: relation_one[OccurrenceSpecification] + interactionFragment: relation_one[InteractionFragment] class Connector(Feature): @@ -778,6 +767,23 @@ class Reception(BehavioralFeature): signal: relation_one[Signal] +class ExecutionSpecification(InteractionFragment): + start: relation_one[OccurrenceSpecification] + finish: relation_one[OccurrenceSpecification] + + +class ExecutionOccurrenceSpecification(OccurrenceSpecification): + execution: relation_one[ExecutionSpecification] + + +class ActionExecutionSpecification(ExecutionSpecification): + action: relation_one[Action] + + +class BehaviorExecutionSpecification(ExecutionSpecification): + behavior: relation_one[Behavior] + + # class 'ValueSpecification' has been stereotyped as 'SimpleAttribute' # class 'InstanceValue' has been stereotyped as 'SimpleAttribute' too # class 'Expression' has been stereotyped as 'SimpleAttribute' too @@ -1175,7 +1181,6 @@ Interaction.lifeline = association( ) # 'Message.argument' is a simple attribute Message.argument = attribute("argument", str) -Message.signature = association("signature", NamedElement, upper=1) MessageEnd.sendMessage = association( "sendMessage", Message, upper=1, opposite="sendEvent" ) @@ -1194,34 +1199,6 @@ Message.interaction = association( Interaction.message = association( "message", Message, composite=True, opposite="interaction" ) -InteractionFragment.generalOrdering = association( - "generalOrdering", GeneralOrdering, composite=True -) -GeneralOrdering.before = association( - "before", OccurrenceSpecification, lower=1, upper=1, opposite="toAfter" -) -OccurrenceSpecification.toAfter = association( - "toAfter", GeneralOrdering, opposite="before" -) -GeneralOrdering.after = association( - "after", OccurrenceSpecification, lower=1, upper=1, opposite="toBefore" -) -OccurrenceSpecification.toBefore = association( - "toBefore", GeneralOrdering, opposite="after" -) -ExecutionOccurence.finish = association( - "finish", OccurrenceSpecification, lower=1, upper=1, opposite="finishExec" -) -OccurrenceSpecification.finishExec = association( - "finishExec", ExecutionOccurence, opposite="finish" -) -ExecutionOccurence.start = association( - "start", OccurrenceSpecification, lower=1, upper=1, opposite="startExec" -) -OccurrenceSpecification.startExec = association( - "startExec", ExecutionOccurence, opposite="start" -) -ExecutionOccurence.behavior = association("behavior", Behavior) StructuredClassifier.ownedConnector = association( "ownedConnector", Connector, composite=True ) @@ -1320,6 +1297,24 @@ Action.interaction = association("interaction", Interaction, upper=1, opposite=" Interaction.action = association( "action", Action, composite=True, opposite="interaction" ) +Message.signature = association("signature", NamedElement, upper=1) +InteractionFragment.generalOrdering = association( + "generalOrdering", GeneralOrdering, composite=True, opposite="interactionFragment" +) +GeneralOrdering.interactionFragment = association( + "interactionFragment", InteractionFragment, upper=1, opposite="generalOrdering" +) +ExecutionSpecification.start = association( + "start", OccurrenceSpecification, lower=1, upper=1 +) +ExecutionSpecification.finish = association( + "finish", OccurrenceSpecification, lower=1, upper=1 +) +ExecutionOccurrenceSpecification.execution = association( + "execution", ExecutionSpecification, lower=1, upper=1 +) +ActionExecutionSpecification.action = association("action", Action, lower=1, upper=1) +BehaviorExecutionSpecification.behavior = association("behavior", Behavior, upper=1) # 96: override NamedElement.qualifiedName(NamedElement.namespace): derived[List[str]] # defined in uml2overrides.py @@ -1641,6 +1636,7 @@ Element.owner = derivedunion( Constraint.stateInvariant, Pseudostate.state, Action.interaction, + GeneralOrdering.interactionFragment, ) Element.ownedElement = derivedunion( Element, @@ -1664,7 +1660,6 @@ Element.ownedElement = derivedunion( Activity.node, Action.output, StateInvariant.invariant, - InteractionFragment.generalOrdering, Connector.end, State.entry, State.exit, @@ -1674,6 +1669,7 @@ Element.ownedElement = derivedunion( Transition.guard, DeploymentTarget.deployment, Interaction.action, + InteractionFragment.generalOrdering, ) ConnectorEnd.definingEnd = derivedunion(ConnectorEnd, "definingEnd", Property, 0, 1) # 164: override StructuredClassifier.part: property From 1225f761716ec485b327432f48ded9494e254664 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 23 Feb 2020 13:17:17 +0100 Subject: [PATCH 10/36] Boyscouting --- gaphor/diagram/actions/activitynodes.py | 2 +- gaphor/diagram/shapes.py | 3 ++- gaphor/ui/elementeditor.py | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gaphor/diagram/actions/activitynodes.py b/gaphor/diagram/actions/activitynodes.py index 2267cd6a2..24da76f53 100644 --- a/gaphor/diagram/actions/activitynodes.py +++ b/gaphor/diagram/actions/activitynodes.py @@ -209,7 +209,7 @@ def draw_decision_node(_box, context, _bounding_box): @represents(UML.ForkNode) -class ForkNodeItem(UML.Presentation, Item): +class ForkNodeItem(UML.Presentation[UML.ForkNode], Item): """ Representation of fork and join node. """ diff --git a/gaphor/diagram/shapes.py b/gaphor/diagram/shapes.py index 0470e0e16..0a870d082 100644 --- a/gaphor/diagram/shapes.py +++ b/gaphor/diagram/shapes.py @@ -98,7 +98,8 @@ class Box: - min-height - min-width - padding: a tuple (top, right, bottom, left) - + - vertical-align: alignment of child shapes + - border-radius """ def __init__(self, *children, style: Style = {}, draw=None): diff --git a/gaphor/ui/elementeditor.py b/gaphor/ui/elementeditor.py index 1e46f4f60..47df3f5ce 100644 --- a/gaphor/ui/elementeditor.py +++ b/gaphor/ui/elementeditor.py @@ -174,7 +174,6 @@ class ElementEditor(UIComponent, ActionProvider): adaptermap[adapter.name] = (adapter.order, adapter.name, adapter) adapters = sorted(adaptermap.values()) - print("adapters", adapters) return adapters def create_pages(self, item): From 5e0dd6df3418436f48d7c169cb37d4dfc7b01f77 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 23 Feb 2020 13:17:54 +0100 Subject: [PATCH 11/36] Allow for filled box - now only white Get the basic styling in place for fill and later stroke color. --- gaphor/diagram/shapes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gaphor/diagram/shapes.py b/gaphor/diagram/shapes.py index 0a870d082..4405ad5dc 100644 --- a/gaphor/diagram/shapes.py +++ b/gaphor/diagram/shapes.py @@ -26,11 +26,13 @@ Style = TypedDict( "line-width": float, "vertical-spacing": float, "border-radius": float, + "fill": str, "font": str, "font-style": FontStyle, "font-weight": Optional[FontWeight], "text-decoration": Optional[TextDecoration], "text-align": TextAlign, + "stoke": str, "vertical-align": VerticalAlign, # CommentItem: "ear": int, @@ -66,6 +68,13 @@ def draw_border(box, context, bounding_box): cr.rectangle(x, y, width, height) cr.close_path() + + fill = box.style("fill") + if fill: + color = cr.get_source() + cr.set_source_rgb(1, 1, 1) # white + cr.fill_preserve() + cr.set_source(color) cr.stroke() @@ -111,6 +120,7 @@ class Box: "padding": (0, 0, 0, 0), "vertical-align": VerticalAlign.MIDDLE, "border-radius": 0, + "fill": None, **style, # type: ignore[misc] # noqa: F821 } self._draw_border = draw From cf6daedf826daf95ec595e0558c8f25c2d4ae1fb Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 23 Feb 2020 13:18:53 +0100 Subject: [PATCH 12/36] Add basic Execution Specification Can place it on canvas, no interaction with model yet. --- gaphor/diagram/diagramtoolbox.py | 9 +++ gaphor/diagram/interactions/__init__.py | 3 + .../interactions/executionspecification.py | 73 +++++++++++++++++++ .../tests/test_executionspecification.py | 16 ++++ 4 files changed, 101 insertions(+) create mode 100644 gaphor/diagram/interactions/executionspecification.py create mode 100644 gaphor/diagram/interactions/tests/test_executionspecification.py diff --git a/gaphor/diagram/diagramtoolbox.py b/gaphor/diagram/diagramtoolbox.py index 451f2badc..41e99a652 100644 --- a/gaphor/diagram/diagramtoolbox.py +++ b/gaphor/diagram/diagramtoolbox.py @@ -372,6 +372,15 @@ TOOLBOX_ACTIONS: Sequence[Tuple[str, Sequence[ToolDef]]] = ( diagram.interactions.MessageItem ), ), + ToolDef( + "toolbox-execution-specification", + gettext("Execution Specification"), + "gaphor-execution-specification-symbolic", + None, + item_factory=PlacementTool.new_item_factory( + diagram.interactions.ExecutionSpecificationItem + ), + ), ToolDef( "toolbox-interaction", gettext("Interaction"), diff --git a/gaphor/diagram/interactions/__init__.py b/gaphor/diagram/interactions/__init__.py index 3c1f3c629..bd1d35eae 100644 --- a/gaphor/diagram/interactions/__init__.py +++ b/gaphor/diagram/interactions/__init__.py @@ -1,3 +1,6 @@ +from gaphor.diagram.interactions.executionspecification import ( + ExecutionSpecificationItem, +) from gaphor.diagram.interactions.interaction import InteractionItem from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.interactions.message import MessageItem diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py new file mode 100644 index 000000000..0d239de1a --- /dev/null +++ b/gaphor/diagram/interactions/executionspecification.py @@ -0,0 +1,73 @@ +""" +An ExecutionSpecification is defined by a white recrange overlaying the lifeline + + + + ,----------. + | lifeline | + `----------' + | --- Lifeline + ,+. -- ExecutionOccurrenceSpecification + | | -- ExecutionSpecification + `+' -- ExecutionOccurrentSpecification + | + + ExecutionOccurrenceSpecification.covered <--> Lifeline.coveredBy + ExecutionOccurrenceSpecification.execution <--> ExecutionSpecification.execution + +TODO:ExecutionSpecification is abstract. Should use either +ActionExecutionSpecification or BehaviorExecutionSpecification. +What's the difference? + +Stick with BehaviorExecutionSpecification, since it has a [0..1] relation to behavior, whereas +ActionExecutionSpecification has a [1] relation to action. +""" +from gaphas import Handle, Item +from gaphas.connector import LinePort +from gaphas.geometry import Rectangle, distance_rectangle_point + +from gaphor import UML +from gaphor.diagram.shapes import Box, draw_border +from gaphor.diagram.support import represents +from gaphor.UML.modelfactory import stereotypes_str + + +@represents(UML.ExecutionSpecification) +class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], Item): + """ + Representation of interaction execution specification. + """ + + def __init__(self, id=None, model=None): + super().__init__(id, model) + self._min_height = 10 + + ht, hb = Handle(), Handle() + # TODO: need better interface for this! + self._handles.append(ht) + self._handles.append(hb) + # self._ports.append(LinePort(h1.pos, h2.pos)) + + self.constraint(above=(ht.pos, hb.pos), delta=self._min_height) + self.constraint(vertical=(ht.pos, hb.pos)) + + self.shape = Box( + style={"fill": "white"}, draw=draw_border + ) # self.draw_execution_specification) + + def dimensions(self): + d = 10 + pt, pb = (h.pos for h in self._handles) + return Rectangle(pt.x - d / 2, pt.y, d, pb.y - pt.y) + + def draw(self, context): + self.shape.draw(context, self.dimensions()) + + def point(self, pos): + return distance_rectangle_point(self.dimensions(), pos) + + def save(self, save_func): + super().save(save_func) + + def load(self, name, value): + super().load(name, value) diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py new file mode 100644 index 000000000..3316b0d8d --- /dev/null +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -0,0 +1,16 @@ +import pytest +from gaphas.canvas import Canvas, Context, instant_cairo_context +from gaphas.view import Context + +from gaphor.diagram.interactions.executionspecification import ( + ExecutionSpecificationItem, +) +from gaphor.UML import Diagram + + +def test_draw_on_canvas(): + canvas = Canvas() + execSpec = ExecutionSpecificationItem() + canvas.add(execSpec) + cr = instant_cairo_context() + execSpec.draw(Context(cairo=cr)) From ebff92256453947f7531a509d4929f9b06b3d04d Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 23 Feb 2020 19:32:41 +0100 Subject: [PATCH 13/36] Clean up fork node item code --- gaphor/diagram/actions/activitynodes.py | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/gaphor/diagram/actions/activitynodes.py b/gaphor/diagram/actions/activitynodes.py index 24da76f53..ad672232a 100644 --- a/gaphor/diagram/actions/activitynodes.py +++ b/gaphor/diagram/actions/activitynodes.py @@ -244,6 +244,9 @@ class ForkNodeItem(UML.Presentation[UML.ForkNode], Item): self.watch("subject.appliedStereotype.classifier.name") self.watch("subject[JoinNode].joinSpec") + self.constraint(vertical=(h1.pos, h2.pos)) + self.constraint(above=(h1.pos, h2.pos), delta=30) + def save(self, save_func): save_func("matrix", tuple(self.matrix)) save_func("height", float(self._handles[1].pos.y)) @@ -269,28 +272,6 @@ class ForkNodeItem(UML.Presentation[UML.ForkNode], Item): combined = reversible_property(lambda s: s._combined, _set_combined) - def setup_canvas(self): - assert self.canvas - super().setup_canvas() - - h1, h2 = self._handles - cadd = self.canvas.solver.add_constraint - c1 = EqualsConstraint(a=h1.pos.x, b=h2.pos.x) - c2 = LessThanConstraint(smaller=h1.pos.y, bigger=h2.pos.y, delta=30) - self.__constraints = (cadd(c1), cadd(c2)) - list(map(self.canvas.solver.add_constraint, self.__constraints)) - - def teardown_canvas(self): - assert self.canvas - super().teardown_canvas() - list(map(self.canvas.solver.remove_constraint, self.__constraints)) - - def pre_update(self, context): - cr = context.cairo - _, h2 = self.handles() - _, height = self.shape.size(cr) - h2.pos.y = max(h2.pos.y, height) - def draw(self, context): h1, h2 = self.handles() height = h2.pos.y - h1.pos.y From ef932aa49e6ef147f203b086e82386ccb6919b00 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 24 Feb 2020 21:38:03 +0100 Subject: [PATCH 14/36] Connect lifeline and execution specification --- Makefile | 6 +- gaphor/UML/properties.py | 4 +- gaphor/UML/uml2.gaphor | 50 ++++++++++++--- gaphor/UML/uml2.override | 8 +++ gaphor/UML/uml2.py | 46 +++++++++++--- gaphor/diagram/diagramtools.py | 6 +- .../interactions/executionspecification.py | 2 + gaphor/diagram/interactions/messageconnect.py | 44 ++++++++++++- gaphor/diagram/interactions/tests/conftest.py | 1 + .../tests/test_executionspecification.py | 62 +++++++++++++++++-- gaphor/diagram/tests/fixtures.py | 56 +++++++++++++++++ gaphor/diagram/tests/test_presentation.py | 15 +---- 12 files changed, 254 insertions(+), 46 deletions(-) create mode 100644 gaphor/diagram/interactions/tests/conftest.py create mode 100644 gaphor/diagram/tests/fixtures.py diff --git a/Makefile b/Makefile index 5e0a71755..738793468 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ icons: ## Generate icons from stensil (requires Inkscape) translate: ## Translate and update .po and .mo files (requires PyBabel) $(MAKE) -C po -model: gaphor/UML/uml2.py ## Generate Python model files from Gaphor models (requires Black) +model: gaphor/UML/uml2.py ## Generate Python model files from Gaphor models (requires Black, MyPy) -gaphor/UML/uml2.py: gaphor/UML/uml2.gaphor utils/model/gen_uml.py utils/model/override.py utils/model/writer.py - utils/model/build_uml.py && black $@ +gaphor/UML/uml2.py: gaphor/UML/uml2.gaphor gaphor/UML/uml2.override utils/model/gen_uml.py utils/model/override.py utils/model/writer.py + utils/model/build_uml.py && black $@ && mypy gaphor/UML .PHONY: help dist test docs icons translate model diff --git a/gaphor/UML/properties.py b/gaphor/UML/properties.py index c0497edd6..01f6aa422 100644 --- a/gaphor/UML/properties.py +++ b/gaphor/UML/properties.py @@ -111,7 +111,7 @@ relation = Union[relation_one, relation_many] T = TypeVar("T") Lower = Union[Literal[0], Literal[1], Literal[2]] -Upper = Union[Literal[1], Literal["*"]] +Upper = Union[Literal[1], Literal[2], Literal["*"]] class umlproperty: @@ -618,7 +618,7 @@ class derived(umlproperty, Generic[T]): pass def __str__(self): - return f"" + return f"" def _update(self, obj): """ diff --git a/gaphor/UML/uml2.gaphor b/gaphor/UML/uml2.gaphor index 2db5c2ae2..27299f285 100755 --- a/gaphor/UML/uml2.gaphor +++ b/gaphor/UML/uml2.gaphor @@ -51981,6 +51981,20 @@ to accomodate simpleAttribute + + +(1.0, 0.0, 0.0, 1.0, 297.61489361702115, 73.00393676757812) + + +210.5 + + +115.5 + + + + + @@ -52269,6 +52283,7 @@ to accomodate simpleAttribute + @@ -52276,8 +52291,8 @@ to accomodate simpleAttribute - + @@ -52345,6 +52360,9 @@ to accomodate simpleAttribute + +1 + start @@ -52403,6 +52421,9 @@ to accomodate simpleAttribute + +1 + finish @@ -52440,11 +52461,6 @@ to accomodate simpleAttribute - - - - - @@ -52455,9 +52471,15 @@ to accomodate simpleAttribute + +composite + + + + 0 @@ -52465,11 +52487,8 @@ to accomodate simpleAttribute 0 -executionOccurenceSpecification +executionOccurrenceSpecification - - - @@ -52731,4 +52750,15 @@ to accomodate simpleAttribute + + +Some changes have been made to the original model, +to make it fit more to what we need in Gaphor. + + + + + + + \ No newline at end of file diff --git a/gaphor/UML/uml2.override b/gaphor/UML/uml2.override index 9a8ee92d1..6e0f3c76b 100644 --- a/gaphor/UML/uml2.override +++ b/gaphor/UML/uml2.override @@ -165,3 +165,11 @@ override StructuredClassifier.part: property StructuredClassifier.part = property(lambda self: tuple(a for a in self.ownedAttribute if a.isComposite), doc=""" Properties owned by a classifier by composition. """) +%% +override ExecutionSpecification.start(ExecutionSpecification.executionOccurrenceSpecification): relation_one[ExecutionOccurrenceSpecification] +ExecutionSpecification.start = derived(ExecutionSpecification, 'start', OccurrenceSpecification, 0, 1, + lambda obj: [eos for i, eos in enumerate(obj.executionOccurrenceSpecification) if i == 0]) +%% +override ExecutionSpecification.finish(ExecutionSpecification.executionOccurrenceSpecification): relation_one[ExecutionOccurrenceSpecification] +ExecutionSpecification.finish = derived(ExecutionSpecification, 'finish', OccurrenceSpecification, 0, 1, + lambda obj: [eos for i, eos in enumerate(obj.executionOccurrenceSpecification) if i == 1]) diff --git a/gaphor/UML/uml2.py b/gaphor/UML/uml2.py index a5e2f3d9e..68703bfd0 100644 --- a/gaphor/UML/uml2.py +++ b/gaphor/UML/uml2.py @@ -768,8 +768,9 @@ class Reception(BehavioralFeature): class ExecutionSpecification(InteractionFragment): - start: relation_one[OccurrenceSpecification] - finish: relation_one[OccurrenceSpecification] + executionOccurrenceSpecification: relation_many[ExecutionOccurrenceSpecification] + start: relation_one[ExecutionOccurrenceSpecification] + finish: relation_one[ExecutionOccurrenceSpecification] class ExecutionOccurrenceSpecification(OccurrenceSpecification): @@ -1304,14 +1305,19 @@ InteractionFragment.generalOrdering = association( GeneralOrdering.interactionFragment = association( "interactionFragment", InteractionFragment, upper=1, opposite="generalOrdering" ) -ExecutionSpecification.start = association( - "start", OccurrenceSpecification, lower=1, upper=1 -) -ExecutionSpecification.finish = association( - "finish", OccurrenceSpecification, lower=1, upper=1 +ExecutionSpecification.executionOccurrenceSpecification = association( + "executionOccurrenceSpecification", + ExecutionOccurrenceSpecification, + upper=2, + composite=True, + opposite="execution", ) ExecutionOccurrenceSpecification.execution = association( - "execution", ExecutionSpecification, lower=1, upper=1 + "execution", + ExecutionSpecification, + lower=1, + upper=1, + opposite="executionOccurrenceSpecification", ) ActionExecutionSpecification.action = association("action", Action, lower=1, upper=1) BehaviorExecutionSpecification.behavior = association("behavior", Behavior, upper=1) @@ -1680,6 +1686,30 @@ StructuredClassifier.part = property( """, ) +# 169: override ExecutionSpecification.start(ExecutionSpecification.executionOccurrenceSpecification): relation_one[ExecutionOccurrenceSpecification] +ExecutionSpecification.start = derived( + ExecutionSpecification, + "start", + OccurrenceSpecification, + 0, + 1, + lambda obj: [ + eos for i, eos in enumerate(obj.executionOccurrenceSpecification) if i == 0 + ], +) + +# 173: override ExecutionSpecification.finish(ExecutionSpecification.executionOccurrenceSpecification): relation_one[ExecutionOccurrenceSpecification] +ExecutionSpecification.finish = derived( + ExecutionSpecification, + "finish", + OccurrenceSpecification, + 0, + 1, + lambda obj: [ + eos for i, eos in enumerate(obj.executionOccurrenceSpecification) if i == 1 + ], +) + # 128: override Class.superClass: derived[Classifier] Class.superClass = Classifier.general diff --git a/gaphor/diagram/diagramtools.py b/gaphor/diagram/diagramtools.py index 309680fc1..719d74e4b 100644 --- a/gaphor/diagram/diagramtools.py +++ b/gaphor/diagram/diagramtools.py @@ -21,7 +21,7 @@ from gaphor.diagram.connectors import IConnect from gaphor.diagram.event import DiagramItemPlaced from gaphor.diagram.grouping import Group from gaphor.diagram.inlineeditors import InlineEditor -from gaphor.diagram.presentation import ElementPresentation, LinePresentation +from gaphor.diagram.presentation import ElementPresentation, Presentation # cursor to indicate grouping IN_CURSOR_TYPE = Gdk.CursorType.DIAMOND_CROSS @@ -32,7 +32,7 @@ OUT_CURSOR_TYPE = Gdk.CursorType.CROSSHAIR log = logging.getLogger(__name__) -@Connector.register(LinePresentation) +@Connector.register(Presentation) class DiagramItemConnector(ItemConnector): """ Handle Tool (acts on item handles) that uses the IConnect protocol @@ -290,7 +290,7 @@ class PlacementTool(_PlacementTool): return item -@InMotion.register(ElementPresentation) +@InMotion.register(Presentation) class DropZoneInMotion(GuidedItemInMotion): def move(self, pos): """ diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index 0d239de1a..a30b1e655 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -43,6 +43,8 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I self._min_height = 10 ht, hb = Handle(), Handle() + ht.connectable = True + # TODO: need better interface for this! self._handles.append(ht) self._handles.append(hb) diff --git a/gaphor/diagram/interactions/messageconnect.py b/gaphor/diagram/interactions/messageconnect.py index 7aacd4ff0..f705ef2e1 100644 --- a/gaphor/diagram/interactions/messageconnect.py +++ b/gaphor/diagram/interactions/messageconnect.py @@ -4,6 +4,9 @@ from typing import Optional from gaphor import UML from gaphor.diagram.connectors import AbstractConnect, IConnect +from gaphor.diagram.interactions.executionspecification import ( + ExecutionSpecificationItem, +) from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.interactions.message import MessageItem @@ -112,8 +115,6 @@ class MessageLifelineConnect(AbstractConnect): def disconnect(self, handle): assert self.canvas - super().disconnect(handle) - line = self.line received = self.get_connected(line.tail) lifeline = self.element @@ -134,3 +135,42 @@ class MessageLifelineConnect(AbstractConnect): # zero, so allow connections to lifeline's lifetime lifetime.connectable = True lifetime.min_length = lifetime.MIN_LENGTH + + +@IConnect.register(LifelineItem, ExecutionSpecificationItem) +class ExecutionSpecificationConnect(AbstractConnect): + + element: LifelineItem + line: ExecutionSpecificationItem + + def allow(self, handle, port): + lifetime = self.element.lifetime + return lifetime.visible + + def connect(self, handle, port): + lifeline = self.element.subject + exec_spec: UML.ExecutionSpecification = self.line.subject + model = self.element.model + if not exec_spec: + exec_spec = model.create(UML.BehaviorExecutionSpecification) + self.line.subject = exec_spec + + start_occurence: UML.ExecutionOccurrenceSpecification = model.create( + UML.ExecutionOccurrenceSpecification + ) + start_occurence.covered = lifeline + start_occurence.execution = exec_spec + + finish_occurence: UML.ExecutionOccurrenceSpecification = model.create( + UML.ExecutionOccurrenceSpecification + ) + finish_occurence.covered = lifeline + finish_occurence.execution = exec_spec + + return True + + def disconnect(self, handle): + exec_spec: Optional[UML.ExecutionSpecification] = self.line.subject + del self.line.subject + if exec_spec: + exec_spec.unlink() diff --git a/gaphor/diagram/interactions/tests/conftest.py b/gaphor/diagram/interactions/tests/conftest.py new file mode 100644 index 000000000..fe42227de --- /dev/null +++ b/gaphor/diagram/interactions/tests/conftest.py @@ -0,0 +1 @@ +from gaphor.diagram.tests.fixtures import diagram, element_factory diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index 3316b0d8d..f1f3ac916 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -2,15 +2,69 @@ import pytest from gaphas.canvas import Canvas, Context, instant_cairo_context from gaphas.view import Context +from gaphor import UML from gaphor.diagram.interactions.executionspecification import ( ExecutionSpecificationItem, ) -from gaphor.UML import Diagram +from gaphor.diagram.interactions.lifeline import LifelineItem +from gaphor.diagram.tests.fixtures import allow, connect, disconnect def test_draw_on_canvas(): canvas = Canvas() - execSpec = ExecutionSpecificationItem() - canvas.add(execSpec) + exec_spec = ExecutionSpecificationItem() + canvas.add(exec_spec) cr = instant_cairo_context() - execSpec.draw(Context(cairo=cr)) + exec_spec.draw(Context(cairo=cr)) + + +def test_allow_execution_specification_to_lifeline(diagram): + lifeline = diagram.create(LifelineItem) + lifeline.lifetime.visible = True + exec_spec = diagram.create(ExecutionSpecificationItem) + + glued = allow(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + + assert glued + + +def test_connect_execution_specification_to_lifeline(diagram, element_factory): + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + exec_spec = diagram.create(ExecutionSpecificationItem) + + connect(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + + assert exec_spec.subject + assert lifeline.subject + assert exec_spec.subject.start.covered is lifeline.subject + assert ( + exec_spec.subject.executionOccurrenceSpecification[0].covered + is lifeline.subject + ) + + +def test_connect_execution_specification_to_lifeline(diagram, element_factory): + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + exec_spec = diagram.create(ExecutionSpecificationItem) + connect(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + + disconnect(exec_spec, exec_spec.handles()[0]) + + assert lifeline.subject + assert exec_spec.subject is None + assert exec_spec.canvas + assert ( + element_factory.lselect(lambda e: e.isKindOf(UML.ExecutionSpecification)) == [] + ) + assert ( + element_factory.lselect( + lambda e: e.isKindOf(UML.ExecutionOccurrenceSpecification) + ) + == [] + ) diff --git a/gaphor/diagram/tests/fixtures.py b/gaphor/diagram/tests/fixtures.py new file mode 100644 index 000000000..d56f20ea5 --- /dev/null +++ b/gaphor/diagram/tests/fixtures.py @@ -0,0 +1,56 @@ +import pytest +from gaphas.aspect import ConnectionSink, Connector + +from gaphor import UML +from gaphor.diagram.connectors import IConnect +from gaphor.services.eventmanager import EventManager +from gaphor.UML.elementfactory import ElementFactory + + +@pytest.fixture +def element_factory(): + return ElementFactory(EventManager()) + + +@pytest.fixture +def diagram(element_factory): + return element_factory.create(UML.Diagram) + + +def allow(line, handle, item, port=None): + if port is None and len(item.ports()) > 0: + port = item.ports()[0] + + adapter = IConnect(item, line) + return adapter.allow(handle, port) + + +def connect(line, handle, item, port=None): + """ + Connect line's handle to an item. + + If port is not provided, then first port is used. + """ + canvas = line.canvas + + if port is None and len(item.ports()) > 0: + port = item.ports()[0] + + sink = ConnectionSink(item, port) + connector = Connector(line, handle) + + connector.connect(sink) + + cinfo = canvas.get_connection(handle) + assert cinfo.connected is item + assert cinfo.port is port + + +def disconnect(line, handle): + """ + Disconnect line's handle. + """ + canvas = line.canvas + + canvas.disconnect_item(line, handle) + assert not canvas.get_connection(handle) diff --git a/gaphor/diagram/tests/test_presentation.py b/gaphor/diagram/tests/test_presentation.py index f59dc59aa..2f34f4069 100644 --- a/gaphor/diagram/tests/test_presentation.py +++ b/gaphor/diagram/tests/test_presentation.py @@ -1,9 +1,6 @@ -import pytest - from gaphor import UML from gaphor.diagram.presentation import ElementPresentation, LinePresentation -from gaphor.services.eventmanager import EventManager -from gaphor.UML.elementfactory import ElementFactory +from gaphor.diagram.tests.fixtures import diagram, element_factory class DummyVisualComponent: @@ -24,16 +21,6 @@ class StubLine(LinePresentation): super().__init__(id, model, shape_middle=DummyVisualComponent()) -@pytest.fixture -def element_factory(): - return ElementFactory(EventManager()) - - -@pytest.fixture -def diagram(element_factory): - return element_factory.create(UML.Diagram) - - def test_creation(element_factory): p = element_factory.create(UML.Presentation) From a4a42f27dba9e4997e474c7d24e9c6b82cad12e4 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 24 Feb 2020 22:00:20 +0100 Subject: [PATCH 15/36] Have one BaseConnector Get rid of ConnectBase and AbstractConnect. --- gaphor/diagram/components/connectorconnect.py | 6 +- gaphor/diagram/connectors.py | 56 +++---------------- gaphor/diagram/general/connectors.py | 6 +- gaphor/diagram/interactions/messageconnect.py | 6 +- gaphor/ui/tests/test_handletool.py | 2 +- 5 files changed, 19 insertions(+), 57 deletions(-) diff --git a/gaphor/diagram/components/connectorconnect.py b/gaphor/diagram/components/connectorconnect.py index 0ba6b0987..3e3933f80 100644 --- a/gaphor/diagram/components/connectorconnect.py +++ b/gaphor/diagram/components/connectorconnect.py @@ -11,10 +11,10 @@ from gaphor import UML from gaphor.diagram.classes.interface import Folded, InterfaceItem from gaphor.diagram.components.component import ComponentItem from gaphor.diagram.components.connector import ConnectorItem -from gaphor.diagram.connectors import AbstractConnect, IConnect +from gaphor.diagram.connectors import BaseConnector, IConnect -class ConnectorConnectBase(AbstractConnect): +class ConnectorConnectBase(BaseConnector): def _get_interfaces(self, c1, c2): """ Return list of common interfaces provided by first component and @@ -195,6 +195,6 @@ class ComponentConnectorConnect(ConnectorConnectBase): class InterfaceConnectorConnect(ConnectorConnectBase): """Connect connector to an interface to maintain assembly connection. - See also `AbstractConnect` class for exception of interface item + See also `BaseConnector` class for exception of interface item connections. """ diff --git a/gaphor/diagram/connectors.py b/gaphor/diagram/connectors.py index bf017f619..49ceaba17 100644 --- a/gaphor/diagram/connectors.py +++ b/gaphor/diagram/connectors.py @@ -20,52 +20,7 @@ from gaphor.UML.properties import association, redefine, relation T = TypeVar("T", bound=UML.Element) -class ConnectBase: - """ - This interface is used by the HandleTool to allow connecting - lines to element items. For each specific case (Element, Line) an - adapter could be written. - """ - - def __init__(self, item: ElementPresentation, line_item: LinePresentation): - self.item = item - self.line_item = line_item - - def allow(self, handle: Handle, port: Port) -> bool: - """ - Determine if a connection is allowed. - - Do some extra checks to see if the items actually can be connected. - """ - return False - - def connect(self, handle: Handle, port: Port) -> bool: - """ - Connect a line's handle to element. - - Note that at the moment of the connect, handle.connected_to may point - to some other item. The implementor should do the disconnect of - the other element themselves. - """ - raise NotImplementedError(f"No connector for {self.item} and {self.line_item}") - - def disconnect(self, handle: Handle) -> None: - """ - The true disconnect. Disconnect a handle.connected_to from an - element. This requires that the relationship is also removed at - model level. - """ - raise NotImplementedError(f"No connector for {self.item} and {self.line_item}") - - -# Work around issue https://github.com/python/mypy/issues/3135 (Class decorators are not type checked) -# This definition, along with the the ignore below, seems to fix the behaviour for mypy at least. -IConnect: FunctionDispatcher[Type[ConnectBase]] = multidispatch(object, object)( - ConnectBase -) - - -class AbstractConnect(ConnectBase): +class BaseConnector: """ Connection adapter for Gaphor diagram items. @@ -152,7 +107,14 @@ class AbstractConnect(ConnectBase): """Disconnect UML model level connections.""" -class UnaryRelationshipConnect(AbstractConnect): +# Work around issue https://github.com/python/mypy/issues/3135 (Class decorators are not type checked) +# This definition, along with the the ignore below, seems to fix the behaviour for mypy at least. +IConnect: FunctionDispatcher[Type[BaseConnector]] = multidispatch(object, object)( + BaseConnector +) + + +class UnaryRelationshipConnect(BaseConnector): """ Base class for relationship connections, such as associations, dependencies and implementations. diff --git a/gaphor/diagram/general/connectors.py b/gaphor/diagram/general/connectors.py index 17cbcb0eb..c4c215406 100644 --- a/gaphor/diagram/general/connectors.py +++ b/gaphor/diagram/general/connectors.py @@ -5,7 +5,7 @@ Connect comments. import logging from gaphor import UML -from gaphor.diagram.connectors import AbstractConnect, IConnect +from gaphor.diagram.connectors import BaseConnector, IConnect from gaphor.diagram.general.comment import CommentItem from gaphor.diagram.general.commentline import CommentLineItem from gaphor.diagram.presentation import ElementPresentation, LinePresentation @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) @IConnect.register(CommentItem, CommentLineItem) @IConnect.register(ElementPresentation, CommentLineItem) -class CommentLineElementConnect(AbstractConnect): +class CommentLineElementConnect(BaseConnector): """Connect a comment line to any element item.""" line: CommentLineItem @@ -98,7 +98,7 @@ class CommentLineElementConnect(AbstractConnect): @IConnect.register(LinePresentation, CommentLineItem) -class CommentLineLineConnect(AbstractConnect): +class CommentLineLineConnect(BaseConnector): """Connect a comment line to any diagram line.""" def allow(self, handle, port): diff --git a/gaphor/diagram/interactions/messageconnect.py b/gaphor/diagram/interactions/messageconnect.py index f705ef2e1..2a820676c 100644 --- a/gaphor/diagram/interactions/messageconnect.py +++ b/gaphor/diagram/interactions/messageconnect.py @@ -3,7 +3,7 @@ from typing import Optional from gaphor import UML -from gaphor.diagram.connectors import AbstractConnect, IConnect +from gaphor.diagram.connectors import BaseConnector, IConnect from gaphor.diagram.interactions.executionspecification import ( ExecutionSpecificationItem, ) @@ -12,7 +12,7 @@ from gaphor.diagram.interactions.message import MessageItem @IConnect.register(LifelineItem, MessageItem) -class MessageLifelineConnect(AbstractConnect): +class MessageLifelineConnect(BaseConnector): """Connect lifeline with a message. A message can connect to both the lifeline's head (the rectangle) @@ -138,7 +138,7 @@ class MessageLifelineConnect(AbstractConnect): @IConnect.register(LifelineItem, ExecutionSpecificationItem) -class ExecutionSpecificationConnect(AbstractConnect): +class ExecutionSpecificationConnect(BaseConnector): element: LifelineItem line: ExecutionSpecificationItem diff --git a/gaphor/ui/tests/test_handletool.py b/gaphor/ui/tests/test_handletool.py index d72d8195c..ebf619324 100644 --- a/gaphor/ui/tests/test_handletool.py +++ b/gaphor/ui/tests/test_handletool.py @@ -77,7 +77,7 @@ def test_aspect_type(commentline): assert isinstance(aspect, DiagramItemConnector) -def test_query(comment): +def test_query(comment, commentline): assert IConnect(comment, commentline) From 30d87c4c97b88efd5b283a0977f94580f847b61c Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 24 Feb 2020 22:10:25 +0100 Subject: [PATCH 16/36] name gaphas' Connector in a unique way So Gaphor can have it's own Connector. --- gaphor/diagram/diagramtools.py | 8 +++++--- gaphor/diagram/presentation.py | 6 ++++-- gaphor/diagram/tests/fixtures.py | 5 +++-- gaphor/tests/testcase.py | 5 +++-- gaphor/ui/tests/test_handletool.py | 11 ++++++----- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/gaphor/diagram/diagramtools.py b/gaphor/diagram/diagramtools.py index 719d74e4b..a32356fe8 100644 --- a/gaphor/diagram/diagramtools.py +++ b/gaphor/diagram/diagramtools.py @@ -9,7 +9,9 @@ Although Gaphas has quite a few useful tools, some tools need to be extended: import logging -from gaphas.aspect import Connector, InMotion, ItemConnector +from gaphas.aspect import Connector as ConnectorAspect +from gaphas.aspect import InMotion as InMotionAspect +from gaphas.aspect import ItemConnector from gaphas.guide import GuidedItemInMotion from gaphas.tool import ConnectHandleTool, HoverTool, ItemTool from gaphas.tool import PlacementTool as _PlacementTool @@ -32,7 +34,7 @@ OUT_CURSOR_TYPE = Gdk.CursorType.CROSSHAIR log = logging.getLogger(__name__) -@Connector.register(Presentation) +@ConnectorAspect.register(Presentation) class DiagramItemConnector(ItemConnector): """ Handle Tool (acts on item handles) that uses the IConnect protocol @@ -290,7 +292,7 @@ class PlacementTool(_PlacementTool): return item -@InMotion.register(Presentation) +@InMotionAspect.register(Presentation) class DropZoneInMotion(GuidedItemInMotion): def move(self, pos): """ diff --git a/gaphor/diagram/presentation.py b/gaphor/diagram/presentation.py index 53079ff44..a76985837 100644 --- a/gaphor/diagram/presentation.py +++ b/gaphor/diagram/presentation.py @@ -2,6 +2,8 @@ import ast from typing import Optional import gaphas +from gaphas.aspect import ConnectionSink +from gaphas.aspect import Connector as ConnectorAspect from gaphas.geometry import Rectangle, distance_rectangle_point from gaphor.diagram.text import TextAlign, text_point_at_line @@ -259,10 +261,10 @@ class LinePresentation(Presentation[S], gaphas.Line): port = p dist = d - return gaphas.aspect.ConnectionSink(item, port) + return ConnectionSink(item, port) def postload_connect(handle, item): - connector = gaphas.aspect.Connector(self, handle) + connector = ConnectorAspect(self, handle) sink = get_sink(handle, item) connector.connect(sink) diff --git a/gaphor/diagram/tests/fixtures.py b/gaphor/diagram/tests/fixtures.py index d56f20ea5..b8d628c72 100644 --- a/gaphor/diagram/tests/fixtures.py +++ b/gaphor/diagram/tests/fixtures.py @@ -1,5 +1,6 @@ import pytest -from gaphas.aspect import ConnectionSink, Connector +from gaphas.aspect import ConnectionSink +from gaphas.aspect import Connector as ConnectorAspect from gaphor import UML from gaphor.diagram.connectors import IConnect @@ -37,7 +38,7 @@ def connect(line, handle, item, port=None): port = item.ports()[0] sink = ConnectionSink(item, port) - connector = Connector(line, handle) + connector = ConnectorAspect(line, handle) connector.connect(sink) diff --git a/gaphor/tests/testcase.py b/gaphor/tests/testcase.py index 9dc4ce5a0..3a12a6289 100644 --- a/gaphor/tests/testcase.py +++ b/gaphor/tests/testcase.py @@ -10,7 +10,8 @@ import unittest from io import StringIO from typing import Type, TypeVar -from gaphas.aspect import ConnectionSink, Connector +from gaphas.aspect import ConnectionSink +from gaphas.aspect import Connector as ConnectorAspect # For DiagramItemConnector aspect: import gaphor.diagram.diagramtools # noqa @@ -82,7 +83,7 @@ class TestCase(unittest.TestCase): port = item.ports()[0] sink = ConnectionSink(item, port) - connector = Connector(line, handle) + connector = ConnectorAspect(line, handle) connector.connect(sink) diff --git a/gaphor/ui/tests/test_handletool.py b/gaphor/ui/tests/test_handletool.py index ebf619324..93e07922c 100644 --- a/gaphor/ui/tests/test_handletool.py +++ b/gaphor/ui/tests/test_handletool.py @@ -3,7 +3,8 @@ Test handle tool functionality. """ import pytest -from gaphas.aspect import ConnectionSink, Connector +from gaphas.aspect import ConnectionSink +from gaphas.aspect import Connector as ConnectorAspect from gi.repository import Gdk, Gtk from gaphor import UML @@ -73,7 +74,7 @@ def commentline(diagram): def test_aspect_type(commentline): - aspect = Connector(commentline, commentline.handles()[0]) + aspect = ConnectorAspect(commentline, commentline.handles()[0]) assert isinstance(aspect, DiagramItemConnector) @@ -82,7 +83,7 @@ def test_query(comment, commentline): def test_allow(commentline, comment): - aspect = Connector(commentline, commentline.handles()[0]) + aspect = ConnectorAspect(commentline, commentline.handles()[0]) assert aspect.item is commentline assert aspect.handle is commentline.handles()[0] @@ -92,7 +93,7 @@ def test_allow(commentline, comment): def test_connect(diagram, comment, commentline): sink = ConnectionSink(comment, comment.ports()[0]) - aspect = Connector(commentline, commentline.handles()[0]) + aspect = ConnectorAspect(commentline, commentline.handles()[0]) aspect.connect(sink) canvas = diagram.canvas cinfo = canvas.get_connection(commentline.handles()[0]) @@ -146,7 +147,7 @@ def test_iconnect(session, event_manager, element_factory): assert cinfo.constraint is not None assert cinfo.connected is actor, cinfo.connected - Connector(line, handle).disconnect() + ConnectorAspect(line, handle).disconnect() cinfo = diagram.canvas.get_connection(handle) From b9c8379eac3cb189de06395234248670fe12a50b Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 24 Feb 2020 22:13:14 +0100 Subject: [PATCH 17/36] Bang! Rename IConnect to Connector A much more lively name. Despite it being used in the model and Gaphas. --- gaphor/diagram/actions/flowconnect.py | 20 +++++++++---------- gaphor/diagram/classes/classconnect.py | 10 +++++----- gaphor/diagram/classes/interfaceconnect.py | 6 +++--- gaphor/diagram/components/connectorconnect.py | 6 +++--- gaphor/diagram/connectors.py | 6 +++--- gaphor/diagram/diagramtools.py | 12 +++++------ gaphor/diagram/general/commentline.py | 4 ++-- gaphor/diagram/general/connectors.py | 10 +++++----- gaphor/diagram/interactions/messageconnect.py | 6 +++--- gaphor/diagram/profiles/extensionconnect.py | 4 ++-- gaphor/diagram/states/vertexconnect.py | 8 ++++---- gaphor/diagram/tests/fixtures.py | 4 ++-- gaphor/diagram/usecases/usecaseconnect.py | 6 +++--- gaphor/tests/testcase.py | 4 ++-- gaphor/ui/tests/test_handletool.py | 4 ++-- 15 files changed, 55 insertions(+), 55 deletions(-) diff --git a/gaphor/diagram/actions/flowconnect.py b/gaphor/diagram/actions/flowconnect.py index 28abe9bf4..dc246d060 100644 --- a/gaphor/diagram/actions/flowconnect.py +++ b/gaphor/diagram/actions/flowconnect.py @@ -20,7 +20,7 @@ from gaphor.diagram.actions.activitynodes import ( ) from gaphor.diagram.actions.flow import FlowItem from gaphor.diagram.actions.objectnode import ObjectNodeItem -from gaphor.diagram.connectors import IConnect, UnaryRelationshipConnect +from gaphor.diagram.connectors import Connector, UnaryRelationshipConnect class FlowConnect(UnaryRelationshipConnect): @@ -77,7 +77,7 @@ class FlowConnect(UnaryRelationshipConnect): opposite = line.opposite(handle) otc = self.get_connected(opposite) if opposite and isinstance(otc, (ForkNodeItem, DecisionNodeItem)): - adapter = IConnect(otc, line) + adapter = Connector(otc, line) adapter.combine_nodes() def disconnect_subject(self, handle): @@ -86,15 +86,15 @@ class FlowConnect(UnaryRelationshipConnect): opposite = line.opposite(handle) otc = self.get_connected(opposite) if opposite and isinstance(otc, (ForkNodeItem, DecisionNodeItem)): - adapter = IConnect(otc, line) + adapter = Connector(otc, line) adapter.decombine_nodes() -IConnect.register(ActionItem, FlowItem)(FlowConnect) -IConnect.register(ActivityNodeItem, FlowItem)(FlowConnect) -IConnect.register(ObjectNodeItem, FlowItem)(FlowConnect) -IConnect.register(SendSignalActionItem, FlowItem)(FlowConnect) -IConnect.register(AcceptEventActionItem, FlowItem)(FlowConnect) +Connector.register(ActionItem, FlowItem)(FlowConnect) +Connector.register(ActivityNodeItem, FlowItem)(FlowConnect) +Connector.register(ObjectNodeItem, FlowItem)(FlowConnect) +Connector.register(SendSignalActionItem, FlowItem)(FlowConnect) +Connector.register(AcceptEventActionItem, FlowItem)(FlowConnect) class FlowForkDecisionNodeConnect(FlowConnect): @@ -210,7 +210,7 @@ class FlowForkDecisionNodeConnect(FlowConnect): self.decombine_nodes() -@IConnect.register(ForkNodeItem, FlowItem) +@Connector.register(ForkNodeItem, FlowItem) class FlowForkNodeConnect(FlowForkDecisionNodeConnect): """Connect Flow to a ForkNode.""" @@ -218,7 +218,7 @@ class FlowForkNodeConnect(FlowForkDecisionNodeConnect): join_node_cls = UML.JoinNode -@IConnect.register(DecisionNodeItem, FlowItem) +@Connector.register(DecisionNodeItem, FlowItem) class FlowDecisionNodeConnect(FlowForkDecisionNodeConnect): """Connect Flow to a DecisionNode.""" diff --git a/gaphor/diagram/classes/classconnect.py b/gaphor/diagram/classes/classconnect.py index 9d0476100..0432f55ba 100644 --- a/gaphor/diagram/classes/classconnect.py +++ b/gaphor/diagram/classes/classconnect.py @@ -6,14 +6,14 @@ from gaphor.diagram.classes.dependency import DependencyItem from gaphor.diagram.classes.generalization import GeneralizationItem from gaphor.diagram.classes.implementation import ImplementationItem from gaphor.diagram.connectors import ( - IConnect, + Connector, RelationshipConnect, UnaryRelationshipConnect, ) from gaphor.diagram.presentation import Classified, ElementPresentation, Named -@IConnect.register(Named, DependencyItem) +@Connector.register(Named, DependencyItem) class DependencyConnect(RelationshipConnect): """Connect two Named elements using a Dependency.""" @@ -68,7 +68,7 @@ class DependencyConnect(RelationshipConnect): line.subject = relation -@IConnect.register(Classified, GeneralizationItem) +@Connector.register(Classified, GeneralizationItem) class GeneralizationConnect(RelationshipConnect): """Connect Classifiers with a Generalization relationship.""" @@ -84,7 +84,7 @@ class GeneralizationConnect(RelationshipConnect): self.line.subject = relation -@IConnect.register(Classified, AssociationItem) +@Connector.register(Classified, AssociationItem) class AssociationConnect(UnaryRelationshipConnect): """Connect association to classifier.""" @@ -171,7 +171,7 @@ class AssociationConnect(UnaryRelationshipConnect): old.unlink() -@IConnect.register(Named, ImplementationItem) +@Connector.register(Named, ImplementationItem) class ImplementationConnect(RelationshipConnect): """Connect Interface and a BehavioredClassifier using an Implementation.""" diff --git a/gaphor/diagram/classes/interfaceconnect.py b/gaphor/diagram/classes/interfaceconnect.py index 9482314b1..9cf7eb2c7 100644 --- a/gaphor/diagram/classes/interfaceconnect.py +++ b/gaphor/diagram/classes/interfaceconnect.py @@ -11,10 +11,10 @@ from gaphor.diagram.classes.classconnect import DependencyConnect, Implementatio from gaphor.diagram.classes.dependency import DependencyItem from gaphor.diagram.classes.implementation import ImplementationItem from gaphor.diagram.classes.interface import Folded, InterfaceItem -from gaphor.diagram.connectors import IConnect +from gaphor.diagram.connectors import Connector -@IConnect.register(InterfaceItem, ImplementationItem) +@Connector.register(InterfaceItem, ImplementationItem) class ImplementationInterfaceConnect(ImplementationConnect): """Connect interface item and a behaviored classifier using an implementation. @@ -39,7 +39,7 @@ class ImplementationInterfaceConnect(ImplementationConnect): self.line.request_update() -@IConnect.register(InterfaceItem, DependencyItem) +@Connector.register(InterfaceItem, DependencyItem) class DependencyInterfaceConnect(DependencyConnect): """Connect interface item with dependency item.""" diff --git a/gaphor/diagram/components/connectorconnect.py b/gaphor/diagram/components/connectorconnect.py index 3e3933f80..7cf615adc 100644 --- a/gaphor/diagram/components/connectorconnect.py +++ b/gaphor/diagram/components/connectorconnect.py @@ -11,7 +11,7 @@ from gaphor import UML from gaphor.diagram.classes.interface import Folded, InterfaceItem from gaphor.diagram.components.component import ComponentItem from gaphor.diagram.components.connector import ConnectorItem -from gaphor.diagram.connectors import BaseConnector, IConnect +from gaphor.diagram.connectors import BaseConnector, Connector class ConnectorConnectBase(BaseConnector): @@ -186,12 +186,12 @@ class ConnectorConnectBase(BaseConnector): iface.request_update() -@IConnect.register(ComponentItem, ConnectorItem) +@Connector.register(ComponentItem, ConnectorItem) class ComponentConnectorConnect(ConnectorConnectBase): """Connection of connector item to a component.""" -@IConnect.register(InterfaceItem, ConnectorItem) +@Connector.register(InterfaceItem, ConnectorItem) class InterfaceConnectorConnect(ConnectorConnectBase): """Connect connector to an interface to maintain assembly connection. diff --git a/gaphor/diagram/connectors.py b/gaphor/diagram/connectors.py index 49ceaba17..ec44c82ac 100644 --- a/gaphor/diagram/connectors.py +++ b/gaphor/diagram/connectors.py @@ -109,7 +109,7 @@ class BaseConnector: # Work around issue https://github.com/python/mypy/issues/3135 (Class decorators are not type checked) # This definition, along with the the ignore below, seems to fix the behaviour for mypy at least. -IConnect: FunctionDispatcher[Type[BaseConnector]] = multidispatch(object, object)( +Connector: FunctionDispatcher[Type[BaseConnector]] = multidispatch(object, object)( BaseConnector ) @@ -243,7 +243,7 @@ class UnaryRelationshipConnect(BaseConnector): for cinfo in connections or canvas.get_connections(connected=line): if line is cinfo.connected: continue - adapter = IConnect(line, cinfo.connected) + adapter = Connector(line, cinfo.connected) assert adapter, "No element to connect {} and {}".format( line, cinfo.connected ) @@ -267,7 +267,7 @@ class UnaryRelationshipConnect(BaseConnector): solver.solve() connections = list(canvas.get_connections(connected=line)) for cinfo in connections: - adapter = IConnect(cinfo.item, cinfo.connected) + adapter = Connector(cinfo.item, cinfo.connected) adapter.disconnect(cinfo.handle) return connections diff --git a/gaphor/diagram/diagramtools.py b/gaphor/diagram/diagramtools.py index a32356fe8..d5d3cede4 100644 --- a/gaphor/diagram/diagramtools.py +++ b/gaphor/diagram/diagramtools.py @@ -19,7 +19,7 @@ from gaphas.tool import RubberbandTool, Tool, ToolChain from gi.repository import Gdk, Gtk from gaphor.core import Transaction, transactional -from gaphor.diagram.connectors import IConnect +from gaphor.diagram.connectors import Connector from gaphor.diagram.event import DiagramItemPlaced from gaphor.diagram.grouping import Group from gaphor.diagram.inlineeditors import InlineEditor @@ -37,7 +37,7 @@ log = logging.getLogger(__name__) @ConnectorAspect.register(Presentation) class DiagramItemConnector(ItemConnector): """ - Handle Tool (acts on item handles) that uses the IConnect protocol + Handle Tool (acts on item handles) that uses the Connector protocol to connect items to one-another. It also adds handles to lines when a line is grabbed on the middle of @@ -45,7 +45,7 @@ class DiagramItemConnector(ItemConnector): """ def allow(self, sink): - adapter = IConnect(sink.item, self.item) + adapter = Connector(sink.item, self.item) return adapter and adapter.allow(self.handle, sink.port) @transactional @@ -67,7 +67,7 @@ class DiagramItemConnector(ItemConnector): elif cinfo: # first disconnect but disable disconnection handle as # reconnection is going to happen - adapter = IConnect(sink.item, item) + adapter = Connector(sink.item, item) try: connect = adapter.reconnect except AttributeError: @@ -83,7 +83,7 @@ class DiagramItemConnector(ItemConnector): connect(handle, sink.port) else: # new connection - adapter = IConnect(sink.item, item) + adapter = Connector(sink.item, item) self.connect_handle(sink, callback=callback) adapter.connect(handle, sink.port) except Exception: @@ -126,7 +126,7 @@ class DisconnectHandle: else: log.debug(f"Disconnecting {item}.{handle}") if cinfo: - adapter = IConnect(cinfo.connected, item) + adapter = Connector(cinfo.connected, item) adapter.disconnect(handle) diff --git a/gaphor/diagram/general/commentline.py b/gaphor/diagram/general/commentline.py index a5c9545fc..327aac684 100644 --- a/gaphor/diagram/general/commentline.py +++ b/gaphor/diagram/general/commentline.py @@ -3,7 +3,7 @@ CommentLine -- A line that connects a comment to another model element. """ -from gaphor.diagram.connectors import IConnect +from gaphor.diagram.connectors import Connector from gaphor.diagram.presentation import LinePresentation @@ -18,6 +18,6 @@ class CommentLineItem(LinePresentation): c1 = canvas.get_connection(self.head) c2 = canvas.get_connection(self.tail) if c1 and c2: - adapter = IConnect(c1.connected, self) + adapter = Connector(c1.connected, self) adapter.disconnect(self.head) super().unlink() diff --git a/gaphor/diagram/general/connectors.py b/gaphor/diagram/general/connectors.py index c4c215406..c120dca09 100644 --- a/gaphor/diagram/general/connectors.py +++ b/gaphor/diagram/general/connectors.py @@ -5,7 +5,7 @@ Connect comments. import logging from gaphor import UML -from gaphor.diagram.connectors import BaseConnector, IConnect +from gaphor.diagram.connectors import BaseConnector, Connector from gaphor.diagram.general.comment import CommentItem from gaphor.diagram.general.commentline import CommentLineItem from gaphor.diagram.presentation import ElementPresentation, LinePresentation @@ -13,8 +13,8 @@ from gaphor.diagram.presentation import ElementPresentation, LinePresentation logger = logging.getLogger(__name__) -@IConnect.register(CommentItem, CommentLineItem) -@IConnect.register(ElementPresentation, CommentLineItem) +@Connector.register(CommentItem, CommentLineItem) +@Connector.register(ElementPresentation, CommentLineItem) class CommentLineElementConnect(BaseConnector): """Connect a comment line to any element item.""" @@ -97,7 +97,7 @@ class CommentLineElementConnect(BaseConnector): super().disconnect(handle) -@IConnect.register(LinePresentation, CommentLineItem) +@Connector.register(LinePresentation, CommentLineItem) class CommentLineLineConnect(BaseConnector): """Connect a comment line to any diagram line.""" @@ -161,7 +161,7 @@ class CommentLineLineConnect(BaseConnector): super().disconnect(handle) -@IConnect.register(CommentLineItem, LinePresentation) +@Connector.register(CommentLineItem, LinePresentation) class InverseCommentLineLineConnect(CommentLineLineConnect): """ In case a line is disconnected that contains a comment-line, diff --git a/gaphor/diagram/interactions/messageconnect.py b/gaphor/diagram/interactions/messageconnect.py index 2a820676c..f3b707843 100644 --- a/gaphor/diagram/interactions/messageconnect.py +++ b/gaphor/diagram/interactions/messageconnect.py @@ -3,7 +3,7 @@ from typing import Optional from gaphor import UML -from gaphor.diagram.connectors import BaseConnector, IConnect +from gaphor.diagram.connectors import BaseConnector, Connector from gaphor.diagram.interactions.executionspecification import ( ExecutionSpecificationItem, ) @@ -11,7 +11,7 @@ from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.interactions.message import MessageItem -@IConnect.register(LifelineItem, MessageItem) +@Connector.register(LifelineItem, MessageItem) class MessageLifelineConnect(BaseConnector): """Connect lifeline with a message. @@ -137,7 +137,7 @@ class MessageLifelineConnect(BaseConnector): lifetime.min_length = lifetime.MIN_LENGTH -@IConnect.register(LifelineItem, ExecutionSpecificationItem) +@Connector.register(LifelineItem, ExecutionSpecificationItem) class ExecutionSpecificationConnect(BaseConnector): element: LifelineItem diff --git a/gaphor/diagram/profiles/extensionconnect.py b/gaphor/diagram/profiles/extensionconnect.py index 371212817..f2b713100 100644 --- a/gaphor/diagram/profiles/extensionconnect.py +++ b/gaphor/diagram/profiles/extensionconnect.py @@ -1,10 +1,10 @@ from gaphor import UML -from gaphor.diagram.connectors import IConnect, RelationshipConnect +from gaphor.diagram.connectors import Connector, RelationshipConnect from gaphor.diagram.presentation import Classified from gaphor.diagram.profiles.extension import ExtensionItem -@IConnect.register(Classified, ExtensionItem) +@Connector.register(Classified, ExtensionItem) class ExtensionConnect(RelationshipConnect): """Connect class and stereotype items using an extension item.""" diff --git a/gaphor/diagram/states/vertexconnect.py b/gaphor/diagram/states/vertexconnect.py index d6b95c76e..16476fafd 100644 --- a/gaphor/diagram/states/vertexconnect.py +++ b/gaphor/diagram/states/vertexconnect.py @@ -7,7 +7,7 @@ gaphor.adapter package. """ from gaphor import UML -from gaphor.diagram.connectors import IConnect, RelationshipConnect +from gaphor.diagram.connectors import Connector, RelationshipConnect from gaphor.diagram.states.pseudostates import ( HistoryPseudostateItem, InitialPseudostateItem, @@ -35,7 +35,7 @@ class VertexConnect(RelationshipConnect): relation.guard = self.line.model.create(UML.Constraint) -@IConnect.register(VertexItem, TransitionItem) +@Connector.register(VertexItem, TransitionItem) class TransitionConnect(VertexConnect): """Connect two state vertices using transition item.""" @@ -59,7 +59,7 @@ class TransitionConnect(VertexConnect): return None -@IConnect.register(InitialPseudostateItem, TransitionItem) +@Connector.register(InitialPseudostateItem, TransitionItem) class InitialPseudostateTransitionConnect(VertexConnect): """Connect initial pseudostate using transition item. @@ -90,7 +90,7 @@ class InitialPseudostateTransitionConnect(VertexConnect): return None -@IConnect.register(HistoryPseudostateItem, TransitionItem) +@Connector.register(HistoryPseudostateItem, TransitionItem) class HistoryPseudostateTransitionConnect(VertexConnect): """Connect history pseudostate using transition item. diff --git a/gaphor/diagram/tests/fixtures.py b/gaphor/diagram/tests/fixtures.py index b8d628c72..a5f087d75 100644 --- a/gaphor/diagram/tests/fixtures.py +++ b/gaphor/diagram/tests/fixtures.py @@ -3,7 +3,7 @@ from gaphas.aspect import ConnectionSink from gaphas.aspect import Connector as ConnectorAspect from gaphor import UML -from gaphor.diagram.connectors import IConnect +from gaphor.diagram.connectors import Connector from gaphor.services.eventmanager import EventManager from gaphor.UML.elementfactory import ElementFactory @@ -22,7 +22,7 @@ def allow(line, handle, item, port=None): if port is None and len(item.ports()) > 0: port = item.ports()[0] - adapter = IConnect(item, line) + adapter = Connector(item, line) return adapter.allow(handle, port) diff --git a/gaphor/diagram/usecases/usecaseconnect.py b/gaphor/diagram/usecases/usecaseconnect.py index 8b55ae338..e6bbc82ba 100644 --- a/gaphor/diagram/usecases/usecaseconnect.py +++ b/gaphor/diagram/usecases/usecaseconnect.py @@ -3,13 +3,13 @@ Use cases related connection adapters. """ from gaphor import UML -from gaphor.diagram.connectors import IConnect, RelationshipConnect +from gaphor.diagram.connectors import Connector, RelationshipConnect from gaphor.diagram.usecases.extend import ExtendItem from gaphor.diagram.usecases.include import IncludeItem from gaphor.diagram.usecases.usecase import UseCaseItem -@IConnect.register(UseCaseItem, IncludeItem) +@Connector.register(UseCaseItem, IncludeItem) class IncludeConnect(RelationshipConnect): """Connect use cases with an include item relationship.""" @@ -33,7 +33,7 @@ class IncludeConnect(RelationshipConnect): self.line.subject = relation -@IConnect.register(UseCaseItem, ExtendItem) +@Connector.register(UseCaseItem, ExtendItem) class ExtendConnect(RelationshipConnect): """Connect use cases with an extend item relationship.""" diff --git a/gaphor/tests/testcase.py b/gaphor/tests/testcase.py index 3a12a6289..9a4fee617 100644 --- a/gaphor/tests/testcase.py +++ b/gaphor/tests/testcase.py @@ -17,7 +17,7 @@ from gaphas.aspect import Connector as ConnectorAspect import gaphor.diagram.diagramtools # noqa from gaphor import UML from gaphor.application import Session -from gaphor.diagram.connectors import IConnect +from gaphor.diagram.connectors import Connector from gaphor.diagram.grouping import Group T = TypeVar("T") @@ -67,7 +67,7 @@ class TestCase(unittest.TestCase): if port is None and len(item.ports()) > 0: port = item.ports()[0] - adapter = IConnect(item, line) + adapter = Connector(item, line) return adapter.allow(handle, port) def connect(self, line, handle, item, port=None): diff --git a/gaphor/ui/tests/test_handletool.py b/gaphor/ui/tests/test_handletool.py index 93e07922c..f76ab7b35 100644 --- a/gaphor/ui/tests/test_handletool.py +++ b/gaphor/ui/tests/test_handletool.py @@ -9,7 +9,7 @@ from gi.repository import Gdk, Gtk from gaphor import UML from gaphor.application import Session -from gaphor.diagram.connectors import IConnect +from gaphor.diagram.connectors import Connector from gaphor.diagram.diagramtools import ConnectHandleTool, DiagramItemConnector from gaphor.diagram.general.comment import CommentItem from gaphor.diagram.general.commentline import CommentLineItem @@ -79,7 +79,7 @@ def test_aspect_type(commentline): def test_query(comment, commentline): - assert IConnect(comment, commentline) + assert Connector(comment, commentline) def test_allow(commentline, comment): From 03def5b40a37d8e5a68d322fb660efdb2f00f43c Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 24 Feb 2020 22:25:23 +0100 Subject: [PATCH 18/36] Rename interactions connector module --- gaphor/diagram/interactions/__init__.py | 2 +- .../interactions/{messageconnect.py => interactionsconnect.py} | 0 .../diagram/interactions/tests/test_executionspecification.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename gaphor/diagram/interactions/{messageconnect.py => interactionsconnect.py} (100%) diff --git a/gaphor/diagram/interactions/__init__.py b/gaphor/diagram/interactions/__init__.py index bd1d35eae..b53e12ee7 100644 --- a/gaphor/diagram/interactions/__init__.py +++ b/gaphor/diagram/interactions/__init__.py @@ -8,7 +8,7 @@ from gaphor.diagram.interactions.message import MessageItem def _load(): from gaphor.diagram.interactions import ( - messageconnect, + interactionsconnect, interactionsgrouping, interactionspropertypages, ) diff --git a/gaphor/diagram/interactions/messageconnect.py b/gaphor/diagram/interactions/interactionsconnect.py similarity index 100% rename from gaphor/diagram/interactions/messageconnect.py rename to gaphor/diagram/interactions/interactionsconnect.py diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index f1f3ac916..05419672d 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -46,7 +46,7 @@ def test_connect_execution_specification_to_lifeline(diagram, element_factory): ) -def test_connect_execution_specification_to_lifeline(diagram, element_factory): +def test_disconnect_execution_specification_from_lifeline(diagram, element_factory): lifeline = diagram.create( LifelineItem, subject=element_factory.create(UML.Lifeline) ) From 5fcdb88efd67477d033e06c50676d06e43f5003a Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 24 Feb 2020 22:41:45 +0100 Subject: [PATCH 19/36] Fix Flake8 issues --- gaphor/diagram/interactions/tests/test_executionspecification.py | 1 - gaphor/diagram/tests/conftest.py | 1 + gaphor/diagram/tests/test_presentation.py | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 gaphor/diagram/tests/conftest.py diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index 05419672d..74e074b80 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -1,6 +1,5 @@ import pytest from gaphas.canvas import Canvas, Context, instant_cairo_context -from gaphas.view import Context from gaphor import UML from gaphor.diagram.interactions.executionspecification import ( diff --git a/gaphor/diagram/tests/conftest.py b/gaphor/diagram/tests/conftest.py new file mode 100644 index 000000000..fe42227de --- /dev/null +++ b/gaphor/diagram/tests/conftest.py @@ -0,0 +1 @@ +from gaphor.diagram.tests.fixtures import diagram, element_factory diff --git a/gaphor/diagram/tests/test_presentation.py b/gaphor/diagram/tests/test_presentation.py index 2f34f4069..ffab1e826 100644 --- a/gaphor/diagram/tests/test_presentation.py +++ b/gaphor/diagram/tests/test_presentation.py @@ -1,6 +1,5 @@ from gaphor import UML from gaphor.diagram.presentation import ElementPresentation, LinePresentation -from gaphor.diagram.tests.fixtures import diagram, element_factory class DummyVisualComponent: From 20cfc5d49f617891e29a71e1f4c2f9f4fa51c672 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Tue, 25 Feb 2020 15:42:56 +0100 Subject: [PATCH 20/36] Do not run tests on release --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 738793468..a22f433e0 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ help: ## Show this help @echo "make , where is one of:" @grep -hP "\t##" $(MAKEFILE_LIST) | sed -e 's/^\([a-z]*\):.*## / \1\t/' | expand -t14 -dist: test translate ## Build application distribution (requires Poetry) +dist: translate ## Build application distribution (requires Poetry) poetry build test: ## Run all but slow tests (requires PyTest) From 340dedbe5fb026680984e3fcadd5b4fd9dc172d8 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 26 Feb 2020 22:34:12 +0100 Subject: [PATCH 21/36] Allow execspec to connect to execspec Still need to move ports in the right place. --- gaphor/diagram/connectors.py | 21 +-- .../interactions/executionspecification.py | 4 +- .../interactions/interactionsconnect.py | 48 +++++- .../tests/test_executionspecification.py | 139 +++++++++++++++++- 4 files changed, 187 insertions(+), 25 deletions(-) diff --git a/gaphor/diagram/connectors.py b/gaphor/diagram/connectors.py index ec44c82ac..e61dac8ad 100644 --- a/gaphor/diagram/connectors.py +++ b/gaphor/diagram/connectors.py @@ -46,10 +46,10 @@ class BaseConnector: def __init__( self, - element: ElementPresentation[UML.Element], - line: LinePresentation[UML.Element], + element: UML.Presentation[UML.Element], + line: UML.Presentation[UML.Element], ) -> None: - assert element.canvas == line.canvas + assert element.canvas is line.canvas self.element = element self.line = line self.canvas = element.canvas @@ -61,7 +61,7 @@ class BaseConnector: assert self.canvas return self.canvas.get_connection(handle) - def get_connected(self, handle: Handle) -> Optional[UML.Presentation]: + def get_connected(self, handle: Handle) -> Optional[UML.Presentation[UML.Element]]: """ Get item connected to a handle. """ @@ -71,16 +71,6 @@ class BaseConnector: return cinfo.connected # type: ignore[no-any-return] # noqa: F723 return None - def get_connected_port(self, handle: Handle) -> Optional[Port]: - """ - Get port of item connected to connecting item via specified handle. - """ - assert self.canvas - cinfo = self.canvas.get_connection(handle) - if cinfo: - return cinfo.port - return None - def allow(self, handle: Handle, port: Port) -> bool: """ Determine if items can be connected. @@ -126,6 +116,9 @@ class UnaryRelationshipConnect(BaseConnector): on the canvas. """ + element: ElementPresentation[UML.Element] + line: LinePresentation[UML.Element] + def relationship( self, required_type: Type[UML.Element], head: relation, tail: relation ) -> Optional[UML.Element]: diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index a30b1e655..fc018e80b 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -43,12 +43,12 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I self._min_height = 10 ht, hb = Handle(), Handle() - ht.connectable = True + hb.connectable = True # TODO: need better interface for this! self._handles.append(ht) self._handles.append(hb) - # self._ports.append(LinePort(h1.pos, h2.pos)) + self._ports.append(LinePort(ht.pos, hb.pos)) self.constraint(above=(ht.pos, hb.pos), delta=self._min_height) self.constraint(vertical=(ht.pos, hb.pos)) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index f3b707843..54c711391 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -9,6 +9,7 @@ from gaphor.diagram.interactions.executionspecification import ( ) from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.interactions.message import MessageItem +from gaphor.diagram.presentation import ElementPresentation @Connector.register(LifelineItem, MessageItem) @@ -21,6 +22,7 @@ class MessageLifelineConnect(BaseConnector): added to a lifetime line, it's considered a sequence diagram. """ + element: LifelineItem line: MessageItem def connect_lifelines(self, line, send, received): @@ -55,7 +57,7 @@ class MessageLifelineConnect(BaseConnector): there are no lifelines connected on both ends, then remove the message from the data model. """ - send = self.get_connected(line.head) + send: Optional[UML.Presentation[UML.Element]] = self.get_connected(line.head) received = self.get_connected(line.tail) if send: @@ -138,7 +140,7 @@ class MessageLifelineConnect(BaseConnector): @Connector.register(LifelineItem, ExecutionSpecificationItem) -class ExecutionSpecificationConnect(BaseConnector): +class LifelineExecutionSpecificationConnect(BaseConnector): element: LifelineItem line: ExecutionSpecificationItem @@ -167,6 +169,10 @@ class ExecutionSpecificationConnect(BaseConnector): finish_occurence.covered = lifeline finish_occurence.execution = exec_spec + canvas = self.line.canvas + assert canvas + for cinfo in canvas.get_connections(connected=self.line): + Connector(self.line, cinfo.item).connect(cinfo.handle, cinfo.port) return True def disconnect(self, handle): @@ -174,3 +180,41 @@ class ExecutionSpecificationConnect(BaseConnector): del self.line.subject if exec_spec: exec_spec.unlink() + + canvas = self.canvas + assert canvas + for cinfo in canvas.get_connections(connected=self.line): + Connector(self.line, cinfo.item).disconnect(cinfo.handle) + + +@Connector.register(ExecutionSpecificationItem, ExecutionSpecificationItem) +class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): + + element: ExecutionSpecificationItem + line: ExecutionSpecificationItem + + def allow(self, _handle, _port): + return True + + def connect(self, handle, _port): + parent_exec_spec = self.element.subject + child_exec_spec: UML.ExecutionSpecification = self.line.subject + model = self.element.model + + if not parent_exec_spec: + # Can connect child exec spec if parent is not connected + return True + + connected_item: Optional[UML.Presentation[UML.Element]] = self.get_connected( + self.element.handles()[0] + ) + assert connected_item + return Connector(connected_item, self.line).connect(handle, None) + + def disconnect(self, handle): + exec_spec: Optional[UML.ExecutionSpecification] = self.line.subject + del self.line.subject + if exec_spec: + exec_spec.unlink() + + # TODO: also disconnect items connected to this item diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index 74e074b80..50cdd0e48 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -46,6 +46,9 @@ def test_connect_execution_specification_to_lifeline(diagram, element_factory): def test_disconnect_execution_specification_from_lifeline(diagram, element_factory): + def elements_of_kind(type): + return element_factory.lselect(lambda e: e.isKindOf(type)) + lifeline = diagram.create( LifelineItem, subject=element_factory.create(UML.Lifeline) ) @@ -58,12 +61,134 @@ def test_disconnect_execution_specification_from_lifeline(diagram, element_facto assert lifeline.subject assert exec_spec.subject is None assert exec_spec.canvas - assert ( - element_factory.lselect(lambda e: e.isKindOf(UML.ExecutionSpecification)) == [] + assert elements_of_kind(UML.ExecutionSpecification) == [] + assert elements_of_kind(UML.ExecutionOccurrenceSpecification) == [] + + +def test_allow_execution_specification_to_execution_specification(diagram): + parent_exec_spec = diagram.create(ExecutionSpecificationItem) + child_exec_spec = diagram.create(ExecutionSpecificationItem) + + glued = allow( + parent_exec_spec, + parent_exec_spec.handles()[0], + child_exec_spec, + child_exec_spec.ports()[0], ) - assert ( - element_factory.lselect( - lambda e: e.isKindOf(UML.ExecutionOccurrenceSpecification) - ) - == [] + + assert glued + + +def test_connect_execution_specification_to_execution_specification( + diagram, element_factory +): + parent_exec_spec = diagram.create(ExecutionSpecificationItem) + child_exec_spec = diagram.create(ExecutionSpecificationItem) + + connect( + child_exec_spec, + child_exec_spec.handles()[0], + parent_exec_spec, + parent_exec_spec.ports()[0], ) + + assert not parent_exec_spec.subject + assert not child_exec_spec.subject + + +def test_connect_execution_specification_to_execution_specification_with_lifeline( + diagram, element_factory +): + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + parent_exec_spec = diagram.create(ExecutionSpecificationItem) + child_exec_spec = diagram.create(ExecutionSpecificationItem) + connect( + parent_exec_spec, + parent_exec_spec.handles()[0], + lifeline, + lifeline.lifetime.port, + ) + + connect( + child_exec_spec, + child_exec_spec.handles()[0], + parent_exec_spec, + parent_exec_spec.ports()[0], + ) + + assert child_exec_spec.subject + assert lifeline.subject + assert child_exec_spec.subject.start.covered is lifeline.subject + assert ( + child_exec_spec.subject.executionOccurrenceSpecification[0].covered + is lifeline.subject + ) + + +def test_connect_execution_specification_with_execution_specification_to_lifeline( + diagram, element_factory +): + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + parent_exec_spec = diagram.create(ExecutionSpecificationItem) + child_exec_spec = diagram.create(ExecutionSpecificationItem) + connect( + child_exec_spec, + child_exec_spec.handles()[0], + parent_exec_spec, + parent_exec_spec.ports()[0], + ) + + connect( + parent_exec_spec, + parent_exec_spec.handles()[0], + lifeline, + lifeline.lifetime.port, + ) + + assert parent_exec_spec.subject + assert child_exec_spec.subject + assert lifeline.subject + assert parent_exec_spec.subject.start.covered is lifeline.subject + assert child_exec_spec.subject.start.covered is lifeline.subject + assert ( + child_exec_spec.subject.executionOccurrenceSpecification[0].covered + is lifeline.subject + ) + + +def test_disconnect_execution_specification_with_execution_specification_from_lifeline( + diagram, element_factory +): + elements_of_kind = lambda type: element_factory.lselect(lambda e: e.isKindOf(type)) + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + parent_exec_spec = diagram.create(ExecutionSpecificationItem) + child_exec_spec = diagram.create(ExecutionSpecificationItem) + connect( + parent_exec_spec, + parent_exec_spec.handles()[0], + lifeline, + lifeline.lifetime.port, + ) + connect( + child_exec_spec, + child_exec_spec.handles()[0], + parent_exec_spec, + parent_exec_spec.ports()[0], + ) + + disconnect(parent_exec_spec, parent_exec_spec.handles()[0]) + + assert lifeline.subject + assert parent_exec_spec.subject is None + assert child_exec_spec.subject is None + assert elements_of_kind(UML.ExecutionSpecification) == [] + assert elements_of_kind(UML.ExecutionOccurrenceSpecification) == [] From f7e85cf91f9b2b36211620f5356cc186227e16a3 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 27 Feb 2020 21:19:36 +0100 Subject: [PATCH 22/36] Add execspecs as child items to lifeline --- gaphor/diagram/diagramtoolbox.py | 1 + .../interactions/executionspecification.py | 2 +- .../interactions/interactionsconnect.py | 36 ++++++++++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/gaphor/diagram/diagramtoolbox.py b/gaphor/diagram/diagramtoolbox.py index 41e99a652..8d9275a19 100644 --- a/gaphor/diagram/diagramtoolbox.py +++ b/gaphor/diagram/diagramtoolbox.py @@ -380,6 +380,7 @@ TOOLBOX_ACTIONS: Sequence[Tuple[str, Sequence[ToolDef]]] = ( item_factory=PlacementTool.new_item_factory( diagram.interactions.ExecutionSpecificationItem ), + handle_index=0, ), ToolDef( "toolbox-interaction", diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index fc018e80b..7ece16f4a 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -43,7 +43,7 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I self._min_height = 10 ht, hb = Handle(), Handle() - hb.connectable = True + ht.connectable = True # TODO: need better interface for this! self._handles.append(ht) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 54c711391..54dd2b930 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -12,6 +12,22 @@ from gaphor.diagram.interactions.message import MessageItem from gaphor.diagram.presentation import ElementPresentation +def reparent(canvas, item, new_parent): + old_parent = canvas.get_parent(item) + + if old_parent: + canvas.reparent(item, None) + m = canvas.get_matrix_i2c(old_parent) + item.matrix *= m + old_parent.request_update() + + if new_parent: + canvas.reparent(item, new_parent) + m = canvas.get_matrix_c2i(new_parent) + item.matrix *= m + new_parent.request_update() + + @Connector.register(LifelineItem, MessageItem) class MessageLifelineConnect(BaseConnector): """Connect lifeline with a message. @@ -171,6 +187,9 @@ class LifelineExecutionSpecificationConnect(BaseConnector): canvas = self.line.canvas assert canvas + + reparent(canvas, self.line, self.element) + for cinfo in canvas.get_connections(connected=self.line): Connector(self.line, cinfo.item).connect(cinfo.handle, cinfo.port) return True @@ -183,6 +202,11 @@ class LifelineExecutionSpecificationConnect(BaseConnector): canvas = self.canvas assert canvas + + if canvas.get_parent(self.line) is self.element: + new_parent = canvas.get_parent(self.element) + reparent(canvas, self.line, new_parent) + for cinfo in canvas.get_connections(connected=self.line): Connector(self.line, cinfo.item).disconnect(cinfo.handle) @@ -205,11 +229,15 @@ class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): # Can connect child exec spec if parent is not connected return True - connected_item: Optional[UML.Presentation[UML.Element]] = self.get_connected( - self.element.handles()[0] - ) + connected_item: Optional[UML.Presentation[UML.Element]] + connected_item = self.get_connected(self.element.handles()[0]) assert connected_item - return Connector(connected_item, self.line).connect(handle, None) + Connector(connected_item, self.line).connect(handle, None) + + canvas = self.line.canvas + reparent(canvas, self.line, self.element) + + return True def disconnect(self, handle): exec_spec: Optional[UML.ExecutionSpecification] = self.line.subject From bd0538f1cc151e0716eefd5bf4303e90558539f7 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 27 Feb 2020 22:03:54 +0100 Subject: [PATCH 23/36] Fix undesired item connections Add ConnectorProtocol and fix a bunch of typing errors --- gaphor/diagram/components/connector.py | 2 +- gaphor/diagram/components/connectorconnect.py | 21 ++++-------- gaphor/diagram/connectors.py | 33 +++++++++++++++++-- gaphor/diagram/general/connectors.py | 11 ++++++- gaphor/diagram/profiles/extensionconnect.py | 9 +++-- 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/gaphor/diagram/components/connector.py b/gaphor/diagram/components/connector.py index 4aded371f..89b53063d 100644 --- a/gaphor/diagram/components/connector.py +++ b/gaphor/diagram/components/connector.py @@ -108,7 +108,7 @@ from gaphor.UML.modelfactory import stereotypes_str @represents(UML.Connector) -class ConnectorItem(LinePresentation, Named): +class ConnectorItem(LinePresentation[UML.Connector], Named): """ Connector item line. diff --git a/gaphor/diagram/components/connectorconnect.py b/gaphor/diagram/components/connectorconnect.py index 7cf615adc..9630a3e5f 100644 --- a/gaphor/diagram/components/connectorconnect.py +++ b/gaphor/diagram/components/connectorconnect.py @@ -6,6 +6,7 @@ Implemented using interface item in assembly connector mode, see """ import operator +from typing import Union from gaphor import UML from gaphor.diagram.classes.interface import Folded, InterfaceItem @@ -14,7 +15,13 @@ from gaphor.diagram.components.connector import ConnectorItem from gaphor.diagram.connectors import BaseConnector, Connector +@Connector.register(ComponentItem, ConnectorItem) +@Connector.register(InterfaceItem, ConnectorItem) class ConnectorConnectBase(BaseConnector): + + element: Union[ComponentItem, InterfaceItem] + line: ConnectorItem + def _get_interfaces(self, c1, c2): """ Return list of common interfaces provided by first component and @@ -184,17 +191,3 @@ class ConnectorConnectBase(BaseConnector): c = self.get_component(line) self.drop_uml(line, c) iface.request_update() - - -@Connector.register(ComponentItem, ConnectorItem) -class ComponentConnectorConnect(ConnectorConnectBase): - """Connection of connector item to a component.""" - - -@Connector.register(InterfaceItem, ConnectorItem) -class InterfaceConnectorConnect(ConnectorConnectBase): - """Connect connector to an interface to maintain assembly connection. - - See also `BaseConnector` class for exception of interface item - connections. - """ diff --git a/gaphor/diagram/connectors.py b/gaphor/diagram/connectors.py index e61dac8ad..a66ef5a3c 100644 --- a/gaphor/diagram/connectors.py +++ b/gaphor/diagram/connectors.py @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, List, Optional, Type, TypeVar, Union from gaphas.canvas import Connection from gaphas.connector import Handle, Port from generic.multidispatch import FunctionDispatcher, multidispatch +from typing_extensions import Protocol from gaphor import UML from gaphor.diagram.presentation import ElementPresentation, LinePresentation @@ -20,6 +21,20 @@ from gaphor.UML.properties import association, redefine, relation T = TypeVar("T", bound=UML.Element) +class ConnectorProtocol(Protocol): + def __init__(self, element: object, line: object,) -> None: + ... + + def allow(self, handle: Handle, port: Port) -> bool: + ... + + def connect(self, handle: Handle, port: Port) -> bool: + ... + + def disconnect(self, handle: Handle) -> None: + ... + + class BaseConnector: """ Connection adapter for Gaphor diagram items. @@ -97,10 +112,24 @@ class BaseConnector: """Disconnect UML model level connections.""" +class NoConnector: + def __init__(self, element, line,) -> None: + pass + + def allow(self, handle: Handle, port: Port) -> bool: + return False + + def connect(self, handle: Handle, port: Port) -> bool: + return False + + def disconnect(self, handle: Handle) -> None: + pass + + # Work around issue https://github.com/python/mypy/issues/3135 (Class decorators are not type checked) # This definition, along with the the ignore below, seems to fix the behaviour for mypy at least. -Connector: FunctionDispatcher[Type[BaseConnector]] = multidispatch(object, object)( - BaseConnector +Connector: FunctionDispatcher[Type[ConnectorProtocol]] = multidispatch(object, object)( + NoConnector ) diff --git a/gaphor/diagram/general/connectors.py b/gaphor/diagram/general/connectors.py index c120dca09..1e88f0139 100644 --- a/gaphor/diagram/general/connectors.py +++ b/gaphor/diagram/general/connectors.py @@ -3,6 +3,7 @@ Connect comments. """ import logging +from typing import Union from gaphor import UML from gaphor.diagram.connectors import BaseConnector, Connector @@ -18,6 +19,7 @@ logger = logging.getLogger(__name__) class CommentLineElementConnect(BaseConnector): """Connect a comment line to any element item.""" + element: Union[CommentItem, ElementPresentation] line: CommentLineItem def allow(self, handle, port): @@ -88,6 +90,7 @@ class CommentLineElementConnect(BaseConnector): if hct.subject and isinstance(oct.subject, UML.Comment): del oct.subject.annotatedElement[hct.subject] elif hct.subject and oct.subject: + assert isinstance(hct.subject, UML.Comment) del hct.subject.annotatedElement[oct.subject] except ValueError: logger.debug( @@ -101,6 +104,9 @@ class CommentLineElementConnect(BaseConnector): class CommentLineLineConnect(BaseConnector): """Connect a comment line to any diagram line.""" + element: LinePresentation + line: CommentLineItem + def allow(self, handle, port): """ In addition to the normal check, both line ends may not be connected @@ -156,7 +162,10 @@ class CommentLineLineConnect(BaseConnector): and c2.subject in c1.subject.annotatedElement ): del c1.subject.annotatedElement[c2.subject] - elif c2.subject and c1.subject in c2.subject.annotatedElement: + elif ( + isinstance(c2.subject, UML.Comment) + and c1.subject in c2.subject.annotatedElement + ): del c2.subject.annotatedElement[c1.subject] super().disconnect(handle) diff --git a/gaphor/diagram/profiles/extensionconnect.py b/gaphor/diagram/profiles/extensionconnect.py index f2b713100..564273f52 100644 --- a/gaphor/diagram/profiles/extensionconnect.py +++ b/gaphor/diagram/profiles/extensionconnect.py @@ -1,3 +1,5 @@ +from typing import Optional + from gaphor import UML from gaphor.diagram.connectors import Connector, RelationshipConnect from gaphor.diagram.presentation import Classified @@ -31,8 +33,11 @@ class ExtensionConnect(RelationshipConnect): c1 = self.get_connected(line.head) c2 = self.get_connected(line.tail) if c1 and c2: - head_type = c1.subject - tail_type = c2.subject + assert isinstance(c1.subject, UML.Class) + assert isinstance(c2.subject, UML.Stereotype) + + head_type: UML.Class = c1.subject + tail_type: UML.Stereotype = c2.subject # First check if we do not already contain the right subject: if line.subject: From 36ac59dae257ab85e86c13ac0cbb01efcc324473 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 27 Feb 2020 22:17:20 +0100 Subject: [PATCH 24/36] Add ports to both sides of the exec spec --- .../interactions/executionspecification.py | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index 7ece16f4a..23ca7277b 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -23,8 +23,9 @@ Stick with BehaviorExecutionSpecification, since it has a [0..1] relation to beh ActionExecutionSpecification has a [1] relation to action. """ from gaphas import Handle, Item -from gaphas.connector import LinePort +from gaphas.connector import LinePort, Position from gaphas.geometry import Rectangle, distance_rectangle_point +from gaphas.solver import WEAK from gaphor import UML from gaphor.diagram.shapes import Box, draw_border @@ -40,7 +41,7 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I def __init__(self, id=None, model=None): super().__init__(id, model) - self._min_height = 10 + self.bar_width = 12 ht, hb = Handle(), Handle() ht.connectable = True @@ -48,19 +49,27 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I # TODO: need better interface for this! self._handles.append(ht) self._handles.append(hb) - self._ports.append(LinePort(ht.pos, hb.pos)) - self.constraint(above=(ht.pos, hb.pos), delta=self._min_height) self.constraint(vertical=(ht.pos, hb.pos)) - self.shape = Box( - style={"fill": "white"}, draw=draw_border - ) # self.draw_execution_specification) + r = self.bar_width / 2 + nw = Position((-r, 0), strength=WEAK) + ne = Position((r, 0), strength=WEAK) + se = Position((r, 0), strength=WEAK) + sw = Position((-r, 0), strength=WEAK) + + self.constraint(horizontal=(sw, hb.pos)) + self.constraint(horizontal=(se, hb.pos)) + + self._ports.append(LinePort(nw, sw)) + self._ports.append(LinePort(ne, se)) + + self.shape = Box(style={"fill": "white"}, draw=draw_border) def dimensions(self): - d = 10 + d = self.bar_width pt, pb = (h.pos for h in self._handles) - return Rectangle(pt.x - d / 2, pt.y, d, pb.y - pt.y) + return Rectangle(pt.x - d / 2, pt.y, d, y1=pb.y) def draw(self, context): self.shape.draw(context, self.dimensions()) From 63d281bcc261949561d3d05c41172672ef69ef79 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 27 Feb 2020 22:23:55 +0100 Subject: [PATCH 25/36] Disconnect execspecs recursively --- gaphor/diagram/interactions/interactionsconnect.py | 7 +++++-- .../interactions/tests/test_executionspecification.py | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 54dd2b930..089bb215c 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -234,7 +234,7 @@ class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): assert connected_item Connector(connected_item, self.line).connect(handle, None) - canvas = self.line.canvas + canvas = self.canvas reparent(canvas, self.line, self.element) return True @@ -245,4 +245,7 @@ class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): if exec_spec: exec_spec.unlink() - # TODO: also disconnect items connected to this item + canvas = self.canvas + assert canvas + for cinfo in canvas.get_connections(connected=self.line): + Connector(self.line, cinfo.item).disconnect(cinfo.handle) diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index 50cdd0e48..dc9e4a92b 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -172,6 +172,7 @@ def test_disconnect_execution_specification_with_execution_specification_from_li lifeline.lifetime.visible = True parent_exec_spec = diagram.create(ExecutionSpecificationItem) child_exec_spec = diagram.create(ExecutionSpecificationItem) + grand_child_exec_spec = diagram.create(ExecutionSpecificationItem) connect( parent_exec_spec, parent_exec_spec.handles()[0], @@ -184,11 +185,18 @@ def test_disconnect_execution_specification_with_execution_specification_from_li parent_exec_spec, parent_exec_spec.ports()[0], ) + connect( + grand_child_exec_spec, + grand_child_exec_spec.handles()[0], + child_exec_spec, + child_exec_spec.ports()[0], + ) disconnect(parent_exec_spec, parent_exec_spec.handles()[0]) assert lifeline.subject assert parent_exec_spec.subject is None assert child_exec_spec.subject is None + assert grand_child_exec_spec.subject is None assert elements_of_kind(UML.ExecutionSpecification) == [] assert elements_of_kind(UML.ExecutionOccurrenceSpecification) == [] From 21969da8f2865e1f524e8362bd6a077345101699 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 27 Feb 2020 23:03:14 +0100 Subject: [PATCH 26/36] Type BaseConnector.canvas This saves a lot of checks in the code. --- gaphor/diagram/connectors.py | 10 ++------- .../interactions/interactionsconnect.py | 22 +++++-------------- gaphor/diagram/states/vertexconnect.py | 2 -- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/gaphor/diagram/connectors.py b/gaphor/diagram/connectors.py index a66ef5a3c..6abd37779 100644 --- a/gaphor/diagram/connectors.py +++ b/gaphor/diagram/connectors.py @@ -9,7 +9,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, List, Optional, Type, TypeVar, Union -from gaphas.canvas import Connection +from gaphas.canvas import Canvas, Connection from gaphas.connector import Handle, Port from generic.multidispatch import FunctionDispatcher, multidispatch from typing_extensions import Protocol @@ -67,20 +67,18 @@ class BaseConnector: assert element.canvas is line.canvas self.element = element self.line = line - self.canvas = element.canvas + self.canvas: Canvas = element.canvas def get_connection(self, handle: Handle) -> Optional[Connection]: """ Get connection information """ - assert self.canvas return self.canvas.get_connection(handle) def get_connected(self, handle: Handle) -> Optional[UML.Presentation[UML.Element]]: """ Get item connected to a handle. """ - assert self.canvas cinfo = self.canvas.get_connection(handle) if cinfo: return cinfo.connected # type: ignore[no-any-return] # noqa: F723 @@ -254,8 +252,6 @@ class UnaryRelationshipConnect(BaseConnector): Cause items connected to ``line`` to reconnect, allowing them to establish or destroy relationships at model level. """ - assert self.canvas - line = self.line canvas = self.canvas solver = canvas.solver @@ -279,8 +275,6 @@ class UnaryRelationshipConnect(BaseConnector): Returns a list of (item, handle) pairs that were connected (this list can be used to connect items again with connect_connected_items()). """ - assert self.canvas - line = self.line canvas = self.canvas solver = canvas.solver diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 089bb215c..2d311c7c3 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -115,8 +115,6 @@ class MessageLifelineConnect(BaseConnector): return not (lifetime.visible ^ (port is element.lifetime.port)) def connect(self, handle, port): - super().connect(handle, port) - line = self.line send = self.get_connected(line.head) received = self.get_connected(line.tail) @@ -129,10 +127,9 @@ class MessageLifelineConnect(BaseConnector): else: lifetime.visible = False lifetime.connectable = False + return True def disconnect(self, handle): - assert self.canvas - line = self.line received = self.get_connected(line.tail) lifeline = self.element @@ -185,9 +182,7 @@ class LifelineExecutionSpecificationConnect(BaseConnector): finish_occurence.covered = lifeline finish_occurence.execution = exec_spec - canvas = self.line.canvas - assert canvas - + canvas = self.canvas reparent(canvas, self.line, self.element) for cinfo in canvas.get_connections(connected=self.line): @@ -201,7 +196,6 @@ class LifelineExecutionSpecificationConnect(BaseConnector): exec_spec.unlink() canvas = self.canvas - assert canvas if canvas.get_parent(self.line) is self.element: new_parent = canvas.get_parent(self.element) @@ -217,9 +211,6 @@ class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): element: ExecutionSpecificationItem line: ExecutionSpecificationItem - def allow(self, _handle, _port): - return True - def connect(self, handle, _port): parent_exec_spec = self.element.subject child_exec_spec: UML.ExecutionSpecification = self.line.subject @@ -234,18 +225,15 @@ class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): assert connected_item Connector(connected_item, self.line).connect(handle, None) - canvas = self.canvas - reparent(canvas, self.line, self.element) + reparent(self.canvas, self.line, self.element) return True def disconnect(self, handle): exec_spec: Optional[UML.ExecutionSpecification] = self.line.subject del self.line.subject - if exec_spec: + if exec_spec and not exec_spec.presentation: exec_spec.unlink() - canvas = self.canvas - assert canvas - for cinfo in canvas.get_connections(connected=self.line): + for cinfo in self.canvas.get_connections(connected=self.line): Connector(self.line, cinfo.item).disconnect(cinfo.handle) diff --git a/gaphor/diagram/states/vertexconnect.py b/gaphor/diagram/states/vertexconnect.py index 16476fafd..15a166ba7 100644 --- a/gaphor/diagram/states/vertexconnect.py +++ b/gaphor/diagram/states/vertexconnect.py @@ -72,8 +72,6 @@ class InitialPseudostateTransitionConnect(VertexConnect): Glue to initial pseudostate with transition's head and when there are no transitions connected. """ - assert self.canvas - line = self.line element = self.element From aec7131779bcd10d6d61f2a1b1f38ce1e6e3ff5e Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 27 Feb 2020 23:04:32 +0100 Subject: [PATCH 27/36] Saver communication diagram check for messages --- gaphor/diagram/interactions/message.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gaphor/diagram/interactions/message.py b/gaphor/diagram/interactions/message.py index 5a62862e5..ce3ddb2bf 100644 --- a/gaphor/diagram/interactions/message.py +++ b/gaphor/diagram/interactions/message.py @@ -49,6 +49,7 @@ See also ``lifeline`` module documentation. from math import atan2, pi from gaphor import UML +from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.presentation import LinePresentation, Named from gaphor.diagram.shapes import Box, EditableText, Text from gaphor.diagram.text import middle_segment @@ -256,8 +257,8 @@ class MessageItem(LinePresentation[UML.Message], Named): c1 = canvas.get_connection(self.head) c2 = canvas.get_connection(self.tail) return ( - c1 + isinstance(c1, LifelineItem) and not c1.connected.lifetime.visible - or c2 + or isinstance(c2, LifelineItem) and not c2.connected.lifetime.visible ) From cb3a8b33f186e5f1d11a371abc68432c0820de10 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Fri, 28 Feb 2020 20:43:58 +0100 Subject: [PATCH 28/36] Rework interaction diagram item tests to pytest --- gaphor/diagram/interactions/tests/conftest.py | 3 +- .../interactions/tests/test_message.py | 18 +- .../interactions/tests/test_messageconnect.py | 445 +++++++++--------- gaphor/diagram/tests/fixtures.py | 37 ++ 4 files changed, 277 insertions(+), 226 deletions(-) diff --git a/gaphor/diagram/interactions/tests/conftest.py b/gaphor/diagram/interactions/tests/conftest.py index fe42227de..c80c02584 100644 --- a/gaphor/diagram/interactions/tests/conftest.py +++ b/gaphor/diagram/interactions/tests/conftest.py @@ -1 +1,2 @@ -from gaphor.diagram.tests.fixtures import diagram, element_factory +import gaphor.diagram.diagramtools +from gaphor.diagram.tests.fixtures import diagram, element_factory, loader, saver diff --git a/gaphor/diagram/interactions/tests/test_message.py b/gaphor/diagram/interactions/tests/test_message.py index 1e4987db4..1aba8e07f 100644 --- a/gaphor/diagram/interactions/tests/test_message.py +++ b/gaphor/diagram/interactions/tests/test_message.py @@ -4,18 +4,14 @@ Test messages. from gaphor import UML from gaphor.diagram.interactions.message import MessageItem -from gaphor.tests.testcase import TestCase -class MessageTestCase(TestCase): - def test_message_persistence(self): - """Test message saving/loading - """ - self.create(MessageItem, UML.Message) +def test_message_persistence(diagram, element_factory, saver, loader): + message = diagram.create(MessageItem, subject=element_factory.create(UML.Message)) - data = self.save() - self.load(data) + data = saver() + loader(data) + new_diagram = next(element_factory.select(lambda e: isinstance(e, UML.Diagram))) + item = new_diagram.canvas.select(lambda e: isinstance(e, MessageItem))[0] - item = self.diagram.canvas.select(lambda e: isinstance(e, MessageItem))[0] - - assert item + assert item diff --git a/gaphor/diagram/interactions/tests/test_messageconnect.py b/gaphor/diagram/interactions/tests/test_messageconnect.py index de9dc4e02..9a005792c 100644 --- a/gaphor/diagram/interactions/tests/test_messageconnect.py +++ b/gaphor/diagram/interactions/tests/test_messageconnect.py @@ -5,237 +5,254 @@ Message connection adapter tests. from gaphor import UML from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.interactions.message import MessageItem -from gaphor.tests import TestCase +from gaphor.diagram.tests.fixtures import allow, connect, disconnect -class BasicMessageConnectionsTestCase(TestCase): - def test_head_glue(self): - """Test message head glue - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) +def test_head_glue(diagram): + """Test message head glue + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) - # get head port - port = ll.ports()[0] - glued = self.allow(msg, msg.head, ll, port) - assert glued - - def test_invisible_lifetime_glue(self): - """Test message to invisible lifetime glue - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) - - glued = self.allow(msg, msg.head, ll, ll.lifetime.port) - - assert not ll.lifetime.visible - assert not glued - - def test_visible_lifetime_glue(self): - """Test message to visible lifetime glue - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) - - ll.lifetime.visible = True - - glued = self.allow(msg, msg.head, ll, ll.lifetime.port) - assert glued - - def test_lost_message_connection(self): - """Test lost message connection - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) - - self.connect(msg, msg.head, ll) - - # If one side is connected a "lost" message is created - self.assertTrue(msg.subject is not None) - assert msg.subject.messageKind == "lost" - - messages = self.kindof(UML.Message) - occurrences = self.kindof(UML.MessageOccurrenceSpecification) - - assert 1 == len(messages) - assert 1 == len(occurrences) - assert messages[0] is msg.subject - assert occurrences[0] is msg.subject.sendEvent - - def test_found_message_connection(self): - """Test found message connection - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) - - self.connect(msg, msg.tail, ll) - - # If one side is connected a "found" message is created - self.assertTrue(msg.subject is not None) - assert msg.subject.messageKind == "found" - - messages = self.kindof(UML.Message) - occurrences = self.kindof(UML.MessageOccurrenceSpecification) - - assert 1 == len(messages) - assert 1 == len(occurrences) - assert messages[0] is msg.subject - assert occurrences[0] is msg.subject.receiveEvent - - def test_complete_message_connection(self): - """Test complete message connection - """ - ll1 = self.create(LifelineItem) - ll2 = self.create(LifelineItem) - msg = self.create(MessageItem) - - self.connect(msg, msg.head, ll1) - self.connect(msg, msg.tail, ll2) - - # two sides are connected - "complete" message is created - self.assertTrue(msg.subject is not None) - assert msg.subject.messageKind == "complete" - - messages = self.kindof(UML.Message) - occurences = self.kindof(UML.MessageOccurrenceSpecification) - - assert 1 == len(messages) - assert 2 == len(occurences) - assert messages[0] is msg.subject - assert msg.subject.sendEvent in occurences, f"{occurences}" - assert msg.subject.receiveEvent in occurences, f"{occurences}" - - def test_lifetime_connection(self): - """Test messages' lifetimes connection - """ - msg = self.create(MessageItem) - ll1 = self.create(LifelineItem) - ll2 = self.create(LifelineItem) - - # make lifelines to be in sequence diagram mode - ll1.lifetime.visible = True - ll2.lifetime.visible = True - assert ll1.lifetime.visible and ll2.lifetime.visible - - # connect lifetimes with messages message to lifeline's head - self.connect(msg, msg.head, ll1, ll1.lifetime.port) - self.connect(msg, msg.tail, ll2, ll2.lifetime.port) - - assert msg.subject is not None - assert msg.subject.messageKind == "complete" - - def test_disconnection(self): - """Test message disconnection - """ - ll1 = self.create(LifelineItem) - ll2 = self.create(LifelineItem) - msg = self.create(MessageItem) - - self.connect(msg, msg.head, ll1) - self.connect(msg, msg.tail, ll2) - - # one side disconnection - self.disconnect(msg, msg.head) - assert msg.subject is not None, f"{msg.subject}" - - # 2nd side disconnection - self.disconnect(msg, msg.tail) - assert msg.subject is None, f"{msg.subject}" - - def test_lifetime_connectivity_on_head(self): - """Test lifeline's lifetime connectivity change on head connection - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) - - # connect message to lifeline's head, lifeline's lifetime - # visibility and connectivity should change - self.connect(msg, msg.head, ll) - assert not ll.lifetime.visible - assert not ll.lifetime.connectable - assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length - - # ... and disconnection - self.disconnect(msg, msg.head) - assert ll.lifetime.connectable - assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length - - def test_lifetime_connectivity_on_lifetime(self): - """Test lifeline's lifetime connectivity change on lifetime connection - """ - ll = self.create(LifelineItem) - msg = self.create(MessageItem) - - ll.lifetime.visible = True - - # connect message to lifeline's lifetime, lifeline's lifetime - # visibility and connectivity should be unchanged - self.connect(msg, msg.head, ll, ll.lifetime.port) - assert ll.lifetime.connectable - assert ll.lifetime.MIN_LENGTH_VISIBLE == ll.lifetime.min_length - - # ... and disconnection - self.disconnect(msg, msg.head) - assert ll.lifetime.connectable - assert ll.lifetime.visible - assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length + # get head port + port = ll.ports()[0] + glued = allow(msg, msg.head, ll, port) + assert glued -class DiagramModeMessageConnectionTestCase(TestCase): - def test_message_glue_cd(self): - """Test gluing message on communication diagram.""" +def test_invisible_lifetime_glue(diagram): + """Test message to invisible lifetime glue + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) - lifeline1 = self.create(LifelineItem) - lifeline2 = self.create(LifelineItem) - message = self.create(MessageItem) + glued = allow(msg, msg.head, ll, ll.lifetime.port) - # make second lifeline to be in sequence diagram mode - lifeline2.lifetime.visible = True + assert not ll.lifetime.visible + assert not glued - # connect head of message to lifeline's head - self.connect(message, message.head, lifeline1) - glued = self.allow(message, message.tail, lifeline2, lifeline2.lifetime.port) - # no connection possible as 2nd lifeline is in sequence diagram - # mode - self.assertFalse(glued) +def test_visible_lifetime_glue(diagram): + """Test message to visible lifetime glue + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) - def test_message_glue_sd(self): - """Test gluing message on sequence diagram.""" + ll.lifetime.visible = True - msg = self.create(MessageItem) - ll1 = self.create(LifelineItem) - ll2 = self.create(LifelineItem) + glued = allow(msg, msg.head, ll, ll.lifetime.port) + assert glued - # 1st lifeline - communication diagram - # 2nd lifeline - sequence diagram - ll2.lifetime.visible = True - # connect lifetime of message to lifeline's lifetime - self.connect(msg, msg.head, ll1, ll1.lifetime.port) +def test_lost_message_connection(diagram, element_factory): + """Test lost message connection + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) - glued = self.allow(msg, msg.tail, ll2) - # no connection possible as 2nd lifeline is in communication - # diagram mode - self.assertFalse(glued) + connect(msg, msg.head, ll) - def test_messages_disconnect_cd(self): - """Test disconnecting messages on communication diagram - """ - ll1 = self.create(LifelineItem) - ll2 = self.create(LifelineItem) - msg = self.create(MessageItem) + messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message)) + occurrences = element_factory.lselect( + lambda e: e.isKindOf(UML.MessageOccurrenceSpecification) + ) - self.connect(msg, msg.head, ll1) - self.connect(msg, msg.tail, ll2) + # If one side is connected a "lost" message is created + assert msg.subject is not None + assert msg.subject.messageKind == "lost" - subject = msg.subject + assert 1 == len(messages) + assert 1 == len(occurrences) + assert messages[0] is msg.subject + assert occurrences[0] is msg.subject.sendEvent - assert subject.sendEvent and subject.receiveEvent - messages = list(self.kindof(UML.Message)) - occurrences = set(self.kindof(UML.MessageOccurrenceSpecification)) +def test_found_message_connection(diagram, element_factory): + """Test found message connection + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) - # verify integrity of messages - self.assertEqual(1, len(messages)) - assert 2 == len(occurrences) + connect(msg, msg.tail, ll) + + messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message)) + occurrences = element_factory.lselect( + lambda e: e.isKindOf(UML.MessageOccurrenceSpecification) + ) + + # If one side is connected a "found" message is created + assert msg.subject is not None + assert msg.subject.messageKind == "found" + + assert 1 == len(messages) + assert 1 == len(occurrences) + assert messages[0] is msg.subject + assert occurrences[0] is msg.subject.receiveEvent + + +def test_complete_message_connection(diagram, element_factory): + """Test complete message connection + """ + ll1 = diagram.create(LifelineItem) + ll2 = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) + + connect(msg, msg.head, ll1) + connect(msg, msg.tail, ll2) + + messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message)) + occurrences = element_factory.lselect( + lambda e: e.isKindOf(UML.MessageOccurrenceSpecification) + ) + + # two sides are connected - "complete" message is created + assert msg.subject is not None + assert msg.subject.messageKind == "complete" + + assert 1 == len(messages) + assert 2 == len(occurrences) + assert messages[0] is msg.subject + assert msg.subject.sendEvent in occurrences, f"{occurrences}" + assert msg.subject.receiveEvent in occurrences, f"{occurrences}" + + +def test_lifetime_connection(diagram): + """Test messages' lifetimes connection + """ + msg = diagram.create(MessageItem) + ll1 = diagram.create(LifelineItem) + ll2 = diagram.create(LifelineItem) + + # make lifelines to be in sequence diagram mode + ll1.lifetime.visible = True + ll2.lifetime.visible = True + assert ll1.lifetime.visible and ll2.lifetime.visible + + # connect lifetimes with messages message to lifeline's head + connect(msg, msg.head, ll1, ll1.lifetime.port) + connect(msg, msg.tail, ll2, ll2.lifetime.port) + + assert msg.subject is not None + assert msg.subject.messageKind == "complete" + + +def test_disconnection(diagram): + """Test message disconnection + """ + ll1 = diagram.create(LifelineItem) + ll2 = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) + + connect(msg, msg.head, ll1) + connect(msg, msg.tail, ll2) + + # one side disconnection + disconnect(msg, msg.head) + assert msg.subject is not None, f"{msg.subject}" + + # 2nd side disconnection + disconnect(msg, msg.tail) + assert msg.subject is None, f"{msg.subject}" + + +def test_lifetime_connectivity_on_head(diagram): + """Test lifeline's lifetime connectivity change on head connection + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) + + # connect message to lifeline's head, lifeline's lifetime + # visibility and connectivity should change + connect(msg, msg.head, ll) + assert not ll.lifetime.visible + assert not ll.lifetime.connectable + assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length + + # ... and disconnection + disconnect(msg, msg.head) + assert ll.lifetime.connectable + assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length + + +def test_lifetime_connectivity_on_lifetime(diagram): + """Test lifeline's lifetime connectivity change on lifetime connection + """ + ll = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) + + ll.lifetime.visible = True + + # connect message to lifeline's lifetime, lifeline's lifetime + # visibility and connectivity should be unchanged + connect(msg, msg.head, ll, ll.lifetime.port) + assert ll.lifetime.connectable + assert ll.lifetime.MIN_LENGTH_VISIBLE == ll.lifetime.min_length + + # ... and disconnection + disconnect(msg, msg.head) + assert ll.lifetime.connectable + assert ll.lifetime.visible + assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length + + +def test_message_glue_cd(diagram): + """Test gluing message on communication diagram.""" + + lifeline1 = diagram.create(LifelineItem) + lifeline2 = diagram.create(LifelineItem) + message = diagram.create(MessageItem) + + # make second lifeline to be in sequence diagram mode + lifeline2.lifetime.visible = True + + # connect head of message to lifeline's head + connect(message, message.head, lifeline1) + + glued = allow(message, message.tail, lifeline2, lifeline2.lifetime.port) + # no connection possible as 2nd lifeline is in sequence diagram + # mode + assert not glued + + +def test_message_glue_sd(diagram): + """Test gluing message on sequence diagram.""" + + msg = diagram.create(MessageItem) + ll1 = diagram.create(LifelineItem) + ll2 = diagram.create(LifelineItem) + + # 1st lifeline - communication diagram + # 2nd lifeline - sequence diagram + ll2.lifetime.visible = True + + # connect lifetime of message to lifeline's lifetime + connect(msg, msg.head, ll1, ll1.lifetime.port) + + glued = allow(msg, msg.tail, ll2) + # no connection possible as 2nd lifeline is in communication + # diagram mode + assert not glued + + +def test_messages_disconnect_cd(diagram, element_factory): + """Test disconnecting messages on communication diagram + """ + ll1 = diagram.create(LifelineItem) + ll2 = diagram.create(LifelineItem) + msg = diagram.create(MessageItem) + + connect(msg, msg.head, ll1) + connect(msg, msg.tail, ll2) + + subject = msg.subject + + assert subject.sendEvent and subject.receiveEvent + + messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message)) + occurrences = element_factory.lselect( + lambda e: e.isKindOf(UML.MessageOccurrenceSpecification) + ) + + # verify integrity of messages + assert 1 == len(messages) + assert 2 == len(occurrences) diff --git a/gaphor/diagram/tests/fixtures.py b/gaphor/diagram/tests/fixtures.py index a5f087d75..a3e46421a 100644 --- a/gaphor/diagram/tests/fixtures.py +++ b/gaphor/diagram/tests/fixtures.py @@ -1,10 +1,14 @@ +from io import StringIO + import pytest from gaphas.aspect import ConnectionSink from gaphas.aspect import Connector as ConnectorAspect from gaphor import UML from gaphor.diagram.connectors import Connector +from gaphor.misc.xmlwriter import XMLWriter from gaphor.services.eventmanager import EventManager +from gaphor.storage import storage from gaphor.UML.elementfactory import ElementFactory @@ -18,6 +22,39 @@ def diagram(element_factory): return element_factory.create(UML.Diagram) +@pytest.fixture +def saver(element_factory): + def save(): + """ + Save diagram into string. + """ + + f = StringIO() + storage.save(XMLWriter(f), element_factory) + data = f.getvalue() + f.close() + + return data + + return save + + +@pytest.fixture +def loader(element_factory): + def load(data): + """ + Load data from specified string. + """ + element_factory.flush() + assert not list(element_factory.select()) + + f = StringIO(data) + storage.load(f, factory=element_factory) + f.close() + + return load + + def allow(line, handle, item, port=None): if port is None and len(item.ports()) > 0: port = item.ports()[0] From 1e623bcf457394417f479822ef3b262a3a2d32af Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Fri, 28 Feb 2020 22:11:29 +0100 Subject: [PATCH 29/36] Add connection between message and exec spec --- .../interactions/interactionsconnect.py | 153 +++++++++++------- .../interactions/tests/test_messageconnect.py | 45 +++++- 2 files changed, 141 insertions(+), 57 deletions(-) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 2d311c7c3..244bbf013 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -28,6 +28,72 @@ def reparent(canvas, item, new_parent): new_parent.request_update() +def get_connected(item, handle) -> Optional[UML.Presentation[UML.Element]]: + """ + Get item connected to a handle. + """ + cinfo = item.canvas.get_connection(handle) + if cinfo: + return cinfo.connected # type: ignore[no-any-return] # noqa: F723 + return None + + +def connect_lifelines(line, send, received): + """ + Always create a new Message with two EventOccurrence instances. + """ + + def get_subject(): + if not line.subject: + message = line.model.create(UML.Message) + message.name = "call()" + line.subject = message + return line.subject + + if send: + message = get_subject() + if not message.sendEvent: + event = message.model.create(UML.MessageOccurrenceSpecification) + event.sendMessage = message + event.covered = send.subject + + if received: + message = get_subject() + if not message.receiveEvent: + event = message.model.create(UML.MessageOccurrenceSpecification) + event.receiveMessage = message + event.covered = received.subject + + +def disconnect_lifelines(line, send, received): + """ + Disconnect lifeline and set appropriate kind of message item. If + there are no lifelines connected on both ends, then remove the message + from the data model. + """ + if not line.subject: + return + + if send: + event = line.subject.receiveEvent + if event: + event.unlink() + + if received: + event = line.subject.sendEvent + if event: + event.unlink() + + # one is disconnected and one is about to be disconnected, + # so destroy the message + if not send or not received: + # Both ends are disconnected: + message = line.subject + del line.subject + if not message.presentation: + message.unlink() + + @Connector.register(LifelineItem, MessageItem) class MessageLifelineConnect(BaseConnector): """Connect lifeline with a message. @@ -41,60 +107,6 @@ class MessageLifelineConnect(BaseConnector): element: LifelineItem line: MessageItem - def connect_lifelines(self, line, send, received): - """ - Always create a new Message with two EventOccurrence instances. - """ - - def get_subject(): - if not line.subject: - message = line.model.create(UML.Message) - message.name = "call()" - line.subject = message - return line.subject - - if send: - message = get_subject() - if not message.sendEvent: - event = message.model.create(UML.MessageOccurrenceSpecification) - event.sendMessage = message - event.covered = send.subject - - if received: - message = get_subject() - if not message.receiveEvent: - event = message.model.create(UML.MessageOccurrenceSpecification) - event.receiveMessage = message - event.covered = received.subject - - def disconnect_lifelines(self, line): - """ - Disconnect lifeline and set appropriate kind of message item. If - there are no lifelines connected on both ends, then remove the message - from the data model. - """ - send: Optional[UML.Presentation[UML.Element]] = self.get_connected(line.head) - received = self.get_connected(line.tail) - - if send: - event = line.subject.receiveEvent - if event: - event.unlink() - - if received: - event = line.subject.sendEvent - if event: - event.unlink() - - # one is disconnected and one is about to be disconnected, - # so destroy the message - if not send or not received: - # Both ends are disconnected: - message = line.subject - del line.subject - if not message.presentation: - message.unlink() - def allow(self, handle, port): """ Glue to lifeline's head or lifetime. If lifeline's lifetime is @@ -118,7 +130,7 @@ class MessageLifelineConnect(BaseConnector): line = self.line send = self.get_connected(line.head) received = self.get_connected(line.tail) - self.connect_lifelines(line, send, received) + connect_lifelines(line, send, received) lifetime = self.element.lifetime # if connected to head, then make lifetime invisible @@ -131,6 +143,7 @@ class MessageLifelineConnect(BaseConnector): def disconnect(self, handle): line = self.line + send: Optional[UML.Presentation[UML.Element]] = get_connected(line, line.head) received = self.get_connected(line.tail) lifeline = self.element lifetime = lifeline.lifetime @@ -143,7 +156,7 @@ class MessageLifelineConnect(BaseConnector): received.is_destroyed = False received.request_update() - self.disconnect_lifelines(line) + disconnect_lifelines(line, send, received) if len(list(self.canvas.get_connections(connected=lifeline))) == 1: # after disconnection count of connected items will be @@ -152,6 +165,34 @@ class MessageLifelineConnect(BaseConnector): lifetime.min_length = lifetime.MIN_LENGTH +@Connector.register(ExecutionSpecificationItem, MessageItem) +class ExecutionSpecificationMessageConnect(BaseConnector): + + element: ExecutionSpecificationItem + line: MessageItem + + def get_lifeline(self, handle): + import gaphas + + connected_item = self.get_connected(handle) + if connected_item is None or isinstance(connected_item, LifelineItem): + return connected_item + return self.get_lifeline(connected_item.handles()[0]) # type: ignore[attr-defined] + + def connect(self, handle, _port): + line = self.line + send = self.get_lifeline(line.head) + received = self.get_lifeline(line.tail) + connect_lifelines(line, send, received) + return True + + def disconnect(self, handle): + line = self.line + send = self.get_lifeline(line.head) + received = self.get_lifeline(line.tail) + disconnect_lifelines(line, send, received) + + @Connector.register(LifelineItem, ExecutionSpecificationItem) class LifelineExecutionSpecificationConnect(BaseConnector): diff --git a/gaphor/diagram/interactions/tests/test_messageconnect.py b/gaphor/diagram/interactions/tests/test_messageconnect.py index 9a005792c..a01d91a52 100644 --- a/gaphor/diagram/interactions/tests/test_messageconnect.py +++ b/gaphor/diagram/interactions/tests/test_messageconnect.py @@ -3,6 +3,9 @@ Message connection adapter tests. """ from gaphor import UML +from gaphor.diagram.interactions.executionspecification import ( + ExecutionSpecificationItem, +) from gaphor.diagram.interactions.lifeline import LifelineItem from gaphor.diagram.interactions.message import MessageItem from gaphor.diagram.tests.fixtures import allow, connect, disconnect @@ -246,7 +249,8 @@ def test_messages_disconnect_cd(diagram, element_factory): subject = msg.subject - assert subject.sendEvent and subject.receiveEvent + assert subject.sendEvent + assert subject.receiveEvent messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message)) occurrences = element_factory.lselect( @@ -256,3 +260,42 @@ def test_messages_disconnect_cd(diagram, element_factory): # verify integrity of messages assert 1 == len(messages) assert 2 == len(occurrences) + + +def test_message_connect_to_execution_specification(diagram, element_factory): + """Test gluing message on sequence diagram.""" + + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + exec_spec = diagram.create(ExecutionSpecificationItem) + message = diagram.create(MessageItem) + connect(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + + connect(message, message.head, exec_spec, exec_spec.ports()[0]) + + assert message.subject + assert message.subject.sendEvent.covered is lifeline.subject + + +def test_message_disconnect_from_execution_specification(diagram, element_factory): + """Test gluing message on sequence diagram.""" + + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + exec_spec = diagram.create(ExecutionSpecificationItem) + message = diagram.create(MessageItem) + connect(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + connect(message, message.head, exec_spec, exec_spec.ports()[0]) + + disconnect(message, message.head) + + messages = element_factory.lselect(lambda e: e.isKindOf(UML.Message)) + occurrences = element_factory.lselect( + lambda e: e.isKindOf(UML.MessageOccurrenceSpecification) + ) + + assert not message.subject + assert not len(messages) + assert not len(occurrences) From 5b53f23c5d8f7f7ddccc7cc3c79e781bd644741e Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Fri, 28 Feb 2020 22:29:17 +0100 Subject: [PATCH 30/36] Add icon for execution-specification --- gaphor/ui/icons/Makefile | 1 + ...aphor-execution-specification-symbolic.svg | 39 +++++++++++++++++++ gaphor/ui/icons/stensil.svg | 30 ++++++++++++-- 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 gaphor/ui/icons/gaphor-execution-specification-symbolic.svg diff --git a/gaphor/ui/icons/Makefile b/gaphor/ui/icons/Makefile index 410d7a5e2..292ebb2a9 100644 --- a/gaphor/ui/icons/Makefile +++ b/gaphor/ui/icons/Makefile @@ -33,6 +33,7 @@ ICONS=diagram \ send-signal-action \ accept-event-action \ lifeline \ + execution-specification \ message \ interaction \ state \ diff --git a/gaphor/ui/icons/gaphor-execution-specification-symbolic.svg b/gaphor/ui/icons/gaphor-execution-specification-symbolic.svg new file mode 100644 index 000000000..89d20d63f --- /dev/null +++ b/gaphor/ui/icons/gaphor-execution-specification-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/gaphor/ui/icons/stensil.svg b/gaphor/ui/icons/stensil.svg index 03b1ceacc..a97eb9e6d 100644 --- a/gaphor/ui/icons/stensil.svg +++ b/gaphor/ui/icons/stensil.svg @@ -33,11 +33,11 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="40.173252" - inkscape:cx="24.316871" - inkscape:cy="1146.0668" + inkscape:zoom="160.69301" + inkscape:cx="229.52617" + inkscape:cy="1157.2818" inkscape:document-units="px" - inkscape:current-layer="pointer" + inkscape:current-layer="execution-specification" showgrid="true" units="px" inkscape:window-width="3840" @@ -1219,6 +1219,28 @@ rx="1.5874995" ry="1.5874976" /> + + + + + Date: Fri, 28 Feb 2020 23:13:09 +0100 Subject: [PATCH 31/36] Fix Flake8 issues --- gaphor/diagram/interactions/interactionsconnect.py | 2 -- .../diagram/interactions/tests/test_executionspecification.py | 4 +++- gaphor/diagram/interactions/tests/test_message.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 244bbf013..293a1eae0 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -254,8 +254,6 @@ class ExecutionSpecificationExecutionSpecificationConnect(BaseConnector): def connect(self, handle, _port): parent_exec_spec = self.element.subject - child_exec_spec: UML.ExecutionSpecification = self.line.subject - model = self.element.model if not parent_exec_spec: # Can connect child exec spec if parent is not connected diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index dc9e4a92b..66cf9f2c7 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -165,7 +165,9 @@ def test_connect_execution_specification_with_execution_specification_to_lifelin def test_disconnect_execution_specification_with_execution_specification_from_lifeline( diagram, element_factory ): - elements_of_kind = lambda type: element_factory.lselect(lambda e: e.isKindOf(type)) + def elements_of_kind(type): + return element_factory.lselect(lambda e: e.isKindOf(type)) + lifeline = diagram.create( LifelineItem, subject=element_factory.create(UML.Lifeline) ) diff --git a/gaphor/diagram/interactions/tests/test_message.py b/gaphor/diagram/interactions/tests/test_message.py index 1aba8e07f..e00151c82 100644 --- a/gaphor/diagram/interactions/tests/test_message.py +++ b/gaphor/diagram/interactions/tests/test_message.py @@ -7,7 +7,7 @@ from gaphor.diagram.interactions.message import MessageItem def test_message_persistence(diagram, element_factory, saver, loader): - message = diagram.create(MessageItem, subject=element_factory.create(UML.Message)) + diagram.create(MessageItem, subject=element_factory.create(UML.Message)) data = saver() loader(data) From ae8e06a4b517cc3b70e4a6906491ddca392e7768 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Fri, 28 Feb 2020 23:47:27 +0100 Subject: [PATCH 32/36] Fix element editor issues with exec specs --- .../interactions/interactionsconnect.py | 26 +++++++++---------- .../interactions/interactionspropertypages.py | 11 +++----- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 293a1eae0..5a648b725 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -38,6 +38,13 @@ def get_connected(item, handle) -> Optional[UML.Presentation[UML.Element]]: return None +def get_lifeline(item, handle): + connected_item = get_connected(item, handle) + if connected_item is None or isinstance(connected_item, LifelineItem): + return connected_item + return get_lifeline(connected_item, connected_item.handles()[0]) # type: ignore[attr-defined] + + def connect_lifelines(line, send, received): """ Always create a new Message with two EventOccurrence instances. @@ -118,8 +125,7 @@ class MessageLifelineConnect(BaseConnector): opposite = line.opposite(handle) ol = self.get_connected(opposite) - if ol: - assert isinstance(ol, LifelineItem) + if isinstance(ol, LifelineItem): opposite_is_visible = ol.lifetime.visible # connect lifetimes if both are visible or both invisible return not (lifetime.visible ^ opposite_is_visible) @@ -171,25 +177,17 @@ class ExecutionSpecificationMessageConnect(BaseConnector): element: ExecutionSpecificationItem line: MessageItem - def get_lifeline(self, handle): - import gaphas - - connected_item = self.get_connected(handle) - if connected_item is None or isinstance(connected_item, LifelineItem): - return connected_item - return self.get_lifeline(connected_item.handles()[0]) # type: ignore[attr-defined] - def connect(self, handle, _port): line = self.line - send = self.get_lifeline(line.head) - received = self.get_lifeline(line.tail) + send = get_lifeline(line, line.head) + received = get_lifeline(line, line.tail) connect_lifelines(line, send, received) return True def disconnect(self, handle): line = self.line - send = self.get_lifeline(line.head) - received = self.get_lifeline(line.tail) + send = get_lifeline(line, line.head) + received = get_lifeline(line, line.tail) disconnect_lifelines(line, send, received) diff --git a/gaphor/diagram/interactions/interactionspropertypages.py b/gaphor/diagram/interactions/interactionspropertypages.py index c2d2e4888..6b7892323 100644 --- a/gaphor/diagram/interactions/interactionspropertypages.py +++ b/gaphor/diagram/interactions/interactionspropertypages.py @@ -2,6 +2,7 @@ from gi.repository import Gtk from gaphor import UML from gaphor.core import gettext, transactional +from gaphor.diagram.interactions.interactionsconnect import get_lifeline from gaphor.diagram.interactions.message import MessageItem from gaphor.diagram.propertypages import ( EditableTreeModel, @@ -45,10 +46,7 @@ class MessagePropertyPage(NamedItemPropertyPage): hbox = create_hbox_label(self, page, gettext("Message sort")) sort_data = self.MESSAGE_SORT - lifeline = None - cinfo = item.canvas.get_connection(item.tail) - if cinfo: - lifeline = cinfo.connected + lifeline = get_lifeline(item, item.tail) # disallow connecting two delete messages to a lifeline if ( @@ -79,10 +77,7 @@ class MessagePropertyPage(NamedItemPropertyPage): item = self.item subject = item.subject - lifeline = None - cinfo = item.canvas.get_connection(item.tail) - if cinfo: - lifeline = cinfo.connected + lifeline = get_lifeline(item, item.tail) # # allow only one delete message to connect to lifeline's lifetime From 65ebee74717b053772a0ae579b3e6adcc1e71990 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 1 Mar 2020 23:40:33 +0100 Subject: [PATCH 33/36] Loading and saving of exec-spec Loading seems flaky still. items do not show on their appropriate place. --- .../interactions/executionspecification.py | 44 ++++++++++++- .../interactions/interactionsconnect.py | 23 +++---- .../tests/test_executionspecification.py | 32 +++++++++ gaphor/diagram/presentation.py | 65 +++++++++++-------- gaphor/storage/storage.py | 5 +- 5 files changed, 127 insertions(+), 42 deletions(-) diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index 23ca7277b..a4af00d8b 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -19,15 +19,18 @@ TODO:ExecutionSpecification is abstract. Should use either ActionExecutionSpecification or BehaviorExecutionSpecification. What's the difference? -Stick with BehaviorExecutionSpecification, since it has a [0..1] relation to behavior, whereas -ActionExecutionSpecification has a [1] relation to action. +Stick with BehaviorExecutionSpecification, since it has a [0..1] relation to +behavior, whereas ActionExecutionSpecification has a [1] relation to action. """ +import ast + from gaphas import Handle, Item from gaphas.connector import LinePort, Position from gaphas.geometry import Rectangle, distance_rectangle_point from gaphas.solver import WEAK from gaphor import UML +from gaphor.diagram.presentation import postload_connect from gaphor.diagram.shapes import Box, draw_border from gaphor.diagram.support import represents from gaphor.UML.modelfactory import stereotypes_str @@ -78,7 +81,42 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I return distance_rectangle_point(self.dimensions(), pos) def save(self, save_func): + def save_connection(name, handle): + assert self.canvas + c = self.canvas.get_connection(handle) + if c: + save_func(name, c.connected, reference=True) + + points = [tuple(map(float, h.pos)) for h in self.handles()] + + save_func("matrix", tuple(self.matrix)) + save_func("points", points) + save_connection("head-connection", self.handles()[0]) super().save(save_func) def load(self, name, value): - super().load(name, value) + if name == "matrix": + self.matrix = ast.literal_eval(value) + elif name == "points": + points = ast.literal_eval(value) + for h, p in zip(self.handles(), points): + h.pos = p + elif name == "head-connection": + self._load_head_connection = value + else: + super().load(name, value) + + def postload(self): + assert self.canvas + + # First update matrix and solve constraints (NE and SW handle are + # lazy and are resolved by the constraint solver rather than set + # directly. + self.canvas.update_matrix(self) + self.canvas.solver.solve() + + if hasattr(self, "_load_head_connection"): + postload_connect(self, self.handles()[0], self._load_head_connection) + del self._load_head_connection + + super().postload() diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 5a648b725..791fb5e2e 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -209,20 +209,21 @@ class LifelineExecutionSpecificationConnect(BaseConnector): exec_spec = model.create(UML.BehaviorExecutionSpecification) self.line.subject = exec_spec - start_occurence: UML.ExecutionOccurrenceSpecification = model.create( - UML.ExecutionOccurrenceSpecification - ) - start_occurence.covered = lifeline - start_occurence.execution = exec_spec + start_occurence: UML.ExecutionOccurrenceSpecification = model.create( + UML.ExecutionOccurrenceSpecification + ) + start_occurence.covered = lifeline + start_occurence.execution = exec_spec - finish_occurence: UML.ExecutionOccurrenceSpecification = model.create( - UML.ExecutionOccurrenceSpecification - ) - finish_occurence.covered = lifeline - finish_occurence.execution = exec_spec + finish_occurence: UML.ExecutionOccurrenceSpecification = model.create( + UML.ExecutionOccurrenceSpecification + ) + finish_occurence.covered = lifeline + finish_occurence.execution = exec_spec canvas = self.canvas - reparent(canvas, self.line, self.element) + if canvas.get_parent(self.line) is not self.element: + reparent(canvas, self.line, self.element) for cinfo in canvas.get_connections(connected=self.line): Connector(self.line, cinfo.item).connect(cinfo.handle, cinfo.port) diff --git a/gaphor/diagram/interactions/tests/test_executionspecification.py b/gaphor/diagram/interactions/tests/test_executionspecification.py index 66cf9f2c7..4b797f9d0 100644 --- a/gaphor/diagram/interactions/tests/test_executionspecification.py +++ b/gaphor/diagram/interactions/tests/test_executionspecification.py @@ -202,3 +202,35 @@ def test_disconnect_execution_specification_with_execution_specification_from_li assert grand_child_exec_spec.subject is None assert elements_of_kind(UML.ExecutionSpecification) == [] assert elements_of_kind(UML.ExecutionOccurrenceSpecification) == [] + + +def test_save_and_load(diagram, element_factory, saver, loader): + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + exec_spec = diagram.create(ExecutionSpecificationItem) + + connect(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + + diagram.canvas.update_now() + + saved_data = saver() + + loader(saved_data) + + exec_specs = element_factory.lselect( + lambda e: e.isKindOf(UML.ExecutionSpecification) + ) + loaded_exec_spec = exec_specs[0].presentation[0] + + assert len(exec_specs) == 1 + assert ( + len( + element_factory.lselect( + lambda e: e.isKindOf(UML.ExecutionOccurrenceSpecification) + ) + ) + == 2 + ) + assert loaded_exec_spec.canvas.get_connection(loaded_exec_spec.handles()[0]) diff --git a/gaphor/diagram/presentation.py b/gaphor/diagram/presentation.py index a76985837..a339816b0 100644 --- a/gaphor/diagram/presentation.py +++ b/gaphor/diagram/presentation.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import ast from typing import Optional @@ -45,6 +47,40 @@ def from_package_str(item): ) +def _get_sink(item, handle, target): + assert item.canvas + + hpos = item.canvas.get_matrix_i2i(item, target).transform_point(*handle.pos) + port = None + dist = 10e6 + for p in target.ports(): + pos, d = p.glue(hpos) + if not port or d < dist: + port = p + dist = d + + return ConnectionSink(target, port) + + +def postload_connect(item: gaphas.Item, handle: gaphas.Handle, target: gaphas.Item): + """ + Helper function: when loading a model, handles should be connected as + part of the `postload` step. This function finds a suitable spot on the + `target` item to connect the handle to. + """ + + # First update matrix and solve constraints (NE and SW handle are + # lazy and are resolved by the constraint solver rather than set + # directly. + item.canvas.update_matrix(item) + item.canvas.update_matrix(target) + item.canvas.solver.solve() + + connector = ConnectorAspect(item, handle) + sink = _get_sink(item, handle, target) + connector.connect(sink) + + # Note: the official documentation is using the terms "Shape" and "Edge" for element and line. @@ -249,25 +285,6 @@ class LinePresentation(Presentation[S], gaphas.Line): def postload(self): assert self.canvas - def get_sink(handle, item): - assert self.canvas - - hpos = self.canvas.get_matrix_i2i(self, item).transform_point(*handle.pos) - port = None - dist = 10e6 - for p in item.ports(): - pos, d = p.glue(hpos) - if not port or d < dist: - port = p - dist = d - - return ConnectionSink(item, port) - - def postload_connect(handle, item): - connector = ConnectorAspect(self, handle) - sink = get_sink(handle, item) - connector.connect(sink) - if hasattr(self, "_load_orthogonal"): # Ensure there are enough handles if self._load_orthogonal and len(self._handles) < 3: @@ -276,18 +293,12 @@ class LinePresentation(Presentation[S], gaphas.Line): self.orthogonal = self._load_orthogonal del self._load_orthogonal - # First update matrix and solve constraints (NE and SW handle are - # lazy and are resolved by the constraint solver rather than set - # directly. - self.canvas.update_matrix(self) - self.canvas.solver.solve() - if hasattr(self, "_load_head_connection"): - postload_connect(self.head, self._load_head_connection) + postload_connect(self, self.head, self._load_head_connection) del self._load_head_connection if hasattr(self, "_load_tail_connection"): - postload_connect(self.tail, self._load_tail_connection) + postload_connect(self, self.tail, self._load_tail_connection) del self._load_tail_connection super().postload() diff --git a/gaphor/storage/storage.py b/gaphor/storage/storage.py index c078249bc..af678c37c 100644 --- a/gaphor/storage/storage.py +++ b/gaphor/storage/storage.py @@ -185,8 +185,11 @@ def load_elements_generator(elements, factory, gaphor_version): yield from _load_attributes_and_references(elements, update_status_queue) for d in factory.select(lambda e: isinstance(e, UML.Diagram)): + canvas = d.canvas + for item in d.canvas.get_all_items(): + canvas.update_matrix(item) # update_now() is implicitly called when lock is released - d.canvas.block_updates = False + canvas.block_updates = False # do a postload: for id, elem in list(elements.items()): From 70ceaa3bd52534bc5a5f54e8860596f946ef739e Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Thu, 5 Mar 2020 23:21:23 +0100 Subject: [PATCH 34/36] Fix loading of nested items Matrices should be updated properly. --- gaphor/diagram/diagramtools.py | 2 +- gaphor/diagram/interactions/executionspecification.py | 8 -------- gaphor/diagram/presentation.py | 8 -------- gaphor/diagram/tests/conftest.py | 2 +- gaphor/storage/storage.py | 2 -- 5 files changed, 2 insertions(+), 20 deletions(-) diff --git a/gaphor/diagram/diagramtools.py b/gaphor/diagram/diagramtools.py index d5d3cede4..ffc56f308 100644 --- a/gaphor/diagram/diagramtools.py +++ b/gaphor/diagram/diagramtools.py @@ -16,7 +16,7 @@ from gaphas.guide import GuidedItemInMotion from gaphas.tool import ConnectHandleTool, HoverTool, ItemTool from gaphas.tool import PlacementTool as _PlacementTool from gaphas.tool import RubberbandTool, Tool, ToolChain -from gi.repository import Gdk, Gtk +from gi.repository import Gdk from gaphor.core import Transaction, transactional from gaphor.diagram.connectors import Connector diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index a4af00d8b..88ee6d714 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -107,14 +107,6 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I super().load(name, value) def postload(self): - assert self.canvas - - # First update matrix and solve constraints (NE and SW handle are - # lazy and are resolved by the constraint solver rather than set - # directly. - self.canvas.update_matrix(self) - self.canvas.solver.solve() - if hasattr(self, "_load_head_connection"): postload_connect(self, self.handles()[0], self._load_head_connection) del self._load_head_connection diff --git a/gaphor/diagram/presentation.py b/gaphor/diagram/presentation.py index a339816b0..aa6580099 100644 --- a/gaphor/diagram/presentation.py +++ b/gaphor/diagram/presentation.py @@ -68,14 +68,6 @@ def postload_connect(item: gaphas.Item, handle: gaphas.Handle, target: gaphas.It part of the `postload` step. This function finds a suitable spot on the `target` item to connect the handle to. """ - - # First update matrix and solve constraints (NE and SW handle are - # lazy and are resolved by the constraint solver rather than set - # directly. - item.canvas.update_matrix(item) - item.canvas.update_matrix(target) - item.canvas.solver.solve() - connector = ConnectorAspect(item, handle) sink = _get_sink(item, handle, target) connector.connect(sink) diff --git a/gaphor/diagram/tests/conftest.py b/gaphor/diagram/tests/conftest.py index fe42227de..de12dd6dd 100644 --- a/gaphor/diagram/tests/conftest.py +++ b/gaphor/diagram/tests/conftest.py @@ -1 +1 @@ -from gaphor.diagram.tests.fixtures import diagram, element_factory +from gaphor.diagram.tests.fixtures import diagram, element_factory, loader diff --git a/gaphor/storage/storage.py b/gaphor/storage/storage.py index af678c37c..965aa1a56 100644 --- a/gaphor/storage/storage.py +++ b/gaphor/storage/storage.py @@ -186,8 +186,6 @@ def load_elements_generator(elements, factory, gaphor_version): for d in factory.select(lambda e: isinstance(e, UML.Diagram)): canvas = d.canvas - for item in d.canvas.get_all_items(): - canvas.update_matrix(item) # update_now() is implicitly called when lock is released canvas.block_updates = False From 14e5aeb9213f973a8ebcad11c27f801c2ae8a15b Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 8 Mar 2020 09:20:51 +0100 Subject: [PATCH 35/36] Fix issue with loading grouped elements Fixes #276 --- examples/sequence-diagram.gaphor | 292 +++++++++++++++++++++++++++++ gaphor/diagram/diagramtools.py | 2 +- gaphor/services/copyservice.py | 2 +- gaphor/storage/tests/conftest.py | 34 ++++ gaphor/storage/tests/test_group.py | 106 +++++++++++ 5 files changed, 434 insertions(+), 2 deletions(-) create mode 100644 examples/sequence-diagram.gaphor create mode 100644 gaphor/storage/tests/conftest.py create mode 100644 gaphor/storage/tests/test_group.py diff --git a/examples/sequence-diagram.gaphor b/examples/sequence-diagram.gaphor new file mode 100644 index 000000000..5febab512 --- /dev/null +++ b/examples/sequence-diagram.gaphor @@ -0,0 +1,292 @@ + + + + +New model + + + + + + + + + +main + + + + + + + +(1.0, 0.0, 0.0, 1.0, 141.0, 205.5) + + +100.0 + + +50.0 + + + + + +172.0 + + + +(1.0, 0.0, 0.0, 1.0, 50.0, 75.0) + + +[(0.0, 0.0), (0.0, 100.5)] + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 416.0, 205.5) + + +100.0 + + +50.0 + + + + + +171.5 + + + +(1.0, 0.0, 0.0, 1.0, 50.0, 92.0) + + +[(0.0, 0.0), (0.0, 74.5)] + + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 197.0, 308.0) + + +0 + + +0 + + +[(0.0, 0.0), (263.0, 2.0)] + + + + + + + + + + + + + +(1.0, 0.0, 0.0, 1.0, 460.0, 360.5) + + +0 + + +0 + + +[(0.0, 0.0), (-263.0, -2.5)] + + + + + + + + + + + + + + + + + + + + +Caller + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Callee + + + + + + + + + +call() + + + + + + + + + + + + + + + +reply + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gaphor/diagram/diagramtools.py b/gaphor/diagram/diagramtools.py index ffc56f308..df54c59d8 100644 --- a/gaphor/diagram/diagramtools.py +++ b/gaphor/diagram/diagramtools.py @@ -225,7 +225,7 @@ class PlacementTool(_PlacementTool): # mechanisms # First make sure all matrices are updated: - view.canvas.update_matrix(self.new_item) + view.canvas.update_matrices([self.new_item]) view.update_matrix(self.new_item) vpos = event.x, event.y diff --git a/gaphor/services/copyservice.py b/gaphor/services/copyservice.py index 4ea3378b2..c081dd2b5 100644 --- a/gaphor/services/copyservice.py +++ b/gaphor/services/copyservice.py @@ -113,7 +113,7 @@ class CopyService(Service, ActionProvider): # update items' matrix immediately for item in new_items.values(): item.matrix.translate(10, 10) - canvas.update_matrix(item) + canvas.update_matrices([item]) # solve internal constraints of items immediately as item.postload # reconnects items and all handles have to be in place diff --git a/gaphor/storage/tests/conftest.py b/gaphor/storage/tests/conftest.py new file mode 100644 index 000000000..c01fa1955 --- /dev/null +++ b/gaphor/storage/tests/conftest.py @@ -0,0 +1,34 @@ +from io import StringIO + +import pytest + +from gaphor import UML +from gaphor.services.eventmanager import EventManager +from gaphor.storage import storage +from gaphor.UML.elementfactory import ElementFactory + + +@pytest.fixture +def element_factory(): + return ElementFactory(EventManager()) + + +@pytest.fixture +def diagram(element_factory): + return element_factory.create(UML.Diagram) + + +@pytest.fixture +def loader(element_factory): + def load(data): + """ + Load data from specified string. + """ + element_factory.flush() + assert not list(element_factory.select()) + + f = StringIO(data) + storage.load(f, factory=element_factory) + f.close() + + return load diff --git a/gaphor/storage/tests/test_group.py b/gaphor/storage/tests/test_group.py new file mode 100644 index 000000000..728859bea --- /dev/null +++ b/gaphor/storage/tests/test_group.py @@ -0,0 +1,106 @@ +import pytest + +from gaphor.diagram.classes import DependencyItem +from gaphor.diagram.components import NodeItem + +PARENT_X = 189 +PARENT_Y = 207 +CHILD_ONE_X = 32 +CHILD_ONE_Y = 54 +CHILD_TWO_X = 44 +CHILD_TWO_Y = 208 + + +def handle_pos(item, handle_index): + return tuple(map(float, item.handles()[handle_index].pos)) + + +def test_load_grouped_connected_items(element_factory, loader): + loader(NODE_EXAMPLE_XML) + + diagram = element_factory.lselect()[0] + canvas = diagram.canvas + node_item, dep_item = canvas.get_root_items() + child_one, child_two = canvas.get_children(node_item) + + assert isinstance(node_item, NodeItem) + assert isinstance(dep_item, DependencyItem) + assert isinstance(child_one, NodeItem) + assert isinstance(child_two, NodeItem) + + assert canvas.get_parent(child_one) is node_item + + assert tuple(canvas.get_matrix_i2c(child_one)) == ( + 1.0, + 0.0, + 0.0, + 1.0, + PARENT_X + CHILD_ONE_X, + PARENT_Y + CHILD_ONE_Y, + ) + assert tuple(canvas.get_matrix_i2c(child_two)) == ( + 1.0, + 0.0, + 0.0, + 1.0, + PARENT_X + CHILD_TWO_X, + PARENT_Y + CHILD_TWO_Y, + ) + + +NODE_EXAMPLE_XML = f"""\ + + + + + + + (1.0, 0.0, 0.0, 1.0, {PARENT_X}, {PARENT_Y}) + + + 388.5 + + + 286.5 + + + + (1.0, 0.0, 0.0, 1.0, {CHILD_ONE_X}, {CHILD_ONE_Y}) + + + 100.0 + + + 50.0 + + + + + (1.0, 0.0, 0.0, 1.0, {CHILD_TWO_X}, {CHILD_TWO_Y}) + + + 100.0 + + + 50.0 + + + + + + (1.0, 0.0, 0.0, 1.0, 274.0, 312.0) + + + [(0.0, 0.0), (6.0, 104.0)] + + + + + + + + + + + +""" From c06748d9f27bd718977ee28a969c1a2a0f8f2b48 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 8 Mar 2020 14:28:19 +0100 Subject: [PATCH 36/36] Order occurrences per lifeline `Lifeline.coveredBy` --- gaphor/UML/collection.py | 7 +-- gaphor/UML/uml2.gaphor | 51 ++++++++++++------- .../interactions/executionspecification.py | 8 +++ .../interactions/interactionsconnect.py | 30 +++++++++++ .../interactions/tests/test_messageconnect.py | 8 +-- .../interactions/tests/test_ordering.py | 46 +++++++++++++++++ 6 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 gaphor/diagram/interactions/tests/test_ordering.py diff --git a/gaphor/UML/collection.py b/gaphor/UML/collection.py index 393d54c7e..a36d17303 100644 --- a/gaphor/UML/collection.py +++ b/gaphor/UML/collection.py @@ -169,7 +169,8 @@ class collection(Generic[T]): self.object.handle(AssociationUpdated(self.object, self.property)) return True - except IndexError: - return False - except ValueError: + except (IndexError, ValueError): return False + + def order(self, key): + self.items.sort(key=key) diff --git a/gaphor/UML/uml2.gaphor b/gaphor/UML/uml2.gaphor index 27299f285..772851170 100755 --- a/gaphor/UML/uml2.gaphor +++ b/gaphor/UML/uml2.gaphor @@ -29954,6 +29954,11 @@ some elements to have a stereotype + + + + + @@ -29977,11 +29982,6 @@ some elements to have a stereotype - - - - - @@ -41201,6 +41201,7 @@ swimlanes + @@ -45732,18 +45733,6 @@ swimlanes ownedElement - - - - - - - - - - - - @@ -52761,4 +52750,32 @@ to make it fit more to what we need in Gaphor. + + + + + + + + + + + + + + + + + + + + + + + + + +true + + \ No newline at end of file diff --git a/gaphor/diagram/interactions/executionspecification.py b/gaphor/diagram/interactions/executionspecification.py index 88ee6d714..a97b85fb0 100644 --- a/gaphor/diagram/interactions/executionspecification.py +++ b/gaphor/diagram/interactions/executionspecification.py @@ -69,6 +69,14 @@ class ExecutionSpecificationItem(UML.Presentation[UML.ExecutionSpecification], I self.shape = Box(style={"fill": "white"}, draw=draw_border) + @property + def top(self): + return self._handles[0] + + @property + def bottom(self): + return self._handles[1] + def dimensions(self): d = self.bar_width pt, pb = (h.pos for h in self._handles) diff --git a/gaphor/diagram/interactions/interactionsconnect.py b/gaphor/diagram/interactions/interactionsconnect.py index 791fb5e2e..13aef84fa 100644 --- a/gaphor/diagram/interactions/interactionsconnect.py +++ b/gaphor/diagram/interactions/interactionsconnect.py @@ -45,6 +45,34 @@ def get_lifeline(item, handle): return get_lifeline(connected_item, connected_item.handles()[0]) # type: ignore[attr-defined] +def order_lifeline_covered_by(lifeline): + canvas = lifeline.canvas + + def y_and_occurence(connected): + for conn in canvas.get_connections(connected=connected): + m = canvas.get_matrix_i2c(conn.item) + if isinstance(conn.item, ExecutionSpecificationItem): + yield ( + m.transform_point(*conn.handle.pos)[1], + conn.item.subject.start, + ) + yield ( + m.transform_point(*conn.item.bottom.pos)[1], + conn.item.subject.finish, + ) + yield from y_and_occurence(conn.item) + elif isinstance(conn.item, MessageItem): + yield ( + m.transform_point(*conn.handle.pos)[1], + conn.item.subject.sendEvent + if conn.handle is conn.item.head + else conn.item.subject.receiveEvent, + ) + + keys = {o: y for y, o in y_and_occurence(lifeline)} + lifeline.subject.coveredBy.order(keys.get) + + def connect_lifelines(line, send, received): """ Always create a new Message with two EventOccurrence instances. @@ -63,6 +91,7 @@ def connect_lifelines(line, send, received): event = message.model.create(UML.MessageOccurrenceSpecification) event.sendMessage = message event.covered = send.subject + order_lifeline_covered_by(send) if received: message = get_subject() @@ -70,6 +99,7 @@ def connect_lifelines(line, send, received): event = message.model.create(UML.MessageOccurrenceSpecification) event.receiveMessage = message event.covered = received.subject + order_lifeline_covered_by(received) def disconnect_lifelines(line, send, received): diff --git a/gaphor/diagram/interactions/tests/test_messageconnect.py b/gaphor/diagram/interactions/tests/test_messageconnect.py index a01d91a52..16a29d5f6 100644 --- a/gaphor/diagram/interactions/tests/test_messageconnect.py +++ b/gaphor/diagram/interactions/tests/test_messageconnect.py @@ -158,10 +158,10 @@ def test_disconnection(diagram): assert msg.subject is None, f"{msg.subject}" -def test_lifetime_connectivity_on_head(diagram): +def test_lifetime_connectivity_on_head(diagram, element_factory): """Test lifeline's lifetime connectivity change on head connection """ - ll = diagram.create(LifelineItem) + ll = diagram.create(LifelineItem, subject=element_factory.create(UML.Lifeline)) msg = diagram.create(MessageItem) # connect message to lifeline's head, lifeline's lifetime @@ -177,10 +177,10 @@ def test_lifetime_connectivity_on_head(diagram): assert ll.lifetime.MIN_LENGTH == ll.lifetime.min_length -def test_lifetime_connectivity_on_lifetime(diagram): +def test_lifetime_connectivity_on_lifetime(diagram, element_factory): """Test lifeline's lifetime connectivity change on lifetime connection """ - ll = diagram.create(LifelineItem) + ll = diagram.create(LifelineItem, subject=element_factory.create(UML.Lifeline)) msg = diagram.create(MessageItem) ll.lifetime.visible = True diff --git a/gaphor/diagram/interactions/tests/test_ordering.py b/gaphor/diagram/interactions/tests/test_ordering.py new file mode 100644 index 000000000..b7f3651dc --- /dev/null +++ b/gaphor/diagram/interactions/tests/test_ordering.py @@ -0,0 +1,46 @@ +from gaphor import UML +from gaphor.diagram.interactions.executionspecification import ( + ExecutionSpecificationItem, +) +from gaphor.diagram.interactions.interactionsconnect import order_lifeline_covered_by +from gaphor.diagram.interactions.lifeline import LifelineItem +from gaphor.diagram.interactions.message import MessageItem +from gaphor.diagram.tests.fixtures import connect + + +def test_ordering(diagram, element_factory): + lifeline = diagram.create( + LifelineItem, subject=element_factory.create(UML.Lifeline) + ) + lifeline.lifetime.visible = True + lifeline.lifetime.bottom.pos.y = 1000 + lifetime_top = lifeline.lifetime.top.pos + + exec_spec = diagram.create(ExecutionSpecificationItem) + message1 = diagram.create(MessageItem) + message2 = diagram.create(MessageItem) + message3 = diagram.create(MessageItem) + + message1.head.pos.y = lifetime_top.y + 300 + exec_spec.top.pos.y = lifetime_top.y + 400 + message2.head.pos.y = lifetime_top.y + 500 + exec_spec.bottom.pos.y = lifetime_top.y + 600 + message3.tail.pos.y = lifetime_top.y + 700 + + # Add in "random" order + connect(exec_spec, exec_spec.handles()[0], lifeline, lifeline.lifetime.port) + connect(message3, message3.tail, lifeline, lifeline.lifetime.port) + connect(message1, message1.head, lifeline, lifeline.lifetime.port) + connect(message2, message2.head, exec_spec, exec_spec.ports()[1]) + + occurrences = [ + message1.subject.sendEvent, + exec_spec.subject.start, + message2.subject.sendEvent, + exec_spec.subject.finish, + message3.subject.receiveEvent, + ] + + order_lifeline_covered_by(lifeline) + + assert list(lifeline.subject.coveredBy) == occurrences