diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index ed33e447a..90fb5eeb3 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -43,6 +43,7 @@ On `Requirement` or `Concern` (either definition or usage) graphical nodes it is Stakeholders are by default represented with a dedicated graphical node connected to the parent graphical node, or can appear inside the `stakeholders` list compartment. - [releng] Fix the license and URL of our maven modules in the SBOM - https://github.com/eclipse-syson/syson/issues/2108[#2108] [diagrams] Leverage the latest change of the selection dialog to allow creating an `Actor` graphical node without specialization. +- https://github.com/eclipse-syson/syson/issues/2111[#2111] [diagrams] Leverage the latest change of the selection dialog to allow creating a `Stakeholder` graphical node without specialization. === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeAnalysisCreationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeAnalysisCreationTests.java index 8da8cfbb5..ea57235cf 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeAnalysisCreationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeAnalysisCreationTests.java @@ -710,7 +710,7 @@ private void createActorWithSubsettingInCaseUsage(EClass caseUsageSubclass, Stri assertThat(referencedObject).isInstanceOf(List.class) .asInstanceOf(type(List.class)) .satisfies(actors -> { - assertThat(actors).size().isEqualTo(1); + assertThat((List) actors).size().isEqualTo(1); assertThat(actors.getFirst()) .isInstanceOf(PartUsage.class) .asInstanceOf(type(PartUsage.class)) @@ -763,7 +763,7 @@ private void createActorWithFeatureTypingInCaseUsage(EClass caseUsageSubclass, S assertThat(referencedObject).isInstanceOf(List.class) .asInstanceOf(type(List.class)) .satisfies(actors -> { - assertThat(actors).size().isEqualTo(1); + assertThat((List) actors).size().isEqualTo(1); assertThat(actors.getFirst()) .isInstanceOf(PartUsage.class) .asInstanceOf(type(PartUsage.class)) @@ -815,7 +815,7 @@ private void createActorWithoutSpecializationInCaseUsage(EClass caseUsageSubclas assertThat(referencedObject).isInstanceOf(List.class) .asInstanceOf(type(List.class)) .satisfies(actors -> { - assertThat(actors).size().isEqualTo(1); + assertThat((List) actors).size().isEqualTo(1); assertThat(actors.getFirst()) .isInstanceOf(PartUsage.class) .asInstanceOf(type(PartUsage.class)) diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java index d35ec86c4..099df69a8 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java @@ -19,7 +19,6 @@ import java.time.Duration; import java.util.List; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -31,15 +30,12 @@ import org.eclipse.emf.ecore.EReference; import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; -import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariable; -import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariableType; import org.eclipse.sirius.components.core.api.IObjectSearchService; import org.eclipse.sirius.components.diagrams.Diagram; import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; import org.eclipse.syson.GivenSysONServer; -import org.eclipse.syson.application.controller.editingcontext.checkers.ISemanticChecker; import org.eclipse.syson.application.controller.editingcontext.checkers.SemanticCheckerService; import org.eclipse.syson.application.controllers.diagrams.checkers.CheckBorderNode; import org.eclipse.syson.application.controllers.diagrams.checkers.CheckDiagramElementCount; @@ -61,7 +57,6 @@ import org.eclipse.syson.sysml.Subsetting; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.sysml.Type; -import org.eclipse.syson.sysml.helper.EMFUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; import org.junit.jupiter.api.BeforeEach; @@ -1055,28 +1050,60 @@ public void createRequirementDefinitionChildNodes(EClass childEClass, String com .verify(Duration.ofSeconds(10)); } + @DisplayName("GIVEN a ConcernDefinition, WHEN creating a new Stakeholder selecting a Part, THEN the Stakeholder subsetted by the Part is created in the ConcernDefinition") + @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @Test + public void createNewStakeholderFromPartInConcernDefinition() { + this.createNewStakeholderSelectingExistingElementIn(SysmlPackage.eINSTANCE.getConcernDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.CONCERN_DEFINITION_ID, "ConcernDefinition", GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_USAGE_ID); + } + + @DisplayName("GIVEN a Concern, WHEN creating a new Stakeholder selecting a Part, THEN the Stakeholder subsetted by the Part is created in the Concern") + @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @Test + public void createNewStakeholderFromPartInConcernUsage() { + this.createNewStakeholderSelectingExistingElementIn(SysmlPackage.eINSTANCE.getConcernUsage(), GeneralViewWithTopNodesTestProjectData.SemanticIds.CONCERN_USAGE_ID, "concern", GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_USAGE_ID); + } + + @DisplayName("Given a RequirementDefinition, WHEN creating a new Stakeholder selecting a Part, THEN the Stakeholder subsetted by the Part is created in the RequirementDefinition") + @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @Test + public void createNewStakeholderFromPartInRequirementDefinition() { + this.createNewStakeholderSelectingExistingElementIn(SysmlPackage.eINSTANCE.getRequirementDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_DEFINITION_ID, "RequirementDefinition", GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_USAGE_ID); + } + + @DisplayName("Given a Requirement, WHEN creating a new Stakeholder selecting a Part, THEN the Stakeholder subsetted by the Part is created in the Requirement") + @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @Test + public void createNewStakeholderFromPartInRequirementUsage() { + this.createNewStakeholderSelectingExistingElementIn(SysmlPackage.eINSTANCE.getRequirementUsage(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_USAGE_ID, "requirement", GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_USAGE_ID); + } + + @DisplayName("GIVEN a ConcernDefinition, WHEN creating a new Stakeholder without selection, THEN the Stakeholder without subsetting is created in the ConcernDefinition") @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) @Test public void createNewStakeholderInConcernDefinition() { - this.createNewStakeholderIn(SysmlPackage.eINSTANCE.getConcernDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.CONCERN_DEFINITION_ID, "ConcernDefinition"); + this.createNewStakeholderWithoutSelectionIn(SysmlPackage.eINSTANCE.getConcernDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.CONCERN_DEFINITION_ID, "ConcernDefinition"); } + @DisplayName("GIVEN a Concern, WHEN creating a new Stakeholder without selection, THEN the Stakeholder without subsetting is created in the Concern") @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) @Test public void createNewStakeholderInConcernUsage() { - this.createNewStakeholderIn(SysmlPackage.eINSTANCE.getConcernUsage(), GeneralViewWithTopNodesTestProjectData.SemanticIds.CONCERN_USAGE_ID, "concern"); + this.createNewStakeholderWithoutSelectionIn(SysmlPackage.eINSTANCE.getConcernUsage(), GeneralViewWithTopNodesTestProjectData.SemanticIds.CONCERN_USAGE_ID, "concern"); } + @DisplayName("GIVEN a RequirementDefinition, WHEN creating a new Stakeholder without selection, THEN the Stakeholder without specialization is created in the RequirementDefinition") @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) @Test public void createNewStakeholderInRequirementDefinition() { - this.createNewStakeholderIn(SysmlPackage.eINSTANCE.getRequirementDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_DEFINITION_ID, "RequirementDefinition"); + this.createNewStakeholderWithoutSelectionIn(SysmlPackage.eINSTANCE.getRequirementDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_DEFINITION_ID, "RequirementDefinition"); } + @DisplayName("GIVEN a Requirement, WHEN creating a new Stakeholder without selection, THEN the Stakeholder without specialization is created in the Requirement") @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) @Test public void createNewStakeholderInRequirementUsage() { - this.createNewStakeholderIn(SysmlPackage.eINSTANCE.getRequirementUsage(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_USAGE_ID, "requirement"); + this.createNewStakeholderWithoutSelectionIn(SysmlPackage.eINSTANCE.getRequirementUsage(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_USAGE_ID, "requirement"); } @DisplayName("GIVEN a Requirement, WHEN creating a new Actor selecting a Part, THEN the Actor subsetted by the Part is created in the Requirement") @@ -1121,7 +1148,7 @@ public void createNewActorWithoutSpecializationInRequirementDefinition() { this.createNewActorWithoutSpecializationIn(SysmlPackage.eINSTANCE.getRequirementDefinition(), GeneralViewWithTopNodesTestProjectData.SemanticIds.REQUIREMENT_DEFINITION_ID, "RequirementDefinition"); } - private void createNewStakeholderIn(EClass eClassWithStakeholderParameter, String targetObjectId, String parentNodeLabel) { + private void createNewStakeholderSelectingExistingElementIn(EClass eClassWithStakeholderParameter, String targetObjectId, String parentNodeLabel, String existingElementId) { var flux = this.givenSubscriptionToDiagram(); AtomicReference diagram = new AtomicReference<>(); @@ -1136,8 +1163,7 @@ private void createNewStakeholderIn(EClass eClassWithStakeholderParameter, Strin .orElseGet(() -> Assertions.fail("No fitting EReference could be found in '%s'.".formatted(eClassWithStakeholderParameter.getName()))); final String stakeholderCreationToolName = "New Stakeholder"; - Runnable createNodeRunnable = this.creationTestsService.createNode(diagramDescriptionIdProvider, diagram, eClassWithStakeholderParameter, targetObjectId, stakeholderCreationToolName, - Stream.of(new ToolVariable("selectedObject", /* PartUsage 'part' */ "2c5fe5a5-18fe-40f4-ab66-a2d91ab7df6a", ToolVariableType.OBJECT_ID)).toList()); + Runnable createNodeRunnable = this.creationTestsService.createNodeWithSelectionDialogWithSingleSelection(diagramDescriptionIdProvider, diagram, eClassWithStakeholderParameter, targetObjectId, stakeholderCreationToolName, existingElementId); Consumer diagramCheck = assertRefreshedDiagramThat(newDiagram -> { var initialDiagram = diagram.get(); @@ -1153,29 +1179,90 @@ private void createNewStakeholderIn(EClass eClassWithStakeholderParameter, Strin .check(initialDiagram, newDiagram); }); - final ISemanticChecker semanticChecker = (editingContext) -> { - final Element semanticRootElement = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID).filter(Element.class::isInstance) - .map(Element.class::cast).orElseGet(() -> Assertions.fail("Could not find the expected root semantic object.")); - final List allStakeholderPartUsages = EMFUtils.allContainedObjectOfType(semanticRootElement, PartUsage.class) - .filter(element -> Objects.equals(element.getName(), "stakeholder1")).toList(); - assertEquals(1, allStakeholderPartUsages.size()); - - final PartUsage stakeholderPartUsage = allStakeholderPartUsages.get(0); - final EList subsettings = stakeholderPartUsage.getOwnedSubsetting(); - assertEquals(1, subsettings.size()); - assertThat(subsettings.get(0).getSubsettedFeature().getName()).isEqualTo("part"); + Consumer additionalCheck = referencedObject -> { + assertThat(referencedObject).isInstanceOf(List.class) + .asInstanceOf(type(List.class)) + .satisfies(stakeholders -> { + assertThat((List) stakeholders).size().isEqualTo(1); + assertThat(stakeholders.getFirst()) + .isInstanceOf(PartUsage.class) + .asInstanceOf(type(PartUsage.class)) + .satisfies(stakeholderPartUsage -> { + final EList subsettings = stakeholderPartUsage.getOwnedSubsetting(); + assertEquals(1, subsettings.size()); + assertThat(subsettings.get(0).getSubsettedFeature().getName()).isEqualTo("part"); + }); + }); }; - Runnable semanticCheck1 = this.semanticCheckerService.checkEditingContext( - this.semanticCheckerService.getElementInParentSemanticChecker(parentNodeLabel, stakeholderParameterEReference, SysmlPackage.eINSTANCE.getPartUsage())); - Runnable semanticCheck2 = this.semanticCheckerService.checkEditingContext(semanticChecker); + Runnable semanticCheck = this.semanticCheckerService.checkEditingContext( + this.semanticCheckerService.getElementInParentSemanticChecker(parentNodeLabel, stakeholderParameterEReference, SysmlPackage.eINSTANCE.getPartUsage(), additionalCheck)); StepVerifier.create(flux) .consumeNextWith(initialDiagramContentConsumer) .then(createNodeRunnable) .consumeNextWith(diagramCheck) - .then(semanticCheck1) - .then(semanticCheck2) + .then(semanticCheck) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + private void createNewStakeholderWithoutSelectionIn(EClass eClassWithStakeholderParameter, String targetObjectId, String parentNodeLabel) { + var flux = this.givenSubscriptionToDiagram(); + + AtomicReference diagram = new AtomicReference<>(); + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider); + + final EReference stakeholderParameterEReference = eClassWithStakeholderParameter.getEAllReferences().stream() + .filter(eReference -> eReference.getName().equals("stakeholderParameter") && eReference.getEType() == SysmlPackage.eINSTANCE.getPartUsage()).findFirst() + .orElseGet(() -> Assertions.fail("No fitting EReference could be found in '%s'.".formatted(eClassWithStakeholderParameter.getName()))); + + final String stakeholderCreationToolName = "New Stakeholder"; + Runnable createNodeRunnable = this.creationTestsService.createNodeWithSelectionDialogWithoutSelectionProvided(diagramDescriptionIdProvider, diagram, eClassWithStakeholderParameter, targetObjectId, stakeholderCreationToolName); + + Consumer diagramCheck = assertRefreshedDiagramThat(newDiagram -> { + var initialDiagram = diagram.get(); + new CheckDiagramElementCount(this.diagramComparator) + .hasNewNodeCount(1) + .hasNewEdgeCount(1) + .check(initialDiagram, newDiagram, true); + new CheckNodeInCompartment(diagramDescriptionIdProvider, this.diagramComparator) + .withTargetObjectId(targetObjectId) + .withCompartmentName("stakeholders") + .hasNodeDescriptionName(this.descriptionNameGenerator.getCompartmentItemName(eClassWithStakeholderParameter, stakeholderParameterEReference)) + .hasCompartmentCount(0) + .check(initialDiagram, newDiagram); + }); + + Consumer additionalCheck = referencedObject -> { + assertThat(referencedObject).isInstanceOf(List.class) + .asInstanceOf(type(List.class)) + .satisfies(stakeholders -> { + assertThat((List) stakeholders).size().isEqualTo(1); + assertThat(stakeholders.getFirst()) + .isInstanceOf(PartUsage.class) + .asInstanceOf(type(PartUsage.class)) + .satisfies(stakeholderPartUsage -> { + assertThat(stakeholderPartUsage.getOwnedSpecialization()).allMatch(Specialization::isIsImplied); + assertThat(stakeholderPartUsage.getType()) + .isNotEmpty() + .allMatch(Element::isIsLibraryElement); + }); + }); + }; + + Runnable semanticCheck = this.semanticCheckerService.checkEditingContext( + this.semanticCheckerService.getElementInParentSemanticChecker(parentNodeLabel, stakeholderParameterEReference, SysmlPackage.eINSTANCE.getPartUsage(), additionalCheck)); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(createNodeRunnable) + .consumeNextWith(diagramCheck) + .then(semanticCheck) .thenCancel() .verify(Duration.ofSeconds(10)); } @@ -1218,7 +1305,7 @@ private void createNewActorWithSubsettingIn(EClass eClassWithActorParameter, Str assertThat(referencedObject).isInstanceOf(List.class) .asInstanceOf(type(List.class)) .satisfies(actors -> { - assertThat(actors).size().isEqualTo(1); + assertThat((List) actors).size().isEqualTo(1); assertThat(actors.getFirst()) .isInstanceOf(PartUsage.class) .asInstanceOf(type(PartUsage.class)) @@ -1280,7 +1367,7 @@ private void createNewActorWithFeatureTypingIn(EClass eClassWithActorParameter, assertThat(referencedObject).isInstanceOf(List.class) .asInstanceOf(type(List.class)) .satisfies(actors -> { - assertThat(actors).size().isEqualTo(1); + assertThat((List) actors).size().isEqualTo(1); assertThat(actors.getFirst()) .isInstanceOf(PartUsage.class) .asInstanceOf(type(PartUsage.class)) @@ -1341,7 +1428,7 @@ private void createNewActorWithoutSpecializationIn(EClass eClassWithActorParamet assertThat(referencedObject).isInstanceOf(List.class) .asInstanceOf(type(List.class)) .satisfies(actors -> { - assertThat(actors).size().isEqualTo(1); + assertThat((List) actors).size().isEqualTo(1); assertThat(actors.getFirst()) .isInstanceOf(PartUsage.class) .asInstanceOf(type(PartUsage.class)) diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java index edc32e6b5..6ba03ad18 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java @@ -290,17 +290,16 @@ public Element createPartUsageAsActor(Element self, Element selectedObject) { * a {@link RequirementUsage} or {@link RequirementDefinition}, otherwise no {@link PartUsage} will be * created. * @param selectedObject - * a {@link ItemUsage} or {@link ItemDefinition} that will be subsetted by (respectively that will type) + * an {@link ItemUsage} or {@link ItemDefinition} that will be subsetted by (respectively that will type) * the created {@link PartUsage}. + * If {@code null} then the stakeholder {@link PartUsage} will not be subsetted. * @return the newly-created {@link PartUsage}, contained by {@code self} through a {@link StakeholderMembership}. * If {@code self} was neither a {@link RequirementUsage} nor a {@link RequirementDefinition}, {@code self} * is returned as-is. */ public Element createPartUsageAsStakeholder(Element self, Element selectedObject) { - Objects.requireNonNull(self); - Objects.requireNonNull(selectedObject); - - if ((self instanceof RequirementUsage || self instanceof RequirementDefinition) && (selectedObject instanceof ItemUsage || selectedObject instanceof ItemDefinition)) { + Element result = self; + if (self instanceof RequirementUsage || self instanceof RequirementDefinition) { var createdPartUsage = SysmlFactory.eINSTANCE.createPartUsage(); var stakeholderMembership = this.createMembership(self, SysmlPackage.eINSTANCE.getStakeholderMembership()); stakeholderMembership.getOwnedRelatedElement().add(createdPartUsage); @@ -312,10 +311,9 @@ public Element createPartUsageAsStakeholder(Element self, Element selectedObject this.utilService.setFeatureTyping(createdPartUsage, selectedItemDefinition); } - return createdPartUsage; - } else { - return self; + result = createdPartUsage; } + return result; } public Element createActionParameter(Element self, String direction) { diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StakeholdersCompartmentNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StakeholdersCompartmentNodeToolProvider.java index e4f887f34..6b50ef944 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StakeholdersCompartmentNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StakeholdersCompartmentNodeToolProvider.java @@ -46,9 +46,18 @@ protected SelectionDialogDescription getSelectionDialogDescription() { .build(); return this.diagramBuilderHelper.newSelectionDialogDescription() .selectionDialogTreeDescription(selectionDialogTree) - .defaultTitleExpression("New Stakeholder") - .descriptionExpression("Select an existing Part as stakeholder:") - .optional(false) + .defaultTitleExpression(this.getNodeToolName()) + .noSelectionTitleExpression(this.getNodeToolName()) + .withSelectionTitleExpression(this.getNodeToolName()) + .descriptionExpression("Select a stakeholder:") + .noSelectionActionLabelExpression("Create a new stakeholder") + .noSelectionActionDescriptionExpression("Create a new stakeholder without specialization") + .withSelectionActionLabelExpression("Select an existing Element as stakeholder") + .withSelectionActionDescriptionExpression("Create a new specialized stakeholder") + .noSelectionActionStatusMessageExpression("It will create a new stakeholder without specialization") + .selectionRequiredWithoutSelectionStatusMessageExpression("Select one Element to specialize the new stakeholder") + .selectionRequiredWithSelectionStatusMessageExpression(AQLConstants.AQL + "'It will create an stakeholder specialized with ' + selectedObjects->first().name") + .optional(true) .build(); } diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc index 757d3f0f4..46222070d 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc @@ -33,6 +33,7 @@ image::release-notes-stakeholder-menu.png[Tool to create a new Stakeholder] image::release-notes-stakeholder-node.png[Default representation of Stakeholder connected to its corresponding `RequirementDefinition`] ** Improve the tool to create an `Actor` graphical node by making specialization selection optional. +** Improve the tool to create a `Stakeholder` graphical node by making specialization selection optional. == Technical details