Skip to content

Commit 9feb4ea

Browse files
adaussyAxelRICHARD
authored andcommitted
[1030] Makes sourceFeature and targetFeature redefine source and target
It also: * Fix the diagram with using the new semantic (creation and reconnection) since the "virtual" membership can no longer be used in the source/target feature * Add IMigrationParticipant to avoid any model breakage * Fix the Importer to be able to keep tracks of the "virtual" membership used to "import" 'start'/'done' ActionUsage Bug: #1030 Signed-off-by: Arthur Daussy <arthur.daussy@obeo.fr>
1 parent b1a234a commit 9feb4ea

File tree

31 files changed

+1131
-130
lines changed

31 files changed

+1131
-130
lines changed

CHANGELOG.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
=== Breaking changes
1010

11+
- https://github.com/eclipse-syson/syson/issues/1030[#1030] [metamodel] The _source_ and _target_ features are no longer editable on all concepts inheriting from `ConnectorAsUsage`.
12+
1113
=== Dependency update
1214

1315
- [releng] Switch to https://github.com/spring-projects/spring-boot/releases/tag/v3.4.3[Spring Boot 3.4.3].
@@ -27,6 +29,7 @@ A new creation tool is available in the `Requirements` section of the palette, a
2729
A new dra & drop tool is available on the diagram, allowing moving `Satisfy Requirement Usage` graphical nodes on the diagram.
2830
- [explorer] The `General View` diagram is now proposed first when creating a diagram.
2931
- 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.
32+
- https://github.com/eclipse-syson/syson/issues/1030[#1030] [metamodel] `ConnectorAsUsage.getSourceFeature` and `ConnectorAsUsage.getTargetFeature` should redefine `Relationship.source` and `Relationship.target` features
3033

3134
=== New features
3235

@@ -91,7 +94,6 @@ The changes are:
9194
- https://github.com/eclipse-syson/syson/issues/1018[#1018] [libraries] Make customizing the default metamodels and libraries available in SysML projects easier by making default implementation `SysONDefaultLibrariesConfiguration.java` more extensible.
9295
To do so, create a `@Primary @Configuration` component that extend `SysMLDefaultLibrariesConfiguration`, and optionally redefine `getDefaultLibraries()` method and/or `getDefaultEPackages()`.
9396

94-
9597
=== New features
9698

9799
- https://github.com/eclipse-syson/syson/issues/977[#977] [validation] SysON now implements the constraints (a.k.a. validation rules) from the SysMLv2 specification.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Obeo.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Obeo - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.syson.application.migration;
14+
15+
import com.google.gson.JsonArray;
16+
import com.google.gson.JsonElement;
17+
import com.google.gson.JsonObject;
18+
import com.google.gson.JsonPrimitive;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
import org.eclipse.emf.ecore.EClass;
24+
import org.eclipse.emf.ecore.EClassifier;
25+
import org.eclipse.sirius.components.emf.migration.api.IMigrationParticipant;
26+
import org.eclipse.sirius.emfjson.resource.JsonResource;
27+
import org.eclipse.syson.sysml.SuccessionAsUsage;
28+
import org.eclipse.syson.sysml.SysmlPackage;
29+
import org.springframework.stereotype.Service;
30+
31+
/**
32+
* Migration participant used to migrate SysON SysMLv2 models previous to 2025.4.0.
33+
*
34+
* <p>
35+
* It is intended to overcome the previous implementation that serialized data in <i>source</i> and <i>target</i>
36+
* features of {@link SuccessionAsUsage} whereas now those features are derived and non editable. See detail at
37+
* https://github.com/eclipse-syson/syson/issues/1030
38+
* </p>
39+
*
40+
* @author Arthur Daussy
41+
*/
42+
@Service
43+
public class SuccessionAsUsageMigrationParticipant implements IMigrationParticipant {
44+
45+
private static final String PARTICIPANT_VERSION = "2025.4.0-202502270000";
46+
47+
@Override
48+
public String getVersion() {
49+
return PARTICIPANT_VERSION;
50+
}
51+
52+
@Override
53+
public void preDeserialization(JsonResource resource, JsonObject jsonObject) {
54+
if (this.isSysMLResource(jsonObject)) {
55+
this.collectElementsOfType(jsonObject, SysmlPackage.eINSTANCE.getConnectorAsUsage()).forEach(successionNode -> {
56+
successionNode.remove("source");
57+
successionNode.remove("target");
58+
});
59+
}
60+
}
61+
62+
private boolean isSysMLResource(JsonObject jsonObject) {
63+
JsonElement nsElements = jsonObject.get("ns");
64+
if (nsElements instanceof JsonObject nsElementObjects) {
65+
for (String currentKey : nsElementObjects.keySet()) {
66+
JsonPrimitive nsURI = nsElementObjects.getAsJsonPrimitive(currentKey);
67+
return SysmlPackage.eNS_URI.equals(nsURI.getAsString());
68+
}
69+
}
70+
return false;
71+
}
72+
73+
private List<JsonObject> collectElementsOfType(JsonObject jsonObject, EClass eClass) {
74+
List<JsonObject> successionAsUsages = new ArrayList<>();
75+
this.collectElementsOfType(jsonObject, eClass, successionAsUsages);
76+
return successionAsUsages;
77+
}
78+
79+
private void collectElementsOfType(JsonObject jsonObject, EClass eClass, List<JsonObject> accumulator) {
80+
for (String currentKey : jsonObject.keySet()) {
81+
Object value = jsonObject.get(currentKey);
82+
83+
if (value instanceof JsonObject child) {
84+
if (this.isInstanceOf(jsonObject, eClass)) {
85+
accumulator.add(child);
86+
}
87+
this.collectElementsOfType(child, eClass, accumulator);
88+
} else if (value instanceof JsonArray jsonArray) {
89+
this.collectElementsOfTypeInArray(jsonArray, eClass, accumulator);
90+
}
91+
}
92+
}
93+
94+
private boolean isInstanceOf(JsonObject child, EClass expectedEClass) {
95+
JsonElement eClassQn = child.get("eClass");
96+
if (eClassQn instanceof JsonPrimitive jsonPrimitive) {
97+
String eClassQualifedName = jsonPrimitive.getAsString();
98+
if (eClassQualifedName != null && eClassQualifedName.startsWith("sysml:")) {
99+
EClassifier objectEClassier = SysmlPackage.eINSTANCE.getEClassifier(eClassQualifedName.replaceFirst("sysml:", ""));
100+
if (objectEClassier instanceof EClass objectEClass && expectedEClass.isSuperTypeOf(objectEClass)) {
101+
return true;
102+
}
103+
}
104+
}
105+
return false;
106+
}
107+
108+
private void collectElementsOfTypeInArray(JsonArray jsonArray, EClass eClass, List<JsonObject> accumulator) {
109+
for (Object obj : jsonArray) {
110+
if (obj instanceof JsonArray) {
111+
this.collectElementsOfTypeInArray((JsonArray) obj, eClass, accumulator);
112+
} else if (obj instanceof JsonObject) {
113+
this.collectElementsOfType((JsonObject) obj, eClass, accumulator);
114+
}
115+
}
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Obeo.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Obeo - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.syson.application.migration;
14+
15+
import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextMigrationParticipantPredicate;
16+
import org.springframework.stereotype.Service;
17+
18+
/**
19+
* {@link IEditingContextMigrationParticipantPredicate} used to plug the migration mechanism on SysON SysMLv2 projects.
20+
*
21+
* @author Arthur Daussy
22+
*/
23+
@Service
24+
public class SysONEditingContextMigrationParticipantPredicate implements IEditingContextMigrationParticipantPredicate {
25+
26+
@Override
27+
public boolean test(String editingContextId) {
28+
// No Sysml/Syson project nature at the time of writing to restrict the number of impacted projects.
29+
return true;
30+
}
31+
}

backend/application/syson-application-configuration/src/main/resources/templates/Batmobile.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/editingContext/checkers/SemanticCheckerService.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@
1212
*******************************************************************************/
1313
package org.eclipse.syson.application.controller.editingContext.checkers;
1414

15+
import static org.assertj.core.api.Assertions.assertThat;
16+
17+
import java.util.Optional;
18+
import java.util.function.Consumer;
19+
import java.util.function.Supplier;
20+
1521
import org.eclipse.emf.ecore.EClass;
1622
import org.eclipse.emf.ecore.EReference;
1723
import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload;
1824
import org.eclipse.sirius.components.core.api.IObjectSearchService;
1925
import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload;
2026
import org.eclipse.syson.application.data.SysMLv2Identifiers;
2127
import org.eclipse.syson.services.SemanticRunnableFactory;
28+
import org.eclipse.syson.sysml.Element;
2229

2330
import reactor.test.StepVerifier.Step;
2431

@@ -33,9 +40,12 @@ public class SemanticCheckerService {
3340

3441
private final IObjectSearchService objectSearchService;
3542

36-
public SemanticCheckerService(SemanticRunnableFactory semanticRunnableFactory, IObjectSearchService objectSearchService) {
43+
private final String editingContextId;
44+
45+
public SemanticCheckerService(SemanticRunnableFactory semanticRunnableFactory, IObjectSearchService objectSearchService, String editingContextId) {
3746
this.semanticRunnableFactory = semanticRunnableFactory;
3847
this.objectSearchService = objectSearchService;
48+
this.editingContextId = editingContextId;
3949
}
4050

4151
public ISemanticChecker getElementInParentSemanticChecker(String parentLabel, EReference containmentReference, EClass childEClass) {
@@ -45,8 +55,34 @@ public ISemanticChecker getElementInParentSemanticChecker(String parentLabel, ER
4555
.hasType(childEClass);
4656
}
4757

58+
/**
59+
* Do some checks on the element identified by the given id.
60+
*
61+
* @param <T>
62+
* the type element under test
63+
* @param verifier
64+
* the verifier
65+
* @param type
66+
* the type of the element under test
67+
* @param idSupplier
68+
* a supplier that returns the id of the semantic object
69+
* @param semanticChecker
70+
* the checks that needs to be run
71+
*/
72+
public <T extends Element> void checkElement(Step<DiagramRefreshedEventPayload> verifier, Class<T> type, Supplier<String> idSupplier, Consumer<T> semanticChecker) {
73+
ISemanticChecker checker = editingContext -> {
74+
Optional<Object> optElement = this.objectSearchService.getObject(editingContext, idSupplier.get());
75+
assertThat(optElement).isPresent();
76+
Object element = optElement.get();
77+
assertThat(element).isInstanceOf(type);
78+
T castedElement = type.cast(element);
79+
semanticChecker.accept(castedElement);
80+
};
81+
this.checkEditingContext(checker, verifier);
82+
}
83+
4884
public void checkEditingContext(ISemanticChecker semanticChecker, Step<DiagramRefreshedEventPayload> verifier) {
49-
Runnable runnableChecker = this.semanticRunnableFactory.createRunnable(SysMLv2Identifiers.GENERAL_VIEW_WITH_TOP_NODES_EDITING_CONTEXT_ID,
85+
Runnable runnableChecker = this.semanticRunnableFactory.createRunnable(this.editingContextId,
5086
(editingContext, executeEditingContextFunctionInput) -> {
5187
semanticChecker.check(editingContext);
5288
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);

0 commit comments

Comments
 (0)