diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 2ecace0c6..f17217964 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -46,6 +46,7 @@ This fix ensure that imported models containing, for example, a top-level `Libra A new compartment named _satisfy requirements_ has also been added to `PartDefinition` and `PartUsage` graphical nodes in diagrams. - https://github.com/eclipse-syson/syson/issues/1762[#1762] [diagrams] Increase the default size of nodes. - https://github.com/eclipse-syson/syson/issues/1752[#1752] [diagrams] In diagrams, hide the node displayed on the top of the diagram if the candidate compartment is not hidden. +- https://github.com/eclipse-syson/syson/issues/1778[#1778] [diagrams] Add new `ReferenceSubsetting` tool to create `ReferenceSubsetting` relationships between `Usage` elements in diagrams. === New features diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java index a765c5d19..0d8333e76 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -83,6 +83,8 @@ public class SysMLv2PropertiesConfigurer implements IPropertiesDescriptionRegist private static final String ADVANCED_PROPERTIES = "Advanced Properties"; + private static final String REFERENCE_SUBSETTING_PROPERTIES = "Reference Subsetting Properties"; + private static final String REDEFINITION_PROPERTIES = "Redefinition Properties"; private static final String SUBCLASSIFICATION_PROPERTIES = "Subclassification Properties"; @@ -176,6 +178,7 @@ private FormDescription createDetailsViewForElement() { pageCore.setLabelExpression("Core"); pageCore.getGroups().add(this.createCorePropertiesGroup()); pageCore.getGroups().add(this.createVisibilityPropertyGroup()); + pageCore.getGroups().add(this.createExtraReferenceSubsettingPropertiesGroup()); pageCore.getGroups().add(this.createExtraRedefinitionPropertiesGroup()); pageCore.getGroups().add(this.createExtraStatesubactionMembershipKindPropertiesGroup()); pageCore.getGroups().add(this.createExtraSubclassificationPropertiesGroup()); @@ -273,6 +276,31 @@ private GroupDescription createExtraRedefinitionPropertiesGroup() { return group; } + // should handle multiple ReferenceSubsetting + private GroupDescription createExtraReferenceSubsettingPropertiesGroup() { + GroupDescription group = FormFactory.eINSTANCE.createGroupDescription(); + group.setDisplayMode(GroupDisplayMode.LIST); + group.setName(REFERENCE_SUBSETTING_PROPERTIES); + group.setLabelExpression(""); + group.setSemanticCandidatesExpression("aql:self.ownedRelationship->filter(sysml::ReferenceSubsetting)"); + + ReferenceWidgetDescription refWidget = ReferenceFactory.eINSTANCE.createReferenceWidgetDescription(); + refWidget.setName("ExtraReferenceWidget"); + refWidget.setLabelExpression("References"); + refWidget.setReferenceNameExpression(SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().getName()); + refWidget.setReferenceOwnerExpression(AQLConstants.AQL_SELF); + refWidget.setIsEnabledExpression(AQL_NOT_SELF_IS_READ_ONLY); + ChangeContext setRefWidget = ViewFactory.eINSTANCE.createChangeContext(); + setRefWidget + .setExpression("aql:self.handleReferenceWidgetNewValue('" + SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().getName() + "', " + ViewFormDescriptionConverter.NEW_VALUE + + LabelConstants.CLOSE_PARENTHESIS); + refWidget.getBody().add(setRefWidget); + + group.getChildren().add(refWidget); + + return group; + } + private GroupDescription createExtraStatesubactionMembershipKindPropertiesGroup() { GroupDescription group = FormFactory.eINSTANCE.createGroupDescription(); group.setDisplayMode(GroupDisplayMode.LIST); diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java index 7a299c5d7..f2c4d8193 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -36,8 +36,8 @@ import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicate; import org.eclipse.sirius.components.representations.Message; import org.eclipse.sirius.components.representations.MessageLevel; -import org.eclipse.syson.form.services.api.IDetailsViewHelpTextProvider; import org.eclipse.syson.application.configuration.SysMLv2PropertiesConfigurer; +import org.eclipse.syson.form.services.api.IDetailsViewHelpTextProvider; import org.eclipse.syson.services.ElementInitializerSwitch; import org.eclipse.syson.services.ImportService; import org.eclipse.syson.services.UtilService; @@ -670,6 +670,8 @@ private void handleImplied(Element element, EStructuralFeature eStructuralFeatur if (element instanceof Relationship relationship) { if (SysmlPackage.eINSTANCE.getRedefinition_RedefinedFeature().equals(eStructuralFeature)) { relationship.setIsImplied(false); + } else if (SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().equals(eStructuralFeature)) { + relationship.setIsImplied(false); } else if (SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().equals(eStructuralFeature)) { relationship.setIsImplied(false); } else if (SysmlPackage.eINSTANCE.getSubclassification_Superclassifier().equals(eStructuralFeature)) { diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/GetChildCreationSwitch.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/GetChildCreationSwitch.java index e69612984..9d273d049 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/GetChildCreationSwitch.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/GetChildCreationSwitch.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 @@ -242,6 +242,7 @@ public List caseUsage(Usage object) { childrenCandidates.add(SysmlPackage.eINSTANCE.getFeatureTyping()); childrenCandidates.add(SysmlPackage.eINSTANCE.getSubsetting()); childrenCandidates.add(SysmlPackage.eINSTANCE.getRedefinition()); + childrenCandidates.add(SysmlPackage.eINSTANCE.getReferenceSubsetting()); childrenCandidates.add(SysmlPackage.eINSTANCE.getLiteralBoolean()); childrenCandidates.add(SysmlPackage.eINSTANCE.getLiteralInfinity()); childrenCandidates.add(SysmlPackage.eINSTANCE.getLiteralInteger()); diff --git a/backend/application/syson-application/README.md b/backend/application/syson-application/README.md index 2ce34f04d..d78bd9de0 100644 --- a/backend/application/syson-application/README.md +++ b/backend/application/syson-application/README.md @@ -2,13 +2,17 @@ ## Developer testing environment -SysON contains tests that can be executed out of the box. The instructions below detail how to configure your development environment to contribute/modify tests and their baseline models. +SysON contains tests that can be executed out of the box. +The instructions below detail how to configure your development environment to contribute/modify tests and their baseline models. ### Prerequisites -The scripts used in the instructions below rely on the `pg_dump` and `psql` executables. We recommand to install [pgAdmin](https://www.pgadmin.org/download/) to get them as well as additional tools to manipulate Postgres databases. +The scripts used in the instructions below rely on the `pg_dump` and `psql` executables. +We recommand to install [pgAdmin](https://www.pgadmin.org/download/) to get them as well as additional tools to manipulate Postgres databases. -The scripts below have been tested with Git Bash on Windows. They rely on `winpty` to communicate with Windows console programs. Please adapt the scripts if you are using a different shell/operating system. +The scripts below must be adapted when using Git Bash on Windows. +In this case they rely on `winpty` to communicate with Windows console programs. +Please adapt the scripts if you are using Git Bash on Windows (just comment/uncomment appropriate line in the scripts). ### Setup the testing environment @@ -33,7 +37,8 @@ docker run -p 5433:5432 --name syson-test-postgres \ 2. Start SysON backend & frontend -This creates the database tables required by the tests. The SysON frontend can be used later to update the test models. +This creates the database tables required by the tests. +The SysON frontend can be used later to update the test models. 3. Run the initialization script diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVEdgeReferenceSubsettingTest.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVEdgeReferenceSubsettingTest.java new file mode 100644 index 000000000..2d033d135 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVEdgeReferenceSubsettingTest.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * 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.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat; + +import java.time.Duration; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; +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.application.controllers.diagrams.testers.EdgeCreationTester; +import org.eclipse.syson.application.data.GeneralViewWithTopNodesTestProjectData; +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.SysmlPackage; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.transaction.annotation.Transactional; + +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +/** + * ReferenceSubsetting-Edge related tests the in the General View diagram. + * + * @author arichard + */ +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GVEdgeReferenceSubsettingTest extends AbstractIntegrationTests { + + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private IGivenDiagramSubscription givenDiagramSubscription; + + @Autowired + private IGivenDiagramDescription givenDiagramDescription; + + @Autowired + private IDiagramIdProvider diagramIdProvider; + + @Autowired + private EdgeCreationTester edgeTester; + + private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); + + private Flux givenSubscriptionToDiagram() { + var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), + GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID); + var flux = this.givenDiagramSubscription.subscribe(diagramEventInput); + return flux; + } + + @BeforeEach + public void beforeEach() { + this.givenInitialServerState.initialize(); + } + + @DisplayName("GIVEN a diagram with two Usage nodes, WHEN a ReferenceSubsetting edge is created between them, THEN the edge is visisble on the diagram") + @Sql(scripts = { GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, + config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @Test + public void testCreateReferenceSubsettingEdge() { + 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 newReferenceSubsettingToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionUsage()), + "New Reference Subsetting"); + assertThat(newReferenceSubsettingToolId).as("The tool 'New Reference Subsetting' should exist on the ActionUsage").isNotNull(); + + var diagram = new AtomicReference(); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diag -> { + diagram.set(diag); + assertThat(new DiagramNavigator(diag).findDiagramEdgeCount()).isEqualTo(3); + }); + + Runnable newReferenceSubsettingTool = () -> this.edgeTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, GeneralViewWithTopNodesTestProjectData.GraphicalIds.STATE_USAGE_ID, newReferenceSubsettingToolId); + + Consumer updatedDiagramContentConsumer = assertRefreshedDiagramThat(diag -> { + var diagramNavigator = new DiagramNavigator(diag); + assertThat(diagramNavigator.findDiagramEdgeCount()).isEqualTo(4); + var edgeDescriptionId = diagramDescriptionIdProvider.getEdgeDescriptionId(this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getReferenceSubsetting())); + var referenceSubsettingEdge = diagramNavigator.edgeWithEdgeDescriptionId(edgeDescriptionId).getEdge(); + assertThat(referenceSubsettingEdge).isNotNull(); + assertThat(referenceSubsettingEdge.getSourceId()).isEqualTo(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID); + assertThat(referenceSubsettingEdge.getTargetId()).isEqualTo(GeneralViewWithTopNodesTestProjectData.GraphicalIds.STATE_USAGE_ID); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(newReferenceSubsettingTool) + .consumeNextWith(updatedDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeActionFlowCreationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeActionFlowCreationTests.java index f626d8a89..6430054c1 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeActionFlowCreationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeActionFlowCreationTests.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 @@ -245,7 +245,7 @@ public void setUp() { public void tearDown() { if (this.verifier != null) { this.verifier.thenCancel() - .verify(Duration.ofSeconds(10)); + .verify(Duration.ofSeconds(1000)); } } @@ -561,11 +561,10 @@ public void createReferencingPerformActionUsageInActionUsage() { this.creationTestsService.createNode(this.verifier, this.diagramDescriptionIdProvider, this.diagram, parentEClass, parentLabel, creationToolName, variables); IDiagramChecker diagramChecker = (initialDiagram, newDiagram) -> { - int createdNodesExpectedCount = 14; new CheckDiagramElementCount(this.diagramComparator) - .hasNewNodeCount(createdNodesExpectedCount) - .hasNewEdgeCount(1) - .check(initialDiagram, newDiagram); + .hasNewNodeCount(1) // 1 visible new PerformActionUsage node + .hasNewEdgeCount(2) // 1 visible composition edge and 1 visible ReferenceSubsetting edge + .check(initialDiagram, newDiagram, true); String listNodeDescription = this.descriptionNameGenerator.getCompartmentItemName(parentEClass, containmentReference); new CheckNodeInCompartment(this.diagramDescriptionIdProvider, this.diagramComparator) .withParentLabel(parentLabel) 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 845c709bc..e59485ed6 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 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 @@ -35,6 +35,10 @@ public static class GraphicalIds { 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"; } /** diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java index 37ab0ef47..52dceaf5f 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 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 @@ -40,6 +40,7 @@ import org.eclipse.syson.sysml.MultiplicityRange; import org.eclipse.syson.sysml.OwningMembership; import org.eclipse.syson.sysml.Redefinition; +import org.eclipse.syson.sysml.ReferenceSubsetting; import org.eclipse.syson.sysml.RequirementConstraintMembership; import org.eclipse.syson.sysml.StateSubactionMembership; import org.eclipse.syson.sysml.Subclassification; @@ -98,6 +99,28 @@ public String getIdentificationLabel(Element element) { return label.toString(); } + @Override + public String getReferenceSubsettingLabel(Element element) { + StringBuilder label = new StringBuilder(); + var optReferenceSubsetting = element.getOwnedRelationship().stream() + .filter(ReferenceSubsetting.class::isInstance) + .map(ReferenceSubsetting.class::cast) + .findFirst(); + if (optReferenceSubsetting.isPresent()) { + ReferenceSubsetting referenceSubsetting = optReferenceSubsetting.get(); + if (!referenceSubsetting.isIsImplied()) { + var referencedFeature = referenceSubsetting.getReferencedFeature(); + if (referencedFeature != null) { + label.append(LabelConstants.SPACE); + label.append(LabelConstants.REFERENCES); + label.append(LabelConstants.SPACE); + label.append(this.getDeclaredNameLabel(referencedFeature)); + } + } + } + return label.toString(); + } + @Override public String getRedefinitionLabel(Element element) { StringBuilder label = new StringBuilder(); @@ -146,7 +169,7 @@ public String getSubclassificationLabel(Element element) { public String getSubsettingLabel(Element element) { StringBuilder label = new StringBuilder(); var optSubsetting = element.getOwnedRelationship().stream() - .filter(r -> r instanceof Subsetting && !(r instanceof Redefinition)) + .filter(r -> r instanceof Subsetting && !(r instanceof Redefinition) && !(r instanceof ReferenceSubsetting)) .map(Subsetting.class::cast) .findFirst(); if (optSubsetting.isPresent()) { diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java index 3abc37330..6bedfd02e 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 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 @@ -62,6 +62,16 @@ public interface IDiagramLabelService { */ String getSubsettingLabel(Element element); + /** + * Return the label of the reference subsetting of the given {@link Element}. + * + * @param usage + * the given {@link Element}. + * @return the label of the reference subsetting of the given {@link Element} if there is one, an empty string + * otherwise. + */ + String getReferenceSubsettingLabel(Element element); + /** * Get the SysML textual representation of the given element. * diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java index 8a7ed5c65..32b124bda 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -111,6 +111,7 @@ public String caseAcceptActionUsage(AcceptActionUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)) .append(LabelConstants.CR) @@ -147,6 +148,7 @@ public String caseActionUsage(ActionUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -180,6 +182,7 @@ public String caseAllocationUsage(AllocationUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -212,6 +215,7 @@ public String caseAttributeUsage(AttributeUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -259,6 +263,7 @@ public String caseCaseUsage(CaseUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -315,6 +320,7 @@ public String caseConcernUsage(ConcernUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -348,6 +354,7 @@ public String caseConstraintUsage(ConstraintUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -437,6 +444,7 @@ public String caseExhibitStateUsage(ExhibitStateUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -470,6 +478,7 @@ public String caseInterfaceUsage(InterfaceUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -519,6 +528,7 @@ public String caseItemUsage(ItemUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -568,6 +578,7 @@ public String caseOccurrenceUsage(OccurrenceUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -612,6 +623,7 @@ public String casePartUsage(PartUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -663,6 +675,7 @@ public String casePortUsage(PortUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -687,6 +700,7 @@ public String caseReferenceUsage(ReferenceUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -720,6 +734,7 @@ public String caseRequirementUsage(RequirementUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -739,6 +754,7 @@ public String caseSatisfyRequirementUsage(SatisfyRequirementUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -772,6 +788,7 @@ public String caseUseCaseUsage(UseCaseUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); @@ -807,6 +824,7 @@ public String caseStateUsage(StateUsage object) { .append(this.multiplicityRange(object)) .append(this.labelService.getTypingLabel(object)) .append(this.labelService.getRedefinitionLabel(object)) + .append(this.labelService.getReferenceSubsettingLabel(object)) .append(this.labelService.getSubsettingLabel(object)) .append(this.labelService.getValueLabel(object)); return label.toString(); diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/DiagramDirectEditListener.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/DiagramDirectEditListener.java index e4142983e..65adb059a 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/DiagramDirectEditListener.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/DiagramDirectEditListener.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -94,6 +94,7 @@ import org.eclipse.syson.sysml.OwningMembership; import org.eclipse.syson.sysml.ParameterMembership; import org.eclipse.syson.sysml.Redefinition; +import org.eclipse.syson.sysml.ReferenceSubsetting; import org.eclipse.syson.sysml.ResultExpressionMembership; import org.eclipse.syson.sysml.Subclassification; import org.eclipse.syson.sysml.Subsetting; @@ -1151,7 +1152,7 @@ private void handleMissingSubsettingExpression(ParserRuleContext ctx) { if (this.element instanceof Usage && featureExpressions != null && this.isDeleteFeatureExpression(featureExpressions, featureExpressions.subsettingExpression(), LabelConstants.SUBSETTING)) { var subsetting = this.element.getOwnedRelationship().stream() - .filter(elt -> elt instanceof Subsetting && !(elt instanceof Redefinition)) + .filter(r -> r instanceof Subsetting && !(r instanceof Redefinition) && !(r instanceof ReferenceSubsetting)) .map(Subsetting.class::cast) .findFirst(); if (subsetting.isPresent()) { diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java index c480702dc..54c72047e 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -120,6 +120,9 @@ public Set caseReferenceSubsetting(ReferenceSubsetting object) { } } } + if (this.eStructuralFeature.equals(SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature())) { + relatedElements.add(object); + } return relatedElements; } diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java index ad14aec14..5637058f5 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -750,7 +750,7 @@ public Usage setFeatureTyping(Usage usage, Type type) { */ public Usage setSubsetting(Usage subsettingUsage, Feature feature) { var optSubsetting = subsettingUsage.getOwnedRelationship().stream() - .filter(elt -> elt instanceof Subsetting && !(elt instanceof Redefinition)) + .filter(r -> r instanceof Subsetting && !(r instanceof Redefinition) && !(r instanceof ReferenceSubsetting)) .map(Subsetting.class::cast) .findFirst(); if (optSubsetting.isPresent()) { diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSubsettingEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSubsettingEdgeDescriptionProvider.java index 000fe50c3..e3d443576 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSubsettingEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSubsettingEdgeDescriptionProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -14,21 +14,17 @@ import java.util.List; +import org.eclipse.emf.ecore.EClass; 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.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.sysml.Subsetting; -import org.eclipse.syson.sysml.SysmlPackage; -import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.SysMLMetamodelHelper; -import org.eclipse.syson.util.ViewConstants; /** * Used to create the {@link Subsetting} edge description. @@ -37,8 +33,11 @@ */ public abstract class AbstractSubsettingEdgeDescriptionProvider extends AbstractEdgeDescriptionProvider { - public AbstractSubsettingEdgeDescriptionProvider(IColorProvider colorProvider) { + private final EClass eClass; + + public AbstractSubsettingEdgeDescriptionProvider(EClass eClass, IColorProvider colorProvider) { super(colorProvider); + this.eClass = eClass; } /** @@ -48,6 +47,24 @@ public AbstractSubsettingEdgeDescriptionProvider(IColorProvider colorProvider) { */ protected abstract String getName(); + /** + * Implementers should provide the source expression of this {@link EdgeDescription}. + * + * @param cache + * the cache used to retrieve node descriptions. + * @return the source expression of this {@link EdgeDescription}.s + */ + protected abstract String getSourceExpression(); + + /** + * Implementers should provide the target expression of this {@link EdgeDescription}. + * + * @param cache + * the cache used to retrieve node descriptions. + * @return the target expression of this {@link EdgeDescription}.s + */ + protected abstract String getTargetExpression(); + /** * Implementers should provide the list of {@link NodeDescription} that can be a source of this * {@link EdgeDescription}. @@ -68,98 +85,38 @@ public AbstractSubsettingEdgeDescriptionProvider(IColorProvider colorProvider) { */ protected abstract List getTargetNodes(IViewDiagramElementFinder cache); + /** + * Create the {@link EdgeStyle} for this {@link EdgeDescription}. + * + * @return a new {@link EdgeStyle}. + */ + protected abstract EdgeStyle createEdgeStyle(); + @Override public EdgeDescription create() { - String domainType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getSubsetting()); + String domainType = SysMLMetamodelHelper.buildQualifiedName(this.eClass); return this.diagramBuilderHelper.newEdgeDescription() .domainType(domainType) .isDomainBasedEdge(true) .centerLabelExpression("") .name(this.getName()) - .semanticCandidatesExpression("aql:self.getAllReachable(" + domainType + ")") - .sourceExpression(AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) + .preconditionExpression("aql:self.oclIsTypeOf(" + domainType + ")") + .semanticCandidatesExpression(AQLUtils.getSelfServiceCallExpression("getAllReachable", domainType)) + .sourceExpression(this.getSourceExpression()) .style(this.createEdgeStyle()) .synchronizationPolicy(SynchronizationPolicy.SYNCHRONIZED) - .targetExpression(AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) + .targetExpression(this.getTargetExpression()) .build(); } - @Override - public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { - var optEdgeDescription = cache.getEdgeDescription(this.getName()); - EdgeDescription edgeDescription = optEdgeDescription.get(); - diagramDescription.getEdgeDescriptions().add(edgeDescription); - - edgeDescription.getSourceDescriptions().addAll(this.getSourceNodes(cache)); - edgeDescription.getTargetDescriptions().addAll(this.getTargetNodes(cache)); - - edgeDescription.setPalette(this.createEdgePalette(cache)); - } - - 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_CLOSED_ARROW) - .build(); - } @Override - protected ChangeContextBuilder getSourceReconnectToolBody() { - var unsetOldSubsettingFeature = this.viewBuilderHelper.newUnsetValue() - .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) - .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); - - var setNewSubsettingFeature = this.viewBuilderHelper.newSetValue() - .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) - .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); - - var unsetOldSpecific = this.viewBuilderHelper.newUnsetValue() - .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) - .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); - - var setNewSpecific = this.viewBuilderHelper.newSetValue() - .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) - .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); - - var setNewContainer = this.viewBuilderHelper.newSetValue() - .featureName(SysmlPackage.eINSTANCE.getElement_OwnedRelationship().getName()) - .valueExpression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT); - - var changeContextNewContainer = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET) - .children(setNewContainer.build()); - - return this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT) - .children(unsetOldSubsettingFeature.build(), unsetOldSpecific.build(), setNewSubsettingFeature.build(), - setNewSpecific.build(), changeContextNewContainer.build()); - } - - @Override - protected ChangeContextBuilder getTargetReconnectToolBody() { - var unsetOldSubsettedFeature = this.viewBuilderHelper.newUnsetValue() - .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) - .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); - - var setNewSubsettedFeature = this.viewBuilderHelper.newSetValue() - .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) - .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); - - var unsetOldGeneral = this.viewBuilderHelper.newUnsetValue() - .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) - .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); - - var setNewGeneral = this.viewBuilderHelper.newSetValue() - .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) - .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); - - return this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT) - .children(unsetOldSubsettedFeature.build(), unsetOldGeneral.build(), setNewSubsettedFeature.build(), - setNewGeneral.build()); + public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { + cache.getEdgeDescription(this.getName()).ifPresent(edgeDescription -> { + diagramDescription.getEdgeDescriptions().add(edgeDescription); + edgeDescription.getSourceDescriptions().addAll(this.getSourceNodes(cache)); + edgeDescription.getTargetDescriptions().addAll(this.getTargetNodes(cache)); + edgeDescription.setPalette(this.createEdgePalette(cache)); + }); } } 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 a7ce1e99e..23e01f574 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 @@ -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 @@ -295,22 +295,22 @@ public EdgeTool createSubsettingEdgeTool(List targetElementDesc .variableName(NEW_INSTANCE) .children(changeContextNewInstance.build()); - String hasExistingSubsetting = "hasExistingSubsetting"; + String hasExistingReferenceSubsetting = "hasExistingSubsetting"; var feedback = this.viewBuilderHelper.newChangeContext() .expression(ServiceMethod.of1(DiagramQueryAQLService::infoMessage).aqlSelf(AQLUtils.aqlString("A subsetting already exists between these elements."))) .build(); var createIfPossible = this.viewBuilderHelper.newIf() - .conditionExpression(AQLConstants.AQL + "not " + hasExistingSubsetting) + .conditionExpression(AQLConstants.AQL + "not " + hasExistingReferenceSubsetting) .children(createInstance.build()); var informOtherwise = this.viewBuilderHelper.newIf() - .conditionExpression(AQLConstants.AQL + hasExistingSubsetting) + .conditionExpression(AQLConstants.AQL + hasExistingReferenceSubsetting) .children(feedback); var letExistingSubsetting = this.viewBuilderHelper.newLet() - .variableName(hasExistingSubsetting) + .variableName(hasExistingReferenceSubsetting) .valueExpression(AQLConstants.AQL_SELF + ".eContents(sysml::Subsetting).subsettedFeature->includes(" + EdgeDescription.SEMANTIC_EDGE_TARGET + ")") .children(createIfPossible.build(), informOtherwise.build()); @@ -326,6 +326,74 @@ public EdgeTool createSubsettingEdgeTool(List targetElementDesc .build(); } + public EdgeTool createReferenceSubsettingEdgeTool(List targetElementDescriptions) { + var builder = this.diagramBuilderHelper.newEdgeTool(); + + var callElementInitializerService = this.viewBuilderHelper.newChangeContext() + .expression(ServiceMethod.of0(ViewCreateService::elementInitializer).aqlSelf()); + + var setReferencedFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().getName()) + .valueExpression(AQLConstants.AQL + EdgeDescription.SEMANTIC_EDGE_TARGET); + + var setSubsettingFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) + .valueExpression(AQLConstants.AQL + EdgeDescription.SEMANTIC_EDGE_SOURCE); + + var setSubsettedFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) + .valueExpression(AQLConstants.AQL + EdgeDescription.SEMANTIC_EDGE_TARGET); + + var setSpecific = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) + .valueExpression(AQLConstants.AQL + EdgeDescription.SEMANTIC_EDGE_SOURCE); + + var setGeneral = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) + .valueExpression(AQLConstants.AQL + EdgeDescription.SEMANTIC_EDGE_TARGET); + + var changeContextNewInstance = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + NEW_INSTANCE) + .children(callElementInitializerService.build(), setSubsettingFeature.build(), setReferencedFeature.build(), setSubsettedFeature.build(), + setSpecific.build(), setGeneral.build()); + + var createInstance = this.viewBuilderHelper.newCreateInstance() + .typeName(SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getReferenceSubsetting())) + .referenceName(SysmlPackage.eINSTANCE.getElement_OwnedRelationship().getName()) + .variableName(NEW_INSTANCE) + .children(changeContextNewInstance.build()); + + String hasExistingReferenceSubsetting = "hasExistingReferenceSubsetting"; + + var feedback = this.viewBuilderHelper.newChangeContext() + .expression(ServiceMethod.of1(DiagramQueryAQLService::infoMessage).aqlSelf(AQLUtils.aqlString("A reference subsetting already exists between these elements."))) + .build(); + + var createIfPossible = this.viewBuilderHelper.newIf() + .conditionExpression(AQLConstants.AQL + "not " + hasExistingReferenceSubsetting) + .children(createInstance.build()); + + var informOtherwise = this.viewBuilderHelper.newIf() + .conditionExpression(AQLConstants.AQL + hasExistingReferenceSubsetting) + .children(feedback); + + var letExistingSubsetting = this.viewBuilderHelper.newLet() + .variableName(hasExistingReferenceSubsetting) + .valueExpression(AQLConstants.AQL_SELF + ".eContents(sysml::ReferenceSubsetting).referencedFeature->includes(" + EdgeDescription.SEMANTIC_EDGE_TARGET + ")") + .children(createIfPossible.build(), informOtherwise.build()); + + var body = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + EdgeDescription.SEMANTIC_EDGE_SOURCE) + .children(letExistingSubsetting.build()); + + return builder + .name(this.nameGenerator.getCreationToolName(SysmlPackage.eINSTANCE.getReferenceSubsetting())) + .iconURLsExpression(METAMODEL_ICONS_PATH + SysmlPackage.eINSTANCE.getReferenceSubsetting().getName() + SVG) + .body(body.build()) + .targetElementDescriptions(targetElementDescriptions.toArray(NodeDescription[]::new)) + .build(); + } + public EdgeTool createFeatureTypingEdgeTool(List targetElementDescriptions) { var builder = this.diagramBuilderHelper.newEdgeTool(); 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 5bc4c0a21..22d2c7faf 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 @@ -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 @@ -48,6 +48,7 @@ import org.eclipse.syson.sysml.Usage; import org.eclipse.syson.sysml.UseCaseUsage; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.SysmlEClassSwitch; /** @@ -268,6 +269,7 @@ public List caseUsage(Usage object) { edgeTools.add(this.edgeToolService.createDependencyEdgeTool(targetDescriptions)); edgeTools.add(this.edgeToolService.createRedefinitionEdgeTool(List.of(this.nodeDescription))); edgeTools.add(this.edgeToolService.createSubsettingEdgeTool(List.of(this.nodeDescription))); + edgeTools.add(this.edgeToolService.createReferenceSubsettingEdgeTool(targetDescriptions.stream().filter(this::isFeatureNodeDescription).toList())); edgeTools.add(this.edgeToolService.createAllocateEdgeTool(targetDescriptions)); var definitionNodeDescription = this.edgeToolService.getNodeDescription(this.edgeToolService.getDefinitionFromUsage(object)); if (definitionNodeDescription.isPresent()) { @@ -317,4 +319,12 @@ private boolean isSuccessionTargetNodeDescription(NodeDescription nodeDesc) { result = result || this.descriptionNameGenerator.getNodeName(DecisionActionNodeDescriptionProvider.DECISION_ACTION_NAME).equals(nodeDesc.getName()); return result; } + + private boolean isFeatureNodeDescription(NodeDescription nodeDesc) { + var domainType = SysMLMetamodelHelper.toEClass(nodeDesc.getDomainType()); + if (domainType != null) { + return domainType.getEAllSuperTypes().contains(SysmlPackage.eINSTANCE.getFeature()); + } + return false; + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/PerformActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/PerformActionNodeToolProvider.java index cab2310b0..7508cd5c0 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/PerformActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/PerformActionNodeToolProvider.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 @@ -14,9 +14,10 @@ import org.eclipse.emf.ecore.EClass; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.services.ViewCreateService; import org.eclipse.syson.sysml.SysmlPackage; -import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; /** * Used to add the 'perform action' action in actions body for all diagrams. @@ -36,7 +37,7 @@ protected String getNodeDescriptionName() { @Override protected String getCreationServiceCallExpression() { - return AQLUtils.getSelfServiceCallExpression("createPerformAction"); + return ServiceMethod.of0(ViewCreateService::createPerformAction).aqlSelf(); } @Override diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ReferencingPerformActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ReferencingPerformActionNodeToolProvider.java index 32f5159ab..4368bb48f 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ReferencingPerformActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ReferencingPerformActionNodeToolProvider.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 @@ -12,8 +12,6 @@ *******************************************************************************/ package org.eclipse.syson.diagram.common.view.tools; -import java.util.List; - import org.eclipse.emf.ecore.EClass; import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; import org.eclipse.sirius.components.core.api.IEditingContext; @@ -22,10 +20,12 @@ import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.emf.diagram.ViewDiagramDescriptionConverter; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewNodeService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; import org.eclipse.syson.model.services.aql.ModelMutationAQLService; import org.eclipse.syson.sysml.SysmlPackage; -import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; @@ -74,7 +74,7 @@ public NodeTool create(IViewDiagramElementFinder cache) { .children(setReferencedFeature.build()); var initializeReferenceSubsetting = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getServiceCallExpression("newRefSubsetting", "elementInitializer")); + .expression(ServiceMethod.of0(ViewCreateService::elementInitializer).aql("newRefSubsetting")); var createReferenceSubsettingInstance = this.viewBuilderHelper.newCreateInstance() .typeName(SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getReferenceSubsetting())) @@ -83,15 +83,15 @@ public NodeTool create(IViewDiagramElementFinder cache) { .children(initializeReferenceSubsetting.build(), changeContextReferenceSubsetting.build()); var changeContextInitializeNewInstance = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getServiceCallExpression("newInstance", "elementInitializer")); + .expression(ServiceMethod.of0(ViewCreateService::elementInitializer).aql("newInstance")); var addToExposedElements = this.viewBuilderHelper.newChangeContext() .expression(ServiceMethod.of4(DiagramMutationAQLService::expose).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT, Node.SELECTED_NODE, ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); var reveal = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getServiceCallExpression(Node.SELECTED_NODE, "revealCompartment", - List.of("newInstance", DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE))); + .expression(ServiceMethod.of4(ViewNodeService::revealCompartment).aql(Node.SELECTED_NODE, "newInstance", DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, + ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); var changeContextNewInstance = this.viewBuilderHelper.newChangeContext() .expression("aql:newInstance") @@ -104,8 +104,8 @@ public NodeTool create(IViewDiagramElementFinder cache) { .children(changeContextNewInstance.build(), changeContextInitializeNewInstance.build()); var selectionDialogTree = this.diagramBuilderHelper.newSelectionDialogTreeDescription() - .elementsExpression(AQLUtils.getServiceCallExpression("editingContext", "getActionReferenceSelectionDialogElements")) - .childrenExpression(AQLUtils.getSelfServiceCallExpression("getActionReferenceSelectionDialogChildren")) + .elementsExpression(ServiceMethod.of0(ViewToolService::getActionReferenceSelectionDialogElements).aql(IEditingContext.EDITING_CONTEXT)) + .childrenExpression(ServiceMethod.of0(ViewToolService::getActionReferenceSelectionDialogChildren).aqlSelf()) .build(); var selectExistingStateUsage = this.diagramBuilderHelper.newSelectionDialogDescription() 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 48f4c43ce..1f0266e7a 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -77,11 +77,13 @@ import org.eclipse.syson.standard.diagrams.view.edges.InterfaceUsageEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.NestedActorEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.RedefinitionEdgeDescriptionProvider; +import org.eclipse.syson.standard.diagrams.view.edges.ReferenceSubsettingEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.SubclassificationEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.SubsettingEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.SuccessionEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.TransitionEdgeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.edges.UsageNestedUsageEdgeDescriptionProvider; +import org.eclipse.syson.standard.diagrams.view.nodes.ActionDefinitionParameterBorderNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.ActionDefinitionParametersCompartmentNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.ActionItemNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.ActionUsageParametersCompartmentNodeDescriptionProvider; @@ -98,7 +100,6 @@ import org.eclipse.syson.standard.diagrams.view.nodes.FakeNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.GeneralViewEmptyDiagramNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.InheritedPortUsageBorderNodeDescriptionProvider; -import org.eclipse.syson.standard.diagrams.view.nodes.ActionDefinitionParameterBorderNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.PerformActionsCompartmentItemNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.PerformActionsCompartmentNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.nodes.PortDefinitionOwnedItemBorderNodeDescriptionProvider; @@ -724,7 +725,8 @@ private List> createAllEdgeDescriptionProv edgeDescriptionProviders.add(new DependencyEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); edgeDescriptionProviders.add(new SubclassificationEdgeDescriptionProvider(colorProvider)); edgeDescriptionProviders.add(new RedefinitionEdgeDescriptionProvider(colorProvider)); - edgeDescriptionProviders.add(new SubsettingEdgeDescriptionProvider(colorProvider)); + edgeDescriptionProviders.add(new SubsettingEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); + edgeDescriptionProviders.add(new ReferenceSubsettingEdgeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); edgeDescriptionProviders.add(new FeatureTypingEdgeDescriptionProvider(colorProvider)); edgeDescriptionProviders.add(new AllocateEdgeDescriptionProvider(colorProvider)); edgeDescriptionProviders.add(new SuccessionEdgeDescriptionProvider(colorProvider)); diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/ReferenceSubsettingEdgeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/ReferenceSubsettingEdgeDescriptionProvider.java new file mode 100644 index 000000000..de4fe1eda --- /dev/null +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/ReferenceSubsettingEdgeDescriptionProvider.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * 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.EdgeStyle; +import org.eclipse.sirius.components.view.diagram.LineStyle; +import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.syson.diagram.common.view.edges.AbstractSubsettingEdgeDescriptionProvider; +import org.eclipse.syson.standard.diagrams.view.SDVDiagramDescriptionProvider; +import org.eclipse.syson.sysml.ReferenceSubsetting; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ViewConstants; + +/** + * Used to create the {@link ReferenceSubsetting} edge description in the standard diagrams. + * + * @author arichard + */ +public class ReferenceSubsettingEdgeDescriptionProvider extends AbstractSubsettingEdgeDescriptionProvider { + + private final IDescriptionNameGenerator descriptionNameGenerator; + + public ReferenceSubsettingEdgeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { + super(SysmlPackage.eINSTANCE.getReferenceSubsetting(), colorProvider); + this.descriptionNameGenerator = Objects.requireNonNull(descriptionNameGenerator); + } + + @Override + protected String getName() { + return this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getReferenceSubsetting()); + } + + @Override + protected String getSourceExpression() { + return AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencingFeature().getName(); + } + + @Override + protected String getTargetExpression() { + return AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().getName(); + } + + @Override + protected List getSourceNodes(IViewDiagramElementFinder cache) { + return this.getSourceAndTargetNodes(cache); + } + + @Override + protected List getTargetNodes(IViewDiagramElementFinder cache) { + return this.getSourceAndTargetNodes(cache); + } + + @Override + protected ChangeContextBuilder getSourceReconnectToolBody() { + var unsetOldSubsettingFeature = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewSubsettingFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var unsetOldSpecific = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewSpecific = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var setNewContainer = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getElement_OwnedRelationship().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT); + + var changeContextNewContainer = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET) + .children(setNewContainer.build()); + + return this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT) + .children(unsetOldSubsettingFeature.build(), unsetOldSpecific.build(), setNewSubsettingFeature.build(), + setNewSpecific.build(), changeContextNewContainer.build()); + } + + @Override + protected ChangeContextBuilder getTargetReconnectToolBody() { + var unsetOldReferencedFeature = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewReferencedFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getReferenceSubsetting_ReferencedFeature().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var unsetOldSubsettedFeature = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewSubsettedFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var unsetOldGeneral = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewGeneral = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + return this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT) + .children(unsetOldReferencedFeature.build(), unsetOldSubsettedFeature.build(), unsetOldGeneral.build(), setNewReferencedFeature.build(), setNewSubsettedFeature.build(), + setNewGeneral.build()); + } + + @Override + protected 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.CLOSED_ARROW_WITH_DOTS) + .build(); + } + + private List getSourceAndTargetNodes(IViewDiagramElementFinder cache) { + var sourcesAndTargets = new ArrayList(); + + SDVDiagramDescriptionProvider.USAGES.forEach(usage -> { + cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(usage)).ifPresent(sourcesAndTargets::add); + }); + return sourcesAndTargets; + } +} diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/SubsettingEdgeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/SubsettingEdgeDescriptionProvider.java index 298bfc899..66a6a4991 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/SubsettingEdgeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/SubsettingEdgeDescriptionProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 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 @@ -14,39 +14,50 @@ 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.EdgeStyle; +import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.syson.diagram.common.view.edges.AbstractSubsettingEdgeDescriptionProvider; -import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator; import org.eclipse.syson.standard.diagrams.view.SDVDiagramDescriptionProvider; import org.eclipse.syson.sysml.Subsetting; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ViewConstants; /** - * Used to create the {@link Subsetting} edge description in the General View diagram. + * Used to create the {@link Subsetting} edge description in the standard diagrams. * * @author arichard */ public class SubsettingEdgeDescriptionProvider extends AbstractSubsettingEdgeDescriptionProvider { - public SubsettingEdgeDescriptionProvider(IColorProvider colorProvider) { - super(colorProvider); + private final IDescriptionNameGenerator descriptionNameGenerator; + + public SubsettingEdgeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { + super(SysmlPackage.eINSTANCE.getSubsetting(), colorProvider); + this.descriptionNameGenerator = Objects.requireNonNull(descriptionNameGenerator); } @Override protected String getName() { - return SDVDescriptionNameGenerator.PREFIX + " Edge Subsetting"; + return this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getSubsetting()); } - private List getSourceAndTargetNodes(IViewDiagramElementFinder cache) { - var nameGenerator = new SDVDescriptionNameGenerator(); - var sourcesAndTargets = new ArrayList(); + @Override + protected String getSourceExpression() { + return AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName(); + } - SDVDiagramDescriptionProvider.USAGES.forEach(usage -> { - cache.getNodeDescription(nameGenerator.getNodeName(usage)).ifPresent(sourcesAndTargets::add); - }); - return sourcesAndTargets; + @Override + protected String getTargetExpression() { + return AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName(); } @Override @@ -58,4 +69,81 @@ protected List getSourceNodes(IViewDiagramElementFinder cache) protected List getTargetNodes(IViewDiagramElementFinder cache) { return this.getSourceAndTargetNodes(cache); } + + @Override + protected ChangeContextBuilder getSourceReconnectToolBody() { + var unsetOldSubsettingFeature = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewSubsettingFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettingFeature().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var unsetOldSpecific = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewSpecific = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_Specific().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var setNewContainer = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getElement_OwnedRelationship().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT); + + var changeContextNewContainer = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET) + .children(setNewContainer.build()); + + return this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT) + .children(unsetOldSubsettingFeature.build(), unsetOldSpecific.build(), setNewSubsettingFeature.build(), + setNewSpecific.build(), changeContextNewContainer.build()); + } + + @Override + protected ChangeContextBuilder getTargetReconnectToolBody() { + var unsetOldSubsettedFeature = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewSubsettedFeature = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSubsetting_SubsettedFeature().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + var unsetOldGeneral = this.viewBuilderHelper.newUnsetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) + .elementExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_SOURCE); + + var setNewGeneral = this.viewBuilderHelper.newSetValue() + .featureName(SysmlPackage.eINSTANCE.getSpecialization_General().getName()) + .valueExpression(AQLConstants.AQL + AQLConstants.SEMANTIC_RECONNECTION_TARGET); + + return this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL + AQLConstants.EDGE_SEMANTIC_ELEMENT) + .children(unsetOldSubsettedFeature.build(), unsetOldGeneral.build(), setNewSubsettedFeature.build(), + setNewGeneral.build()); + } + + @Override + protected 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_CLOSED_ARROW) + .build(); + } + + private List getSourceAndTargetNodes(IViewDiagramElementFinder cache) { + var sourcesAndTargets = new ArrayList(); + + SDVDiagramDescriptionProvider.USAGES.forEach(usage -> { + cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(usage)).ifPresent(sourcesAndTargets::add); + }); + return sourcesAndTargets; + } } diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc index a23feb19d..3c2ffec8f 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.1.0.adoc @@ -111,6 +111,7 @@ a|image::release-notes-enumeration-definition-after.png[Enumeration definition n - In diagrams, an `ItemUsage` graphical border node is now available on `PortDefinition` graphical nodes. - In diagrams, when creating an element from a compartment, it will not be displayed by default on the diagram, but only in the compartment. - In the _Advanced_ tab of the _Details_ view, _isComposite_ and _isReference_ properties (checkbox widgets) now have a help text. +- In diagrams, a new tool named `Reference Subsetting` is available in the palette to create `ReferenceSubsetting` relationships between `Usage` elements. == Technical details