From 8892f68c833698f83663688207410bc8248b22a5 Mon Sep 17 00:00:00 2001 From: Jerome Gout Date: Wed, 17 Jun 2026 17:18:37 +0200 Subject: [PATCH] [2113] Handle control node graphical nodes on AFV diagram background Bug: https://github.com/eclipse-syson/syson/issues/2113 Signed-off-by: Jerome Gout --- CHANGELOG.adoc | 1 + ...oneAndControlNodeMigrationParticipant.java | 87 ++++++++++ .../view/AFVAddTopControlNodeActionTests.java | 148 ++++++++++++++++++ .../view/GVAddExistingElementsTests.java | 14 +- .../GVSubNodeActionFlowCreationTests.java | 16 +- ...InsideActionUsageEmptyTestProjectData.java | 53 +++++++ .../ActionFlowView-InsideActionUsageEmpty.sql | 100 ++++++++++++ .../DiagramMutationExposeService.java | 21 +-- ...trolNodeActionNodeDescriptionProvider.java | 3 +- .../AbstractFakeNodeDescriptionProvider.java | 12 -- ...DecisionActionNodeDescriptionProvider.java | 2 +- .../DoneActionNodeDescriptionProvider.java | 3 +- .../ForkActionNodeDescriptionProvider.java | 2 +- .../JoinActionNodeDescriptionProvider.java | 2 +- .../MergeActionNodeDescriptionProvider.java | 2 +- .../StartActionNodeDescriptionProvider.java | 3 +- .../common/view/services/ViewToolService.java | 50 ++++++ ...ctFreeFormCompartmentNodeToolProvider.java | 27 +++- .../tools/DecisionActionNodeToolProvider.java | 15 ++ .../tools/DoneActionNodeToolProvider.java | 15 ++ .../tools/ForkActionNodeToolProvider.java | 15 ++ .../tools/JoinActionNodeToolProvider.java | 15 ++ .../tools/MergeActionNodeToolProvider.java | 15 ++ .../tools/StartActionNodeToolProvider.java | 15 ++ .../view/SDVDiagramDescriptionProvider.java | 32 ++++ .../pages/release-notes/2026.7.0.adoc | 58 +++---- 26 files changed, 650 insertions(+), 76 deletions(-) create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StartDoneAndControlNodeMigrationParticipant.java create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/actionflow/view/AFVAddTopControlNodeActionTests.java create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionFlowViewInsideActionUsageEmptyTestProjectData.java create mode 100644 backend/application/syson-application/src/test/resources/scripts/database-content/ActionFlowView-InsideActionUsageEmpty.sql diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 3d85a8a5f..38e7ccaf6 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -71,6 +71,7 @@ It leverages the selection dialog to either create an _occurrence timeslice/snap - https://github.com/eclipse-syson/syson/issues/2250[#2250] [diagrams] Leverage the selection dialog to improve the graphical node tools creating a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes. - https://github.com/eclipse-syson/syson/issues/2254[#2254] [diagrams] Add the support for _assume_ and _require_ graphical edges. - https://github.com/eclipse-syson/syson/issues/2260[#2260] [diagrams] Add the _New Assume Constraint_ or _New Require Constraint_ edge tools to create _assume_ and _require_ graphical edges. +- https://github.com/eclipse-syson/syson/issues/2113[#2113] [diagrams] Handle start/end/merge/decision... graphical nodes on Action Flow View diagram background == v2026.5.0 diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StartDoneAndControlNodeMigrationParticipant.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StartDoneAndControlNodeMigrationParticipant.java new file mode 100644 index 000000000..b87df862d --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StartDoneAndControlNodeMigrationParticipant.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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.migration; + +import org.eclipse.sirius.components.collaborative.representations.migration.IRepresentationMigrationParticipant; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.springframework.stereotype.Service; + +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.ObjectNode; + +/** + * Migration participant used to migrate start, done actions and control nodes elements previous to 2026.7.0. + * + * We need to update descriptionId, because it has been changed due to VSM description change. + * + * @author Jerome Gout + */ +@Service +public class StartDoneAndControlNodeMigrationParticipant implements IRepresentationMigrationParticipant { + + public static final String PARTICIPANT_VERSION = "2026.7.0-202606240000"; + public static final String DESCRIPTION_ID = "descriptionId"; + + public static final String OLD_START_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=bf75f645-8a6d-30d9-9514-4e9738e212a3"; + public static final String NEW_START_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=596f7e8d-ba35-38d6-937f-011e05b74063"; + + public static final String OLD_DONE_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=03789d5f-51d6-3f66-a496-146b1685930c"; + public static final String NEW_DONE_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=e7465fdc-fec8-38da-b7c0-26ba953b96de"; + + public static final String OLD_DECISION_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=d6e7c035-98ae-3fcf-a5a8-a8e502cd447e"; + public static final String NEW_DECISION_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=b2f1248d-2a25-37a2-8822-9e21ed709c72"; + + public static final String OLD_FORK_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=46dc7020-1788-3413-967d-e1e3053349ca"; + public static final String NEW_FORK_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=e940b643-cce5-3904-b785-fb3814493642"; + + public static final String OLD_JOIN_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=ae252080-4a68-3129-970b-2d049bb3e5a3"; + public static final String NEW_JOIN_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=963e573a-d0ff-3134-9e96-9ce55e3384ec"; + + public static final String OLD_MERGE_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=91f597c3-96b8-3a9e-8b30-99e5401952fa"; + public static final String NEW_MERGE_DESCRIPTION_ID = "siriusComponents://nodeDescription?sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=d47553da-9705-3eca-99ea-dc05ff972f55"; + + @Override + public String getVersion() { + return PARTICIPANT_VERSION; + } + + @Override + public String getKind() { + return "siriusComponents://representation?type=Diagram"; + } + + @Override + public void replaceJsonNode(IEditingContext editingContext, ObjectNode root, String currentAttribute, JsonNode currentValue) { + if (currentAttribute.equals(DESCRIPTION_ID) && currentValue.isString()) { + if (OLD_START_DESCRIPTION_ID.equals(currentValue.asString())) { + root.put(DESCRIPTION_ID, NEW_START_DESCRIPTION_ID); + } + if (OLD_DONE_DESCRIPTION_ID.equals(currentValue.asString())) { + root.put(DESCRIPTION_ID, NEW_DONE_DESCRIPTION_ID); + } + if (OLD_DECISION_DESCRIPTION_ID.equals(currentValue.asString())) { + root.put(DESCRIPTION_ID, NEW_DECISION_DESCRIPTION_ID); + } + if (OLD_FORK_DESCRIPTION_ID.equals(currentValue.asString())) { + root.put(DESCRIPTION_ID, NEW_FORK_DESCRIPTION_ID); + } + if (OLD_JOIN_DESCRIPTION_ID.equals(currentValue.asString())) { + root.put(DESCRIPTION_ID, NEW_JOIN_DESCRIPTION_ID); + } + if (OLD_MERGE_DESCRIPTION_ID.equals(currentValue.asString())) { + root.put(DESCRIPTION_ID, NEW_MERGE_DESCRIPTION_ID); + } + } + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/actionflow/view/AFVAddTopControlNodeActionTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/actionflow/view/AFVAddTopControlNodeActionTests.java new file mode 100644 index 000000000..dab07cbb0 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/actionflow/view/AFVAddTopControlNodeActionTests.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * 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.actionflow.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 java.util.stream.Stream; + +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.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.controllers.diagrams.checkers.CheckDiagramElementCount; +import org.eclipse.syson.application.controllers.diagrams.checkers.CheckNodeOnDiagram; +import org.eclipse.syson.application.controllers.diagrams.testers.ToolTester; +import org.eclipse.syson.application.data.ActionFlowViewInsideActionUsageEmptyTestProjectData; +import org.eclipse.syson.services.diagrams.DiagramComparator; +import org.eclipse.syson.services.diagrams.DiagramDescriptionIdProvider; +import org.eclipse.syson.services.diagrams.api.IGivenDiagramDescription; +import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription; +import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +/** + * Tests the creation of control nodes (Decision, Fork, Join and Merge) as well as Start and Done actions in a Action Flow view diagram background when it is contained by an ActionUsage. + * + * @author Jerome Gout + */ +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class AFVAddTopControlNodeActionTests extends AbstractIntegrationTests { + + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private IGivenDiagramDescription givenDiagramDescription; + + @Autowired + private IGivenDiagramSubscription givenDiagramSubscription; + + @Autowired + private IDiagramIdProvider diagramIdProvider; + + @Autowired + private ToolTester toolTester; + + @Autowired + private DiagramComparator diagramComparator; + + private final SDVDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); + + private static Stream topControlNodeParameters() { + return Stream.of( + Arguments.of("New Start Action", "StartAction"), + Arguments.of("New Done Action", "DoneAction"), + Arguments.of("New Decision", SysmlPackage.eINSTANCE.getDecisionNode().getName()), + Arguments.of("New Fork", SysmlPackage.eINSTANCE.getForkNode().getName()), + Arguments.of("New Join", SysmlPackage.eINSTANCE.getJoinNode().getName()), + Arguments.of("New Merge", SysmlPackage.eINSTANCE.getMergeNode().getName()) + ); + } + + private Flux givenSubscriptionToDiagram() { + var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), + ActionFlowViewInsideActionUsageEmptyTestProjectData.EDITING_CONTEXT_ID, + ActionFlowViewInsideActionUsageEmptyTestProjectData.GraphicalIds.AFV_IN_ACTION_DIAGRAM_ID); + return this.givenDiagramSubscription.subscribe(diagramEventInput); + } + + @BeforeEach + public void beforeEach() { + this.givenInitialServerState.initialize(); + } + + @DisplayName("GIVEN an ActionFlow View diagram, WHEN control node creation tool is requested on digram background, THEN a new control node is created") + @GivenSysONServer({ ActionFlowViewInsideActionUsageEmptyTestProjectData.SCRIPT_PATH }) + @ParameterizedTest + @MethodSource("topControlNodeParameters") + public void givenAnAFVDiagramWhenControlNodeCreationToolIsRequestedOnDiagramBackgroundThenANewControlNodeIsCreated(String toolLabel, String nodeName) { + var flux = this.givenSubscriptionToDiagram(); + + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(ActionFlowViewInsideActionUsageEmptyTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider); + + String creationToolId = diagramDescriptionIdProvider.getDiagramCreationToolId(toolLabel); + assertThat(creationToolId).as("The tool '" + toolLabel + "' should exist on diagram background").isNotNull(); + + var diagram = new AtomicReference(); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + Runnable createNodeRunnable = () -> { + this.toolTester.invokeTool(ActionFlowViewInsideActionUsageEmptyTestProjectData.EDITING_CONTEXT_ID, diagram, null, creationToolId); + }; + + Consumer diagramCheck = assertRefreshedDiagramThat(newDiagram -> { + var initialDiagram = diagram.get(); + new CheckDiagramElementCount(this.diagramComparator) + .hasNewEdgeCount(0) + .hasNewNodeCount(1) + .check(initialDiagram, newDiagram); + + new CheckNodeOnDiagram(diagramDescriptionIdProvider, this.diagramComparator) + .hasNodeDescriptionName(this.descriptionNameGenerator.getNodeName(nodeName)) + .check(initialDiagram, newDiagram); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(createNodeRunnable) + .consumeNextWith(diagramCheck) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddExistingElementsTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddExistingElementsTests.java index 276dfc96d..03b9bc0a8 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddExistingElementsTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddExistingElementsTests.java @@ -76,6 +76,8 @@ public class GVAddExistingElementsTests extends AbstractIntegrationTests { private static final String PART2 = "part2"; + private static final String SUCCESSION = "SuccessionAsUsage"; + private static final String ATTRIBUTE_DEFINITION = "AttributeDefinition1"; private static final String NODE_SHOULD_BE_ON_DIAGRAM_MESSAGE = "Node {0} should exist on the diagram"; @@ -166,15 +168,17 @@ public void addExistingElementsRecursiveOnDiagram() { Runnable addExistingElementTool = () -> this.nodeCreationTester.invokeTool(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram, addExistingElementToolId); Consumer updatedDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { - assertThat(newDiagram.getNodes()).as("7 root nodes should be visible on the diagram").hasSize(7); + assertThat(newDiagram.getNodes()).as("8 root nodes should be visible on the diagram").hasSize(8); assertThat(newDiagram.getEdges().stream().filter(e -> ViewModifier.Normal.equals(e.getState())).toList()) - .as("3 edges should be visible on the diagram").hasSize(3) + .as("4 edges should be visible on the diagram").hasSize(4) .as("The diagram should contain a composite edge between part2 and part1") .anyMatch(edge -> edge.getTargetObjectLabel().equals(PART1)) .as("The diagram should contain a composite edge between action1 and action2") .anyMatch(edge -> edge.getTargetObjectLabel().equals(ACTION1)) .as("The diagram should contain a composite edge between action2 and action3") - .anyMatch(edge -> edge.getTargetObjectLabel().equals(ACTION2)); + .anyMatch(edge -> edge.getTargetObjectLabel().equals(ACTION2)) + .as("The diagram should contain a SuccessionAsUsage edge between start and action2") + .anyMatch(edge -> edge.getTargetObjectLabel().equals(SUCCESSION)); assertThat(newDiagram.getNodes()) .as(MessageFormat.format(NODE_SHOULD_BE_ON_DIAGRAM_MESSAGE, PACKAGE1)) .anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), PACKAGE1)) @@ -185,7 +189,9 @@ public void addExistingElementsRecursiveOnDiagram() { .as(MessageFormat.format(NODE_SHOULD_BE_ON_DIAGRAM_MESSAGE, PART2)) .anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), PART2)) .as(MessageFormat.format(NODE_SHOULD_BE_ON_DIAGRAM_MESSAGE, "RequirementUsage")) - .anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), "RequirementUsage")); + .anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), "RequirementUsage")) + .as(MessageFormat.format(NODE_SHOULD_BE_ON_DIAGRAM_MESSAGE, "start")) + .anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), "start")); this.checkPackageNode(newDiagram); 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 e212e6ba8..e3f7e71c8 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 @@ -168,10 +168,10 @@ private static Stream actionDefinitionBorderAndChildNodeParameters() private static Stream actionUsageFreeFormNodeParameters() { return Stream.of( - Arguments.of(SysmlPackage.eINSTANCE.getDecisionNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 0, 0, 0), - Arguments.of(SysmlPackage.eINSTANCE.getForkNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 0, 0, 0), - Arguments.of(SysmlPackage.eINSTANCE.getJoinNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 0, 0, 0), - Arguments.of(SysmlPackage.eINSTANCE.getMergeNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 0, 0, 0), + Arguments.of(SysmlPackage.eINSTANCE.getDecisionNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 1, 0, 1), + Arguments.of(SysmlPackage.eINSTANCE.getForkNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 1, 0, 1), + Arguments.of(SysmlPackage.eINSTANCE.getJoinNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 1, 0, 1), + Arguments.of(SysmlPackage.eINSTANCE.getMergeNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 1, 0, 1), Arguments.of(SysmlPackage.eINSTANCE.getAcceptActionUsage(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), 5, 2, 1)) .map(TestNameGenerator::namedArguments); } @@ -191,10 +191,10 @@ private static Stream actionDefinitionSiblingNodeParameters() { private static Stream actionDefinitionFreeFormNodeParameters() { return Stream.of( - Arguments.of(SysmlPackage.eINSTANCE.getDecisionNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 0, 0, 0), - Arguments.of(SysmlPackage.eINSTANCE.getForkNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 0, 0, 0), - Arguments.of(SysmlPackage.eINSTANCE.getJoinNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 0, 0, 0), - Arguments.of(SysmlPackage.eINSTANCE.getMergeNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 0, 0, 0), + Arguments.of(SysmlPackage.eINSTANCE.getDecisionNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 1, 0, 1), + Arguments.of(SysmlPackage.eINSTANCE.getForkNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 1, 0, 1), + Arguments.of(SysmlPackage.eINSTANCE.getJoinNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 1, 0, 1), + Arguments.of(SysmlPackage.eINSTANCE.getMergeNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 1, 0, 1), Arguments.of(SysmlPackage.eINSTANCE.getAcceptActionUsage(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), 5, 2, 1)) .map(TestNameGenerator::namedArguments); } diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionFlowViewInsideActionUsageEmptyTestProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionFlowViewInsideActionUsageEmptyTestProjectData.java new file mode 100644 index 000000000..5d6e6a691 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionFlowViewInsideActionUsageEmptyTestProjectData.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.data; + +/** + * Project data for the "AFVAddTopControlNodeActionTests" project. + * + * @author Jerome Gout + */ +public class ActionFlowViewInsideActionUsageEmptyTestProjectData { + + public static final String SCRIPT_PATH = "/scripts/database-content/ActionFlowView-InsideActionUsageEmpty.sql"; + + public static final String EDITING_CONTEXT_ID = "6bb410a0-56ee-4fb4-9419-d8426354799a"; + + /** + * Ids of graphical elements. + */ + public static final class GraphicalIds { + + public static final String AFV_IN_ACTION_DIAGRAM_ID = "b4b25646-3579-44f1-8c81-9a2cfa031293"; + + } + + /** + * Ids for the semantic elements. + */ + public static final class SemanticIds { + + public static final String NAMESPACE_NAM_ID = "cdcecc76-ff3a-40e5-9134-6de8c6d20545"; + + public static final String PACKAGE_PAC_ID = "bc22bde3-cf75-4173-b079-d71a4da3a88e"; + + public static final String PACKAGE_ACTION_AFV_IN_ACTION_FEATURE_TYPING_FEA_ID = "c4c38bce-441e-4944-b7f0-b87e0ebf691b"; + + public static final String ACTION_ACT_ID = "3b915bf4-64ab-407e-9bb7-0ca73a9a5c85"; + + public static final String AFV_IN_ACTION_VIE_ID = "97781b18-033c-4024-94bb-3515c192033c"; + + } + +} \ No newline at end of file diff --git a/backend/application/syson-application/src/test/resources/scripts/database-content/ActionFlowView-InsideActionUsageEmpty.sql b/backend/application/syson-application/src/test/resources/scripts/database-content/ActionFlowView-InsideActionUsageEmpty.sql new file mode 100644 index 000000000..89cbfc7e6 --- /dev/null +++ b/backend/application/syson-application/src/test/resources/scripts/database-content/ActionFlowView-InsideActionUsageEmpty.sql @@ -0,0 +1,100 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 12.19 (Debian 12.19-1.pgdg120+1) +-- Dumped by pg_dump version 16.3 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Data for Name: semantic_data; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.semantic_data (id, created_on, last_modified_on) VALUES ('6bb410a0-56ee-4fb4-9419-d8426354799a', '2026-06-19 08:55:36.44591+00', '2026-06-19 15:23:48.537966+00'); + + +-- +-- Data for Name: document; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.document (id, semantic_data_id, name, content, created_on, last_modified_on, is_read_only) VALUES ('3415f482-afb7-4adb-9865-7b001fa4739b', '6bb410a0-56ee-4fb4-9419-d8426354799a', 'SysMLv2.sysml', '{"json":{"version":"1.0","encoding":"utf-8"},"ns":{"sysml":"http://www.eclipse.org/syson/sysml"},"migration":{"lastMigrationPerformed":"none","migrationVersion":"2026.5.0-202603171430"},"content":[{"id":"cdcecc76-ff3a-40e5-9134-6de8c6d20545","eClass":"sysml:Namespace","data":{"elementId":"5351129d-506f-4a17-9b86-d8c15e4ad044","ownedRelationship":[{"id":"de8eb985-ed12-4b68-96cb-a2e6ae185dae","eClass":"sysml:OwningMembership","data":{"elementId":"dce8e091-4710-4612-bf0f-b0327f1f7644","ownedRelatedElement":[{"id":"bc22bde3-cf75-4173-b079-d71a4da3a88e","eClass":"sysml:Package","data":{"declaredName":"Package","elementId":"b66254bd-d4ec-4d94-96b6-7c68cdbf81b7","ownedRelationship":[{"id":"46a29e8c-a405-4365-8ec9-bbfc3dda4fa3","eClass":"sysml:OwningMembership","data":{"elementId":"88cd1dc4-6755-4d65-a241-c7623c8d8b9d","ownedRelatedElement":[{"id":"3b915bf4-64ab-407e-9bb7-0ca73a9a5c85","eClass":"sysml:ActionUsage","data":{"declaredName":"action","elementId":"1f5c1ee3-bc3c-40e9-979e-f8beaa039430","ownedRelationship":[{"id":"fbf86c12-e2ab-4336-b3a9-2403984b9717","eClass":"sysml:FeatureMembership","data":{"elementId":"710c645e-3e58-4715-bfbe-ec5f83e0d0b7","ownedRelatedElement":[{"id":"97781b18-033c-4024-94bb-3515c192033c","eClass":"sysml:ViewUsage","data":{"declaredName":"afv in action","elementId":"39f838e5-2336-4f9f-9414-49398a0bd0e1","ownedRelationship":[{"id":"c4c38bce-441e-4944-b7f0-b87e0ebf691b","eClass":"sysml:FeatureTyping","data":{"elementId":"5f383d81-5e1a-4983-812b-7e6bba03ceb0","type":"sysml:ViewDefinition sysmllibrary:///faf517ae-dbcd-30a4-b3b9-3d9cb3bbf5c1#e1e8729f-a571-520c-b3f8-1b8dcdf5014d","typedFeature":"97781b18-033c-4024-94bb-3515c192033c"}}]}}]}}],"isComposite":true}}]}}]}}]}}]}}]}', '2026-06-19 15:23:48.537966+00', '2026-06-19 15:23:48.537966+00', false); + + +-- +-- Data for Name: image; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + + + +-- +-- Data for Name: library; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + + + +-- +-- Data for Name: project; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.project (id, name, created_on, last_modified_on) VALUES ('f094eea6-7c16-4047-92d7-e743905e2163', 'SysMLv2', '2026-06-19 08:55:36.049948+00', '2026-06-19 08:55:36.049948+00'); + + +-- +-- Data for Name: nature; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + + + +-- +-- Data for Name: project_image; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + + + +-- +-- Data for Name: project_semantic_data; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.project_semantic_data (id, project_id, semantic_data_id, name, created_on, last_modified_on) VALUES ('443a821d-a7e1-4d7d-98d1-490c8f0f218b', 'f094eea6-7c16-4047-92d7-e743905e2163', '6bb410a0-56ee-4fb4-9419-d8426354799a', 'main', '2026-06-19 08:55:36.622816+00', '2026-06-19 08:55:36.622816+00'); + + +-- +-- Data for Name: representation_metadata; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.representation_metadata (id, target_object_id, description_id, label, kind, created_on, last_modified_on, documentation, semantic_data_id, representation_metadata_id) VALUES ('6bb410a0-56ee-4fb4-9419-d8426354799a#b4b25646-3579-44f1-8c81-9a2cfa031293', '97781b18-033c-4024-94bb-3515c192033c', 'siriusComponents://representationDescription?kind=diagramDescription&sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=db495705-e917-319b-af55-a32ad63f4089', 'afv in action', 'siriusComponents://representation?type=Diagram', '2026-06-19 09:00:32.65801+00', '2026-06-19 09:00:32.65801+00', '', '6bb410a0-56ee-4fb4-9419-d8426354799a', 'b4b25646-3579-44f1-8c81-9a2cfa031293'); + + +-- +-- Data for Name: representation_content; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.representation_content (id, content, last_migration_performed, migration_version, created_on, last_modified_on, semantic_data_id, representation_metadata_id) VALUES ('6bb410a0-56ee-4fb4-9419-d8426354799a#b4b25646-3579-44f1-8c81-9a2cfa031293', '{"id":"b4b25646-3579-44f1-8c81-9a2cfa031293","kind":"siriusComponents://representation?type=Diagram","targetObjectId":"97781b18-033c-4024-94bb-3515c192033c","descriptionId":"siriusComponents://representationDescription?kind=diagramDescription&sourceKind=view&sourceId=8dcd14b0-6259-3193-ad2c-743f394c68e4&sourceElementId=db495705-e917-319b-af55-a32ad63f4089","nodes":[],"edges":[],"layoutData":{"nodeLayoutData":{},"edgeLayoutData":{},"labelLayoutData":{},"autoLaidOut":false},"style":{"background":"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAwYAAAFbCAYAAABvSZnUAAAACXBIWXMAAArDAAAKwwE0KSSrAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJzs3ed3VXee7/n3SdJRzglFhAJCIATCmGSMsTE2LpddznZXddXtrjt95/4Ts+bRXTNrzax5MLPWvbe7b9etrtTldsQJMCaDCMo555zTkY5OmgdCx8jKWOII/HmtxSrX2em7N+Jof/YvbIPH4+FBBoPBAHD27KUo25Tzf3fYHS8GBPgnmEzGIERERERE5LHicrmnpm0z3X5Wv3Oh4UH/20svHRkB8PwgCBge/P/3Q4Hxk08uvT9js/9/B57aFbp9+zZMZtOjrV5ERERERDaMy+mipaWbons1Y1Z/639+/c1n/w1wPxgOvMHAGwo+uvgrs9n8T6dfPmKyWMw+Kl1ERERERDaaw+Hk3Nc3XQ6X6+/eeOPkH3kgHBg8Ho83FHz22fW4GZut/o23TgaZ1UogIiIiIvLEcTicfPLRpUmDyZL5zjvPDXA/HBjvhwIDYJ6emvovTx/arVAgIiIiIvKEsljMHHw6N9jtcvwXwMxc5yGD8f5yE2BxudwvpKTG+65KERERERHZdCmp8bicrhcAC3NZACNzrQXGv/61ajYkJDDh/qREIiIiIiLyhDIajQQHB277b//tv9m5nwnmg4E5zG8wzt/fT6lAREREROQnwGr1M6bF7YznfnciI3MJwex0OY1qLBARERER+WkwGMDpchqZCwZGbzDwbVkiIiIiIuIj3mBgYC4ciIiIiIjIT8+CMQYKBiIiIiIiP03eYCAiIiIiIj9xCgYiIiIiIqJgICIiIiIiCgYiIiIiIoKCgYiIiIiIoGAgIiIiIiIoGIiIiIiICHrjsYjII+Fyumhr66WjvZfJSRtul9vXJW1pJrOJ4OBAUlLjSUmJx2jScywRkc2mYCAissmqq5q5drmYiQmbr0t57NwprCQ0LJhnT+wnOyfN1+WIiDzRFAxERDaJx+Phuwt3KSmu9XUpj7XxsUnOfnaV7u5BTpwswGAw+LokEZEnktpmRUQ2SeHNCoWCDVR0t5o7hVW+LkNE5ImlYCAisgmGh8e5daPM12U8cW5eL2VkZNzXZYiIPJEUDERENsG9O1W43R5fl/HEcbncFN2t8XUZIiJPJAUDEZFN0NzY5esSnlhNDR2+LkFE5ImkYCAissHsdgeTk5qBaLNMTNiYnXX4ugwRkSeOZiUSEdlgM9P2FZdbA/w5dfrQI6rm8XThXOGK13HaZsfPz/IIKxIRefIpGIiIbDCPZ+WxBRazieydqY+omsfT5Yt3mfF1ESIiPzHqSiQiIiIiIgoGIiIiIiKiYCAiIiIiIigYiIiIiIgICgYiIiIiIoKCgYiIiIiIoOlKRUR+cjweD198dnXFdX722nEMBsMjqkhERLYCBQMRkZ8YjwfqattWXOeVn8NWzgUKLSIiG09diURERERERMFAREREREQUDEREREREBAUDERF5LHl8XYCIyBNHwUBERERERBQMREREREREwUBERERERFAwEBGRx5DeYyAisvEUDERERERERMFARERERETA7OsCRETkx3G5XFy8cGcdW6zeDefb87dZz5Sgz586iMlkWkcNIiKy1SgYiIg85lwuN+WlDRu6z/LS+nWtf+LkAQUDEZHHnLoSiYiIiIiIgoGIiIiIiCgYiIiIiIgICgYiIiIiIoKCgYiIPI70gjMRkQ2nYCAiIiIiIpquVETkcWcymXjxpcNrXt/j8XDhXOGK65w6fQjDOp7KP/KpSj1rf8eCiIisjYKBiMhjzmQykpefueb13e7Vg8GevZkYjVu4u466EomIbDh1JRIREREREQUDERERERFRMBARERERERQMREREREQEBQMREREREUHBQEREHkOalEhEZOMpGIiIiIiIiIKBiIiIiIgoGIiIiIiICAoGIiIiIiICmH1dgIiIPFoGAxw/UbDqOiIi8tOiYCAi8hNjMBg4eCjX12WIiMgWo65EIiIiIiKiYCAiIiIiIgoGIiLyGDJoEISIyIZTMBAREREREQUDERERERFRMBARERERETRdqYjIhjMaV+7/brc7uHq56BFV83iy2x0rLjdqjIGIyIZTMBAR2WBBwQErLp+ddXCnsOoRVfPkMZqMWAP8fV2GiMgTR12JREQ2mMlkIjIqzNdlPLFiYyM1K5GIyCZQMBAR2QTZO1N9XcITS9dWRGRzKBiIiGyCggM5WK1+vi7jiRMcHEj+/mxflyEi8kRSMBAR2QTWAH/OvPrMqgORZe2MRgOnzxzGYtHwOBGRzaBgICKySdJ3JPLyK8cwmUy+LuWxZ7GYefX1Z9menujr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) no-repeat center center / contain"}}', 'EdgeHandleLayoutDataRelativePositionBendingPointsMigrationParticipant', '2026.5.0-202604141050', '2026-06-19 09:00:32.68568+00', '2026-06-19 15:23:48.593732+00', '6bb410a0-56ee-4fb4-9419-d8426354799a', 'b4b25646-3579-44f1-8c81-9a2cfa031293'); + + +-- +-- Data for Name: semantic_data_dependency; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + + + +-- +-- Data for Name: semantic_data_domain; Type: TABLE DATA; Schema: public; Owner: dbuser +-- + +INSERT INTO public.semantic_data_domain (semantic_data_id, uri) VALUES ('6bb410a0-56ee-4fb4-9419-d8426354799a', 'http://www.eclipse.org/syson/sysml'); + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationExposeService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationExposeService.java index e5d5cb54e..4698aee66 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationExposeService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationExposeService.java @@ -40,6 +40,7 @@ import org.eclipse.syson.sysml.ActionUsage; import org.eclipse.syson.sysml.AttributeUsage; import org.eclipse.syson.sysml.Comment; +import org.eclipse.syson.sysml.ControlNode; import org.eclipse.syson.sysml.Definition; import org.eclipse.syson.sysml.Documentation; import org.eclipse.syson.sysml.Element; @@ -413,18 +414,20 @@ private void hideNodeIfVisibleCompartmentCouldHostTheFutureNode(Element element, } // 1- get visible compartments of selected node // 2- get visible compartments that could host the future node - // 3- if a visible compartment could host the future node, then hide the tree node + // 3- if a visible compartment could host the future node, or it is a control node, then hide the tree node List visibleCompartments = referenceParent.getChildNodes().stream().filter(n -> n.getState().equals(ViewModifier.Normal)).toList(); - boolean visibleCompartmentsThatCouldHostTheFutureNode = false; - for (Node visibleCompartment : visibleCompartments) { - Optional childNodeDescriptionIdForRendering = this.diagramMutationElementService.getChildNodeDescriptionIdForRendering(element, editingContext, diagramContext, visibleCompartment, - convertedNodes); - if (childNodeDescriptionIdForRendering.isPresent()) { - visibleCompartmentsThatCouldHostTheFutureNode = true; - break; + boolean shouldHide = element instanceof ControlNode; + if (!shouldHide) { + for (Node visibleCompartment : visibleCompartments) { + Optional childNodeDescriptionIdForRendering = this.diagramMutationElementService.getChildNodeDescriptionIdForRendering(element, editingContext, diagramContext, visibleCompartment, + convertedNodes); + if (childNodeDescriptionIdForRendering.isPresent()) { + shouldHide = true; + break; + } } } - if (visibleCompartmentsThatCouldHostTheFutureNode) { + if (shouldHide) { var parentId = this.diagramQueryElementService.getGraphicalParentId(diagramContext, referenceParent); var descriptionId = this.diagramQueryElementService.getNodeDescriptionId(element, diagramContext.diagram(), editingContext); if (parentId != null && descriptionId.isPresent()) { diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java index eb8cafb55..3393bc4cc 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java @@ -129,9 +129,8 @@ private OutsideLabelStyle createOutsideLabelStyle() { @Override public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { - // this nodeDescription has not been added to the diagramDescription children but to the fakeNodeDescription - // children instead cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(this.getNodeDescriptionName())).ifPresent(nodeDescription -> { + diagramDescription.getNodeDescriptions().add(nodeDescription); nodeDescription.setPalette(this.createNodePalette(cache)); }); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractFakeNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractFakeNodeDescriptionProvider.java index 5e378b53f..fc2dd2ad1 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractFakeNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractFakeNodeDescriptionProvider.java @@ -112,18 +112,6 @@ protected NodeStyleDescription createFakeNodeStyle() { } protected void addReusableCustomNodes(IViewDiagramElementFinder cache, List childrenNodes) { - cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(DecisionActionNodeDescriptionProvider.DECISION_ACTION_NAME)) - .ifPresent(childrenNodes::add); - cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(ForkActionNodeDescriptionProvider.FORK_ACTION_NAME)) - .ifPresent(childrenNodes::add); - cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(JoinActionNodeDescriptionProvider.JOIN_ACTION_NAME)) - .ifPresent(childrenNodes::add); - cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(MergeActionNodeDescriptionProvider.MERGE_ACTION_NAME)) - .ifPresent(childrenNodes::add); - cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME)) - .ifPresent(childrenNodes::add); - cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(DoneActionNodeDescriptionProvider.DONE_ACTION_NAME)) - .ifPresent(childrenNodes::add); cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME)) .ifPresent(childrenNodes::add); cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME)) diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DecisionActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DecisionActionNodeDescriptionProvider.java index f33c99465..2eafcf81e 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DecisionActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DecisionActionNodeDescriptionProvider.java @@ -26,7 +26,7 @@ */ public class DecisionActionNodeDescriptionProvider extends AbstractControlNodeActionNodeDescriptionProvider { - public static final String DECISION_ACTION_NAME = "DecisionAction"; + public static final String DECISION_ACTION_NAME = SysmlPackage.eINSTANCE.getDecisionNode().getName(); public DecisionActionNodeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { super(colorProvider, descriptionNameGenerator); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DoneActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DoneActionNodeDescriptionProvider.java index 90d45bba2..0f85e3923 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DoneActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DoneActionNodeDescriptionProvider.java @@ -61,9 +61,8 @@ public NodeDescription create() { @Override public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { - // this nodeDescription has not been added to the diagramDescription children but to the fakeNodeDescription - // children instead cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(DONE_ACTION_NAME)).ifPresent(nodeDescription -> { + diagramDescription.getNodeDescriptions().add(nodeDescription); nodeDescription.setPalette(this.createNodePalette()); }); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ForkActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ForkActionNodeDescriptionProvider.java index d2335b9e8..96c93efab 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ForkActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ForkActionNodeDescriptionProvider.java @@ -26,7 +26,7 @@ */ public class ForkActionNodeDescriptionProvider extends AbstractControlNodeActionNodeDescriptionProvider { - public static final String FORK_ACTION_NAME = "ForkAction"; + public static final String FORK_ACTION_NAME = SysmlPackage.eINSTANCE.getForkNode().getName(); public ForkActionNodeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { super(colorProvider, descriptionNameGenerator); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/JoinActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/JoinActionNodeDescriptionProvider.java index b51a0f841..01774f0e3 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/JoinActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/JoinActionNodeDescriptionProvider.java @@ -26,7 +26,7 @@ */ public class JoinActionNodeDescriptionProvider extends AbstractControlNodeActionNodeDescriptionProvider { - public static final String JOIN_ACTION_NAME = "JoinAction"; + public static final String JOIN_ACTION_NAME = SysmlPackage.eINSTANCE.getJoinNode().getName(); public JoinActionNodeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { super(colorProvider, descriptionNameGenerator); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/MergeActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/MergeActionNodeDescriptionProvider.java index f087cbcaf..709591dd5 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/MergeActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/MergeActionNodeDescriptionProvider.java @@ -26,7 +26,7 @@ */ public class MergeActionNodeDescriptionProvider extends AbstractControlNodeActionNodeDescriptionProvider { - public static final String MERGE_ACTION_NAME = "MergeAction"; + public static final String MERGE_ACTION_NAME = SysmlPackage.eINSTANCE.getMergeNode().getName(); public MergeActionNodeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { super(colorProvider, descriptionNameGenerator); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java index 2f556ccf8..9d2a9f5b4 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java @@ -68,9 +68,8 @@ public NodeDescription create() { @Override public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { - // this nodeDescription has not been added to the diagramDescription children but to the fakeNodeDescription - // children instead cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(START_ACTION_NAME)).ifPresent(nodeDescription -> { + diagramDescription.getNodeDescriptions().add(nodeDescription); nodeDescription.setPalette(this.createNodePalette(cache)); }); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java index 06d27d6cf..13736845f 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java @@ -30,6 +30,7 @@ import org.eclipse.sirius.components.core.api.ILabelService; import org.eclipse.sirius.components.core.api.IObjectSearchService; import org.eclipse.sirius.components.core.api.labels.StyledString; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.emf.ResourceMetadataAdapter; import org.eclipse.sirius.components.representations.Message; import org.eclipse.sirius.components.representations.MessageLevel; @@ -70,8 +71,11 @@ import org.eclipse.syson.sysml.Usage; import org.eclipse.syson.sysml.UseCaseDefinition; import org.eclipse.syson.sysml.UseCaseUsage; +import org.eclipse.syson.sysml.ViewDefinition; +import org.eclipse.syson.sysml.ViewUsage; import org.eclipse.syson.tree.explorer.services.api.ISysONExplorerFragment; import org.eclipse.syson.tree.explorer.services.api.ISysONExplorerService; +import org.eclipse.syson.util.StandardDiagramsConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,6 +102,52 @@ public ViewToolService(IIdentityService identityService, IObjectSearchService ob this.labelService = Objects.requireNonNull(labelService); } + /** + * Tool precondition for control node actions (Start/Done/Decision/Fork/Join/Merge) invoked on diagram background. + * + * @param element + * the given {@link Element}. + * @return true if the tool should be available, false otherwise. + */ + public boolean isControlNodeActionCreationToolInsideActionOnAFV(Element element, IEditingContext editingContext, DiagramContext diagramContext) { + if (this.isAFVDiagram(editingContext, diagramContext)) { + var owner = this.utilService.getViewUsageOwner(element); + return owner instanceof ActionUsage || owner instanceof ActionDefinition; + } + return false; + } + + /** + * Tool precondition for control node actions (Start/Done/Decision/Fork/Join/Merge) invoked on a selected node. + * + * @param editingContext + * the (non-{@code null}) {@link IEditingContext}. + * @param selectedNode + * the selected node. It corresponds to a variable accessible from the variable + * manager. + * @return true if the tool should be available, false otherwise. + */ + public boolean isControlNodeActionCreationToolInAction(IEditingContext editingContext, Node selectedNode) { + return this.objectSearchService.getObject(editingContext, selectedNode.getTargetObjectId()) + .map(object -> { + return object instanceof ActionUsage || object instanceof ActionDefinition; + }).orElse(false); + } + + private boolean isAFVDiagram(IEditingContext editingContext, DiagramContext diagramContext) { + var objectId = diagramContext.diagram().getTargetObjectId(); + return this.objectSearchService.getObject(editingContext, objectId) + .map(object -> { + if (object instanceof ViewUsage viewUsage) { + if (!viewUsage.getType().isEmpty()) { + Type type = viewUsage.getType().getFirst(); + return type instanceof ViewDefinition && StandardDiagramsConstants.AFV_QN.equals(type.getQualifiedName()); + } + } + return false; + }).orElse(false); + } + /** * Check if a tool that will create an instance of the given type should be available for a diagram associated to * the given {@link Element}. diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractFreeFormCompartmentNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractFreeFormCompartmentNodeToolProvider.java index 663a72ca1..0cd6a934e 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractFreeFormCompartmentNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/AbstractFreeFormCompartmentNodeToolProvider.java @@ -25,6 +25,8 @@ import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.emf.diagram.ViewDiagramDescriptionConverter; import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.services.UtilService; +import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -43,9 +45,12 @@ public abstract class AbstractFreeFormCompartmentNodeToolProvider implements INo protected final String compartmentName; + protected final EClass ownerEClass; + public AbstractFreeFormCompartmentNodeToolProvider(EClass ownerEClass, String compartmentName, IDescriptionNameGenerator descriptionNameGenerator) { this.compartmentName = Objects.requireNonNull(compartmentName); this.descriptionNameGenerator = Objects.requireNonNull(descriptionNameGenerator); + this.ownerEClass = ownerEClass; } /** @@ -115,18 +120,30 @@ public NodeTool create(IViewDiagramElementFinder cache) { ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); var revealOperation = this.viewBuilderHelper.newChangeContext() - .expression(ServiceMethod.of4(DiagramMutationAQLService::revealCompartment) - .aql(Node.SELECTED_NODE, "self", DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); + .expression(ServiceMethod.of4(DiagramMutationAQLService::revealCompartment).aql(Node.SELECTED_NODE, AQLConstants.SELF, DiagramContext.DIAGRAM_CONTEXT, IEditingContext.EDITING_CONTEXT, + ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE)); + + var conditionalRevealOperation = this.viewBuilderHelper.newIf() + .conditionExpression(AQLConstants.AQL + Node.SELECTED_NODE + " <> null") + .children(revealOperation.build()); - var creationServiceCall = this.viewBuilderHelper.newChangeContext() + var changeContextCreateInstance = this.viewBuilderHelper.newChangeContext() .expression(this.getCreationServiceCallExpression()) - .children(addToExposedElements.build(), revealOperation.build()); + .children(addToExposedElements.build(), conditionalRevealOperation.build()); + + var changeContextViewUsageOwner = this.viewBuilderHelper.newChangeContext() + .expression(ServiceMethod.of0(UtilService::getViewUsageOwner).aqlSelf()) + .children(changeContextCreateInstance.build()); + + var changeContextRoot = this.viewBuilderHelper.newChangeContext() + .expression(AQLConstants.AQL_SELF) + .children(changeContextViewUsageOwner.build()); String preconditionExpression = this.getPreconditionServiceCallExpression(); return builder.name(this.getLabel()) .iconURLsExpression(this.getIconPath()) - .body(creationServiceCall.build()) + .body(changeContextRoot.build()) .preconditionExpression(preconditionExpression) .build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DecisionActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DecisionActionNodeToolProvider.java index 45690c332..0c9e63515 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DecisionActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DecisionActionNodeToolProvider.java @@ -13,9 +13,13 @@ package org.eclipse.syson.diagram.common.view.tools; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DecisionActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -49,4 +53,15 @@ protected String getLabel() { protected String getIconPath() { return "/icons/full/obj16/DecisionNode.svg"; } + + @Override + protected String getPreconditionServiceCallExpression() { + if (this.ownerEClass == null) { + // this tool will be invoked on the diagram background + return ServiceMethod.of2(ViewToolService::isControlNodeActionCreationToolInsideActionOnAFV).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT); + } else { + // this tool will be invoked from a selected node + return ServiceMethod.of1(ViewToolService::isControlNodeActionCreationToolInAction).aql(IEditingContext.EDITING_CONTEXT, Node.SELECTED_NODE); + } + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneActionNodeToolProvider.java index 6bd9f8396..935db9baa 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneActionNodeToolProvider.java @@ -13,9 +13,13 @@ package org.eclipse.syson.diagram.common.view.tools; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -49,4 +53,15 @@ protected String getLabel() { protected String getIconPath() { return "/icons/done_action.svg"; } + + @Override + protected String getPreconditionServiceCallExpression() { + if (this.ownerEClass == null) { + // this tool will be invoked on the diagram background + return ServiceMethod.of2(ViewToolService::isControlNodeActionCreationToolInsideActionOnAFV).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT); + } else { + // this tool will be invoked from a selected node + return ServiceMethod.of1(ViewToolService::isControlNodeActionCreationToolInAction).aql(IEditingContext.EDITING_CONTEXT, Node.SELECTED_NODE); + } + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ForkActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ForkActionNodeToolProvider.java index dd369a2de..be8f08fa0 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ForkActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ForkActionNodeToolProvider.java @@ -13,9 +13,13 @@ package org.eclipse.syson.diagram.common.view.tools; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ForkActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -49,4 +53,15 @@ protected String getLabel() { protected String getIconPath() { return "/icons/full/obj16/ForkNode.svg"; } + + @Override + protected String getPreconditionServiceCallExpression() { + if (this.ownerEClass == null) { + // this tool will be invoked on the diagram background + return ServiceMethod.of2(ViewToolService::isControlNodeActionCreationToolInsideActionOnAFV).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT); + } else { + // this tool will be invoked from a selected node + return ServiceMethod.of1(ViewToolService::isControlNodeActionCreationToolInAction).aql(IEditingContext.EDITING_CONTEXT, Node.SELECTED_NODE); + } + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/JoinActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/JoinActionNodeToolProvider.java index b68b8fb7f..12211ecef 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/JoinActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/JoinActionNodeToolProvider.java @@ -13,9 +13,13 @@ package org.eclipse.syson.diagram.common.view.tools; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.JoinActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -49,4 +53,15 @@ protected String getLabel() { protected String getIconPath() { return "/icons/full/obj16/JoinNode.svg"; } + + @Override + protected String getPreconditionServiceCallExpression() { + if (this.ownerEClass == null) { + // this tool will be invoked on the diagram background + return ServiceMethod.of2(ViewToolService::isControlNodeActionCreationToolInsideActionOnAFV).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT); + } else { + // this tool will be invoked from a selected node + return ServiceMethod.of1(ViewToolService::isControlNodeActionCreationToolInAction).aql(IEditingContext.EDITING_CONTEXT, Node.SELECTED_NODE); + } + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/MergeActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/MergeActionNodeToolProvider.java index 682bbf7c9..ac238515d 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/MergeActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/MergeActionNodeToolProvider.java @@ -13,9 +13,13 @@ package org.eclipse.syson.diagram.common.view.tools; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.MergeActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -49,4 +53,15 @@ protected String getLabel() { protected String getIconPath() { return "/icons/full/obj16/MergeNode.svg"; } + + @Override + protected String getPreconditionServiceCallExpression() { + if (this.ownerEClass == null) { + // this tool will be invoked on the diagram background + return ServiceMethod.of2(ViewToolService::isControlNodeActionCreationToolInsideActionOnAFV).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT); + } else { + // this tool will be invoked from a selected node + return ServiceMethod.of1(ViewToolService::isControlNodeActionCreationToolInAction).aql(IEditingContext.EDITING_CONTEXT, Node.SELECTED_NODE); + } + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java index 52af78d04..7ebc34a85 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java @@ -13,9 +13,13 @@ package org.eclipse.syson.diagram.common.view.tools; import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.diagram.common.view.services.ViewToolService; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.ServiceMethod; @@ -49,4 +53,15 @@ protected String getLabel() { protected String getIconPath() { return "/icons/start_action.svg"; } + + @Override + protected String getPreconditionServiceCallExpression() { + if (this.ownerEClass == null) { + // this tool will be invoked on the diagram background + return ServiceMethod.of2(ViewToolService::isControlNodeActionCreationToolInsideActionOnAFV).aqlSelf(IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT); + } else { + // this tool will be invoked from a selected node + return ServiceMethod.of1(ViewToolService::isControlNodeActionCreationToolInAction).aql(IEditingContext.EDITING_CONTEXT, Node.SELECTED_NODE); + } + } } 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 ce2d2bd5d..c3a2f8ec6 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 @@ -73,9 +73,15 @@ import org.eclipse.syson.diagram.common.view.services.ViewCreateService; import org.eclipse.syson.diagram.common.view.services.description.ToolConstants; import org.eclipse.syson.diagram.common.view.services.description.ToolDescriptionService; +import org.eclipse.syson.diagram.common.view.tools.DecisionActionNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.DoneActionNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.ExhibitStateNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.ForkActionNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.JoinActionNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.MergeActionNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.NamespaceImportNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.SetAsViewToolProvider; +import org.eclipse.syson.diagram.common.view.tools.StartActionNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.ToolSectionDescription; import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; import org.eclipse.syson.standard.diagrams.view.edges.AllocateEdgeDescriptionProvider; @@ -980,6 +986,18 @@ private List> createAllDefinitionOwnedUsag definitionOwnedUsageEdgeDescriptionProviders .add(new DefinitionOwnedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getActionUsage(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), colorProvider, this.getDescriptionNameGenerator())); + definitionOwnedUsageEdgeDescriptionProviders + .add(new DefinitionOwnedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getDecisionNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), + colorProvider, this.getDescriptionNameGenerator())); + definitionOwnedUsageEdgeDescriptionProviders + .add(new DefinitionOwnedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getForkNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), + colorProvider, this.getDescriptionNameGenerator())); + definitionOwnedUsageEdgeDescriptionProviders + .add(new DefinitionOwnedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getJoinNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), + colorProvider, this.getDescriptionNameGenerator())); + definitionOwnedUsageEdgeDescriptionProviders + .add(new DefinitionOwnedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getMergeNode(), SysmlPackage.eINSTANCE.getDefinition_OwnedAction(), + colorProvider, this.getDescriptionNameGenerator())); definitionOwnedUsageEdgeDescriptionProviders .add(new DefinitionOwnedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getAllocationUsage(), SysmlPackage.eINSTANCE.getDefinition_OwnedAllocation(), colorProvider, this.getDescriptionNameGenerator())); @@ -1101,6 +1119,14 @@ private List> createAllUsageBehaviorCompos colorProvider, this.getDescriptionNameGenerator())); usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getActionUsage(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), colorProvider, this.getDescriptionNameGenerator())); + usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getDecisionNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), + colorProvider, this.getDescriptionNameGenerator())); + usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getForkNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), + colorProvider, this.getDescriptionNameGenerator())); + usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getJoinNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), + colorProvider, this.getDescriptionNameGenerator())); + usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getMergeNode(), SysmlPackage.eINSTANCE.getUsage_NestedAction(), + colorProvider, this.getDescriptionNameGenerator())); usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getAllocationUsage(), SysmlPackage.eINSTANCE.getUsage_NestedAllocation(), colorProvider, this.getDescriptionNameGenerator())); usageCompositeEdgeDescriptionProviders.add(new UsageNestedUsageEdgeDescriptionProvider(SysmlPackage.eINSTANCE.getExhibitStateUsage(), SysmlPackage.eINSTANCE.getUsage_NestedState(), @@ -1629,6 +1655,12 @@ private List addCustomTools(IViewDiagramElementFinder cache, String se if (BEHAVIOR_TOOL_SECTION.name().equals(sectionName)) { nodeTools.add(new ExhibitStateNodeToolProvider(false).create(cache)); nodeTools.add(new ExhibitStateNodeToolProvider(true).create(cache)); + nodeTools.add(new StartActionNodeToolProvider(null, this.getDescriptionNameGenerator()).create(cache)); + nodeTools.add(new DoneActionNodeToolProvider(null, this.getDescriptionNameGenerator()).create(cache)); + nodeTools.add(new JoinActionNodeToolProvider(null, this.getDescriptionNameGenerator()).create(cache)); + nodeTools.add(new DecisionActionNodeToolProvider(null, this.getDescriptionNameGenerator()).create(cache)); + nodeTools.add(new ForkActionNodeToolProvider(null, this.getDescriptionNameGenerator()).create(cache)); + nodeTools.add(new MergeActionNodeToolProvider(null, this.getDescriptionNameGenerator()).create(cache)); } else if (STRUCTURE_TOOL_SECTION.name().equals(sectionName)) { NodeDescription nodeDescription = cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(SysmlPackage.eINSTANCE.getNamespaceImport())).orElse(null); nodeTools.add(new NamespaceImportNodeToolProvider(nodeDescription, this.getDescriptionNameGenerator()).create(cache)); diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc index 881fd3bcf..614eaaa87 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc @@ -13,9 +13,9 @@ A _frame_ graphical edge can be created between a `RequirementUsage` or a `Requi + image::release-notes-frame-edge-tool.png[Edge tool to frame a ConcernUsage, width=50%,height=50%] + -image::release-notes-frame-edge.png[Frame edge between a RequirementUsage and a ConcernUsage, width=60%,height=60%] +image::release-notes-frame-edge.png[Frame edge between a RequirementUsage and a ConcernUsage, width=40%,height=40%] -** Add tools to create _Start_ and _Done_ on `StateUsage` and `StateDefinition` graphical nodes. +** Add tools to create _Start_ and _Done_ states on `StateUsage` and `StateDefinition` graphical nodes. + image::release-notes-start-done-states.png[Tool to create a start and done, width=60%,height=60%] @@ -27,13 +27,13 @@ image::release-notes-framed-concern-tool.png[Tool to create a _frame_ concern us + image::release-notes-frames-compartment.png[frames compartment displaying concern usages of a requirement, width=40%,height=40%] -** Leverage the selection dialog to improve the graphical node tool creating a _frame_ `ConcernUsage` from `RequirementUsage` and `RequirementDefinition` graphical nodes. -** Leverage the selection dialog to improve the graphical node tools creating a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes. +** Use the selection dialog to improve the graphical node tool creating a _frame_ `ConcernUsage` from `RequirementUsage` and `RequirementDefinition` graphical nodes. +** Use the selection dialog to improve the graphical node tools creating a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes. -** Add the support for creating _timeslices/snapshots_ from the different kind of `OccurrenceDefiniton` graphical nodes. +** Add support for creating _timeslices/snapshots_ from the different kinds of `OccurrenceDefinition` graphical nodes. It leverages the selection dialog to either create an _occurrence timeslice/snapshot_, or the _usage timeslice/snapshot_ matching the `OccurrenceDefinition` on which the tool is applied. -** Add the support for _assume_ and _require_ graphical edges. +** Add support for _assume_ and _require_ graphical edges. These graphical edges are displayed using the tool to create a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes, when another `ConstraintUsage` is selected. These graphical edges can also be created using the _New Assume Constraint_ or _New Require Constraint_ graphical edge tools. + @@ -41,11 +41,13 @@ image::release-notes-assume-and-require-edges.png[assume and require edge betwee ** Add the _New Assume Constraint_ or _New Require Constraint_ edge tools to create _assume_ and _require_ graphical edges. + -image::release-notes-assume-and-require-edge-tools.png[list available edge tools containing the two new tools, between a RequirementUsage and a ConstraintUsage, width=60%,height=60%] +image::release-notes-assume-and-require-edge-tools.png[list available edge tools containing the two new tools, between a RequirementUsage and a ConstraintUsage, width=40%,height=40%] -** When using direct edit on feature elements which have a value expression (e.g. an `Attribute` with a default value defined), the expression part (after the `=` sign) is no longer part of the initial direct edit value, and will be ignored if supplied in the new text value. +** When using direct edit on feature elements that have a value expression (e.g., an `Attribute` with a default value defined), the expression part (after the `=` sign) is no longer part of the initial direct edit value and will be ignored if supplied in the new text value. Users should use the dedicated tools to edit expressions instead, as they ensure only valid expressions (with all names resolving) are accepted. +** Add support for creating _Start_, _Done_, _Decision_, _Fork_, _Join_, and _Merge_ actions directly on the background of an `Action Flow View` diagram if its root element is an `ActionUsage` or `ActionDefinition`. + * In the _Explorer_ view: ** The tree items corresponding to the internals of `Expression` elements (syntax tree) are now hidden by default. @@ -60,13 +62,13 @@ a|image::explorer-expression-internals-hidden.png[Internals hidden (default)] a|image::explorer-expression-internals-visible.png[Internals visible] |=== -** The dialog creating a child element provides two more options for `RequirementUsage` or `RequirementDefinition` tree items. -One creates a `ConcernUsage` and another one creates a `FramedConcernMembership`. -The option that creates a `ConcernUsage` creates a `FramedConcernMembership` as an intermediate container. +** The dialog for creating a child element provides two additional options for `RequirementUsage` or `RequirementDefinition` tree items. +One creates a `ConcernUsage` and another creates a `FramedConcernMembership`. +The option that creates a `ConcernUsage` also creates a `FramedConcernMembership` as an intermediate container. The option that creates a `FramedConcernMembership` only creates the membership. In both cases, to display the `FramedConcernMembership`, deactivate the `Hide Memberships` filter. -** It is now possible to create, edit and delete _Expressions_ using their textual syntax from the _Explorer_ view. -To _create_ an expression, invoke the _New expression_ context menu action on a compatible element (supported elements are `Attributes`, `Constraints` and `Transitions`). +** It is now possible to create, edit, and delete _Expressions_ using their textual syntax from the _Explorer_ view. +To _create_ an expression, invoke the _New expression_ context menu action on a compatible element (supported elements are `Attributes`, `Constraints`, and `Transitions`). A modal will open where you can enter the textual representation of the expression to create. Click on the _Update_ button or hit _Ctrl-RET_ to validate. If the entered expression is valid, it will be created and the modal will automatically close. @@ -76,13 +78,13 @@ Fix any error reported before clicking on _Update_ again (or canceling the opera image::edit-expression-modal.png[Edit expression modal, width=80%] image::edit-expression-modal-error.png[Edit expression modal showing an error message if the new expression is invalid, width=80%] + -To _edit_ an existing expression, simply invoke the _Edit expression_ context menu action directly on the existing expression or on its parent element; the same modal will open but with the current textual representation of the expression pre-filled. +To _edit_ an existing expression, invoke the _Edit expression_ context menu action directly on the existing expression or on its parent element. The same modal will open with the current textual representation of the expression pre-filled. + To _delete_ an existing expression, you can simply invoke the normal _Delete_ menu item action on the expression itself or the new _Delete expression_ on its parent element. * In the _Details_ view: -** When an `Expression` element or an `Element` which contains a single `Expression` element is selected (for example from the _Explorer_ view), the _Details_ view displays the textual representation of the `Expression`, with a button to open the expression edition modal (see above) directly on it: +** When an `Expression` element or an `Element` that contains a single `Expression` element is selected (for example from the _Explorer_ view), the _Details_ view displays the textual representation of the `Expression`, with a button to open the expression editing modal (see above) directly on it: + image::expression-details.png[Expression value displayed in the _Details_ veiw, width=80%] @@ -98,27 +100,27 @@ image::expression-details.png[Expression value displayed in the _Details_ veiw, ** Improve diagram-to-diagram drag and drop to support dropping multiple graphical nodes at once. ** Improve feedback message reporting while moving elements in diagrams. -** Fix and improve the labeling logic inside `frames`, `require constraints`, and `assume constraints` compartments. +** Improve the labeling logic inside `frames`, `require constraints`, and `assume constraints` compartments. Previously, the label was always the name of the `ConcernUsage` or the `ConstraintUsage` displayed in the compartment. Now, the label dynamically adapts based on whether the `ConcernUsage` or `ConstraintUsage` has a name and an owned reference subsetting: -*** Elements having both a name and an owned reference subsetting now display using the standard subsetting notation (e.g., `ElementName ::> SubsettingName`). -*** Elements without name and an owned reference subsetting display only the reference subsetting name -*** Elements without reference subsetting display their name. -** Update the choice of _timeslice_ and _snapshot_ elements that can be created in the selection dialog of tools creating _timeslice_ and _snapshot_ elements. -Before, the selection dialog allowed choosing between all _timeslice_ and _snapshot_ types. +*** Elements with both a name and an owned reference subsetting now display using the standard subsetting notation (e.g., `ElementName ::> SubsettingName`). +*** Elements without a name and an owned reference subsetting display only the reference subsetting name. +*** Elements without a reference subsetting display their name. +** Update the choice of _timeslice_ and _snapshot_ elements that can be created in the selection dialog of tools for creating _timeslice_ and _snapshot_ elements. +Previously, the selection dialog allowed choosing from all _timeslice_ and _snapshot_ types. Now, the choice is restricted to the _timeslice_ or _snapshot_ type that matches the graphical node type on which the tool is applied. -** It is now possible to create, edit and delete _Expressions_ using their textual syntax from the a diagram. -To _create_ an expression, invoke the _New expression_ action in the _Expression_ palette section of a compatible element (supported elements are `Attributes`, `Constraints` and `Transitions`). +** It is now possible to create, edit, and delete _Expressions_ using their textual syntax from a diagram. +To _create_ an expression, invoke the _New expression_ action in the _Expression_ palette section of a compatible element (supported elements are `Attributes`, `Constraints`, and `Transitions`). The same modal as described above when invoked from the _Explorer_ or _Details_ view will open where you can enter the textual representation of the expression to create. Editing and deleting an _Expression_ is also available on compatible diagram elements with the corresponding actions in their _Expressions_ palette section. -The behavior is the same when invoking the equivalen action from the _Explorer_ or _Details_ view. +The behavior is the same when invoking the equivalent action from the _Explorer_ or _Details_ view. + -image::expression-palette-section.png[Expression palette tool section on an `Attribute` in a diagram, width=80%] -** In an _action flow_ compartment, `Succession` edges which have a guard now display the guard condition `Expression` as a _begin label_: +image::expression-palette-section.png[Expression palette tool section on an `Attribute` in a diagram, width=40%,height=40%] +** In an _action flow_ compartment, `Succession` edges that have a guard now display the guard condition `Expression` as a _begin label_: + -image::action-flow-guard-labels.png[Succession edges with visible guard expressions, width=80%] +image::action-flow-guard-labels.png[Succession edges with visible guard expressions, width=40%,height=40%] ** Display inherited behavior parameters in the _parameters_ compartment of `ActionDefinition` and `ActionUsage` graphical nodes. -** Display inherited `ItemUsages` in the _items_ compartment, and inherited `ActionUsages` in the _actions_ compartment of `ActionDefinitions`, `ActionUsages`, `PartUsages`... graphical nodes. +** Display inherited `ItemUsages` in the _items_ compartment and inherited `ActionUsages` in the _actions_ compartment of `ActionDefinitions`, `ActionUsages`, `PartUsages`, and other graphical nodes. == Technical details