From 9306e7abe3d6106a8a8bafd0c86616b9a3291e34 Mon Sep 17 00:00:00 2001 From: Guillaume Coutable Date: Mon, 27 Apr 2026 10:45:56 +0200 Subject: [PATCH] [2174] Add the support for frame edge Bug: https://github.com/eclipse-syson/syson/issues/2174 Signed-off-by: Guillaume Coutable --- CHANGELOG.adoc | 1 + .../general/view/GVFramedConcernTests.java | 188 ++++++++++++++++++ .../diagrams/testers/EdgeCreationTester.java | 1 + ...eneralViewWithTopNodesTestProjectData.java | 18 +- .../syson/sysml/helper/LabelConstants.java | 2 + .../impl/FramedConcernMembershipImpl.java | 2 +- .../services/ModelMutationElementService.java | 31 +++ .../services/aql/ModelMutationAQLService.java | 11 +- .../services/aql/ModelQueryAQLService.java | 9 + .../syson/util/SysONEContentAdapter.java | 18 +- .../MetamodelQueryElementService.java | 21 +- .../DescriptionsId.txt | 1 + .../view/services/ViewEdgeToolService.java | 20 ++ .../view/services/ViewEdgeToolSwitch.java | 10 + .../view/SDVDiagramDescriptionProvider.java | 2 + .../FrameConcernEdgeDescriptionProvider.java | 143 +++++++++++++ .../view/SDVDiagramDescriptionTests.java | 4 +- .../images/release-notes-frame-edge-tool.png | Bin 0 -> 41835 bytes .../images/release-notes-frame-edge.png | Bin 0 -> 13480 bytes .../pages/release-notes/2026.5.0.adoc | 7 + 20 files changed, 475 insertions(+), 14 deletions(-) create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFramedConcernTests.java create mode 100644 backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java create mode 100644 doc/content/modules/user-manual/assets/images/release-notes-frame-edge-tool.png create mode 100644 doc/content/modules/user-manual/assets/images/release-notes-frame-edge.png diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4853a7d32..c37ff254a 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -96,6 +96,7 @@ Both tools optionally allow selecting an existing `StateUsage` to exhibit. - https://github.com/eclipse-syson/syson/issues/1979[#1979] [diagrams] Add tools to create timeslices and snapshots, available on `OccurrenceUsage`, `ItemUsage`, and `PartUsage` graphical nodes. These tools leverage a selection dialog to either create a specific timeslice/snapshot graphical node or, if no selection is made, default to a timeslice/snapshot `OccurrenceUsage` graphical node. - https://github.com/eclipse-syson/syson/issues/2176[#2176] [metamodel] Implements `ownedConcern` and `referencedConcern` of `FramedConcernMembership`. +- https://github.com/eclipse-syson/syson/issues/2174[#2174] [diagrams] Add a new edge tool creating a _frame_ graphical edge between a `RequirementUsage` or a `RequirementDefinition` graphical node, and a `ConcernUsage` graphical node. == v2026.3.0 diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFramedConcernTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFramedConcernTests.java new file mode 100644 index 000000000..5b4c7cf86 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVFramedConcernTests.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.diagrams.general.view; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.type; +import static org.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat; +import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE; +import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE_STYLE; + +import java.time.Duration; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.diagrams.ArrowStyle; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Edge; +import org.eclipse.sirius.components.diagrams.Label; +import org.eclipse.sirius.components.diagrams.ViewModifier; +import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; +import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; +import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.GivenSysONServer; +import org.eclipse.syson.application.controller.editingcontext.checkers.SemanticCheckerService; +import org.eclipse.syson.application.controllers.diagrams.testers.EdgeCreationTester; +import org.eclipse.syson.application.data.GeneralViewWithTopNodesTestProjectData; +import org.eclipse.syson.services.SemanticRunnableFactory; +import org.eclipse.syson.services.diagrams.DiagramComparator; +import org.eclipse.syson.services.diagrams.DiagramDescriptionIdProvider; +import org.eclipse.syson.services.diagrams.api.IGivenDiagramDescription; +import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription; +import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator; +import org.eclipse.syson.sysml.FramedConcernMembership; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.sysml.helper.LabelConstants; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +/** + * {@link org.eclipse.syson.sysml.FramedConcernMembership} related test in General View. + * + * @author gcoutable + */ +@Transactional +@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) +@SuppressWarnings("checkstyle:MultipleStringLiterals") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GVFramedConcernTests extends AbstractIntegrationTests { + + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private IGivenDiagramSubscription givenDiagramSubscription; + + @Autowired + private IGivenDiagramDescription givenDiagramDescription; + + @Autowired + private IDiagramIdProvider diagramIdProvider; + + @Autowired + private DiagramComparator diagramComparator; + + @Autowired + private SemanticRunnableFactory semanticRunnableFactory; + + @Autowired + private IObjectSearchService objectSearchService; + + @Autowired + private EdgeCreationTester edgeCreationTester; + + private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); + + private SemanticCheckerService semanticCheckerService; + + private Flux givenSubscriptionToDiagram() { + var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID); + return this.givenDiagramSubscription.subscribe(diagramEventInput); + } + + @BeforeEach + public void setUp() { + this.givenInitialServerState.initialize(); + this.semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID); + } + + @Test + public void testCreateFramedConcernWithEdgeBetweenRequirementUsageAndConcernUsage() { + this.createFramedConcernWithEdge(SysmlPackage.eINSTANCE.getRequirementUsage(), GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_USAGE_ID, "requirement"); + } + + @Test + public void testCreateFramedConcernWithEdgeBetweenRequirementDefinitionAndConcernUsage() { + this.createFramedConcernWithEdge(SysmlPackage.eINSTANCE.getRequirementDefinition(), GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_DEFINITION_ID, "RequirementDefinition"); + } + + private void createFramedConcernWithEdge(EClass parentEClass, String graphicalSourceNodeId, String parentLabel) { + var flux = this.givenSubscriptionToDiagram(); + + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider); + + var edgeCreationToolId = diagramDescriptionIdProvider.getEdgeCreationToolId( + this.descriptionNameGenerator.getNodeName(parentEClass), + "New framed Concern"); + + AtomicReference diagram = new AtomicReference<>(); + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + Runnable createEdgeRunnable = () -> this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + diagram, + graphicalSourceNodeId, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.CONCERN_USAGE_ID, + edgeCreationToolId); + + Consumer diagramContentConsumerAfterNewEdge = assertRefreshedDiagramThat(newDiagram -> { + var newEdges = this.diagramComparator.newEdges(diagram.get(), newDiagram); + var newVisibleEdges = newEdges.stream().filter(edge -> edge.getState() != ViewModifier.Hidden).toList(); + + assertThat(newVisibleEdges).hasSize(1).first(EDGE) + .hasSourceId(graphicalSourceNodeId) + .hasTargetId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.CONCERN_USAGE_ID) + .extracting(Edge::getCenterLabel) + .extracting(Label::text) + .hasToString(LabelConstants.OPEN_QUOTE + LabelConstants.FRAME + LabelConstants.CLOSE_QUOTE); + + assertThat(newVisibleEdges).hasSize(1).first(EDGE) + .extracting(Edge::getStyle, EDGE_STYLE) + .hasSourceArrow(ArrowStyle.None) + .hasTargetArrow(ArrowStyle.InputArrow); + }); + + Consumer additionalCheck = object -> { + assertThat(object).isInstanceOf(List.class) + .asInstanceOf(type(List.class)) + .satisfies(framedConcerns -> { + assertThat((List) framedConcerns).size().isEqualTo(1); + assertThat(framedConcerns.getFirst()) + .isInstanceOf(FramedConcernMembership.class) + .asInstanceOf(type(FramedConcernMembership.class)) + .satisfies(framedConcernMembership -> { + var ownedConcern = framedConcernMembership.getOwnedConcern(); + var ownedSubsetting = ownedConcern.getOwnedSubsetting(); + assertThat(ownedSubsetting).isNotEmpty(); + assertThat(ownedSubsetting.getFirst().getSubsettedFeature()).isEqualTo(framedConcernMembership.getReferencedConcern()); + }); + }); + }; + + Runnable semanticCheck = this.semanticCheckerService.checkEditingContext(this.semanticCheckerService.getElementInParentSemanticChecker(parentLabel, SysmlPackage.eINSTANCE.getElement_OwnedRelationship(), SysmlPackage.eINSTANCE.getFramedConcernMembership(), additionalCheck)); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(createEdgeRunnable) + .consumeNextWith(diagramContentConsumerAfterNewEdge) + .then(semanticCheck) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/EdgeCreationTester.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/EdgeCreationTester.java index 073bf50e1..456318a2e 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/EdgeCreationTester.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/EdgeCreationTester.java @@ -47,6 +47,7 @@ public class EdgeCreationTester { @Autowired private InvokeSingleClickOnTwoDiagramElementsToolMutationRunner invokeSingleClickOnTwoDiagramElementsToolMutationRunner; + @Deprecated public void createEdge(String projectId, AtomicReference diagram, String sourceNodeTargetObjectLabel, String targetNodeTargetObjectLabel, String toolId) { DiagramNavigator diagramNavigator = new DiagramNavigator(diagram.get()); String sourceId = diagramNavigator.nodeWithTargetObjectLabel(sourceNodeTargetObjectLabel).getNode().getId(); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java index f5f41aa8d..66abadd3a 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java @@ -28,23 +28,27 @@ public class GeneralViewWithTopNodesTestProjectData { */ public static class GraphicalIds { + public static final String ACTION_USAGE_ID = "61aaf64a-4fbc-356e-ba73-4bd47b386989"; + + public static final String ATTRIBUTE_USAGE_ID = "7b8e6835-c563-35cd-8991-e2f894fc2139"; + + public static final String CONCERN_USAGE_ID = "0999b8c3-d37c-3644-a1d6-b9777a499d11"; + public static final String DIAGRAM_ID = "fa8c8a8d-2813-404c-876f-c04e8b297134"; + public static final String ITEM_DEFINITION_ID = "df3542d9-6314-3da5-993c-a296f4042674"; + public static final String PART_USAGE_ID = "4c4fe0d5-4974-377e-9113-9ab022c75f8c"; public static final String PART_DEFINITION_ID = "fa617798-658e-3812-92f2-52e2fc39f851"; public static final String PART_DEFINITION_TEXTUAL_REP_ID = "3a992e49-95fa-384a-bb54-47284825bf17"; - public static final String ACTION_USAGE_ID = "61aaf64a-4fbc-356e-ba73-4bd47b386989"; - - public static final String STATE_USAGE_ID = "1541c013-2cc7-3dd7-a39f-6e33d07b411e"; - - public static final String ATTRIBUTE_USAGE_ID = "7b8e6835-c563-35cd-8991-e2f894fc2139"; + public static final String REQUIREMENT_USAGE_ID = "3eea9d01-7033-3f31-b7ef-561b5bc86d10"; - public static final String ITEM_DEFINITION_ID = "df3542d9-6314-3da5-993c-a296f4042674"; + public static final String REQUIREMENT_DEFINITION_ID = "b83f2b54-1602-382d-beb1-c500e08a8684"; - public static final String REQUIREMENT_USAGE_ID = "3eea9d01-7033-3f31-b7ef-561b5bc86d10"; + public static final String STATE_USAGE_ID = "1541c013-2cc7-3dd7-a39f-6e33d07b411e"; } /** diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/LabelConstants.java b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/LabelConstants.java index 034db8269..ab7a2e91b 100644 --- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/LabelConstants.java +++ b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/LabelConstants.java @@ -49,6 +49,8 @@ public class LabelConstants { public static final String EQUAL = SysMLv2Keywords.EQUALS; + public static final String FRAME = SysMLv2Keywords.FRAME; + public static final String GREATER_THAN = SysMLv2Keywords.GREATER_THAN; public static final String IN = SysMLv2Keywords.IN; diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/FramedConcernMembershipImpl.java b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/FramedConcernMembershipImpl.java index 7433487e5..a2f3fcec3 100644 --- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/FramedConcernMembershipImpl.java +++ b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/FramedConcernMembershipImpl.java @@ -91,7 +91,7 @@ public ConcernUsage getReferencedConcern() { /** * * - * @generated + * @generated NOT */ public ConcernUsage basicGetReferencedConcern() { var referencedConstraint = super.getReferencedConstraint(); diff --git a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/ModelMutationElementService.java b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/ModelMutationElementService.java index 09da1c24e..4a90a2a22 100644 --- a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/ModelMutationElementService.java +++ b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/ModelMutationElementService.java @@ -18,15 +18,19 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.sirius.components.emf.utils.SiriusEMFCopier; +import org.eclipse.syson.sysml.ConcernUsage; import org.eclipse.syson.sysml.Definition; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.FeatureTyping; +import org.eclipse.syson.sysml.FramedConcernMembership; import org.eclipse.syson.sysml.Namespace; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.Relationship; +import org.eclipse.syson.sysml.RequirementDefinition; import org.eclipse.syson.sysml.RequirementUsage; import org.eclipse.syson.sysml.SatisfyRequirementUsage; import org.eclipse.syson.sysml.SysmlFactory; +import org.eclipse.syson.sysml.Type; import org.eclipse.syson.sysml.ViewDefinition; import org.eclipse.syson.sysml.ViewUsage; import org.eclipse.syson.sysml.helper.EMFUtils; @@ -199,6 +203,33 @@ public SatisfyRequirementUsage createSatisfy(Element element, RequirementUsage e return newSatisfyRequirementUsage; } + /** + * In a {@link RequirementDefinition}, or a {@link RequirementUsage}, creates {@link FramedConcernMembership} containing a {@link ConcernUsage} subsetted by reference the given concernUsage. + * + * @param type The type that will hold the {@link FramedConcernMembership}, must be a {@link RequirementDefinition}, or a {@link RequirementUsage} + * @param concernUsage The {@link ConcernUsage} subsetted by reference + * @return The created {@link FramedConcernMembership} when type is a {@link RequirementDefinition}, or a {@link RequirementUsage}, {@code null} otherwise + */ + public FramedConcernMembership createFramedConcern(Type type, ConcernUsage concernUsage) { + if (type instanceof RequirementDefinition || type instanceof RequirementUsage) { + var newFramedConcernMembership = SysmlFactory.eINSTANCE.createFramedConcernMembership(); + type.getOwnedRelationship().add(newFramedConcernMembership); + + var newConcernUsage = SysmlFactory.eINSTANCE.createConcernUsage(); + newFramedConcernMembership.getOwnedRelatedElement().add(newConcernUsage); + + var newReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); + newConcernUsage.getOwnedRelationship().add(newReferenceSubsetting); + newReferenceSubsetting.setReferencedFeature(concernUsage); + + this.metamodelMutationElementService.initialize(newFramedConcernMembership); + this.metamodelMutationElementService.initialize(newConcernUsage); + this.metamodelMutationElementService.initialize(newReferenceSubsetting); + return newFramedConcernMembership; + } + return null; + } + /** * Creates a {@link SatisfyRequirementUsage SatisfyRequirement} on {@code parentElement}. *

diff --git a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelMutationAQLService.java b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelMutationAQLService.java index 2e9e3714a..c6fc77dfe 100644 --- a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelMutationAQLService.java +++ b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelMutationAQLService.java @@ -17,12 +17,14 @@ import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.syson.model.services.ModelMutationElementService; +import org.eclipse.syson.sysml.ConcernUsage; import org.eclipse.syson.sysml.Documentation; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.Membership; import org.eclipse.syson.sysml.OccurrenceUsage; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.RequirementUsage; +import org.eclipse.syson.sysml.Type; import org.eclipse.syson.sysml.ViewUsage; import org.eclipse.syson.sysml.metamodel.services.MetamodelMutationElementService; @@ -79,7 +81,7 @@ public Element createPartUsageAndFlowConnection(PartUsage self) { } /** - * {@link ModelMutationElementService#createPartUsageAndFlowConnection(PartUsage)}. + * {@link ModelMutationElementService#createSatisfy(Element, RequirementUsage)}. */ public Element createSatisfy(Element element, RequirementUsage existingRequirement) { return this.modelMutationElementService.createSatisfy(element, existingRequirement); @@ -105,4 +107,11 @@ public EObject createOccurrenceInOccurrence(OccurrenceUsage container, EClass eC public Element setAsView(ViewUsage viewUsage, String newViewDefinition) { return this.modelMutationElementService.setAsView(viewUsage, newViewDefinition); } + + /** + * {@link ModelMutationElementService#createFramedConcern(Type, ConcernUsage)}. + */ + public Element createFramedConcern(Type type, ConcernUsage concernUsage) { + return this.modelMutationElementService.createFramedConcern(type, concernUsage); + } } diff --git a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java index 6bff05564..348f200d4 100644 --- a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java +++ b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java @@ -14,10 +14,12 @@ import java.util.List; +import org.eclipse.syson.sysml.ConcernUsage; import org.eclipse.syson.sysml.Connector; import org.eclipse.syson.sysml.ConnectorAsUsage; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.Feature; +import org.eclipse.syson.sysml.FramedConcernMembership; import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; /** @@ -68,4 +70,11 @@ public Feature getConnectorSource(Connector connector) { public List getConnectorTarget(ConnectorAsUsage connector) { return this.metamodelQueryElementService.getConnectorTarget(connector); } + + /** + * {@link MetamodelQueryElementService#getFramedConcernTarget(FramedConcernMembership)}. + */ + public ConcernUsage getFramedConcernTarget(FramedConcernMembership framedConcernMembership) { + return this.metamodelQueryElementService.getFramedConcernTarget(framedConcernMembership); + } } diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/util/SysONEContentAdapter.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/util/SysONEContentAdapter.java index 6daec31a2..6f3609d58 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/util/SysONEContentAdapter.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/util/SysONEContentAdapter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -23,6 +23,7 @@ import org.eclipse.emf.ecore.util.EContentAdapter; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.FeatureValue; +import org.eclipse.syson.sysml.FramedConcernMembership; import org.eclipse.syson.sysml.Membership; /** @@ -41,9 +42,8 @@ public Map> getCache() { @Override protected void addAdapter(Notifier notifier) { super.addAdapter(notifier); - // We need to keep FeatureValue since they may be displayed in representations // This adapter is used for semantic candidate expression - if (notifier instanceof Element element && (!(notifier instanceof Membership) || notifier instanceof FeatureValue)) { + if (notifier instanceof Element element && (!(notifier instanceof Membership) || this.shouldCacheMembership(notifier))) { EClass eClass = element.eClass(); List value; if (this.cache.containsKey(eClass)) { @@ -74,4 +74,16 @@ protected void removeAdapter(Notifier notifier) { public boolean isAdapterForType(Object type) { return SysONEContentAdapter.class.equals(type); } + + /** + * Whether a membership should be cached. + *

+ * We cache some memberships because they are displayed in a representation, either as a Node, or as an Edge. + *

+ * @param notifier The notifier + * @return whether the notifier is a membership that should be cached + */ + private boolean shouldCacheMembership(Notifier notifier) { + return notifier instanceof FramedConcernMembership || notifier instanceof FeatureValue; + } } diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java index 2f3bc0086..bb513edf4 100644 --- a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java +++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java @@ -19,11 +19,13 @@ import java.util.function.Predicate; import org.eclipse.syson.sysml.ActorMembership; +import org.eclipse.syson.sysml.ConcernUsage; import org.eclipse.syson.sysml.Connector; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.Expression; import org.eclipse.syson.sysml.Feature; import org.eclipse.syson.sysml.FeatureValue; +import org.eclipse.syson.sysml.FramedConcernMembership; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.ReferenceUsage; import org.eclipse.syson.sysml.StakeholderMembership; @@ -158,7 +160,7 @@ public Optional getCommonOwnerAncestor(Element e1, Elemen * Gets the value expression of a feature (Value of the FeatureValue owned by this feature). * * @param feature - * a non null feature + * a non-null feature * @return an optional expression */ public Optional getValueExpression(Feature feature) { @@ -168,4 +170,21 @@ public Optional getValueExpression(Feature feature) { .findFirst() .map(FeatureValue::getValue); } + + /** + * Returns the framed concern target of {@link FramedConcernMembership}. + *

+ * It returns a concern when the framed concern membership owned concern is subsetted by another concern. + *

+ * + * @param framedConcernMembership + * The framed concern membership + * @return the framed concern target of {@link FramedConcernMembership} + */ + public ConcernUsage getFramedConcernTarget(FramedConcernMembership framedConcernMembership) { + if (framedConcernMembership.getOwnedConcern() != framedConcernMembership.getReferencedConcern()) { + return framedConcernMembership.getReferencedConcern(); + } + return null; + } } diff --git a/backend/views/syson-diagram-common-view/DescriptionsId.txt b/backend/views/syson-diagram-common-view/DescriptionsId.txt index 2a3a69f70..0ad604e1d 100644 --- a/backend/views/syson-diagram-common-view/DescriptionsId.txt +++ b/backend/views/syson-diagram-common-view/DescriptionsId.txt @@ -534,3 +534,4 @@ GV Edge BindingConnectorAsUsage => siriusComponents://edgeDescription?sourceKind GV Edge FeatureValue => siriusComponents://edgeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=6fde9074-7fd4-375f-9ba0-c70c4408ca66 GV Edge IncludeUseCaseUsage => siriusComponents://edgeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=72e2d57a-8fd1-3b99-b9de-e6575a97ff10 GV Edge ConnectionUsage => siriusComponents://edgeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=b51cf026-b4cc-3d4a-a4e3-ca4a1d50a2e0 +GV Edge FramedConcernMembership => siriusComponents://edgeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=64234f9b-80ec-3cdd-97aa-15a42b7d3fc2 diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolService.java index 333ec0f0a..1bc3c8d3e 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolService.java @@ -662,4 +662,24 @@ public EdgeTool createSatisfyRequirementEdgeTool() { .targetElementDescriptions(targetNodeDescriptions.toArray(NodeDescription[]::new)) .build(); } + + public EdgeTool createFramedConcernEdgeTool() { + var builder = this.diagramBuilderHelper.newEdgeTool(); + var body = this.viewBuilderHelper.newChangeContext() + .expression(ServiceMethod.of1(ModelMutationAQLService::createFramedConcern).aql(EdgeDescription.SEMANTIC_EDGE_SOURCE, EdgeDescription.SEMANTIC_EDGE_TARGET)); + + var targetNodeDescriptions = new ArrayList(); + var concernUsageNodeDescriptionName = this.nameGenerator.getNodeName(SysmlPackage.eINSTANCE.getConcernUsage()); + this.allNodeDescriptions.stream() + .filter(nd -> concernUsageNodeDescriptionName.equals(nd.getName())) + .findFirst() + .ifPresent(targetNodeDescriptions::add); + + return builder + .name(this.nameGenerator.getCreationToolName("New framed ", SysmlPackage.eINSTANCE.getConcernUsage())) + .iconURLsExpression(METAMODEL_ICONS_PATH + SysmlPackage.eINSTANCE.getFramedConcernMembership().getName() + SVG) + .body(body.build()) + .targetElementDescriptions(targetNodeDescriptions.toArray(NodeDescription[]::new)) + .build(); + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java index 9f0967d15..edbca1802 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java @@ -43,6 +43,7 @@ import org.eclipse.syson.sysml.PartDefinition; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.PortUsage; +import org.eclipse.syson.sysml.RequirementDefinition; import org.eclipse.syson.sysml.RequirementUsage; import org.eclipse.syson.sysml.StateDefinition; import org.eclipse.syson.sysml.StateUsage; @@ -251,6 +252,14 @@ public List casePortUsage(PortUsage object) { return edgeTools; } + @Override + public List caseRequirementDefinition(RequirementDefinition object) { + var edgeTools = new ArrayList(); + edgeTools.add(this.edgeToolService.createFramedConcernEdgeTool()); + edgeTools.addAll(this.caseDefinition(object)); + return edgeTools; + } + @Override public List caseRequirementUsage(RequirementUsage object) { var edgeTools = new ArrayList(); @@ -265,6 +274,7 @@ public List caseRequirementUsage(RequirementUsage object) { || this.edgeToolService.isTheNodeDescriptionFor(n, SysmlPackage.eINSTANCE.getUseCaseDefinition())) .toList(); edgeTools.add(this.edgeToolService.createBecomeObjectiveRequirementEdgeTool(objectiveTargets)); + edgeTools.add(this.edgeToolService.createFramedConcernEdgeTool()); edgeTools.addAll(this.caseUsage(object)); return edgeTools; } diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java index ec38cd0e8..8e1e952bf 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java @@ -77,6 +77,7 @@ import org.eclipse.syson.standard.diagrams.view.edges.FeatureTypingEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.FeatureValueEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.FlowUsageEdgeDescriptionProvider; +import org.eclipse.syson.standard.diagrams.view.edges.FrameConcernEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.IncludeUseCaseDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.InterfaceUsageEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.NestedActorEdgeDescriptionProvider; @@ -882,6 +883,7 @@ private List> createAllEdgeDescriptionProv edgeDescriptionProviders.add(new IncludeUseCaseDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); edgeDescriptionProviders.add(new ConnectionUsageEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); edgeDescriptionProviders.add(new SatisfyRequirementEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); + edgeDescriptionProviders.add(new FrameConcernEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); return edgeDescriptionProviders; } diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java new file mode 100644 index 000000000..eeca4f992 --- /dev/null +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.standard.diagrams.view.edges; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; +import org.eclipse.sirius.components.view.builder.generated.view.ChangeContextBuilder; +import org.eclipse.sirius.components.view.builder.providers.IColorProvider; +import org.eclipse.sirius.components.view.diagram.ArrowStyle; +import org.eclipse.sirius.components.view.diagram.DeleteTool; +import org.eclipse.sirius.components.view.diagram.DiagramDescription; +import org.eclipse.sirius.components.view.diagram.EdgeDescription; +import org.eclipse.sirius.components.view.diagram.EdgeStyle; +import org.eclipse.sirius.components.view.diagram.LineStyle; +import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.common.view.DescriptionFinder; +import org.eclipse.syson.diagram.common.view.edges.AbstractEdgeDescriptionProvider; +import org.eclipse.syson.model.services.aql.ModelQueryAQLService; +import org.eclipse.syson.services.DeleteService; +import org.eclipse.syson.services.UtilService; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.sysml.helper.LabelConstants; +import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; +import org.eclipse.syson.util.SysMLMetamodelHelper; +import org.eclipse.syson.util.ViewConstants; + +/** + * 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}. + * + * @author gcoutable + */ +public class FrameConcernEdgeDescriptionProvider extends AbstractEdgeDescriptionProvider { + + private final IDescriptionNameGenerator descriptionNameGenerator; + + public FrameConcernEdgeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { + super(colorProvider); + this.descriptionNameGenerator = Objects.requireNonNull(descriptionNameGenerator); + } + + @Override + protected ChangeContextBuilder getSourceReconnectToolBody() { + return this.viewBuilderHelper.newChangeContext() + .expression(""); + } + + @Override + protected ChangeContextBuilder getTargetReconnectToolBody() { + return this.viewBuilderHelper.newChangeContext() + .expression(""); + } + + @Override + public EdgeDescription create() { + String domainType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getFramedConcernMembership()); + return this.diagramBuilderHelper.newEdgeDescription() + .domainType(domainType) + .isDomainBasedEdge(true) + .centerLabelExpression(LabelConstants.OPEN_QUOTE + LabelConstants.FRAME + LabelConstants.CLOSE_QUOTE) + .name(this.getName()) + .semanticCandidatesExpression(ServiceMethod.of1(UtilService::getAllReachable).aqlSelf(domainType)) + .sourceExpression(AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getRelationship_OwningRelatedElement().getName()) + .style(this.createEdgeStyle()) + .synchronizationPolicy(SynchronizationPolicy.SYNCHRONIZED) + .targetExpression(ServiceMethod.of0(ModelQueryAQLService::getFramedConcernTarget).aqlSelf()) + .build(); + } + + @Override + public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { + var optEdgeDescription = cache.getEdgeDescription(this.getName()); + optEdgeDescription.ifPresent(edgeDescription -> { + diagramDescription.getEdgeDescriptions().add(edgeDescription); + + var sourceNodes = this.getSourceNodes(cache); + var targetNodes = this.getTargetNodes(cache); + edgeDescription.getSourceDescriptions().addAll(sourceNodes); + edgeDescription.getTargetDescriptions().addAll(targetNodes); + + edgeDescription.setPalette(this.createEdgePalette(cache)); + }); + + } + + @Override + protected DeleteTool getEdgeDeleteTool() { + // Delete the referenced subsetting only. + var referenceSubsetting = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL_SELF + ".ownedConcern.ownedRelationship->select(rel | rel.oclIsKindOf(sysml::ReferenceSubsetting))->first()") + .children(this.viewBuilderHelper.newChangeContext() + .expression(ServiceMethod.of0(DeleteService::deleteFromModel).aqlSelf()) + .build() + ); + + return this.diagramBuilderHelper.newDeleteTool() + .name("Delete from Model") + .body(referenceSubsetting.build()) + .build(); + } + + private String getName() { + return this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getFramedConcernMembership()); + } + + private EdgeStyle createEdgeStyle() { + return this.diagramBuilderHelper.newEdgeStyle() + .borderSize(0) + .color(this.colorProvider.getColor(ViewConstants.DEFAULT_EDGE_COLOR)) + .edgeWidth(1) + .lineStyle(LineStyle.SOLID) + .sourceArrowStyle(ArrowStyle.NONE) + .targetArrowStyle(ArrowStyle.INPUT_ARROW) + .build(); + } + + private List getSourceNodes(IViewDiagramElementFinder cache) { + List sources = new ArrayList<>(); + var descriptionFinder = new DescriptionFinder(this.descriptionNameGenerator); + sources.addAll(descriptionFinder.getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getRequirementUsage())); + sources.addAll(descriptionFinder.getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getRequirementDefinition())); + return sources; + } + + private List getTargetNodes(IViewDiagramElementFinder cache) { + return new DescriptionFinder(this.descriptionNameGenerator).getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getConcernUsage()); + } +} diff --git a/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java b/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java index 22d4b3c13..5c3a1248a 100644 --- a/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java +++ b/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java @@ -87,7 +87,7 @@ public void eachEdgeWithCenterLabelHasDirectEditTool() { // SuccessionAsUsage has a label but the grammar does not support the direct edit tool yet .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getSuccessionAsUsage()).negate()) .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getAllocationUsage()).negate()) - // Use a non editable fixed label + // Use a non-editable fixed label .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getIncludeUseCaseUsage()).negate()) // TransitionUsage has a label but the grammar does not support the direct edit tool yet .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getTransitionUsage()).negate()) @@ -95,6 +95,8 @@ public void eachEdgeWithCenterLabelHasDirectEditTool() { .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getFeatureValue()).negate()) // SatisfyRequirementUsage edge has a label (satisfy) but it is a constant and should not be modifiable .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getSatisfyRequirementUsage()).negate()) + // FramedConcernMembership edge has a label (frame) but it is a constant and should not be modifiable + .filter(this.diagramPredicates.hasDomainType(SysmlPackage.eINSTANCE.getFramedConcernMembership()).negate()) .toList(); new EdgeDescriptionHasDirectEditToolChecker().checkAll(edgeDescriptionCandidates); } diff --git a/doc/content/modules/user-manual/assets/images/release-notes-frame-edge-tool.png b/doc/content/modules/user-manual/assets/images/release-notes-frame-edge-tool.png new file mode 100644 index 0000000000000000000000000000000000000000..34271a0f699d1bc09b3dc2b494d1e5ebab34813f GIT binary patch literal 41835 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 KGG$VbcmD%aRftRg literal 0 HcmV?d00001 diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc index 7321c37a0..15e94ab81 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc @@ -28,6 +28,13 @@ image::release-notes-stakeholder-menu.png[Tool to create a new Stakeholder, widt + image::release-notes-stakeholder-node.png[Default representation of Stakeholder connected to its corresponding `RequirementDefinition`, width=60%,height=60%] +** Add support for the _frame_ graphical edge. +A frame edge can be created between a `RequirementUsage` or a `RequirementDefinition` graphical node and a `ConcernUsage` graphical node. ++ +image::release-notes-frame-edge-tool.png[Edge tool to frame a ConcernUsage, width=60%,height=60%] ++ +image::release-notes-frame-edge.png[Frame edge between a RequirementUsage and a ConcernUsage, width=60%,height=60%] + == Bug fixes * In diagrams: