Skip to content

Commit 1c1765e

Browse files
adaussyAxelRICHARD
authored andcommitted
[1895] Implement textual export of StateUsage and StateDefinition
Signed-off-by: Arthur Daussy <arthur.daussy@obeo.fr> Bug: #1895
1 parent 9203957 commit 1c1765e

4 files changed

Lines changed: 149 additions & 22 deletions

File tree

CHANGELOG.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ SysON now uses its own indexing logic instead of the default one provided by Sir
2222
This allows to limit the size of the indices, and ensure information stored in the indices are useful to perform cross-project search.
2323
You can find more information on how to setup Elasticsearch, how elements are mapped to index documents, and how to query them in the documentation.
2424
- https://github.com/eclipse-syson/syson/issues/1861[#1861] [publication] Split `SysONLibraryPublicationHandler` in two distinct classes so the publishing logic can be extended or re-used through the `ISysMLLibraryPublisher` API.
25+
- https://github.com/eclipse-syson/syson/issues/1895[#1895] [export] Implement textual export of `StateUsage` and `StateDefinition`.
26+
2527

2628
=== New features
2729

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,64 @@ public void checkConnectionUsageWithBody() throws IOException {
13351335
.check();
13361336
}
13371337

1338+
/**
1339+
* Test import/export on test file StateTest.sysml. The content of StateTest.sysml that have been copied below
1340+
* is under LGPL-3.0-only license. The LGPL-3.0-only license is accessible at the root of this repository, in the
1341+
* LICENSE-LGPL file.
1342+
*
1343+
* <p><b>NOTE:</b> The TransitionUsage has been remove from the original model for this test.</p>
1344+
*
1345+
* @see <a href=
1346+
* "https://github.com/Systems-Modeling/SysML-v2-Release/blob/master/sysml/src/examples/Simple%20Tests/StateTest.sysml">StateTest.sysml</a>
1347+
*/
1348+
@Test
1349+
@DisplayName("GIVEN a model with StateUsage and StateDefinition, WHEN importing and exporting the model, THEN the states are properly exported")
1350+
public void checkStates() throws IOException {
1351+
var input = """
1352+
package StateTest {
1353+
attribute def Sig {
1354+
x;
1355+
}
1356+
attribute def Exit;
1357+
part p;
1358+
action act;
1359+
state def S {
1360+
do action A;
1361+
entry ;
1362+
state S1;
1363+
state S2 {
1364+
state S3;
1365+
}
1366+
exit act;
1367+
state S3 {
1368+
state S3a;
1369+
}
1370+
}
1371+
state s0 {
1372+
state s1 {
1373+
state s2;
1374+
}
1375+
state s3 {
1376+
state s4;
1377+
}
1378+
}
1379+
state s parallel {
1380+
state s1;
1381+
state s2;
1382+
}
1383+
state s4 {
1384+
do action a;
1385+
action c;
1386+
}
1387+
state s5 :> s4 {
1388+
do action b :>> c;
1389+
}
1390+
}""";
1391+
this.checker.textToImport(input)
1392+
.expectedResult(input)
1393+
.check();
1394+
}
1395+
13381396
@Test
13391397
@DisplayName("GIVEN with conflicting names used in refence, WHEN importing and exporting the model, THEN the names used in the qualified name should be properly escaped.")
13401398
public void checkNameConflictResolutionWithQn() throws IOException {

backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@
128128
import org.eclipse.syson.sysml.SelectExpression;
129129
import org.eclipse.syson.sysml.Specialization;
130130
import org.eclipse.syson.sysml.StakeholderMembership;
131+
import org.eclipse.syson.sysml.StateDefinition;
132+
import org.eclipse.syson.sysml.StateSubactionMembership;
131133
import org.eclipse.syson.sysml.StateUsage;
132134
import org.eclipse.syson.sysml.Subclassification;
133135
import org.eclipse.syson.sysml.SubjectMembership;
@@ -228,13 +230,24 @@ public String caseActionDefinition(ActionDefinition actionDef) {
228230
@Override
229231
public String caseActionUsage(ActionUsage actionUsage) {
230232
Appender builder = new Appender(this.lineSeparator, this.indentation);
231-
this.appendOccurrenceUsagePrefix(builder, actionUsage);
232-
builder.appendWithSpaceIfNeeded("action");
233-
this.appendActionUsageDeclaration(builder, actionUsage);
233+
if (!this.isEmptyActionAsStateSubAction(actionUsage)) {
234+
// Normal rule "ActionUsage"
235+
this.appendOccurrenceUsagePrefix(builder, actionUsage);
236+
builder.appendWithSpaceIfNeeded("action");
237+
this.appendActionUsageDeclaration(builder, actionUsage);
238+
239+
} // Rule "EmptyActionUsage" when store in StateSubactionMembership => Do nothing
240+
234241
this.appendChildrenContent(builder, actionUsage, actionUsage.getOwnedMembership());
242+
235243
return builder.toString();
236244
}
237245

246+
private boolean isEmptyActionAsStateSubAction(ActionUsage actionUsage) {
247+
return actionUsage.getOwningMembership() instanceof StateSubactionMembership && actionUsage.getOwnedRelationship()
248+
.isEmpty() && actionUsage.getDeclaredName() == null && actionUsage.getShortName() == null;
249+
}
250+
238251
@Override
239252
public String caseAnalysisCaseUsage(AnalysisCaseUsage analysisCaseUsage) {
240253
this.reportUnhandledType(analysisCaseUsage);
@@ -863,29 +876,21 @@ public String casePartUsage(PartUsage partUsage) {
863876
}
864877

865878
@Override
866-
public String casePerformActionUsage(PerformActionUsage perfomActionUsage) {
879+
public String casePerformActionUsage(PerformActionUsage performActionUsage) {
867880

868881
Appender builder = new Appender(this.lineSeparator, this.indentation);
882+
if (!(performActionUsage.getOwningMembership() instanceof StateSubactionMembership)) {
883+
// Rule simple "PerformActionUsage" from BehaviorUsageElement
884+
this.appendOccurrenceUsagePrefix(builder, performActionUsage);
869885

870-
this.appendOccurrenceUsagePrefix(builder, perfomActionUsage);
886+
builder.appendWithSpaceIfNeeded("perform");
887+
} // Else do nothing using rule PerformActionUsageDeclaration
871888

872-
builder.appendWithSpaceIfNeeded("perform");
889+
this.appendPerformActionUsageDeclaration(performActionUsage, builder);
873890

874-
Appender nameAppender = new Appender(this.lineSeparator, this.indentation);
875-
this.appendNameWithShortName(nameAppender, perfomActionUsage);
876-
877-
if (nameAppender.isEmpty() && perfomActionUsage.getOwnedReferenceSubsetting() != null) {
878-
// Use simple form : perfom <nameOfReferenceSubSetting>
879-
this.appendOwnedReferenceSubsetting(builder, perfomActionUsage.getOwnedReferenceSubsetting());
880-
} else {
881-
// Use complete form
882-
builder.appendWithSpaceIfNeeded("action");
883-
this.appendUsageDeclaration(builder, perfomActionUsage);
884-
}
891+
this.appendValuePart(builder, performActionUsage);
885892

886-
this.appendValuePart(builder, perfomActionUsage);
887-
888-
this.appendChildrenContent(builder, perfomActionUsage, perfomActionUsage.getOwnedMembership());
893+
this.appendChildrenContent(builder, performActionUsage, performActionUsage.getOwnedMembership());
889894

890895
return builder.toString();
891896
}
@@ -1065,10 +1070,56 @@ public String caseStakeholderMembership(StakeholderMembership stakeholderMembers
10651070
return builder.toString();
10661071
}
10671072

1073+
@Override
1074+
public String caseStateDefinition(StateDefinition stateDefinition) {
1075+
Appender builder = this.newAppender();
1076+
this.appendDefinitionPrefix(builder, stateDefinition);
1077+
builder.appendSpaceIfNeeded().append(SysMLv2Keywords.STATE + " " + SysMLv2Keywords.DEF);
1078+
this.appendDefinitionDeclaration(builder, stateDefinition);
1079+
1080+
if (stateDefinition.isIsParallel()) {
1081+
builder.appendWithSpaceIfNeeded(SysMLv2Keywords.PARALLEL);
1082+
}
1083+
1084+
this.appendChildrenContent(builder, stateDefinition, stateDefinition.getOwnedMembership());
1085+
return builder.toString();
1086+
}
1087+
1088+
@Override
1089+
public String caseStateSubactionMembership(StateSubactionMembership stateSubactionMembership) {
1090+
Appender builder = this.newAppender();
1091+
1092+
this.appendMembershipPrefix(stateSubactionMembership, builder);
1093+
1094+
String stateKind = switch (stateSubactionMembership.getKind()) {
1095+
case DO -> SysMLv2Keywords.DO;
1096+
case ENTRY -> SysMLv2Keywords.ENTRY;
1097+
case EXIT -> SysMLv2Keywords.EXIT;
1098+
};
1099+
1100+
builder.appendWithSpaceIfNeeded(stateKind);
1101+
1102+
String content = stateSubactionMembership.getOwnedRelatedElement().stream()
1103+
.map(this::doSwitch).filter(Objects::nonNull).collect(joining(builder.getNewLine()));
1104+
builder.appendSpaceIfNeeded().append(content);
1105+
1106+
return builder.toString();
1107+
}
1108+
10681109
@Override
10691110
public String caseStateUsage(StateUsage stateUsage) {
1070-
this.reportUnhandledType(stateUsage);
1071-
return "";
1111+
Appender builder = this.newAppender();
1112+
1113+
this.appendOccurrenceUsagePrefix(builder, stateUsage);
1114+
builder.appendWithSpaceIfNeeded(SysMLv2Keywords.STATE);
1115+
this.appendActionUsageDeclaration(builder, stateUsage);
1116+
1117+
if (stateUsage.isIsParallel()) {
1118+
builder.appendWithSpaceIfNeeded(SysMLv2Keywords.PARALLEL);
1119+
}
1120+
1121+
this.appendChildrenContent(builder, stateUsage, stateUsage.getOwnedMembership());
1122+
return builder.toString();
10721123
}
10731124

10741125
@Override
@@ -2803,4 +2854,18 @@ private boolean useRequirementConstraintUsageShortHandNotation(ConstraintUsage c
28032854
&& constraintUsage.getOwnedReferenceSubsetting() != null
28042855
&& constraintUsage.getNestedMetadata().isEmpty();
28052856
}
2857+
2858+
private void appendPerformActionUsageDeclaration(PerformActionUsage performActionUsage, Appender builder) {
2859+
Appender nameAppender = new Appender(this.lineSeparator, this.indentation);
2860+
this.appendNameWithShortName(nameAppender, performActionUsage);
2861+
2862+
if (nameAppender.isEmpty() && performActionUsage.getOwnedReferenceSubsetting() != null) {
2863+
// Use simple form : perfom <nameOfReferenceSubSetting>
2864+
this.appendOwnedReferenceSubsetting(builder, performActionUsage.getOwnedReferenceSubsetting());
2865+
} else {
2866+
// Use complete form
2867+
builder.appendWithSpaceIfNeeded("action");
2868+
this.appendUsageDeclaration(builder, performActionUsage);
2869+
}
2870+
}
28062871
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ It's not recommended for production use.
2020

2121
== Improvements
2222

23+
* Implement the textual export for `StateUsage` and `StateDefinition`.
24+
2325
== Technical details
2426

2527
* For technical details on this {product} release (including breaking changes), please refer to https://github.com/eclipse-syson/syson/blob/main/CHANGELOG.adoc[changelog].

0 commit comments

Comments
 (0)