Skip to content

Commit 1f702dd

Browse files
gcoutableAxelRICHARD
authored andcommitted
[2254] Add the support for 'assume' and 'require' edges
Bug: #2254 Signed-off-by: Guillaume Coutable <guillaume.coutable@obeo.fr>
1 parent 9d67f66 commit 1f702dd

12 files changed

Lines changed: 194 additions & 6 deletions

File tree

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ This is currently supported on `Features` (e.g. `Attribute`), `Constraints` and
5454
- https://github.com/eclipse-syson/syson/issues/2247[#2247] [diagrams] Add the support for creating _timeslices/snapshots_ from the different kind of `OccurrenceDefiniton` graphical nodes.
5555
It leverages the selection dialog to either create an _occurrence timeslice/snapshot_, or the _usage timeslice/snapshot_ matching the `OccurrenceDefinition` on which the tool is applied.
5656
- https://github.com/eclipse-syson/syson/issues/2250[#2250] [diagrams] Leverage the selection dialog to improve the graphical node tools creating a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes.
57+
- https://github.com/eclipse-syson/syson/issues/2254[#2254] [diagrams] Add the support for _assume_ and _require_ graphical edges.
5758

5859
== v2026.5.0
5960

backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,12 @@ public void createSubsettedConstraintUsageNodes(EClass parentEClass, String targ
422422
Consumer<Object> diagramCheck = assertRefreshedDiagramThat(newDiagram -> {
423423
new CheckDiagramElementCount(this.diagramComparator)
424424
.hasNewNodeCount(6)
425-
.hasNewEdgeCount(2)
425+
.hasNewEdgeCount(3)
426426
.check(diagram.get(), newDiagram);
427427

428428
new CheckDiagramElementCount(this.diagramComparator)
429429
.hasNewNodeCount(0)
430-
.hasNewEdgeCount(0)
430+
.hasNewEdgeCount(1)
431431
.check(diagram.get(), newDiagram, true);
432432

433433
String listConstraintNodeDescription = this.descriptionNameGenerator.getCompartmentItemName(parentEClass, containmentReference) + compartmentItemSuffix;

backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
import org.eclipse.syson.sysml.ConcernUsage;
1818
import org.eclipse.syson.sysml.Connector;
1919
import org.eclipse.syson.sysml.ConnectorAsUsage;
20+
import org.eclipse.syson.sysml.ConstraintUsage;
2021
import org.eclipse.syson.sysml.Element;
2122
import org.eclipse.syson.sysml.Feature;
2223
import org.eclipse.syson.sysml.FramedConcernMembership;
24+
import org.eclipse.syson.sysml.RequirementConstraintMembership;
2325
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
2426

2527
/**
@@ -77,4 +79,11 @@ public List<Feature> getConnectorTarget(ConnectorAsUsage connector) {
7779
public ConcernUsage getFramedConcernTarget(FramedConcernMembership framedConcernMembership) {
7880
return this.metamodelQueryElementService.getFramedConcernTarget(framedConcernMembership);
7981
}
82+
83+
/**
84+
* {@link MetamodelQueryElementService#getRequirementConstraintTarget(RequirementConstraintMembership)}.
85+
*/
86+
public ConstraintUsage getRequirementConstraintTarget(RequirementConstraintMembership requirementConstraintMembership) {
87+
return this.metamodelQueryElementService.getRequirementConstraintTarget(requirementConstraintMembership);
88+
}
8089
}

backend/services/syson-services/src/main/java/org/eclipse/syson/util/SysONEContentAdapter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
import org.eclipse.emf.ecore.util.EContentAdapter;
2424
import org.eclipse.syson.sysml.Element;
2525
import org.eclipse.syson.sysml.FeatureValue;
26-
import org.eclipse.syson.sysml.FramedConcernMembership;
2726
import org.eclipse.syson.sysml.Membership;
27+
import org.eclipse.syson.sysml.RequirementConstraintMembership;
28+
import org.eclipse.syson.sysml.RequirementVerificationMembership;
2829

2930
/**
3031
* EContentAdapter for SysON. Allow to cache SysML elements by their type.
@@ -84,6 +85,6 @@ public boolean isAdapterForType(Object type) {
8485
* @return whether the notifier is a membership that should be cached
8586
*/
8687
private boolean shouldCacheMembership(Notifier notifier) {
87-
return notifier instanceof FramedConcernMembership || notifier instanceof FeatureValue;
88+
return (!(notifier instanceof RequirementVerificationMembership) && notifier instanceof RequirementConstraintMembership) || notifier instanceof FeatureValue;
8889
}
8990
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public class LabelConstants {
2323

2424
public static final String ABSTRACT = SysMLv2Keywords.ABSTRACT;
2525

26+
public static final String ASSUME = SysMLv2Keywords.ASSUME;
27+
2628
public static final String CLOSE_BRACKET = SysMLv2Keywords.RIGHT_BRACKET;
2729

2830
public static final String CLOSE_PARENTHESIS = SysMLv2Keywords.RIGHT_PAREN;
@@ -77,6 +79,8 @@ public class LabelConstants {
7779

7880
public static final String REFERENCES = SysMLv2Keywords.DOUBLE_COLON_SPECIALIZES;
7981

82+
public static final String REQUIRE = SysMLv2Keywords.REQUIRE;
83+
8084
public static final String SATISFY = SysMLv2Keywords.SATISFY;
8185

8286
public static final String SPACE = " ";

backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.eclipse.syson.sysml.BooleanExpression;
2323
import org.eclipse.syson.sysml.ConcernUsage;
2424
import org.eclipse.syson.sysml.Connector;
25+
import org.eclipse.syson.sysml.ConstraintUsage;
2526
import org.eclipse.syson.sysml.Element;
2627
import org.eclipse.syson.sysml.Expression;
2728
import org.eclipse.syson.sysml.Feature;
@@ -31,6 +32,7 @@
3132
import org.eclipse.syson.sysml.OwningMembership;
3233
import org.eclipse.syson.sysml.PartUsage;
3334
import org.eclipse.syson.sysml.ReferenceUsage;
35+
import org.eclipse.syson.sysml.RequirementConstraintMembership;
3436
import org.eclipse.syson.sysml.ResultExpressionMembership;
3537
import org.eclipse.syson.sysml.StakeholderMembership;
3638
import org.eclipse.syson.sysml.SubjectMembership;
@@ -309,6 +311,23 @@ public ConcernUsage getFramedConcernTarget(FramedConcernMembership framedConcern
309311
return null;
310312
}
311313

314+
/**
315+
* Returns the requirement constraint target of {@link RequirementConstraintMembership}.
316+
* <p>
317+
* It returns a constraint when the requirement constraint membership owned constraint is subsetted by another constraint.
318+
* </p>
319+
*
320+
* @param requirementConstraintMembership
321+
* The requirement constraint membership
322+
* @return the requirement constraint target of {@link RequirementConstraintMembership}
323+
*/
324+
public ConstraintUsage getRequirementConstraintTarget(RequirementConstraintMembership requirementConstraintMembership) {
325+
if (requirementConstraintMembership.getOwnedConstraint() != requirementConstraintMembership.getReferencedConstraint()) {
326+
return requirementConstraintMembership.getReferencedConstraint();
327+
}
328+
return null;
329+
}
330+
312331
/**
313332
* Get the {@link ResultExpressionMembership} contained inside a given {@link Namespace}.
314333
*
@@ -323,5 +342,4 @@ public ResultExpressionMembership getResultExpressionMembership(Namespace namesp
323342
.findFirst()
324343
.orElse(null);
325344
}
326-
327345
}

backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
import org.eclipse.syson.standard.diagrams.view.edges.NestedSubjectEdgeDescriptionProvider;
9595
import org.eclipse.syson.standard.diagrams.view.edges.RedefinitionEdgeDescriptionProvider;
9696
import org.eclipse.syson.standard.diagrams.view.edges.ReferenceSubsettingEdgeDescriptionProvider;
97+
import org.eclipse.syson.standard.diagrams.view.edges.RequirementConstraintEdgeDescriptionProvider;
9798
import org.eclipse.syson.standard.diagrams.view.edges.SatisfyRequirementEdgeDescriptionProvider;
9899
import org.eclipse.syson.standard.diagrams.view.edges.SubclassificationEdgeDescriptionProvider;
99100
import org.eclipse.syson.standard.diagrams.view.edges.SubsettingEdgeDescriptionProvider;
@@ -963,6 +964,7 @@ private List<IDiagramElementDescriptionProvider<?>> createAllEdgeDescriptionProv
963964
edgeDescriptionProviders.add(new ConnectionUsageEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator()));
964965
edgeDescriptionProviders.add(new SatisfyRequirementEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator()));
965966
edgeDescriptionProviders.add(new FrameConcernEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator()));
967+
edgeDescriptionProviders.add(new RequirementConstraintEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator()));
966968

967969
return edgeDescriptionProviders;
968970
}

backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.eclipse.syson.util.ViewConstants;
4242

4343
/**
44-
* Used to create the edge description representing the `frame` relationship between a {@link org.eclipse.syson.sysml.RequirementUsage} and a {@link org.eclipse.syson.sysml.ConcernUsage}.
44+
* Used to create the edge description representing the `frame` relationship between a {@link org.eclipse.syson.sysml.RequirementDefinition} or a {@link org.eclipse.syson.sysml.RequirementUsage} and a {@link org.eclipse.syson.sysml.ConcernUsage}.
4545
*
4646
* @author gcoutable
4747
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 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.standard.diagrams.view.edges;
14+
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.Objects;
18+
19+
import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder;
20+
import org.eclipse.sirius.components.view.builder.generated.view.ChangeContextBuilder;
21+
import org.eclipse.sirius.components.view.builder.providers.IColorProvider;
22+
import org.eclipse.sirius.components.view.diagram.ArrowStyle;
23+
import org.eclipse.sirius.components.view.diagram.DeleteTool;
24+
import org.eclipse.sirius.components.view.diagram.DiagramDescription;
25+
import org.eclipse.sirius.components.view.diagram.EdgeDescription;
26+
import org.eclipse.sirius.components.view.diagram.EdgeStyle;
27+
import org.eclipse.sirius.components.view.diagram.LineStyle;
28+
import org.eclipse.sirius.components.view.diagram.NodeDescription;
29+
import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy;
30+
import org.eclipse.syson.diagram.common.view.DescriptionFinder;
31+
import org.eclipse.syson.diagram.common.view.edges.AbstractEdgeDescriptionProvider;
32+
import org.eclipse.syson.model.services.aql.ModelQueryAQLService;
33+
import org.eclipse.syson.services.DeleteService;
34+
import org.eclipse.syson.services.UtilService;
35+
import org.eclipse.syson.sysml.SysmlPackage;
36+
import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
37+
import org.eclipse.syson.util.AQLConstants;
38+
import org.eclipse.syson.util.IDescriptionNameGenerator;
39+
import org.eclipse.syson.util.ServiceMethod;
40+
import org.eclipse.syson.util.SysMLMetamodelHelper;
41+
import org.eclipse.syson.util.ViewConstants;
42+
43+
/**
44+
* Used to create the edge description representing both the 'require' and 'assume' relationships between a {@link org.eclipse.syson.sysml.RequirementDefinition} or a {@link org.eclipse.syson.sysml.RequirementUsage} and a {@link org.eclipse.syson.sysml.ConstraintUsage}.
45+
*
46+
* @author gcoutable
47+
*/
48+
public class RequirementConstraintEdgeDescriptionProvider extends AbstractEdgeDescriptionProvider {
49+
50+
private final IDescriptionNameGenerator descriptionNameGenerator;
51+
52+
public RequirementConstraintEdgeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) {
53+
super(colorProvider);
54+
this.descriptionNameGenerator = Objects.requireNonNull(descriptionNameGenerator);
55+
}
56+
57+
@Override
58+
protected ChangeContextBuilder getSourceReconnectToolBody() {
59+
return this.viewBuilderHelper.newChangeContext()
60+
.expression("");
61+
}
62+
63+
@Override
64+
protected ChangeContextBuilder getTargetReconnectToolBody() {
65+
return this.viewBuilderHelper.newChangeContext()
66+
.expression("");
67+
}
68+
69+
@Override
70+
public EdgeDescription create() {
71+
String domainType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getRequirementConstraintMembership());
72+
return this.diagramBuilderHelper.newEdgeDescription()
73+
.domainType(domainType)
74+
.isDomainBasedEdge(true)
75+
.centerLabelExpression(this.getCenterLabelExpression())
76+
.name(this.getName())
77+
.semanticCandidatesExpression(ServiceMethod.of1(UtilService::getAllReachable).aqlSelf(domainType))
78+
.sourceExpression(AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getRelationship_OwningRelatedElement().getName())
79+
.style(this.createEdgeStyle())
80+
.synchronizationPolicy(SynchronizationPolicy.SYNCHRONIZED)
81+
.targetExpression(ServiceMethod.of0(ModelQueryAQLService::getRequirementConstraintTarget).aqlSelf())
82+
.build();
83+
}
84+
85+
@Override
86+
public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) {
87+
var optEdgeDescription = cache.getEdgeDescription(this.getName());
88+
optEdgeDescription.ifPresent(edgeDescription -> {
89+
diagramDescription.getEdgeDescriptions().add(edgeDescription);
90+
91+
var sourceNodes = this.getSourceNodes(cache);
92+
var targetNodes = this.getTargetNodes(cache);
93+
edgeDescription.getSourceDescriptions().addAll(sourceNodes);
94+
edgeDescription.getTargetDescriptions().addAll(targetNodes);
95+
96+
edgeDescription.setPalette(this.createEdgePalette(cache));
97+
});
98+
}
99+
100+
@Override
101+
protected DeleteTool getEdgeDeleteTool() {
102+
// Delete the referenced subsetting only.
103+
var referenceSubsetting = this.viewBuilderHelper.newChangeContext()
104+
.expression(AQLConstants.AQL_SELF + ".ownedConstraint.ownedRelationship->select(rel | rel.oclIsKindOf(sysml::ReferenceSubsetting))->first()")
105+
.children(this.viewBuilderHelper.newChangeContext()
106+
.expression(ServiceMethod.of0(DeleteService::deleteFromModel).aqlSelf())
107+
.build()
108+
);
109+
110+
return this.diagramBuilderHelper.newDeleteTool()
111+
.name("Delete from Model")
112+
.body(referenceSubsetting.build())
113+
.build();
114+
}
115+
116+
private String getCenterLabelExpression() {
117+
return AQLConstants.AQL + "if self.kind = sysml::RequirementConstraintKind.getEEnumLiteral('requirement').instance then '" + LabelConstants.OPEN_QUOTE + LabelConstants.REQUIRE + LabelConstants.CLOSE_QUOTE + "' else '" + LabelConstants.OPEN_QUOTE + LabelConstants.ASSUME + LabelConstants.CLOSE_QUOTE + "' endif";
118+
}
119+
120+
private String getName() {
121+
return this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getRequirementConstraintMembership());
122+
}
123+
124+
private EdgeStyle createEdgeStyle() {
125+
return this.diagramBuilderHelper.newEdgeStyle()
126+
.borderSize(0)
127+
.color(this.colorProvider.getColor(ViewConstants.DEFAULT_EDGE_COLOR))
128+
.edgeWidth(1)
129+
.lineStyle(LineStyle.SOLID)
130+
.sourceArrowStyle(ArrowStyle.NONE)
131+
.targetArrowStyle(ArrowStyle.INPUT_ARROW)
132+
.build();
133+
}
134+
135+
private List<NodeDescription> getSourceNodes(IViewDiagramElementFinder cache) {
136+
List<NodeDescription> sources = new ArrayList<>();
137+
var descriptionFinder = new DescriptionFinder(this.descriptionNameGenerator);
138+
sources.addAll(descriptionFinder.getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getRequirementUsage()));
139+
sources.addAll(descriptionFinder.getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getRequirementDefinition()));
140+
return sources;
141+
}
142+
143+
private List<NodeDescription> getTargetNodes(IViewDiagramElementFinder cache) {
144+
return new DescriptionFinder(this.descriptionNameGenerator).getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getConstraintUsage());
145+
}
146+
}

backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ public void eachEdgeWithCenterLabelHasDirectEditTool() {
9797
.filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getSatisfyRequirementUsage()).negate())
9898
// FramedConcernMembership edge has a label (frame) but it is a constant and should not be modifiable
9999
.filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getFramedConcernMembership()).negate())
100+
// RequirementConstraintMembership edges have a label (assume or require) but they are a constant and should not be modifiable
101+
.filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getRequirementConstraintMembership()).negate())
100102
.toList();
101103
new EdgeDescriptionHasDirectEditToolChecker().checkAll(edgeDescriptionCandidates);
102104
}

0 commit comments

Comments
 (0)