Skip to content

Commit 82e702a

Browse files
adaussyAxelRICHARD
authored andcommitted
[1042] Improve SuccessionAsUsage defining new the actions
Bug: #1042 Signed-off-by: Arthur Daussy <arthur.daussy@obeo.fr>
1 parent c186beb commit 82e702a

File tree

6 files changed

+173
-20
lines changed

6 files changed

+173
-20
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ A new dra & drop tool is available on the diagram, allowing moving `Satisfy Requ
3030
- [explorer] The `General View` diagram is now proposed first when creating a diagram.
3131
- https://github.com/eclipse-syson/syson/issues/1024[#1024] [diagrams] Allow `Usage` and `Definition` graphical node labels to be wrapped to handle long names more easily.
3232
- https://github.com/eclipse-syson/syson/issues/1030[#1030] [metamodel] `ConnectorAsUsage.getSourceFeature` and `ConnectorAsUsage.getTargetFeature` should redefine `Relationship.source` and `Relationship.target` features
33+
- https://github.com/eclipse-syson/syson/issues/1042[#1042] [import] Improve textual import of `SuccessionAsUsage` which define a new target action directly after the 'then' keyword.
3334

3435
=== New features
3536

backend/application/syson-application/src/test/java/org/eclipse/syson/application/export/ImportExportTests.java

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,38 @@ public void checkSuccessionAsUsageImplicitSourceTest() throws IOException {
7070
this.checker.check(input, input);
7171
}
7272

73+
@Test
74+
@DisplayName("Given a SuccessionAsUsage with target defined after the then keyword, when importing and exporting the model, then the exported text file should be semantically equal.")
75+
public void checkSuccessionDefiningImplicitTarget() throws IOException {
76+
var input = """
77+
action def ActionDef1 {
78+
action a0;
79+
first a0;
80+
then action a1;
81+
then action a2;
82+
}""";
83+
/**
84+
* Here we have differences here because :
85+
*
86+
* <ul>
87+
* <li>The construction of SuccessionAsUsage defining new ActionUsage is hard to detect so we chose to use the
88+
* complete syntax "first source then target;"</li>
89+
* <li>The current implementation of implicit specialization causes some issues during name de-resolution see
90+
* https://github.com/eclipse-syson/syson/issues/1029</li>
91+
* <ul>
92+
*/
93+
var expected = """
94+
action def ActionDef1 {
95+
action a0;
96+
then ActionDef1::a1;
97+
action a1;
98+
then ActionDef1::a2;
99+
action a2;
100+
}""";
101+
102+
this.checker.check(input, expected);
103+
}
104+
73105
@Test
74106
@DisplayName("Given a SuccessionAsUsage with an implicit source feature targeting the 'start' standard library element, when importing and exporting the model, then the exported text file should be semantically equal.")
75107
public void checkSuccessionAsUsageImplicitSourceToStartTest() throws IOException {
@@ -124,13 +156,9 @@ public void checkSuccessionAsUsageExplicitSourceTest() throws IOException {
124156
@Test
125157
public void checkUseCaseTest() throws IOException {
126158
/*
127-
* The file has been modified because a problem has been detected during the export phase.
128-
* Those problem force us to use some full-length qualified name. This should be investigated.
129-
*
130-
* for example:
131-
include UseCaseTest::uc2;
132-
* instead of
133-
include uc2;
159+
* The file has been modified because a problem has been detected during the export phase. Those problem force
160+
* us to use some full-length qualified name. This should be investigated. for example: include
161+
* UseCaseTest::uc2; instead of include uc2;
134162
*/
135163
var input = """
136164
package UseCaseTest {
@@ -242,18 +270,15 @@ public void checkScalarValueAttribute() throws IOException {
242270
/**
243271
* Test import/export on test file ImportTest.sysml.
244272
*
245-
* @see <a href="https://github.com/Systems-Modeling/SysML-v2-Release/blob/master/sysml/src/examples/Simple%20Tests/ImportTest.sysml">ImportTest</a>
273+
* @see <a href=
274+
* "https://github.com/Systems-Modeling/SysML-v2-Release/blob/master/sysml/src/examples/Simple%20Tests/ImportTest.sysml">ImportTest</a>
246275
*/
247276
@Test
248277
public void checkImportTest() throws IOException {
249278
/*
250-
* The file has been modified because a problem has been detected during the export phase.
251-
* Those problem force us to use some full-length qualified name. This should be investigated.
252-
*
253-
* for example:
254-
private import Pkg2::Pkg21::Pkg211::*::**;
255-
* instead of
256-
private import Pkg211::*::**;
279+
* The file has been modified because a problem has been detected during the export phase. Those problem force
280+
* us to use some full-length qualified name. This should be investigated. for example: private import
281+
* Pkg2::Pkg21::Pkg211::*::**; instead of private import Pkg211::*::**;
257282
*/
258283
var input = """
259284
package ImportTest {
@@ -335,7 +360,8 @@ public void checkConcernTest() throws IOException {
335360
/**
336361
* Test import/export on test file OccurrenceTest.sysml.
337362
*
338-
* @see <a href="https://github.com/Systems-Modeling/SysML-v2-Release/blob/master/sysml/src/examples/Simple%20Tests/OccurrenceTest.sysml">OccurrenceTest</a>
363+
* @see <a href=
364+
* "https://github.com/Systems-Modeling/SysML-v2-Release/blob/master/sysml/src/examples/Simple%20Tests/OccurrenceTest.sysml">OccurrenceTest</a>
339365
*/
340366
@Test
341367
public void checkOccurrenceTest() throws IOException {

backend/application/syson-application/src/test/java/org/eclipse/syson/application/imports/ImportSysMLModelTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,33 @@ public void setUp() {
6262
this.checker = new SysMLv2SemanticImportChecker(this.sysmlResourceLoader, this.editingDomainFactory, this.sysMLEditingContextProcessor);
6363
}
6464

65+
@Test
66+
@DisplayName("Given a SuccessionAsUsage using the syntax that create a new target action, when importing and exporting the model, then the source and target should be correctly computed.")
67+
public void checkSuccessionToDefinedActionSourceTest() throws IOException {
68+
var input = """
69+
action def ActionDef1 {
70+
first start;
71+
then action a1;
72+
then action a2;
73+
}""";
74+
75+
this.checker.checkImportedModel(resource -> {
76+
List<SuccessionAsUsage> successionAsUsages = EMFUtils.allContainedObjectOfType(resource, SuccessionAsUsage.class).toList();
77+
78+
assertThat(successionAsUsages).hasSize(2);
79+
80+
SuccessionAsUsage firstToA1 = successionAsUsages.get(0);
81+
82+
assertThat(firstToA1.getSourceFeature().getName()).isEqualTo("start");
83+
assertThat(firstToA1.getTargetFeature()).hasSize(1).allMatch(f -> "a1".equals(f.getName()));
84+
85+
SuccessionAsUsage firstToA2 = successionAsUsages.get(1);
86+
87+
assertThat(firstToA2.getSourceFeature().getName()).isEqualTo("a1");
88+
assertThat(firstToA2.getTargetFeature()).hasSize(1).allMatch(f -> "a2".equals(f.getName()));
89+
}).check(input);
90+
}
91+
6592
@Test
6693
@DisplayName("Given a SuccessionAsUsage with an implicit source feature targeting the 'start' standard library element, "
6794
+ "when importing the model, "

backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.eclipse.syson.sysml.parser.NonContainmentReferenceHandler;
3737
import org.eclipse.syson.sysml.parser.ProxiedReference;
3838
import org.eclipse.syson.sysml.parser.ProxyResolver;
39+
import org.eclipse.syson.sysml.util.ElementUtil;
3940
import org.eclipse.syson.sysml.utils.MessageReporter;
4041
import org.slf4j.Logger;
4142
import org.slf4j.LoggerFactory;
@@ -101,8 +102,43 @@ private void preResolvingFixingPhase(List<EObject> rootSysmlObjects) {
101102
* a root element.
102103
*/
103104
private void fixSuccessionUsageImplicitSource(EObject root) {
104-
EMFUtils.allContainedObjectOfType(root, SuccessionAsUsage.class)
105-
.filter(this::hasImplicitSourceFeature)
105+
List<SuccessionAsUsage> succesionAsUsage = EMFUtils.allContainedObjectOfType(root, SuccessionAsUsage.class).toList();
106+
this.fixImplicitSourceFeature(succesionAsUsage);
107+
this.fixImplicitTarget(succesionAsUsage);
108+
}
109+
110+
/**
111+
* Try to fix all {@link SuccessionAsUsage} elements contained in the given root to workaround
112+
* https://github.com/eclipse-syson/syson/issues/1042 and https://github.com/sensmetry/sysml-2ls/issues/13.
113+
*
114+
* @param root
115+
* a root element.
116+
*/
117+
private void fixImplicitTarget(List<SuccessionAsUsage> succesionAsUsage) {
118+
succesionAsUsage.stream().filter(this::hasImplicitTargetFeature)
119+
.forEach(suc -> {
120+
Feature invalidFeature = suc.getConnectorEnd().get(1);
121+
FeatureMembership owningFeatureMembershit = invalidFeature.getOwningFeatureMembership();
122+
ReferenceUsage refUsage = SysmlFactory.eINSTANCE.createReferenceUsage();
123+
EList<Element> ownedRelatedElements = owningFeatureMembershit.getOwnedRelatedElement();
124+
125+
Membership previousMembershipFeature = this.computeTargetFeatureMembership(suc);
126+
if (previousMembershipFeature != null && ElementUtil.isFromLibrary(previousMembershipFeature.getMemberElement(), true)) {
127+
// For implicit target that targets an element of the standard library, we need to keep a
128+
// "virtual link" to the previous feature membership to identify the source of SuccessionAsUsage
129+
// see implementation :
130+
// org.eclipse.syson.diagram.common.view.services.ViewCreateService.createEndFeatureMembershipFor(Element)
131+
refUsage.getAliasIds().add(previousMembershipFeature.getElementId());
132+
}
133+
134+
int index = ownedRelatedElements.indexOf(invalidFeature);
135+
ownedRelatedElements.remove(index);
136+
ownedRelatedElements.add(index, refUsage);
137+
});
138+
}
139+
140+
private void fixImplicitSourceFeature(List<SuccessionAsUsage> succesionAsUsage) {
141+
succesionAsUsage.stream().filter(this::hasImplicitSourceFeature)
106142
.forEach(suc -> {
107143
Feature invalidFeature = suc.getConnectorEnd().get(0);
108144
FeatureMembership owningFeatureMembershit = invalidFeature.getOwningFeatureMembership();
@@ -142,6 +178,26 @@ private Membership computePreviousFeatureMembership(SuccessionAsUsage succession
142178
return null;
143179
}
144180

181+
private Membership computeTargetFeatureMembership(SuccessionAsUsage successionAsUsage) {
182+
Type owningType = successionAsUsage.getOwningType();
183+
if (owningType != null) {
184+
185+
EList<Membership> ownedMemberships = owningType.getOwnedMembership();
186+
int index = ownedMemberships.indexOf(successionAsUsage.getOwningMembership());
187+
if (index > 0 && ownedMemberships.size() > index) {
188+
ListIterator<Membership> iterator = ownedMemberships.subList(index + 1, ownedMemberships.size()).listIterator();
189+
while (iterator.hasNext()) {
190+
Membership next = iterator.next();
191+
if (next.getMemberElement() instanceof Feature) {
192+
return next;
193+
}
194+
}
195+
}
196+
197+
}
198+
return null;
199+
}
200+
145201
private boolean hasImplicitSourceFeature(SuccessionAsUsage successionUsage) {
146202
EList<Feature> ends = successionUsage.getConnectorEnd();
147203
if (!ends.isEmpty()) {
@@ -151,6 +207,15 @@ private boolean hasImplicitSourceFeature(SuccessionAsUsage successionUsage) {
151207
return false;
152208
}
153209

210+
private boolean hasImplicitTargetFeature(SuccessionAsUsage successionUsage) {
211+
EList<Feature> ends = successionUsage.getConnectorEnd();
212+
if (ends.size() > 1) {
213+
Feature sourceEnd = ends.get(1);
214+
return sourceEnd.eClass() == SysmlPackage.eINSTANCE.getFeature() && sourceEnd.getOwnedRelationship().isEmpty();
215+
}
216+
return false;
217+
}
218+
154219
public List<Message> getTransformationMessages() {
155220
return this.messageReporter.getReportedMessages();
156221
}

backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/ImplicitSpecializationSwitch.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,17 @@ private List<Specialization> handleReferenceUsageInSuccessionAsUsage(ReferenceUs
203203
// At this moment we only handle the case of implicit source since we haven't found a
204204
// case where the target is implicit
205205
int index = parentSuccessionAsUsage.getOwnedFeature().indexOf(referenceUsage);
206+
boolean hasNoExpliciteSubsetting = this.getOwnedRelations(ReferenceSubsetting.class, referenceUsage).isEmpty();
206207
if (index == 0 && this.getOwnedRelations(ReferenceSubsetting.class, referenceUsage).isEmpty()) {
207208
// Source feature
208-
// Add a reference ReferenceSubsetting to the previous setting
209+
// Add a reference ReferenceSubsetting to the previous feature
209210
Feature sourceFeature = this.computeSourceFeature(parentSuccessionAsUsage);
210211
result = List.of(this.implicitReferenceSubsetting(referenceUsage, sourceFeature));
212+
} else if (index == 1 && hasNoExpliciteSubsetting) {
213+
// Target feature
214+
// Add a reference ReferenceSubsetting to the next feature
215+
Feature targetFeature = this.computeTargetFeature(parentSuccessionAsUsage);
216+
result = List.of(this.implicitReferenceSubsetting(referenceUsage, targetFeature));
211217
} else {
212218
result = List.of();
213219
}
@@ -1436,6 +1442,24 @@ private FeatureTyping implicitTyping(Feature feature, String implicitTypeQualifi
14361442
return null;
14371443
}
14381444

1445+
private Feature computeTargetFeature(SuccessionAsUsage successionAsUsage) {
1446+
Type owningType = successionAsUsage.getOwningType();
1447+
if (owningType != null) {
1448+
EList<Membership> ownedMemberships = owningType.getOwnedMembership();
1449+
int index = ownedMemberships.indexOf(successionAsUsage.getOwningMembership());
1450+
if (index > 0 && ownedMemberships.size() > index) {
1451+
ListIterator<Membership> iterator = ownedMemberships.subList(index + 1, ownedMemberships.size()).listIterator();
1452+
while (iterator.hasNext()) {
1453+
Membership next = iterator.next();
1454+
if (next.getMemberElement() instanceof Feature feature) {
1455+
return feature;
1456+
}
1457+
}
1458+
}
1459+
}
1460+
return null;
1461+
}
1462+
14391463
private boolean hasRedefinition(Element element) {
14401464
return this.existingSpecializations.stream().anyMatch(Redefinition.class::isInstance);
14411465
}

doc/content/modules/user-manual/pages/release-notes/2025.4.0.adoc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ image::release-notes-create-diagram-on-root-namespace-element.png[Create diagram
2626

2727
image::release-notes-gv-subject-creation-selection-dialog-tree.png['subject creation tool selection dialog tree' compartment, width=50%, height=50%]
2828

29-
- Improve handling of `SuccessionAsUsage` textual import with implicit source feature.
29+
- Improve handling of `SuccessionAsUsage` textual import with implicit _source_ property.
3030
For example importing the following SysMLv2 content would now create a valid semantic model:
3131

3232
```
@@ -50,6 +50,16 @@ image::release-notes-gv-satisfy-requirement-usage.png[Satisfy Requirement Usage
5050
- The `General View` diagram is now proposed first when creating a diagram.
5151
- It is now possible, in diagrams, to reduce the width of `Usages` (e.g. `Part`) and `Definition` (e.g. `Part Definition`) graphical nodes with a long name as their label can now be wrapped.
5252

53+
- Improve textual import of `SuccessionAsUsage` which define a new target action directly after the 'then' keyword.
54+
For example importing the following SysML file would now create a valid semantic model:
55+
56+
```
57+
action def ActionDef1 {
58+
first start;
59+
then action a1;
60+
then action a2;
61+
}
62+
```
5363

5464
== Dependency update
5565

0 commit comments

Comments
 (0)