diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 6bb91af68c..24f687b01c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -115,6 +115,7 @@ The edge goes from the derived requirement to the original one and is displayed - https://github.com/eclipse-syson/syson/issues/2428[#2428] [diagrams] Add the _New Derived Requirement_ edge tool on `RequirementUsage` graphical nodes, creating a requirement derivation towards another `RequirementUsage`. The edge is drawn from the derived requirement to the original one, and the created derivation has the same shape as one written in text: a `ConnectionUsage` annotated with `#derivation`, whose ends are annotated with `#original` and `#derive`. The import of the `RequirementDerivation` library is added to the owning namespace when it is missing. +- https://github.com/eclipse-syson/syson/issues/2470[#2470] [diagrams] Add the selection of an existing element in `New ItemDefinition as Payload` and `New PartDefinition as Payload` tools on `AcceptAction` graphical node. == v20 == v2026.7.0 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 c52dff1d53..9cba572546 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 @@ -128,9 +128,11 @@ public class GVSubNodeActionFlowCreationTests extends AbstractIntegrationTests { private static Stream acceptActionUsagePayloadParameters() { return Stream.of( - Arguments.of(SysmlPackage.eINSTANCE.getPartDefinition()), - Arguments.of(SysmlPackage.eINSTANCE.getItemDefinition())) - .map(TestNameGenerator::namedArguments); + Arguments.of(SysmlPackage.eINSTANCE.getPartDefinition(), ""), + Arguments.of(SysmlPackage.eINSTANCE.getPartDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_DEFINITION_ID), + Arguments.of(SysmlPackage.eINSTANCE.getItemDefinition(), ""), + Arguments.of(SysmlPackage.eINSTANCE.getItemDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.ITEM_DEFINITION_ID) + ); } private static Stream actionUsageSiblingNodeParameters() { @@ -223,9 +225,19 @@ public void setUp() { @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) @ParameterizedTest @MethodSource("acceptActionUsagePayloadParameters") - public void createAcceptActionUsagePayload(EClass eClass) { + public void createAcceptActionUsagePayload(EClass eClass, String selectedNodeId) { var flux = this.givenSubscriptionToDiagram(); + AtomicReference expectedPayloadTypeName = new AtomicReference<>(); + ISemanticChecker initialSemanticChecker = (editingContext) -> { + expectedPayloadTypeName.set(this.objectSearchService.getObject(editingContext, selectedNodeId) + .filter(Element.class::isInstance) + .map(Element.class::cast) + .map(Element::getDeclaredName) + .orElse("acceptActionPayloadType")); + }; + Runnable initialSemanticCheck = this.semanticCheckerService.checkEditingContext(initialSemanticChecker); + AtomicReference diagram = new AtomicReference<>(); Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); @@ -236,8 +248,8 @@ public void createAcceptActionUsagePayload(EClass eClass) { EClass parentEClass = SysmlPackage.eINSTANCE.getAcceptActionUsage(); String targetObjectId = GeneralViewWithTopNodesTestProjectData.SemanticIds.ACCEPT_ACTION_USAGE_ID; - Runnable createNodeRunnable = this.creationTestsService.createNode(diagramDescriptionIdProvider, diagram, parentEClass, targetObjectId, - this.descriptionNameGenerator.getCreationToolName("New {0} as Payload", eClass)); + Runnable createNodeRunnable = this.creationTestsService.createNodeWithSelectionDialogWithSingleSelection(diagramDescriptionIdProvider, diagram, parentEClass, targetObjectId, + this.descriptionNameGenerator.getCreationToolName("New {0} as Payload", eClass), selectedNodeId); Consumer diagramCheck = assertRefreshedDiagramThat(newDiagram -> { new CheckDiagramElementCount(this.diagramComparator) @@ -251,7 +263,7 @@ public void createAcceptActionUsagePayload(EClass eClass) { assertThat(updatedNode.getInsideLabel()).as("The updated node label should exist").isNotNull(); assertThat(updatedNode.getInsideLabel().getText()) .contains(LabelConstants.OPEN_QUOTE + "accept" + LabelConstants.CLOSE_QUOTE) - .contains("payload: acceptActionPayloadType"); + .contains("payload: " + expectedPayloadTypeName.get()); }); ISemanticChecker semanticChecker = (editingContext) -> { @@ -269,13 +281,14 @@ public void createAcceptActionUsagePayload(EClass eClass) { assertThat(relationship).isInstanceOf(FeatureTyping.class); FeatureTyping featureTyping = (FeatureTyping) relationship; assertThat(eClass.isInstance(featureTyping.getType())); - assertThat(featureTyping.getType().getName()).isEqualTo("acceptActionPayloadType"); + assertThat(featureTyping.getType().getName()).isEqualTo(expectedPayloadTypeName.get()); }; Runnable semanticCheck = this.semanticCheckerService.checkEditingContext(semanticChecker); StepVerifier.create(flux) .consumeNextWith(initialDiagramContentConsumer) + .then(initialSemanticCheck) .then(createNodeRunnable) .consumeNextWith(diagramCheck) .then(semanticCheck) diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationToolService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationToolService.java index 50da322c74..21eabd0d37 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationToolService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationToolService.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.eclipse.emf.ecore.EClass; @@ -596,38 +597,60 @@ public Element createAcceptAction(Element ownerElement) { * Create or replace the payload parameter of an accept action usage. * * @param self - * the accept action usage + * the accept action usage * @param payloadEClassName - * the payload classifier name + * the payload classifier name + * @param selectedObject + * the payload classifier given by the selection dialog * @return the given accept action usage */ - public Element createAcceptActionPayload(AcceptActionUsage self, String payloadEClassName) { + public Element createAcceptActionPayload(AcceptActionUsage self, String payloadEClassName, Element selectedObject) { + var payloadType = Optional.ofNullable(selectedObject) + .filter(Type.class::isInstance) + .map(Type.class::cast) + .orElseGet(() -> this.createNewAcceptActionPayload(payloadEClassName, self)); + if (payloadType != null) { + var featureTyping = SysmlFactory.eINSTANCE.createFeatureTyping(); + featureTyping.setType(payloadType); + var referenceUsage = SysmlFactory.eINSTANCE.createReferenceUsage(); + referenceUsage.setDeclaredName("payload"); + referenceUsage.setDirection(FeatureDirectionKind.INOUT); + referenceUsage.getOwnedRelationship().add(featureTyping); + var parameterMembership = this.getPayloadParameterMembership(self); + var oldParameterContent = parameterMembership.getOwnedMemberParameter(); + if (oldParameterContent != null) { + this.deleteService.deleteFromModel(oldParameterContent); + } + parameterMembership = this.getPayloadParameterMembership(self); + parameterMembership.getOwnedRelatedElement().add(referenceUsage); + self.getOwnedRelationship().add(parameterMembership); + } + return self; + } + + /** + * Creates a new payload element of the given EClass name to set in the given {@link AcceptActionUsage}. + * + * @param payloadEClassName + * the name of the EClass of the payload. It could be {@link ItemDefinition} or {@link PartDefinition}. + * @param acceptActionName + * the accept action in which the new payload should be set. + * @return the newly created payload element or null if the given EClass name is not a correct Classifier name. + */ + private Type createNewAcceptActionPayload(String payloadEClassName, AcceptActionUsage acceptActionName) { var classifier = SysmlPackage.eINSTANCE.getEClassifier(payloadEClassName); if (classifier instanceof EClass eClass) { var payload = SysmlFactory.eINSTANCE.create(eClass); if (payload instanceof Type payloadType) { - payloadType.setDeclaredName(self.getDeclaredName() + "PayloadType"); + payloadType.setDeclaredName(acceptActionName.getDeclaredName() + "PayloadType"); var membership = SysmlFactory.eINSTANCE.createOwningMembership(); membership.getOwnedRelatedElement().add(payloadType); - var payloadParent = this.getClosestContainingPackageFrom(self); + var payloadParent = this.getClosestContainingPackageFrom(acceptActionName); payloadParent.getOwnedRelationship().add(membership); - var featureTyping = SysmlFactory.eINSTANCE.createFeatureTyping(); - featureTyping.setType(payloadType); - var referenceUsage = SysmlFactory.eINSTANCE.createReferenceUsage(); - referenceUsage.setDeclaredName("payload"); - referenceUsage.setDirection(FeatureDirectionKind.INOUT); - referenceUsage.getOwnedRelationship().add(featureTyping); - var parameterMembership = this.getPayloadParameterMembership(self); - var oldParameterContent = parameterMembership.getOwnedMemberParameter(); - if (oldParameterContent != null) { - this.deleteService.deleteFromModel(oldParameterContent); - } - parameterMembership = this.getPayloadParameterMembership(self); - parameterMembership.getOwnedRelatedElement().add(referenceUsage); - self.getOwnedRelationship().add(parameterMembership); + return payloadType; } } - return self; + return null; } /** diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java index 81f0c9badd..4bac75bf2b 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java @@ -144,10 +144,10 @@ public Element createAcceptAction(Element ownerElement) { } /** - * {@link DiagramMutationToolService#createAcceptActionPayload(AcceptActionUsage, String)}. + * {@link DiagramMutationToolService#createAcceptActionPayload(AcceptActionUsage, String, Element)}. */ - public Element createAcceptActionPayload(AcceptActionUsage self, String payloadEClassName) { - return this.diagramMutationToolService.createAcceptActionPayload(self, payloadEClassName); + public Element createAcceptActionPayload(AcceptActionUsage self, String payloadEClassName, Element selectedObject) { + return this.diagramMutationToolService.createAcceptActionPayload(self, payloadEClassName, selectedObject); } /** diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AcceptActionPayloadNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AcceptActionPayloadNodeToolProvider.java index c797fb54c1..72d03d7852 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AcceptActionPayloadNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AcceptActionPayloadNodeToolProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 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,16 +12,25 @@ *******************************************************************************/ package org.eclipse.syson.diagram.common.view.tools; +import java.util.List; import java.util.Objects; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.trees.renderer.TreeRenderer; import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; import org.eclipse.sirius.components.view.builder.generated.diagram.DiagramBuilders; import org.eclipse.sirius.components.view.builder.generated.view.ViewBuilders; import org.eclipse.sirius.components.view.builder.providers.INodeToolProvider; +import org.eclipse.sirius.components.view.diagram.DialogDescription; import org.eclipse.sirius.components.view.diagram.NodeTool; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.tree.services.aql.TreeQueryAQLService; import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; +import org.eclipse.syson.util.SysMLMetamodelHelper; /** * Used to create element as a payload of an accept action usage. @@ -48,7 +57,7 @@ public NodeTool create(IViewDiagramElementFinder cache) { var builder = this.diagramBuilderHelper.newNodeTool(); var creationPayloadServiceCall = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL_SELF + ".createAcceptActionPayload('" + this.payloadEClass.getName() + "')") + .expression(ServiceMethod.of2(DiagramMutationAQLService::createAcceptActionPayload).aqlSelf(AQLUtils.aqlString(this.payloadEClass.getName()), "selectedObject")) .build(); var rootChangContext = this.viewBuilderHelper.newChangeContext() @@ -60,6 +69,31 @@ public NodeTool create(IViewDiagramElementFinder cache) { .iconURLsExpression("/icons/full/obj16/" + this.payloadEClass.getName() + ".svg") .body(rootChangContext) .preconditionExpression(AQLConstants.AQL_SELF + ".isEmptyAcceptActionUsagePayload()") + .dialogDescription(this.getSelectionDialogDescription()) + .build(); + } + + private DialogDescription getSelectionDialogDescription() { + var payloadTypeName = SysMLMetamodelHelper.buildQualifiedName(this.payloadEClass); + var selectionDialogTree = this.diagramBuilderHelper.newSelectionDialogTreeDescription() + .elementsExpression(ServiceMethod.of1(TreeQueryAQLService::getSelectionDialogElements).aql(IEditingContext.EDITING_CONTEXT, AQLUtils.aqlSequence(List.of(payloadTypeName)))) + .childrenExpression( + ServiceMethod.of3(TreeQueryAQLService::getSelectionDialogChildren).aqlSelf(IEditingContext.EDITING_CONTEXT, TreeRenderer.EXPANDED, AQLUtils.aqlSequence(List.of(payloadTypeName)))) + .isSelectableExpression(AQLConstants.AQL_SELF + ".oclIsKindOf(" + payloadTypeName + ")") + .build(); + String payloadName = this.payloadEClass.getName(); + return this.diagramBuilderHelper.newSelectionDialogDescription() + .selectionDialogTreeDescription(selectionDialogTree) + .defaultTitleExpression("Set payload") + .descriptionExpression(payloadName + " as payload") + .noSelectionActionLabelExpression("Create a New " + payloadName) + .noSelectionActionDescriptionExpression("Set the payload with a New " + payloadName) + .withSelectionActionLabelExpression("Select an existing " + payloadName) + .withSelectionActionDescriptionExpression("Set the payload with an existing " + payloadName) + .noSelectionActionStatusMessageExpression("It will set the payload with a New " + payloadName) + .selectionRequiredWithoutSelectionStatusMessageExpression("Select one " + payloadName) + .selectionRequiredWithSelectionStatusMessageExpression(AQLConstants.AQL + "'It will set the payload with ' + selectedObjects->first().name") + .optional(true) .build(); } } diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc index 0555ca7159..45a4354f86 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc @@ -95,6 +95,8 @@ image::release-notes-action-inherited-item-border-nodes.png[ItemUsage border nod ** Add support for the inheritance of `ItemUsage` border nodes on `PortDefinition` and `PortUsage` graphical nodes. + image::release-notes-port-inherited-item-border-nodes.png[ItemUsage border node inheritance on PortDefinition and PortUsage, width=80%,height=80%] +** Add the capability of selecting an existing `ItemDefinition` in _New ItemDefinition as Payload_ and _New PartDefinition as Payload_ tools on `AcceptAction` graphical nodes. +The creation of a new `ItemDefinition` remains available. == Bug fixes