From 169ae9c2e8405092ae6092de73a7ac99ac9732a0 Mon Sep 17 00:00:00 2001 From: Jerome Gout Date: Fri, 24 Apr 2026 09:01:59 +0200 Subject: [PATCH] [2112] Handle Start and Done StateUsages Bug: https://github.com/eclipse-syson/syson/issues/2112 Signed-off-by: Jerome Gout --- CHANGELOG.adoc | 8 +- .../view/GVNewStartDoneStatesTests.java | 202 ++++++++++++++++++ .../eclipse/syson/services/UtilService.java | 44 +++- ...ractTransitionEdgeDescriptionProvider.java | 10 +- .../AbstractFakeNodeDescriptionProvider.java | 6 +- .../DoneStateNodeDescriptionProvider.java | 77 +++++++ .../StartStateNodeDescriptionProvider.java | 99 +++++++++ ...ionCompartmentNodeDescriptionProvider.java | 6 + .../view/services/ViewCreateService.java | 98 +++++---- .../view/services/ViewEdgeToolSwitch.java | 14 +- .../view/tools/DoneStateNodeToolProvider.java | 52 +++++ .../tools/StartStateNodeToolProvider.java | 52 +++++ .../tests/predicates/DiagramPredicates.java | 8 +- .../view/SDVDiagramDescriptionProvider.java | 4 + .../DefinitionNodeDescriptionProvider.java | 4 + .../nodes/UsageNodeDescriptionProvider.java | 4 + .../services/SDVNodeToolSectionSwitch.java | 6 + .../release-notes-start-done-states.png | Bin 0 -> 50156 bytes .../pages/release-notes/2026.7.0.adoc | 29 +++ 19 files changed, 675 insertions(+), 48 deletions(-) create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVNewStartDoneStatesTests.java create mode 100644 backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/DoneStateNodeDescriptionProvider.java create mode 100644 backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartStateNodeDescriptionProvider.java create mode 100644 backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneStateNodeToolProvider.java create mode 100644 backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartStateNodeToolProvider.java create mode 100644 doc/content/modules/user-manual/assets/images/release-notes-start-done-states.png create mode 100644 doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7ff2b8d2a..465521501 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,6 +1,12 @@ = Changelog -== v2026.5.0 (work in progress) +== v2026.7.0 (work in progress) + +=== New features + +- https://github.com/eclipse-syson/syson/issues/2112[#2112] [Diagram] Add tools to create Start and Done states, available on `StateUsage` and `StateDefinition` graphical nodes. + +== v2026.5.0 === Shapes diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVNewStartDoneStatesTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVNewStartDoneStatesTests.java new file mode 100644 index 000000000..78e48e7ac --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVNewStartDoneStatesTests.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.diagrams.general.view; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat; + +import java.time.Duration; +import java.util.Optional; +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.core.api.IObjectSearchService; +import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionInput; +import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload; +import org.eclipse.sirius.components.graphql.tests.api.IExecuteEditingContextFunctionRunner; +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.testers.ToolTester; +import org.eclipse.syson.application.controllers.utils.TestNameGenerator; +import org.eclipse.syson.application.data.GeneralViewWithTopNodesTestProjectData; +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.Package; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.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 New Start State and New Done State tools. + * + * @author Jerome Gout + */ +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GVNewStartDoneStatesTests extends AbstractIntegrationTests { + + private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); + + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private IGivenDiagramDescription givenDiagramDescription; + + @Autowired + private IGivenDiagramSubscription givenDiagramSubscription; + + @Autowired + private IDiagramIdProvider diagramIdProvider; + + @Autowired + private ToolTester nodeCreationTester; + + @Autowired + private DiagramComparator diagramComparator; + + @Autowired + private IExecuteEditingContextFunctionRunner executeEditingContextFunctionRunner; + + @Autowired + private IObjectSearchService objectSearchService; + + private Flux givenSubscriptionToDiagram() { + var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), + GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID); + return this.givenDiagramSubscription.subscribe(diagramEventInput); + } + + @BeforeEach + public void setUp() { + this.givenInitialServerState.initialize(); + } + + private static Stream toolParameters() { + return Stream.of(Arguments.of("New Start State"), Arguments.of("New Done State")).map(TestNameGenerator::namedArguments); + } + + @DisplayName("GIVEN a SysML Project with a StateUsage inside a package named States, WHEN invoking $toolName, THEN the state is added in the state transition compartment") + @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @ParameterizedTest + @MethodSource("toolParameters") + public void checkNewStartDoneStateInStateUsage(String toolName) { + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider); + String creationToolId = diagramDescriptionIdProvider.getNodeToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getStateUsage()), toolName); + AtomicReference diagram = new AtomicReference<>(); + + var flux = this.givenSubscriptionToDiagram(); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + Runnable renamePackageAction = () -> { + var input = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + (editingContext, executeEditingContextFunctionInput) -> { + Optional optPackage = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID); + assertThat(optPackage).isPresent().get().isInstanceOf(Package.class); + var parentPackage = (Package) optPackage.get(); + parentPackage.setDeclaredName("States"); + return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true); + }); + var payload = this.executeEditingContextFunctionRunner.execute(input).block(); + assertThat(payload).isInstanceOf(ExecuteEditingContextFunctionSuccessPayload.class); + }; + + Runnable invokeCreationTool = () -> this.nodeCreationTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_USAGE_ID, creationToolId); + + Consumer diagramCheck = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator) + .hasNewNodeCount(1) + .check(diagram.get(), newDiagram); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(renamePackageAction) + .then(invokeCreationTool) + .consumeNextWith(diagramCheck) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML Project with a StateDefinition inside a package named States, WHEN invoking $toolName, THEN the state is added in the state transition compartment") + @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @ParameterizedTest + @MethodSource("toolParameters") + public void checkNewStartDoneStateInStateDefinition(String toolName) { + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider); + String creationToolId = diagramDescriptionIdProvider.getNodeToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getStateDefinition()), toolName); + AtomicReference diagram = new AtomicReference<>(); + + var flux = this.givenSubscriptionToDiagram(); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + Runnable renamePackageAction = () -> { + var input = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + (editingContext, executeEditingContextFunctionInput) -> { + Optional optPackage = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID); + assertThat(optPackage).isPresent().get().isInstanceOf(Package.class); + var parentPackage = (Package) optPackage.get(); + parentPackage.setDeclaredName("States"); + return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true); + }); + var payload = this.executeEditingContextFunctionRunner.execute(input).block(); + assertThat(payload).isInstanceOf(ExecuteEditingContextFunctionSuccessPayload.class); + }; + + Runnable invokeCreationTool = () -> this.nodeCreationTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_DEFINITION_ID , creationToolId); + + Consumer diagramCheck = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator) + .hasNewNodeCount(1) + .check(diagram.get(), newDiagram); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(renamePackageAction) + .then(invokeCreationTool) + .consumeNextWith(diagramCheck) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + +} diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java index ce289d355..e851d17d2 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java @@ -238,7 +238,7 @@ public List getAllNonExhibitStates(Element element) { * * @param eObject * the {@link EObject} stored in a {@link ResourceSet} - * @param type + * @param eClass * the searched type, represented by its qualified name * @return a list of reachable object */ @@ -246,6 +246,20 @@ public List getAllReachable(EObject eObject, EClass eClass) { return this.getAllReachableType(eObject, eClass); } + /** + * * Get all reachable elements of the type given by the {@link EClass} in the {@link ResourceSet} of the given type + * (represented by its qualified name) without considering elements inside standard libs. + * + * @param eObject + * the {@link EObject} stored in a {@link ResourceSet} + * @param eClass + * the searched type, represented by its qualified name + * @return a list of reachable object + */ + public List getAllReachableWithoutStandardLibs(EObject eObject, EClass eClass) { + return this.getAllReachableType(eObject, eClass, false); + } + /** * Get all reachable elements of the type given by the {@link EClass} in the {@link ResourceSet} of the given * {@link EObject}. @@ -605,6 +619,18 @@ public ActionUsage retrieveStandardStartAction(Element eObject) { return this.findByNameAndTypeInStandardLibraries(eObject, ActionUsage.class, "Actions::Action::start"); } + /** + * Retrieve the start state defined inside the standard library States. + * + * @param eObject + * an object to access to the library resources. + * + * @return the standard start StateUsage defined in the States library. + */ + public StateUsage retrieveStandardStartState(Element eObject) { + return this.findByNameAndTypeInStandardLibraries(eObject, StateUsage.class, "States::StateAction::start"); + } + /** * Retrieve the done action defined inside the standard library Actions. * @@ -617,6 +643,18 @@ public ActionUsage retrieveStandardDoneAction(Element eObject) { return this.findByNameAndTypeInStandardLibraries(eObject, ActionUsage.class, "Actions::Action::done"); } + /** + * Retrieve the done state defined inside the standard library States. + * + * @param eObject + * an object to access to the library resources. + * + * @return the standard done StateUsage defined in the States library. + */ + public ActionUsage retrieveStandardDoneState(Element eObject) { + return this.findByNameAndTypeInStandardLibraries(eObject, ActionUsage.class, "States::StateAction::done"); + } + private T findByNameAndTypeInStandardLibraries(Element context, Class klass, String qualifiedName) { return context.eResource().getResourceSet().getResources().stream() .flatMap(resource -> this.getLibraries(resource, true).stream()) @@ -1022,6 +1060,10 @@ public boolean isUnsynchronized(Element element) { isUnsynchronized = true; } else if (Objects.equals(element, this.retrieveStandardDoneAction(element))) { isUnsynchronized = true; + } else if (Objects.equals(element, this.retrieveStandardStartState(element))) { + isUnsynchronized = true; + } else if (Objects.equals(element, this.retrieveStandardDoneState(element))) { + isUnsynchronized = true; } else if (element instanceof NamespaceImport) { isUnsynchronized = true; } else if (element instanceof AnnotatingElement) { diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractTransitionEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractTransitionEdgeDescriptionProvider.java index d4b8ad14c..5f7b79301 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractTransitionEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractTransitionEdgeDescriptionProvider.java @@ -33,7 +33,9 @@ import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.services.ViewEdgeService; import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.services.UtilService; @@ -73,7 +75,7 @@ public EdgeDescription create() { .name(this.getEdgeDescriptionName()) .preconditionExpression(ServiceMethod.of2(ViewEdgeService::isInSameGraphicalContainer).aql(org.eclipse.sirius.components.diagrams.description.EdgeDescription.GRAPHICAL_EDGE_SOURCE, org.eclipse.sirius.components.diagrams.description.EdgeDescription.GRAPHICAL_EDGE_TARGET, org.eclipse.sirius.components.diagrams.description.DiagramDescription.CACHE)) - .semanticCandidatesExpression(ServiceMethod.of1(UtilService::getAllReachable).aqlSelf(domainType)) + .semanticCandidatesExpression(ServiceMethod.of1(UtilService::getAllReachableWithoutStandardLibs).aqlSelf(domainType)) .sourceExpression(AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getTransitionUsage_Source().getName()) .style(this.createDefaultEdgeStyle()) .conditionalStyles(this.createStateConditionalStyle()) @@ -181,11 +183,13 @@ protected List getAllActionOrStateUsage(IViewDiagramElementFind } protected boolean isStartNode(NodeDescription n) { - return Objects.equals(this.getDescriptionNameGenerator().getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME), n.getName()); + return Objects.equals(this.getDescriptionNameGenerator().getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME), n.getName()) || + Objects.equals(this.getDescriptionNameGenerator().getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME), n.getName()); } protected boolean isDoneNode(NodeDescription n) { - return Objects.equals(this.getDescriptionNameGenerator().getNodeName(DoneActionNodeDescriptionProvider.DONE_ACTION_NAME), n.getName()); + return Objects.equals(this.getDescriptionNameGenerator().getNodeName(DoneActionNodeDescriptionProvider.DONE_ACTION_NAME), n.getName()) || + Objects.equals(this.getDescriptionNameGenerator().getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME), n.getName()); } protected boolean isActionOrStateUsage(NodeDescription nodeDescription) { 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 aa45b3078..5e378b53f 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 Obeo. + * Copyright (c) 2023, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -124,5 +124,9 @@ protected void addReusableCustomNodes(IViewDiagramElementFinder cache, List { + nodeDescription.setPalette(this.createNodePalette()); + }); + } + + private NodePalette createNodePalette() { + return this.diagramBuilderHelper.newNodePalette() + .quickAccessTools(this.getDeleteFromDiagramTool()) + .toolSections(this.defaultToolsFactory.createDefaultHideRevealNodeToolSection()) + .build(); + } +} diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartStateNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartStateNodeDescriptionProvider.java new file mode 100644 index 000000000..4930c5463 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartStateNodeDescriptionProvider.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.diagram.common.view.nodes; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; +import org.eclipse.sirius.components.view.builder.providers.IColorProvider; +import org.eclipse.sirius.components.view.diagram.DiagramDescription; +import org.eclipse.sirius.components.view.diagram.EdgeTool; +import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.sirius.components.view.diagram.NodePalette; +import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.sirius.components.view.diagram.UserResizableDirection; +import org.eclipse.syson.diagram.common.view.DescriptionFinder; +import org.eclipse.syson.diagram.common.view.services.ViewEdgeToolService; +import org.eclipse.syson.services.UtilService; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; +import org.eclipse.syson.util.SysMLMetamodelHelper; + +/** + * Used to create the starting node description of a state. + * + * @author Jerome Gout + */ +public class StartStateNodeDescriptionProvider extends AbstractNodeDescriptionProvider { + + public static final String START_STATE_NAME = "StartState"; + + private final IDescriptionNameGenerator descriptionNameGenerator; + + public StartStateNodeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { + super(colorProvider); + this.descriptionNameGenerator = Objects.requireNonNull(descriptionNameGenerator); + } + + @Override + public NodeDescription create() { + String domainType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getStateUsage()); + return this.diagramBuilderHelper.newNodeDescription() + .collapsible(false) + .domainType(domainType) + .defaultWidthExpression("28") + .defaultHeightExpression("28") + .name(this.descriptionNameGenerator.getNodeName(START_STATE_NAME)) + .semanticCandidatesExpression(ServiceMethod.of0(UtilService::retrieveStandardStartState).aqlSelf()) + .style(this.createImageNodeStyleDescription("images/start_action.svg")) + .userResizable(UserResizableDirection.NONE) + .synchronizationPolicy(SynchronizationPolicy.UNSYNCHRONIZED) + .build(); + } + + @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_STATE_NAME)).ifPresent(nodeDescription -> { + nodeDescription.setPalette(this.createNodePalette(cache)); + }); + } + + private NodePalette createNodePalette(IViewDiagramElementFinder cache) { + var edgeTools = new ArrayList(this.getEdgeTools(cache)); + + return this.diagramBuilderHelper.newNodePalette() + .edgeTools(edgeTools.toArray(EdgeTool[]::new)) + .quickAccessTools(this.getDeleteFromDiagramTool()) + .toolSections(this.defaultToolsFactory.createDefaultHideRevealNodeToolSection()) + .build(); + } + + private List getEdgeTools(IViewDiagramElementFinder cache) { + var targetElementDescriptions = this.getStartTargetDescriptions(cache); + var edgeToolService = new ViewEdgeToolService(this.viewBuilderHelper, this.diagramBuilderHelper, cache.getNodeDescriptions(), this.descriptionNameGenerator); + var connectionTool = edgeToolService.createConnectionUsageEdgeTool(new DescriptionFinder(this.descriptionNameGenerator).getConnectableNodeDescriptions(cache.getNodeDescriptions(), SysmlPackage.eINSTANCE.getStateUsage())); + var transitionTool = edgeToolService.createTransitionUsageEdgeTool(SysmlPackage.eINSTANCE.getTransitionUsage(), targetElementDescriptions); + return List.of(transitionTool, connectionTool); + } + + private List getStartTargetDescriptions(IViewDiagramElementFinder cache) { + var targets = new ArrayList(); + cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getStateUsage())).ifPresent(targets::add); + return targets; + } +} diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StateTransitionCompartmentNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StateTransitionCompartmentNodeDescriptionProvider.java index dbaa97ec8..cd073f7c2 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StateTransitionCompartmentNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StateTransitionCompartmentNodeDescriptionProvider.java @@ -33,6 +33,8 @@ import org.eclipse.sirius.components.view.diagram.UserResizableDirection; import org.eclipse.syson.diagram.common.view.services.description.ToolConstants; import org.eclipse.syson.diagram.common.view.tools.ExhibitStateNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.DoneStateNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.StartStateNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.StateTransitionCompartmentNodeToolProvider; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; @@ -77,6 +79,8 @@ public void link(DiagramDescription diagramDescription, IViewDiagramElementFinde cache.getNodeDescription(this.name).ifPresent(nodeDescription -> { cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(SysmlPackage.eINSTANCE.getStateUsage())).ifPresent(nodeDescription.getReusedChildNodeDescriptions()::add); cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(SysmlPackage.eINSTANCE.getStateDefinition())).ifPresent(nodeDescription.getReusedChildNodeDescriptions()::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME)).ifPresent(nodeDescription.getReusedChildNodeDescriptions()::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME)).ifPresent(nodeDescription.getReusedChildNodeDescriptions()::add); nodeDescription.setPalette(this.createCompartmentPalette(cache)); }); } @@ -118,6 +122,8 @@ protected NodePalette createCompartmentPalette(IViewDiagramElementFinder cache) var toolSections = this.toolDescriptionService.createDefaultNodeToolSections(); // Do not use getItemCreationToolProvider because the compartment contains multiple creation tools. + this.toolDescriptionService.addNodeTool(toolSections, ToolConstants.BEHAVIOR, new StartStateNodeToolProvider(this.eClass, this.getDescriptionNameGenerator()).create(cache)); + this.toolDescriptionService.addNodeTool(toolSections, ToolConstants.BEHAVIOR, new DoneStateNodeToolProvider(this.eClass, this.getDescriptionNameGenerator()).create(cache)); this.toolDescriptionService.addNodeTool(toolSections, ToolConstants.BEHAVIOR, new StateTransitionCompartmentNodeToolProvider(false).create(cache)); this.toolDescriptionService.addNodeTool(toolSections, ToolConstants.BEHAVIOR, new StateTransitionCompartmentNodeToolProvider(true).create(cache)); this.toolDescriptionService.addNodeTool(toolSections, ToolConstants.BEHAVIOR, new ExhibitStateNodeToolProvider(false).create(cache)); 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 6ba03ad18..415a43bcb 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 @@ -784,6 +784,17 @@ public ActionUsage addStartAction(Element ownerElement) { return this.utilService.retrieveStandardStartAction(ownerElement); } + /** + * Add the standard start state as the child of the given element. + * + * @param ownerElement + * an element that will own the standard start state. + * @return the {@link Membership} element containing the start state in its memberElement feature. + */ + public ActionUsage addStartState(Element ownerElement) { + return this.utilService.retrieveStandardStartState(ownerElement); + } + /** * Add the standard done action as the child of the given element. * @@ -795,6 +806,17 @@ public ActionUsage addDoneAction(Element ownerElement) { return this.utilService.retrieveStandardDoneAction(ownerElement); } + /** + * Add the standard done state as the child of the given element. + * + * @param ownerElement + * an element that will own the standard done state. + * @return the {@link Membership} element containing the done state in its memberElement feature. + */ + public ActionUsage addDoneState(Element ownerElement) { + return this.utilService.retrieveStandardDoneState(ownerElement); + } + /** * Create a new action {@link ActionUsage} inside the given element which should be an {@link ActionUsage} or an {@link ActionDefinition}. * @@ -1131,48 +1153,46 @@ public PerformActionUsage createStateSubaction(Element self, ActionUsage perform * @return the given source {@link Feature}. */ public Feature createTransitionUsage(Feature sourceUsage, Feature targetUsage, Node source, Node target, IDiagramService diagramService, IEditingContext editingContext) { - if (this.isInSameGraphicalContainer(source, target, diagramService)) { - // Check source and target have the same parent - Element semanticContainer = this.getEdgeSemanticContainer(source, target, diagramService.getDiagramContext().diagram(), editingContext); - if (semanticContainer != null) { - Element sourceParentElement = sourceUsage.getOwner(); - if (this.utilService.isParallelState(sourceParentElement)) { - // Should probably not be here as the transition creation should not be allowed. - return sourceUsage; - } - // Create transition usage and add it to the parent element - // sourceParentElement <>-> FeatureMembership -> RelatedElement = TransitionUsage - TransitionUsage newTransitionUsage = SysmlFactory.eINSTANCE.createTransitionUsage(); - var featureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); - featureMembership.getOwnedRelatedElement().add(newTransitionUsage); - sourceParentElement.getOwnedRelationship().add(featureMembership); - - // Create EndFeature - // TransitionUsage <>-> Membership -> MemberElement = sourceAction - var sourceMembership = SysmlFactory.eINSTANCE.createMembership(); - newTransitionUsage.getOwnedRelationship().add(sourceMembership); - sourceMembership.setMemberElement(sourceUsage); - - // Create Succession - // TransitionUsage <>-> FeatureMembership -> RelatedElement = succession - Succession succession = SysmlFactory.eINSTANCE.createSuccession(); - this.elementInitializerSwitch.doSwitch(succession); - var successionFeatureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); - successionFeatureMembership.getOwnedRelatedElement().add(succession); - newTransitionUsage.getOwnedRelationship().add(successionFeatureMembership); - - // Set Succession Source and Target Features - succession.getOwnedRelationship().add(this.createConnectorEndFeatureMembership(sourceUsage)); - succession.getOwnedRelationship().add(this.createConnectorEndFeatureMembership(targetUsage)); - this.elementInitializerSwitch.doSwitch(newTransitionUsage); - } else { - this.feedbackMessageService.addFeedbackMessage(new Message("Unable to find a suitable semantic owner for the new transition", MessageLevel.WARNING)); - } - - } else { + if (!this.isInSameGraphicalContainer(source, target, diagramService)) { + // The current implementation only rely on the semantic features "sourceFeature" and "targetFeature" to find source and target + // In order to avoid duplicated edges in case the source/target is displayed more than once we forbid the display of cross container edge this.feedbackMessageService.addFeedbackMessage(new Message("Can't create cross container TransitionUsage", MessageLevel.WARNING)); + return sourceUsage; } + EObject transitionOwner = this.getSourceOwner(source, editingContext, diagramService); + return this.createTransitionUsage(sourceUsage, targetUsage, transitionOwner); + } + private Feature createTransitionUsage(Feature sourceUsage, Feature targetUsage , EObject transitionOwner) { + if (transitionOwner instanceof Element ownerElement) { + // Create transition usage and add it to the parent element + // sourceParentElement <>-> FeatureMembership -> RelatedElement = TransitionUsage + TransitionUsage newTransitionUsage = SysmlFactory.eINSTANCE.createTransitionUsage(); + var featureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); + featureMembership.getOwnedRelatedElement().add(newTransitionUsage); + ownerElement.getOwnedRelationship().add(featureMembership); + + // Create EndFeature + // TransitionUsage <>-> Membership -> MemberElement = sourceAction + var sourceMembership = SysmlFactory.eINSTANCE.createMembership(); + newTransitionUsage.getOwnedRelationship().add(sourceMembership); + sourceMembership.setMemberElement(sourceUsage); + + // Create Succession + // TransitionUsage <>-> FeatureMembership -> RelatedElement = succession + Succession succession = SysmlFactory.eINSTANCE.createSuccession(); + this.elementInitializerSwitch.doSwitch(succession); + var successionFeatureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); + successionFeatureMembership.getOwnedRelatedElement().add(succession); + newTransitionUsage.getOwnedRelationship().add(successionFeatureMembership); + + // Set Succession Source and Target Features + succession.getOwnedRelationship().add(this.createConnectorEndFeatureMembership(sourceUsage)); + succession.getOwnedRelationship().add(this.createConnectorEndFeatureMembership(targetUsage)); + this.elementInitializerSwitch.doSwitch(newTransitionUsage); + } else { + this.feedbackMessageService.addFeedbackMessage(new Message("Unable to find a suitable semantic owner for the new transition", MessageLevel.WARNING)); + } return sourceUsage; } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java index 9f0967d15..31b94eb06 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewEdgeToolSwitch.java @@ -25,10 +25,12 @@ import org.eclipse.syson.diagram.common.view.DescriptionFinder; import org.eclipse.syson.diagram.common.view.nodes.DecisionActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ForkActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.JoinActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.MergeActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; import org.eclipse.syson.sysml.AcceptActionUsage; import org.eclipse.syson.sysml.ActionUsage; import org.eclipse.syson.sysml.AllocationUsage; @@ -279,12 +281,20 @@ public List caseStateDefinition(StateDefinition object) { @Override public List caseStateUsage(StateUsage object) { var edgeTools = new ArrayList(); - var targetNodes = this.allNodeDescriptions.stream().filter(nodeDesc -> this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getStateUsage()).equals(nodeDesc.getName())).collect(Collectors.toList()); + var targetNodes = this.allNodeDescriptions.stream() + .filter(this::isTransitionEdgeTargetNodeDescription) + .collect(Collectors.toList()); edgeTools.add(this.edgeToolService.createTransitionUsageEdgeTool(SysmlPackage.eINSTANCE.getTransitionUsage(), targetNodes)); edgeTools.addAll(this.caseUsage(object)); return edgeTools; } + private boolean isTransitionEdgeTargetNodeDescription(NodeDescription nodeDesc) { + boolean result = this.edgeToolService.isTheNodeDescriptionFor(nodeDesc, SysmlPackage.eINSTANCE.getStateUsage()); + result = result || this.descriptionNameGenerator.getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME).equals(nodeDesc.getName()); + return result; + } + @Override public List caseUsage(Usage object) { var edgeTools = new ArrayList(); @@ -328,6 +338,8 @@ private boolean isRegularNodeDescription(NodeDescription nodeDesc) { isSpecial = isSpecial || this.descriptionNameGenerator.getNodeName(ForkActionNodeDescriptionProvider.FORK_ACTION_NAME).equals(nodeDesc.getName()); isSpecial = isSpecial || this.descriptionNameGenerator.getNodeName(MergeActionNodeDescriptionProvider.MERGE_ACTION_NAME).equals(nodeDesc.getName()); isSpecial = isSpecial || this.descriptionNameGenerator.getNodeName(DecisionActionNodeDescriptionProvider.DECISION_ACTION_NAME).equals(nodeDesc.getName()); + isSpecial = isSpecial || this.descriptionNameGenerator.getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME).equals(nodeDesc.getName()); + isSpecial = isSpecial || this.descriptionNameGenerator.getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME).equals(nodeDesc.getName()); return !isSpecial; } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneStateNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneStateNodeToolProvider.java new file mode 100644 index 000000000..e18f75690 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/DoneStateNodeToolProvider.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * 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.diagram.common.view.tools; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StateTransitionCompartmentNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; + +/** + * Used to add the standard done state in states body for all diagrams. + * + * @author Jerome Gout + */ +public class DoneStateNodeToolProvider extends AbstractFreeFormCompartmentNodeToolProvider { + + public DoneStateNodeToolProvider(EClass ownerEClass, IDescriptionNameGenerator descriptionNameGenerator) { + super(ownerEClass, StateTransitionCompartmentNodeDescriptionProvider.STATE_COMPARTMENT_NAME, descriptionNameGenerator); + } + + @Override + protected String getNodeDescriptionName() { + return this.getDescriptionNameGenerator().getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME); + } + + @Override + protected String getCreationServiceCallExpression() { + return ServiceMethod.of0(ViewCreateService::addDoneState).aqlSelf(); + } + + @Override + protected String getLabel() { + return "New Done State"; + } + + @Override + protected String getIconPath() { + return "/icons/done_action.svg"; + } +} diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartStateNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartStateNodeToolProvider.java new file mode 100644 index 000000000..905672351 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartStateNodeToolProvider.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * 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.diagram.common.view.tools; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StateTransitionCompartmentNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.services.ViewCreateService; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; + +/** + * Used to add the standard start state in states body for all diagrams. + * + * @author Jerome Gout + */ +public class StartStateNodeToolProvider extends AbstractFreeFormCompartmentNodeToolProvider { + + public StartStateNodeToolProvider(EClass ownerEClass, IDescriptionNameGenerator descriptionNameGenerator) { + super(ownerEClass, StateTransitionCompartmentNodeDescriptionProvider.STATE_COMPARTMENT_NAME, descriptionNameGenerator); + } + + @Override + protected String getNodeDescriptionName() { + return this.getDescriptionNameGenerator().getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME); + } + + @Override + protected String getCreationServiceCallExpression() { + return ServiceMethod.of0(ViewCreateService::addStartState).aqlSelf(); + } + + @Override + protected String getLabel() { + return "New Start State"; + } + + @Override + protected String getIconPath() { + return "/icons/start_action.svg"; + } +} diff --git a/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/predicates/DiagramPredicates.java b/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/predicates/DiagramPredicates.java index 3f93fb293..96165f6c7 100644 --- a/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/predicates/DiagramPredicates.java +++ b/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/predicates/DiagramPredicates.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -20,7 +20,9 @@ import org.eclipse.sirius.components.view.diagram.DiagramElementDescription; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.SysMLMetamodelHelper; @@ -57,7 +59,9 @@ public DiagramPredicates(String diagramPrefix, IDescriptionNameGenerator descrip this.isInheritedBorderNode = n -> n.getName().contains(inheritedBorderNodeNameFragment); this.isNamespaceImportNode = n -> n.getName().equals(diagramPrefix + " Node NamespaceImport"); this.isStartOrDoneNode = n -> n.getName().equals(descriptionNameGenerator.getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME)) - || n.getName().equals(descriptionNameGenerator.getNodeName(DoneActionNodeDescriptionProvider.DONE_ACTION_NAME)); + || n.getName().equals(descriptionNameGenerator.getNodeName(DoneActionNodeDescriptionProvider.DONE_ACTION_NAME)) + || n.getName().equals(descriptionNameGenerator.getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME)) + || n.getName().equals(descriptionNameGenerator.getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME)); } public Predicate isFakeNode() { 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 290ef8c11..5f489117f 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 @@ -50,6 +50,7 @@ import org.eclipse.syson.diagram.common.view.nodes.CompartmentItemNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DecisionActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ForkActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ImportedPackageNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.InheritedCompartmentItemNodeDescriptionProvider; @@ -59,6 +60,7 @@ import org.eclipse.syson.diagram.common.view.nodes.SatisfyRequirementCompartmentItemNodeDescription; import org.eclipse.syson.diagram.common.view.nodes.SatisfyRequirementCompartmentNodeDescription; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StateTransitionCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StatesCompartmentItemNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StatesCompartmentNodeDescriptionProvider; @@ -860,6 +862,8 @@ private List> createAllCustomNodeDescripti customNodeDescriptionProviders.add(new StakeholderNodeDescriptionProvider(colorProvider)); customNodeDescriptionProviders.add(new SubjectNodeDescriptionProvider(colorProvider)); customNodeDescriptionProviders.add(new ImportedPackageNodeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); + customNodeDescriptionProviders.add(new StartStateNodeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); + customNodeDescriptionProviders.add(new DoneStateNodeDescriptionProvider(colorProvider, this.getDescriptionNameGenerator())); return customNodeDescriptionProviders; } diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/DefinitionNodeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/DefinitionNodeDescriptionProvider.java index eade01314..3cceba6bb 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/DefinitionNodeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/DefinitionNodeDescriptionProvider.java @@ -30,11 +30,13 @@ import org.eclipse.syson.diagram.common.view.nodes.AbstractDefinitionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DecisionActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ForkActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.JoinActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.MergeActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator; import org.eclipse.syson.standard.diagrams.view.SDVDiagramDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.services.SDVNodeToolSectionSwitch; @@ -123,6 +125,8 @@ protected List getAllNodeDescriptions(IViewDiagramElementFinder cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(ForkActionNodeDescriptionProvider.FORK_ACTION_NAME)).ifPresent(allNodes::add); cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(MergeActionNodeDescriptionProvider.MERGE_ACTION_NAME)).ifPresent(allNodes::add); cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(DecisionActionNodeDescriptionProvider.DECISION_ACTION_NAME)).ifPresent(allNodes::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME)).ifPresent(allNodes::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME)).ifPresent(allNodes::add); return allNodes; } diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/UsageNodeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/UsageNodeDescriptionProvider.java index 2e60eff45..12bee5bff 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/UsageNodeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/UsageNodeDescriptionProvider.java @@ -30,11 +30,13 @@ import org.eclipse.syson.diagram.common.view.nodes.AbstractUsageNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DecisionActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ForkActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.JoinActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.MergeActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; +import org.eclipse.syson.diagram.common.view.nodes.StartStateNodeDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator; import org.eclipse.syson.standard.diagrams.view.SDVDiagramDescriptionProvider; import org.eclipse.syson.standard.diagrams.view.services.SDVNodeToolSectionSwitch; @@ -135,6 +137,8 @@ protected List getAllNodeDescriptions(IViewDiagramElementFinder cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(ForkActionNodeDescriptionProvider.FORK_ACTION_NAME)).ifPresent(allNodes::add); cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(MergeActionNodeDescriptionProvider.MERGE_ACTION_NAME)).ifPresent(allNodes::add); cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(DecisionActionNodeDescriptionProvider.DECISION_ACTION_NAME)).ifPresent(allNodes::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(StartStateNodeDescriptionProvider.START_STATE_NAME)).ifPresent(allNodes::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getNodeName(DoneStateNodeDescriptionProvider.DONE_STATE_NAME)).ifPresent(allNodes::add); return allNodes; } diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SDVNodeToolSectionSwitch.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SDVNodeToolSectionSwitch.java index 1b4e286da..4d13515ec 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SDVNodeToolSectionSwitch.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SDVNodeToolSectionSwitch.java @@ -34,6 +34,7 @@ import org.eclipse.syson.diagram.common.view.tools.ConnectionDefinitionEndCompartmentNodeToolProvider; 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.DoneStateNodeToolProvider; 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.InterfaceDefinitionEndCompartmentNodeToolProvider; @@ -56,6 +57,7 @@ import org.eclipse.syson.diagram.common.view.tools.SetAsViewToolProvider; import org.eclipse.syson.diagram.common.view.tools.StakeholdersCompartmentNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.StartActionNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.StartStateNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.StateSubactionNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.StateTransitionCompartmentNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.SubjectCompartmentNodeToolProvider; @@ -734,6 +736,8 @@ public List caseRequirementDefinition(RequirementDefinition obj @Override public List caseStateDefinition(StateDefinition object) { var sections = this.toolDescriptionService.createDefaultNodeToolSections(); + this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new StartStateNodeToolProvider(object.eClass(), this.descriptionNameGenerator).create(this.cache)); + this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new DoneStateNodeToolProvider(object.eClass(), this.descriptionNameGenerator).create(this.cache)); this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new StateTransitionCompartmentNodeToolProvider(false).create(this.cache)); this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new StateTransitionCompartmentNodeToolProvider(true).create(this.cache)); this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new ExhibitStateNodeToolProvider(false).create(this.cache)); @@ -757,6 +761,8 @@ public List caseStateDefinition(StateDefinition object) { @Override public List caseStateUsage(StateUsage object) { var sections = this.toolDescriptionService.createDefaultNodeToolSections(); + this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new StartStateNodeToolProvider(object.eClass(), this.descriptionNameGenerator).create(this.cache)); + this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new DoneStateNodeToolProvider(object.eClass(), this.descriptionNameGenerator).create(this.cache)); this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new StateTransitionCompartmentNodeToolProvider(false).create(this.cache)); this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new StateTransitionCompartmentNodeToolProvider(true).create(this.cache)); this.toolDescriptionService.addNodeTool(sections, ToolConstants.BEHAVIOR, new ExhibitStateNodeToolProvider(false).create(this.cache)); diff --git a/doc/content/modules/user-manual/assets/images/release-notes-start-done-states.png b/doc/content/modules/user-manual/assets/images/release-notes-start-done-states.png new file mode 100644 index 0000000000000000000000000000000000000000..77a378c5fc002070d72c69078163b87428a71804 GIT binary patch literal 50156 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 KNEJz#Jo!I}v8qh~ literal 0 HcmV?d00001 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 new file mode 100644 index 000000000..6be143390 --- /dev/null +++ b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc @@ -0,0 +1,29 @@ += 2026.7.0 + +== Key highlights + + +== Breaking changes + + +== New features + +* In diagrams: + +** Add tools to create _start state_ and _done state_ on `StateUsage` and `StateDefinition` graphical nodes. ++ +image::release-notes-start-done-states.png[Tool to create a new Stakeholder, width=60%,height=60%] + +== Bug fixes + + +== Improvements + +* In diagrams: + +* In the _Explorer_ view: + + +== Technical details + +* For technical details on this {product} release, including breaking changes, please refer to the https://github.com/eclipse-syson/syson/blob/main/CHANGELOG.adoc[changelog].