From 486c5b4979e91056b6feb2d128a5e3d80a848913 Mon Sep 17 00:00:00 2001 From: Axel RICHARD Date: Mon, 13 Jan 2025 16:11:06 +0100 Subject: [PATCH] [961] Add valid default names for Elements Bug: https://github.com/eclipse-syson/syson/issues/961 Signed-off-by: Axel RICHARD --- CHANGELOG.adoc | 2 + ...AddNewFeatureTypingFromPartUsageTests.java | 10 +- .../GVSubNodeRequirementCreationTests.java | 2 +- ...ingConnectorAsUsageFromPartUsageTests.java | 14 +- ...ddNewFlowConnectionFromPartUsageTests.java | 14 +- .../IVAddNewInterfaceFromPartUsageTests.java | 23 ++- .../diagrams/testers/NodeCreationTester.java | 23 ++- .../services/ElementInitializerSwitch.java | 46 ++++- .../ElementInitializerSwitchTest.java | 173 ++++++++++++++++++ .../view/services/ViewCreateService.java | 70 +++---- .../InterconnectionViewCreateService.java | 46 ++--- ...InterconnectionViewCreateServiceTests.java | 14 +- .../pages/release-notes/2025.2.0.adoc | 3 + .../cypress/e2e/project/details/details.cy.ts | 4 +- .../diagrams/diagramCreationTests.cy.ts | 18 +- .../semanticElementCreationTests.cy.ts | 10 +- .../cypress/workbench/Explorer.ts | 3 +- 17 files changed, 358 insertions(+), 117 deletions(-) create mode 100644 backend/services/syson-services/src/test/java/org/eclipse/syson/services/ElementInitializerSwitchTest.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 26c173b1b..6ab3ebaf4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -14,6 +14,8 @@ - https://github.com/eclipse-syson/syson/issues/953[#953] [rest-apis] Fix an issue where the server could crash when successive REST APIs calls are executed. More precisely, the dates fields were not serialized correctly. +- https://github.com/eclipse-syson/syson/issues/961[#961] [core] Fix an issue where default names of Elements were sometimes invalid because corresponding to SysMLv2 keywords. +New default names now includes a number, this number corresponding to the count of Elements of the same kind in the scope. === Improvements diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddNewFeatureTypingFromPartUsageTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddNewFeatureTypingFromPartUsageTests.java index 8153e6670..120f850f7 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddNewFeatureTypingFromPartUsageTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVAddNewFeatureTypingFromPartUsageTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -136,9 +136,9 @@ public void tearDown() { private static Stream partUsageNodeParameters() { return Stream.of( - Arguments.of(SysmlPackage.eINSTANCE.getPartUsage(), "part", 7), - Arguments.of(SysmlPackage.eINSTANCE.getAllocationUsage(), "allocation", 2), - Arguments.of(SysmlPackage.eINSTANCE.getInterfaceUsage(), "interface", 4) + Arguments.of(SysmlPackage.eINSTANCE.getPartUsage(), "part1", 7), + Arguments.of(SysmlPackage.eINSTANCE.getAllocationUsage(), "allocation1", 2), + Arguments.of(SysmlPackage.eINSTANCE.getInterfaceUsage(), "interface1", 4) ); } @@ -156,7 +156,7 @@ public void testApplyTool(EClass eClass, String nodeName, int definitionCompartm var diagramAfterAddingElement = this.givenDiagram.getDiagram(this.verifier); - this.verifier.then(() -> this.nodeCreationTester.renameNode(SysMLv2Identifiers.GENERAL_VIEW_EMPTY_PROJECT, + this.verifier.then(() -> this.nodeCreationTester.renameRootNode(SysMLv2Identifiers.GENERAL_VIEW_EMPTY_PROJECT, diagramAfterAddingElement, nodeName, this.getNewName(nodeName))); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java index 8fe61877a..8c63994de 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVSubNodeRequirementCreationTests.java @@ -439,7 +439,7 @@ private void createNewStakeholderIn(EClass eClassWithStakeholderParameter, Strin final Element semanticRootElement = this.objectService.getObject(editingContext, SysMLv2Identifiers.GENERAL_VIEW_WITH_TOP_NODES_DIAGRAM_OBJECT).filter(Element.class::isInstance) .map(Element.class::cast).orElseGet(() -> Assertions.fail("Could not find the expected root semantic object.")); final List allStakeholderPartUsages = EMFUtils.allContainedObjectOfType(semanticRootElement, PartUsage.class) - .filter(element -> Objects.equals(element.getName(), "stakeholder")).toList(); + .filter(element -> Objects.equals(element.getName(), "stakeholder1")).toList(); assertEquals(1, allStakeholderPartUsages.size()); final PartUsage stakeholderPartUsage = allStakeholderPartUsages.get(0); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewBindingConnectorAsUsageFromPartUsageTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewBindingConnectorAsUsageFromPartUsageTests.java index af95520b1..a6cc90420 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewBindingConnectorAsUsageFromPartUsageTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewBindingConnectorAsUsageFromPartUsageTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -145,10 +145,12 @@ public void givenASysMLProjectWhenNewBindingConnectorAsUsageToolOfFirstLevelElem public void givenASysMLProjectWhenNewBindingConnectorAsUsageToolOfNestedElementIsRequestedOnAPartUsageThenANewPartUsageAndABindingConnectorAsUsageEdgeAreCreated() { String creationPartToolId = this.diagramDescriptionIdProvider.getNodeCreationToolId(this.descriptionNameGenerator.getFirstLevelNodeName(SysmlPackage.eINSTANCE.getPartUsage()), "New Part"); assertThat(creationPartToolId).as("The tool 'New Part' should exist on a first level PartUsage").isNotNull(); - this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, - this.diagram, - "part1", - creationPartToolId)); + + this.verifier.then(() -> this.nodeCreationTester.renameNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, this.diagram, "part1", "firstLevelPart")); + + var diagramAfterRenaming = this.givenDiagram.getDiagram(this.verifier); + + this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, diagramAfterRenaming, "firstLevelPart", creationPartToolId)); var diagramAfterNestedPartUsageCreation = this.givenDiagram.getDiagram(this.verifier); @@ -156,7 +158,7 @@ public void givenASysMLProjectWhenNewBindingConnectorAsUsageToolOfNestedElementI assertThat(creationToolId).as("The tool 'New Binding Connector As Usage' should exist on a nested PartUsage").isNotNull(); this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, diagramAfterNestedPartUsageCreation, - "part", + "part1", creationToolId)); IDiagramChecker diagramChecker = (initialDiagram, newDiagram) -> { diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewFlowConnectionFromPartUsageTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewFlowConnectionFromPartUsageTests.java index 3f3f876a0..0df944c59 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewFlowConnectionFromPartUsageTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewFlowConnectionFromPartUsageTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -146,10 +146,12 @@ public void givenASysMLProjectWhenNewFlowConnectionToolOfFirstLevelElementIsRequ public void givenASysMLProjectWhenNewFlowConnectionToolOfNestedElementIsRequestedOnAPartUsageThenANewPartUsageAndAFlowConnectionEdgeAreCreated() { String creationPartToolId = this.diagramDescriptionIdProvider.getNodeCreationToolId(this.descriptionNameGenerator.getFirstLevelNodeName(SysmlPackage.eINSTANCE.getPartUsage()), "New Part"); assertThat(creationPartToolId).as("The tool 'New Part' should exist on a first level PartUsage").isNotNull(); - this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, - this.diagram, - "part1", - creationPartToolId)); + + this.verifier.then(() -> this.nodeCreationTester.renameNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, this.diagram, "part1", "firstLevelPart")); + + var diagramAfterRenaming = this.givenDiagram.getDiagram(this.verifier); + + this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, diagramAfterRenaming, "firstLevelPart", creationPartToolId)); var diagramAfterNestedPartUsageCreation = this.givenDiagram.getDiagram(this.verifier); @@ -157,7 +159,7 @@ public void givenASysMLProjectWhenNewFlowConnectionToolOfNestedElementIsRequeste assertThat(creationToolId).as("The tool 'New Flow Connection' should exist on a nested PartUsage").isNotNull(); this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, diagramAfterNestedPartUsageCreation, - "part", + "part1", creationToolId)); IDiagramChecker diagramChecker = (initialDiagram, newDiagram) -> { diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewInterfaceFromPartUsageTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewInterfaceFromPartUsageTests.java index a96128615..91e0aae07 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewInterfaceFromPartUsageTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVAddNewInterfaceFromPartUsageTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -114,13 +114,13 @@ public void tearDown() { } } - @DisplayName("Given a SysML Project, when New Interface tool of first level element is requested on a PartUsage, then a new PartUsage and a Interface edge are created") + @DisplayName("Given a SysML Project, when New Interface tool of first level element is requested on a PartUsage, then a new PartUsage and an Interface edge are created") @Sql(scripts = { "/scripts/syson-test-database.sql" }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) @Test public void givenASysMLProjectWhenNewInterfaceToolIsRequestedOnAPartUsageThenANewPartUsageAndAInterfaceEdgeAreCreated() { String creationToolId = this.diagramDescriptionIdProvider.getNodeCreationToolId(this.descriptionNameGenerator.getFirstLevelNodeName(SysmlPackage.eINSTANCE.getPartUsage()), "New Interface"); - assertThat(creationToolId).as("The tool 'New Interface' should exist on a PartUsage").isNotNull(); + assertThat(creationToolId).as("The tool 'New Interface' should exist on a first level PartUsage").isNotNull(); this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, this.diagram, "part1", @@ -139,25 +139,28 @@ public void givenASysMLProjectWhenNewInterfaceToolIsRequestedOnAPartUsageThenANe this.diagramCheckerService.checkDiagram(diagramChecker, this.diagram, this.verifier); } - @DisplayName("Given a SysML Project, when New Interface tool of nested element is requested on a PartUsage, then a new PartUsage and a Interface edge are created") + @DisplayName("Given a SysML Project, when New Interface tool of nested element is requested on a PartUsage, then a new PartUsage and an Interface edge are created") @Sql(scripts = { "/scripts/syson-test-database.sql" }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) @Test - public void givenASysMLProjectWhenNewFlowConnectionToolOfNestedElementIsRequestedOnAPartUsageThenANewPartUsageAndAFlowConnectionEdgeAreCreated() { + public void givenASysMLProjectWhenNewInterfaceToolOfNestedElementIsRequestedOnAPartUsageThenANewPartUsageAndAnInterfaceEdgeAreCreated() { String creationPartToolId = this.diagramDescriptionIdProvider.getNodeCreationToolId(this.descriptionNameGenerator.getFirstLevelNodeName(SysmlPackage.eINSTANCE.getPartUsage()), "New Part"); assertThat(creationPartToolId).as("The tool 'New Part' should exist on a first level PartUsage").isNotNull(); - this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, - this.diagram, - "part1", - creationPartToolId)); + + this.verifier.then(() -> this.nodeCreationTester.renameNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, this.diagram, "part1", "firstLevelPart")); + + var diagramAfterRenaming = this.givenDiagram.getDiagram(this.verifier); + + this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, diagramAfterRenaming, "firstLevelPart", creationPartToolId)); var diagramAfterNestedPartUsageCreation = this.givenDiagram.getDiagram(this.verifier); String creationToolId = this.diagramDescriptionIdProvider.getNodeCreationToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getPartUsage()), "New Interface"); assertThat(creationToolId).as("The tool 'New Interface' should exist on a nested PartUsage").isNotNull(); + this.verifier.then(() -> this.nodeCreationTester.createNode(SysMLv2Identifiers.INTERCONNECTION_VIEW_WITH_TOP_NODES_PROJECT, diagramAfterNestedPartUsageCreation, - "part", + "part1", creationToolId)); IDiagramChecker diagramChecker = (initialDiagram, newDiagram) -> { diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/NodeCreationTester.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/NodeCreationTester.java index 0a201e7c8..d106f17d6 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/NodeCreationTester.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/NodeCreationTester.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -16,6 +16,7 @@ import com.jayway.jsonpath.JsonPath; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -82,7 +83,7 @@ public void createNode(String projectId, AtomicReference diagram, Strin assertThat(typename).isEqualTo(InvokeSingleClickOnDiagramElementToolSuccessPayload.class.getSimpleName()); } - public void renameNode(String projectId, AtomicReference diagram, String nodeName, String newName) { + public void renameRootNode(String projectId, AtomicReference diagram, String nodeName, String newName) { Optional optionalNode = diagram.get().getNodes().stream().filter(n -> n.getTargetObjectLabel().equals(nodeName)).findFirst(); assertThat(optionalNode).as("the node " + nodeName + " is not present in the diagram").isNotEmpty(); @@ -93,4 +94,22 @@ public void renameNode(String projectId, AtomicReference diagram, Strin String invokeSingleClickOnDiagramElementToolResultTypename = JsonPath.read(invokeSingleClickOnDiagramElementToolResult, "$.data.editLabel.__typename"); assertThat(invokeSingleClickOnDiagramElementToolResultTypename).isEqualTo(EditLabelSuccessPayload.class.getSimpleName()); } + + public void renameNode(String projectId, AtomicReference diagram, String nodeName, String newName) { + List nodes = new ArrayList<>(); + List rootNodes = diagram.get().getNodes(); + nodes.addAll(rootNodes); + for (Node node : rootNodes) { + nodes.addAll(node.getChildNodes()); + } + Optional optionalNode = nodes.stream().filter(n -> n.getTargetObjectLabel().equals(nodeName)).findFirst(); + + assertThat(optionalNode).as("the node " + nodeName + " is not present in the diagram").isNotEmpty(); + + var input = new EditLabelInput(UUID.randomUUID(), projectId, diagram.get().getId(), optionalNode.get().getInsideLabel().getId(), newName); + var invokeSingleClickOnDiagramElementToolResult = this.editLabelMutationRunner.run(input); + + String invokeSingleClickOnDiagramElementToolResultTypename = JsonPath.read(invokeSingleClickOnDiagramElementToolResult, "$.data.editLabel.__typename"); + assertThat(invokeSingleClickOnDiagramElementToolResultTypename).isEqualTo(EditLabelSuccessPayload.class.getSimpleName()); + } } diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ElementInitializerSwitch.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ElementInitializerSwitch.java index c5da1eca4..8fd91ad73 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ElementInitializerSwitch.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ElementInitializerSwitch.java @@ -27,6 +27,7 @@ import org.eclipse.syson.sysml.FeatureDirectionKind; import org.eclipse.syson.sysml.FeatureTyping; import org.eclipse.syson.sysml.FlowConnectionUsage; +import org.eclipse.syson.sysml.Namespace; import org.eclipse.syson.sysml.ObjectiveMembership; import org.eclipse.syson.sysml.OwningMembership; import org.eclipse.syson.sysml.Package; @@ -102,7 +103,8 @@ public Element caseDependency(Dependency object) { @Override public Element caseDefinition(Definition object) { - object.setDeclaredName(object.eClass().getName()); + var existingElements = this.existingElementsCount(object); + object.setDeclaredName(object.eClass().getName() + existingElements); return object; } @@ -120,7 +122,8 @@ public Element caseElement(Element object) { @Override public Element caseEnumerationDefinition(EnumerationDefinition object) { object.setIsVariation(true); - object.setDeclaredName(object.eClass().getName()); + var existingElements = this.existingElementsCount(object); + object.setDeclaredName(object.eClass().getName() + existingElements); return object; } @@ -136,7 +139,8 @@ public Element caseFlowConnectionUsage(FlowConnectionUsage object) { @Override public Element casePackage(Package object) { - object.setDeclaredName(object.eClass().getName()); + var existingElements = this.existingElementsCount(object); + object.setDeclaredName(object.eClass().getName() + existingElements); return object; } @@ -144,9 +148,23 @@ public Element casePackage(Package object) { public Element casePartUsage(PartUsage object) { this.caseUsage(object); if (object.getOwningMembership() instanceof ActorMembership) { - object.setDeclaredName("actor"); + long existingElements = 0; + Namespace owningNamespace = object.getOwningNamespace(); + if (owningNamespace != null) { + existingElements = owningNamespace.getOwnedMember().stream() + .filter(member -> object.eClass().equals(member.eClass()) && object.getOwningMembership() instanceof ActorMembership) + .count(); + } + object.setDeclaredName("actor" + existingElements); } else if (object.getOwningMembership() instanceof StakeholderMembership) { - object.setDeclaredName("stakeholder"); + long existingElements = 0; + Namespace owningNamespace = object.getOwningNamespace(); + if (owningNamespace != null) { + existingElements = owningNamespace.getOwnedMember().stream() + .filter(member -> object.eClass().equals(member.eClass()) && object.getOwningMembership() instanceof StakeholderMembership) + .count(); + } + object.setDeclaredName("stakeholder" + existingElements); } return object; } @@ -159,7 +177,8 @@ public Element casePerformActionUsage(PerformActionUsage object) { @Override public Element casePortDefinition(PortDefinition object) { - object.setDeclaredName(object.eClass().getName()); + var existingElements = this.existingElementsCount(object); + object.setDeclaredName(object.eClass().getName() + existingElements); OwningMembership owningMembership = SysmlFactory.eINSTANCE.createOwningMembership(); object.getOwnedRelationship().add(owningMembership); // No need to set the declaredName for the ConjugatedPortDefinition here, it is always the same than its @@ -236,11 +255,24 @@ public Element caseUsage(Usage object) { if (defaultName.endsWith("Usage")) { defaultName = defaultName.substring(0, defaultName.length() - 5); } - object.setDeclaredName(defaultName); + + var existingElements = this.existingElementsCount(object); + + object.setDeclaredName(defaultName + existingElements); object.setIsComposite(true); return object; } + private long existingElementsCount(Element element) { + Namespace owningNamespace = element.getOwningNamespace(); + if (owningNamespace != null) { + return owningNamespace.getOwnedMember().stream() + .filter(member -> element.eClass().equals(member.eClass())) + .count(); + } + return 0; + } + private ParameterMembership createParameterMembershipWithReferenceUsage(String refName, FeatureDirectionKind direction) { var reference = SysmlFactory.eINSTANCE.createReferenceUsage(); reference.setDirection(direction); diff --git a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/ElementInitializerSwitchTest.java b/backend/services/syson-services/src/test/java/org/eclipse/syson/services/ElementInitializerSwitchTest.java new file mode 100644 index 000000000..ddc55359e --- /dev/null +++ b/backend/services/syson-services/src/test/java/org/eclipse/syson/services/ElementInitializerSwitchTest.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2025 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.services; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.sirius.components.emf.services.EditingContextCrossReferenceAdapter; +import org.eclipse.sirius.emfjson.resource.JsonResourceFactoryImpl; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Membership; +import org.eclipse.syson.sysml.OwningMembership; +import org.eclipse.syson.sysml.Relationship; +import org.eclipse.syson.sysml.SysmlFactory; +import org.eclipse.syson.sysml.SysmlPackage; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * ElementInitializerSwitch-related tests. + * + * @author arichard + */ +public class ElementInitializerSwitchTest { + + private static final String ROOT = "root"; + + private ResourceSet rSet; + + private Resource resource; + + private ElementInitializerSwitch elementInitializerSwitch; + + @BeforeEach + public void setUp() { + this.rSet = new ResourceSetImpl(); + // Make sure the resources we manipulate use the Sirius Web CrossReferenceAdapter. + // This is particularly important when manipulating memberships, where the cross-referencer may produce + // unexpected behaviors that cannot be observed in a resource without it. + this.rSet.eAdapters().add(new EditingContextCrossReferenceAdapter()); + this.resource = new JsonResourceFactoryImpl().createResource(URI.createURI("ElementInitializerSwitchTest")); + this.rSet.getResources().add(this.resource); + this.elementInitializerSwitch = new ElementInitializerSwitch(); + } + + @DisplayName("Given a PartUsage, when it is initialized, then it's name contains the count of the same kind of elements") + @Test + public void testPartUsageDefaultName() { + var root = SysmlFactory.eINSTANCE.createPackage(); + this.resource.getContents().add(root); + root.setDeclaredName(ROOT); + var p1 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(p1, root); + var initializedP1 = this.elementInitializerSwitch.doSwitch(p1); + assertThat(initializedP1.getDeclaredName()).isEqualTo("part1"); + var p2 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(p2, root); + var initializedP2 = this.elementInitializerSwitch.doSwitch(p2); + assertThat(initializedP2.getDeclaredName()).isEqualTo("part2"); + } + + @DisplayName("Given a PartDefinition, when it is initialized, then it's name contains the count of the same kind of elements") + @Test + public void testPartDefinitionDefaultName() { + var root = SysmlFactory.eINSTANCE.createPackage(); + this.resource.getContents().add(root); + root.setDeclaredName(ROOT); + var p1 = SysmlFactory.eINSTANCE.createPartDefinition(); + this.addInParent(p1, root); + var initializedP1 = this.elementInitializerSwitch.doSwitch(p1); + assertThat(initializedP1.getDeclaredName()).isEqualTo("PartDefinition1"); + var p2 = SysmlFactory.eINSTANCE.createPartDefinition(); + this.addInParent(p2, root); + var initializedP2 = this.elementInitializerSwitch.doSwitch(p2); + assertThat(initializedP2.getDeclaredName()).isEqualTo("PartDefinition2"); + } + + @DisplayName("Given a EnumerationDefinition, when it is initialized, then it's name contains the count of the same kind of elements") + @Test + public void testEnumerationDefinitionDefaultName() { + var root = SysmlFactory.eINSTANCE.createPackage(); + this.resource.getContents().add(root); + root.setDeclaredName(ROOT); + var ed1 = SysmlFactory.eINSTANCE.createEnumerationDefinition(); + this.addInParent(ed1, root); + var initializedED1 = this.elementInitializerSwitch.doSwitch(ed1); + assertThat(initializedED1.getDeclaredName()).isEqualTo("EnumerationDefinition1"); + var ed2 = SysmlFactory.eINSTANCE.createEnumerationDefinition(); + this.addInParent(ed2, root); + var initializedED2 = this.elementInitializerSwitch.doSwitch(ed2); + assertThat(initializedED2.getDeclaredName()).isEqualTo("EnumerationDefinition2"); + } + + @DisplayName("Given a Package, when it is initialized, then it's name contains the count of the same kind of elements") + @Test + public void testPackageDefaultName() { + var root = SysmlFactory.eINSTANCE.createPackage(); + this.resource.getContents().add(root); + root.setDeclaredName(ROOT); + var p1 = SysmlFactory.eINSTANCE.createPackage(); + this.addInParent(p1, root); + var initializedP1 = this.elementInitializerSwitch.doSwitch(p1); + assertThat(initializedP1.getDeclaredName()).isEqualTo("Package1"); + var p2 = SysmlFactory.eINSTANCE.createPackage(); + this.addInParent(p2, root); + var initializedP2 = this.elementInitializerSwitch.doSwitch(p2); + assertThat(initializedP2.getDeclaredName()).isEqualTo("Package2"); + } + + @DisplayName("Given an Actor, when it is initialized, then it's name contains the count of the same kind of elements") + @Test + public void testActorDefaultName() { + var root = SysmlFactory.eINSTANCE.createPackage(); + this.resource.getContents().add(root); + root.setDeclaredName(ROOT); + var p1 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(p1, root); + var a1 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(a1, p1, SysmlPackage.eINSTANCE.getActorMembership()); + var initializedA1 = this.elementInitializerSwitch.doSwitch(a1); + assertThat(initializedA1.getDeclaredName()).isEqualTo("actor1"); + var a2 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(a2, p1, SysmlPackage.eINSTANCE.getActorMembership()); + var initializedA2 = this.elementInitializerSwitch.doSwitch(a2); + assertThat(initializedA2.getDeclaredName()).isEqualTo("actor2"); + } + + @DisplayName("Given a Stakeholder, when it is initialized, then it's name contains the count of the same kind of elements") + @Test + public void testStakeholderDefaultName() { + var root = SysmlFactory.eINSTANCE.createPackage(); + this.resource.getContents().add(root); + root.setDeclaredName(ROOT); + var p1 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(p1, root); + var s1 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(s1, p1, SysmlPackage.eINSTANCE.getStakeholderMembership()); + var initializedS1 = this.elementInitializerSwitch.doSwitch(s1); + assertThat(initializedS1.getDeclaredName()).isEqualTo("stakeholder1"); + var s2 = SysmlFactory.eINSTANCE.createPartUsage(); + this.addInParent(s2, p1, SysmlPackage.eINSTANCE.getStakeholderMembership()); + var initializedS2 = this.elementInitializerSwitch.doSwitch(s2); + assertThat(initializedS2.getDeclaredName()).isEqualTo("stakeholder2"); + } + + private void addInParent(Element element, Element parent) { + OwningMembership owningMembership = SysmlFactory.eINSTANCE.createOwningMembership(); + parent.getOwnedRelationship().add(owningMembership); + owningMembership.getOwnedRelatedElement().add(element); + } + + private void addInParent(Element element, Element parent, EClass membershipKind) { + var membership = SysmlFactory.eINSTANCE.create(membershipKind); + assertThat(membership).isInstanceOf(Membership.class); + parent.getOwnedRelationship().add((Relationship) membership); + ((Relationship) membership).getOwnedRelatedElement().add(element); + } +} 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 5f0acb95c..9d0ee397f 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 @@ -220,10 +220,10 @@ public Element createCompartmentItem(Element element, String eReferenceName) { if (feature.getEType() instanceof EClass itemEClass) { var item = SysmlFactory.eINSTANCE.create(itemEClass); if (item instanceof Element elementItem) { - result = elementItem; var membership = this.createAppropriateMembership(feature); - membership.getOwnedRelatedElement().add(this.elementInitializer(elementItem)); element.getOwnedRelationship().add(membership); + membership.getOwnedRelatedElement().add(elementItem); + result = this.elementInitializer(elementItem); } } return result; @@ -235,14 +235,14 @@ public Element createCompartmentItemWithDirection(Element element, String eRefer if (structuralFeature.getEType() instanceof EClass itemEClass) { var item = SysmlFactory.eINSTANCE.create(itemEClass); if (item instanceof Element elementItem) { - result = this.elementInitializer(elementItem); + var membership = this.createAppropriateMembership(structuralFeature); + element.getOwnedRelationship().add(membership); + membership.getOwnedRelatedElement().add(result); if (directionLiteral != null && item instanceof Feature feature) { feature.setDirection(FeatureDirectionKind.get(directionLiteral)); result.setDeclaredName(result.getName() + StringUtils.capitalize(directionLiteral)); } - var membership = this.createAppropriateMembership(structuralFeature); - membership.getOwnedRelatedElement().add(result); - element.getOwnedRelationship().add(membership); + result = this.elementInitializer(elementItem); } } return result; @@ -588,10 +588,10 @@ private Package getClosestContainingPackageFrom(Element element) { public Element createAcceptAction(Element ownerElement) { if (this.isPart(ownerElement) || this.isAction(ownerElement)) { var featureMember = SysmlFactory.eINSTANCE.createFeatureMembership(); + ownerElement.getOwnedRelationship().add(featureMember); var acceptAction = SysmlFactory.eINSTANCE.createAcceptActionUsage(); - this.elementInitializerSwitch.doSwitch(acceptAction); featureMember.getOwnedRelatedElement().add(acceptAction); - ownerElement.getOwnedRelationship().add(featureMember); + this.elementInitializerSwitch.doSwitch(acceptAction); return acceptAction; } return ownerElement; @@ -751,7 +751,9 @@ public Element createSuccessionEdge(Element successionSource, Element succession private Element createSuccessionEdge(Element successionSource, Element successionTarget, EObject successionOwner) { if (successionOwner instanceof Element ownerElement) { var featureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); + ownerElement.getOwnedRelationship().add(featureMembership); var succession = SysmlFactory.eINSTANCE.createSuccessionAsUsage(); + featureMembership.getOwnedRelatedElement().add(succession); this.elementInitializerSwitch.doSwitch(succession); var sourceEnd = this.createEndFeatureMembershipFor(successionSource); var targetEnd = this.createEndFeatureMembershipFor(successionTarget); @@ -761,8 +763,6 @@ private Element createSuccessionEdge(Element successionSource, Element successio // to be able to retrieve Membership element holding standard actions. succession.getSource().add(successionSource); succession.getTarget().add(successionTarget); - featureMembership.getOwnedRelatedElement().add(succession); - ownerElement.getOwnedRelationship().add(featureMembership); } return successionSource; } @@ -829,11 +829,11 @@ public Membership addDoneAction(Element ownerElement) { * @return the newly created action usage. */ public ActionUsage createSubActionUsage(Element ownerElement) { - var newActionUsage = SysmlFactory.eINSTANCE.createActionUsage(); - this.elementInitializerSwitch.doSwitch(newActionUsage); var featureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); - featureMembership.getOwnedRelatedElement().add(newActionUsage); ownerElement.getOwnedRelationship().add(featureMembership); + var newActionUsage = SysmlFactory.eINSTANCE.createActionUsage(); + featureMembership.getOwnedRelatedElement().add(newActionUsage); + this.elementInitializerSwitch.doSwitch(newActionUsage); return newActionUsage; } @@ -890,10 +890,10 @@ public Element createJoinAction(Element ownerElement) { public Element createForkAction(Element ownerElement) { if (this.isPart(ownerElement) || this.isAction(ownerElement)) { var featureMember = SysmlFactory.eINSTANCE.createFeatureMembership(); + ownerElement.getOwnedRelationship().add(featureMember); var fork = SysmlFactory.eINSTANCE.createForkNode(); - this.elementInitializerSwitch.doSwitch(fork); featureMember.getOwnedRelatedElement().add(fork); - ownerElement.getOwnedRelationship().add(featureMember); + this.elementInitializerSwitch.doSwitch(fork); return fork; } return ownerElement; @@ -928,10 +928,10 @@ public Element createMergeAction(Element ownerElement) { public Element createDecisionAction(Element ownerElement) { if (this.isPart(ownerElement) || this.isAction(ownerElement)) { var featureMember = SysmlFactory.eINSTANCE.createFeatureMembership(); + ownerElement.getOwnedRelationship().add(featureMember); var decision = SysmlFactory.eINSTANCE.createDecisionNode(); - this.elementInitializerSwitch.doSwitch(decision); featureMember.getOwnedRelatedElement().add(decision); - ownerElement.getOwnedRelationship().add(featureMember); + this.elementInitializerSwitch.doSwitch(decision); return decision; } return ownerElement; @@ -947,10 +947,10 @@ public Element createDecisionAction(Element ownerElement) { public Element createAssignmentAction(Element ownerElement) { if (ownerElement instanceof ActionUsage || ownerElement instanceof ActionDefinition) { var featureMember = SysmlFactory.eINSTANCE.createFeatureMembership(); + ownerElement.getOwnedRelationship().add(featureMember); var assignmentAction = SysmlFactory.eINSTANCE.createAssignmentActionUsage(); - this.elementInitializerSwitch.doSwitch(assignmentAction); featureMember.getOwnedRelatedElement().add(assignmentAction); - ownerElement.getOwnedRelationship().add(featureMember); + this.elementInitializerSwitch.doSwitch(assignmentAction); return assignmentAction; } return ownerElement; @@ -958,32 +958,32 @@ public Element createAssignmentAction(Element ownerElement) { public Element createPerform(Element ownerElement) { // create an action usage as the performed action of this perform action + var performedFeatureMember = this.createMembership(ownerElement); + ownerElement.getOwnedRelationship().add(performedFeatureMember); var performedAction = SysmlFactory.eINSTANCE.createActionUsage(); + performedFeatureMember.getOwnedRelatedElement().add(performedAction); this.elementInitializerSwitch.doSwitch(performedAction); performedAction.setDeclaredName("performedAction"); - var performedFeatureMember = this.createMembership(ownerElement); - performedFeatureMember.getOwnedRelatedElement().add(performedAction); - ownerElement.getOwnedRelationship().add(performedFeatureMember); // create the perform action + var featureMember = this.createMembership(ownerElement); + ownerElement.getOwnedRelationship().add(featureMember); var perform = SysmlFactory.eINSTANCE.createPerformActionUsage(); + featureMember.getOwnedRelatedElement().add(perform); this.elementInitializerSwitch.doSwitch(perform); // set the reference subsetting relationship to the performed action var referenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); referenceSubsetting.setReferencedFeature(performedAction); perform.getOwnedRelationship().add(referenceSubsetting); - var featureMember = this.createMembership(ownerElement); - featureMember.getOwnedRelatedElement().add(perform); - ownerElement.getOwnedRelationship().add(featureMember); return perform; } public Element createPerformAction(Element ownerElement) { - var perform = SysmlFactory.eINSTANCE.createPerformActionUsage(); - this.elementInitializerSwitch.doSwitch(perform); // no subsetting relationship for the performed action since it is the same as the perform action var featureMember = this.createMembership(ownerElement); - featureMember.getOwnedRelatedElement().add(perform); ownerElement.getOwnedRelationship().add(featureMember); + var perform = SysmlFactory.eINSTANCE.createPerformActionUsage(); + featureMember.getOwnedRelatedElement().add(perform); + this.elementInitializerSwitch.doSwitch(perform); return perform; } @@ -1006,11 +1006,11 @@ public PartUsage createPartUsageAndSubsetting(PartUsage self) { var parent = self.getOwner(); if (parent != null) { // create a new part usage - var newPartUsage = SysmlFactory.eINSTANCE.createPartUsage(); - this.elementInitializer(newPartUsage); var membership = SysmlFactory.eINSTANCE.createOwningMembership(); - membership.getOwnedRelatedElement().add(newPartUsage); parent.getOwnedRelationship().add(membership); + var newPartUsage = SysmlFactory.eINSTANCE.createPartUsage(); + membership.getOwnedRelatedElement().add(newPartUsage); + this.elementInitializer(newPartUsage); // create subsetting edge between self and new part usage this.utilService.setSubsetting(self, newPartUsage); return newPartUsage; @@ -1029,11 +1029,11 @@ public Element createPartDefinitionAndFeatureTyping(PartUsage self) { var parent = self.getOwner(); if (parent != null) { // create a new definition associated to the given part usage - var newPartDefinition = this.utilService.createPartDefinitionFrom(self); - this.elementInitializerSwitch.doSwitch(newPartDefinition); var membership = SysmlFactory.eINSTANCE.createOwningMembership(); - membership.getOwnedRelatedElement().add(newPartDefinition); parent.getOwnedRelationship().add(membership); + var newPartDefinition = this.utilService.createPartDefinitionFrom(self); + membership.getOwnedRelatedElement().add(newPartDefinition); + this.elementInitializerSwitch.doSwitch(newPartDefinition); // create feature typing edge between self and new part definition this.utilService.setFeatureTyping(self, newPartDefinition); return newPartDefinition; @@ -1044,9 +1044,9 @@ public Element createPartDefinitionAndFeatureTyping(PartUsage self) { public Element createNamespaceImport(Element self, Namespace importedNamespace) { if (self instanceof Namespace namespace) { var namespaceImport = SysmlFactory.eINSTANCE.createNamespaceImport(); + namespace.getOwnedRelationship().add(namespaceImport); this.elementInitializer(namespaceImport); namespaceImport.setImportedNamespace(importedNamespace); - self.getOwnedRelationship().add(namespaceImport); return namespaceImport; } return self; diff --git a/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/services/InterconnectionViewCreateService.java b/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/services/InterconnectionViewCreateService.java index 6271b1b00..3ba28bee7 100644 --- a/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/services/InterconnectionViewCreateService.java +++ b/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/services/InterconnectionViewCreateService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2024 Obeo. + * Copyright (c) 2023, 2025 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 @@ -64,29 +64,29 @@ public BindingConnectorAsUsage createBindingConnectorAsUsage(Feature source, Fea } BindingConnectorAsUsage bindingConnectorAsUsage = SysmlFactory.eINSTANCE.createBindingConnectorAsUsage(); - this.elementInitializer(bindingConnectorAsUsage); this.addChildInParent(bindingContainer, bindingConnectorAsUsage); + this.elementInitializer(bindingConnectorAsUsage); EndFeatureMembership sourceEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership(); bindingConnectorAsUsage.getOwnedRelationship().add(sourceEndFeatureMembership); Feature sourceFeature = SysmlFactory.eINSTANCE.createFeature(); - this.elementInitializer(sourceFeature); sourceFeature.setIsEnd(true); sourceEndFeatureMembership.getOwnedRelatedElement().add(sourceFeature); + this.elementInitializer(sourceFeature); ReferenceSubsetting sourceReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); - this.elementInitializer(sourceReferenceSubsetting); sourceFeature.getOwnedRelationship().add(sourceReferenceSubsetting); + this.elementInitializer(sourceReferenceSubsetting); sourceReferenceSubsetting.setReferencedFeature(source); EndFeatureMembership targetEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership(); bindingConnectorAsUsage.getOwnedRelationship().add(targetEndFeatureMembership); Feature targetFeature = SysmlFactory.eINSTANCE.createFeature(); - this.elementInitializer(sourceFeature); targetFeature.setIsEnd(true); targetEndFeatureMembership.getOwnedRelatedElement().add(targetFeature); + this.elementInitializer(sourceFeature); ReferenceSubsetting targetReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); - this.elementInitializer(targetReferenceSubsetting); targetFeature.getOwnedRelationship().add(targetReferenceSubsetting); + this.elementInitializer(targetReferenceSubsetting); targetReferenceSubsetting.setReferencedFeature(target); return bindingConnectorAsUsage; @@ -99,32 +99,32 @@ public InterfaceUsage createInterfaceUsage(PortUsage sourcePort, PortUsage targe } InterfaceUsage interfaceUsage = SysmlFactory.eINSTANCE.createInterfaceUsage(); + this.addChildInParent(interfaceContainer, interfaceUsage); this.elementInitializer(interfaceUsage); // Edges should have an empty default name. This is not the case when using the initializer, because // InterfaceUsage can be a node, which requires a default name. interfaceUsage.setDeclaredName(""); - this.addChildInParent(interfaceContainer, interfaceUsage); EndFeatureMembership sourceEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership(); interfaceUsage.getOwnedRelationship().add(sourceEndFeatureMembership); Feature sourceFeature = SysmlFactory.eINSTANCE.createFeature(); - this.elementInitializer(sourceFeature); sourceFeature.setIsEnd(true); sourceEndFeatureMembership.getOwnedRelatedElement().add(sourceFeature); + this.elementInitializer(sourceFeature); ReferenceSubsetting sourceReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); - this.elementInitializer(sourceReferenceSubsetting); sourceFeature.getOwnedRelationship().add(sourceReferenceSubsetting); + this.elementInitializer(sourceReferenceSubsetting); sourceReferenceSubsetting.setReferencedFeature(sourcePort); EndFeatureMembership targetEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership(); interfaceUsage.getOwnedRelationship().add(targetEndFeatureMembership); Feature targetFeature = SysmlFactory.eINSTANCE.createFeature(); - this.elementInitializer(targetFeature); targetFeature.setIsEnd(true); targetEndFeatureMembership.getOwnedRelatedElement().add(targetFeature); + this.elementInitializer(targetFeature); ReferenceSubsetting targetReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); - this.elementInitializer(targetReferenceSubsetting); targetFeature.getOwnedRelationship().add(targetReferenceSubsetting); + this.elementInitializer(targetReferenceSubsetting); targetReferenceSubsetting.setReferencedFeature(targetPort); return interfaceUsage; @@ -139,8 +139,8 @@ public FlowConnectionUsage createFlowConnectionUsage(Feature source, Feature tar flowContainer.getOwnedRelationship().add(featureMembership); FlowConnectionUsage flowConnectionUsage = SysmlFactory.eINSTANCE.createFlowConnectionUsage(); - this.elementInitializer(flowConnectionUsage); this.addChildInParent(flowContainer, flowConnectionUsage); + this.elementInitializer(flowConnectionUsage); EndFeatureMembership sourceEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership(); flowConnectionUsage.getOwnedRelationship().add(sourceEndFeatureMembership); @@ -156,12 +156,12 @@ public FlowConnectionUsage createFlowConnectionUsage(Feature source, Feature tar EndFeatureMembership targetEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership(); flowConnectionUsage.getOwnedRelationship().add(targetEndFeatureMembership); Feature targetFeature = SysmlFactory.eINSTANCE.createFeature(); - this.elementInitializer(targetFeature); targetFeature.setIsEnd(true); targetEndFeatureMembership.getOwnedRelatedElement().add(targetFeature); + this.elementInitializer(targetFeature); ReferenceSubsetting targetReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting(); - this.elementInitializer(targetReferenceSubsetting); targetFeature.getOwnedRelationship().add(targetReferenceSubsetting); + this.elementInitializer(targetReferenceSubsetting); targetReferenceSubsetting.setReferencedFeature(target); return flowConnectionUsage; @@ -172,16 +172,16 @@ public Element createPartUsageAndBindingConnectorAsUsage(PartUsage self) { if (parent != null) { // create a new port on given part usage var newSelfPort = SysmlFactory.eINSTANCE.createPortUsage(); - this.elementInitializer(newSelfPort); this.addChildInParent(self, newSelfPort); + this.elementInitializer(newSelfPort); // create a new part usage as a self sibling var newPartUsage = SysmlFactory.eINSTANCE.createPartUsage(); - this.elementInitializer(newPartUsage); this.addChildInParent(parent, newPartUsage); + this.elementInitializer(newPartUsage); // create a new port on the new part usage var newPartUsagePort = SysmlFactory.eINSTANCE.createPortUsage(); - this.elementInitializer(newPartUsagePort); this.addChildInParent(newPartUsage, newPartUsagePort); + this.elementInitializer(newPartUsagePort); // create binding connector as usage edge between both new ports this.createBindingConnectorAsUsage(newSelfPort, newPartUsagePort); return newPartUsage; @@ -194,16 +194,16 @@ public Element createPartUsageAndFlowConnection(PartUsage self) { if (parent != null) { // create a new port on given part usage var newSelfPort = SysmlFactory.eINSTANCE.createPortUsage(); - this.elementInitializer(newSelfPort); this.addChildInParent(self, newSelfPort); + this.elementInitializer(newSelfPort); // create a new part usage as a self sibling var newPartUsage = SysmlFactory.eINSTANCE.createPartUsage(); - this.elementInitializer(newPartUsage); this.addChildInParent(parent, newPartUsage); + this.elementInitializer(newPartUsage); // create a new port on the new part usage var newPartUsagePort = SysmlFactory.eINSTANCE.createPortUsage(); - this.elementInitializer(newPartUsagePort); this.addChildInParent(newPartUsage, newPartUsagePort); + this.elementInitializer(newPartUsagePort); // create flow connection edge between both new ports this.createFlowConnectionUsage(newSelfPort, newPartUsagePort); return newPartUsage; @@ -216,16 +216,16 @@ public Element createPartUsageAndInterface(PartUsage self) { if (parent != null) { // create a new port on given part usage var newSelfPort = SysmlFactory.eINSTANCE.createPortUsage(); - this.elementInitializer(newSelfPort); this.addChildInParent(self, newSelfPort); + this.elementInitializer(newSelfPort); // create a new part usage as a self sibling var newPartUsage = SysmlFactory.eINSTANCE.createPartUsage(); - this.elementInitializer(newPartUsage); this.addChildInParent(parent, newPartUsage); + this.elementInitializer(newPartUsage); // create a new port on the new part usage var newPartUsagePort = SysmlFactory.eINSTANCE.createPortUsage(); - this.elementInitializer(newPartUsagePort); this.addChildInParent(newPartUsage, newPartUsagePort); + this.elementInitializer(newPartUsagePort); // create interface edge between both new ports this.createInterfaceUsage(newSelfPort, newPartUsagePort); return newPartUsage; diff --git a/backend/views/syson-diagram-interconnection-view/src/test/java/org/eclipse/syson/diagram/interconnection/view/InterconnectionViewCreateServiceTests.java b/backend/views/syson-diagram-interconnection-view/src/test/java/org/eclipse/syson/diagram/interconnection/view/InterconnectionViewCreateServiceTests.java index f0d65a0a0..dde6f1543 100644 --- a/backend/views/syson-diagram-interconnection-view/src/test/java/org/eclipse/syson/diagram/interconnection/view/InterconnectionViewCreateServiceTests.java +++ b/backend/views/syson-diagram-interconnection-view/src/test/java/org/eclipse/syson/diagram/interconnection/view/InterconnectionViewCreateServiceTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -32,6 +32,7 @@ import org.eclipse.syson.sysml.Namespace; import org.eclipse.syson.sysml.OwningMembership; import org.eclipse.syson.sysml.Package; +import org.eclipse.syson.sysml.PartDefinition; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.PortUsage; import org.eclipse.syson.sysml.util.ModelBuilder; @@ -52,6 +53,8 @@ public class InterconnectionViewCreateServiceTests { private Package rootPkg; + private PartDefinition partDef1; + private PartUsage part1; private PartUsage part2; @@ -94,7 +97,7 @@ public void testCreateBindingConnectorAsUsage() { BindingConnectorAsUsage bindingConnectorAsUsage = this.interconnectionViewCreateService.createBindingConnectorAsUsage(this.port2, this.port3); assertNotNull(bindingConnectorAsUsage); var owningNamespace = bindingConnectorAsUsage.getOwningNamespace(); - assertSame(this.rootPkg, owningNamespace); + assertSame(this.partDef1, owningNamespace); var owningMembership = bindingConnectorAsUsage.getOwningMembership(); assertInstanceOf(OwningMembership.class, owningMembership); } @@ -105,7 +108,7 @@ public void testCreateInterfaceUsage() { InterfaceUsage interfaceUsage = this.interconnectionViewCreateService.createInterfaceUsage(this.port2, this.port3); assertNotNull(interfaceUsage); var owningNamespace = interfaceUsage.getOwningNamespace(); - assertSame(this.rootPkg, owningNamespace); + assertSame(this.partDef1, owningNamespace); var owningMembership = interfaceUsage.getOwningMembership(); assertInstanceOf(OwningMembership.class, owningMembership); } @@ -116,7 +119,7 @@ public void testCreateFlowConnectionUsage() { FlowConnectionUsage flowConnectionUsage = this.interconnectionViewCreateService.createFlowConnectionUsage(this.port2, this.port3); assertNotNull(flowConnectionUsage); var owningNamespace = flowConnectionUsage.getOwningNamespace(); - assertSame(this.rootPkg, owningNamespace); + assertSame(this.partDef1, owningNamespace); var owningMembership = flowConnectionUsage.getOwningMembership(); assertInstanceOf(OwningMembership.class, owningMembership); } @@ -124,7 +127,8 @@ public void testCreateFlowConnectionUsage() { private void build() { Namespace rootNamespace = this.builder.createRootNamespace(); this.rootPkg = this.builder.createInWithName(org.eclipse.syson.sysml.Package.class, rootNamespace, "RootPkg"); - this.part1 = this.builder.createInWithName(PartUsage.class, this.rootPkg, "part1"); + this.partDef1 = this.builder.createInWithName(PartDefinition.class, this.rootPkg, "PartDefinition1"); + this.part1 = this.builder.createInWithName(PartUsage.class, this.partDef1, "part1"); this.part2 = this.builder.createInWithName(PartUsage.class, this.part1, "part2"); this.port2 = this.builder.createInWithName(PortUsage.class, this.part2, "port2"); this.part3 = this.builder.createInWithName(PartUsage.class, this.part1, "part3"); diff --git a/doc/content/modules/user-manual/pages/release-notes/2025.2.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2025.2.0.adoc index acc0e9c96..b7e1110dc 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2025.2.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2025.2.0.adoc @@ -11,6 +11,9 @@ - Fix an issue where the server could crash when successive REST APIs calls are executed. More precisely, the dates fields were not serialized correctly. +- Fix an issue where default names of `Elements` were sometimes invalid because corresponding to SysMLv2 keywords. +New default names now includes a number, this number corresponding to the count of `Elements` of the same kind in the scope. +For example, if a `PartDefinition` already define 5 parts, then a new `Part` created under this `PartDefinition` would have _part6_ for name. == New features diff --git a/integration-tests/cypress/e2e/project/details/details.cy.ts b/integration-tests/cypress/e2e/project/details/details.cy.ts index f6793b77f..f904c57e0 100644 --- a/integration-tests/cypress/e2e/project/details/details.cy.ts +++ b/integration-tests/cypress/e2e/project/details/details.cy.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -48,7 +48,7 @@ describe('Details View Tests', () => { context('When we select a PartUsage without type', () => { it('Then the details view contains the extra property "Typed by" even if the PartUsage has no type yet.', () => { explorer.createObject(batmobile.getRootElementLabel(), 'SysMLv2EditService-PartUsage'); - explorer.select('part'); + explorer.select('part2'); details.getGroup('Part Properties').should('be.visible'); details.getReferenceWidget('Typed by').should('exist'); details.getReferenceWidgetSelectedValue('Typed by', '').should('not.exist'); diff --git a/integration-tests/cypress/e2e/project/diagrams/diagramCreationTests.cy.ts b/integration-tests/cypress/e2e/project/diagrams/diagramCreationTests.cy.ts index 45fde81fb..e345e684d 100644 --- a/integration-tests/cypress/e2e/project/diagrams/diagramCreationTests.cy.ts +++ b/integration-tests/cypress/e2e/project/diagrams/diagramCreationTests.cy.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -55,12 +55,12 @@ describe('Diagram Creation Tests', () => { beforeEach(() => explorer.createObject(sysmlv2.getRootElementLabel(), 'SysMLv2EditService-PartUsage')); it('Then we can create a General View diagram', () => { - explorer.createRepresentation('part', 'General View', 'generalView'); + explorer.createRepresentation('part1', 'General View', 'generalView'); diagram.getDiagram('generalView').should('exist'); }); it('Then we can create an Interconnection View diagram', () => { - explorer.createRepresentation('part', 'Interconnection View', 'interconnectionView'); + explorer.createRepresentation('part1', 'Interconnection View', 'interconnectionView'); diagram.getDiagram('interconnectionView').should('exist'); }); }); @@ -69,17 +69,17 @@ describe('Diagram Creation Tests', () => { beforeEach(() => explorer.createObject(sysmlv2.getRootElementLabel(), 'SysMLv2EditService-ActionUsage')); it('Then we can create a General View diagram', () => { - explorer.createRepresentation('action', 'General View', 'generalView'); + explorer.createRepresentation('action1', 'General View', 'generalView'); diagram.getDiagram('generalView').should('exist'); }); it('Then we can create an Interconnection View diagram', () => { - explorer.createRepresentation('action', 'Interconnection View', 'interconnectionView'); + explorer.createRepresentation('action1', 'Interconnection View', 'interconnectionView'); diagram.getDiagram('interconnectionView').should('exist'); }); it('Then we can create an Action Flow View diagram', () => { - explorer.createRepresentation('action', 'Action Flow View', 'actionFlowView'); + explorer.createRepresentation('action1', 'Action Flow View', 'actionFlowView'); diagram.getDiagram('actionFlowView').should('exist'); }); }); @@ -88,17 +88,17 @@ describe('Diagram Creation Tests', () => { beforeEach(() => explorer.createObject(sysmlv2.getRootElementLabel(), 'SysMLv2EditService-ActionDefinition')); it('Then we can create a General View diagram', () => { - explorer.createRepresentation('ActionDefinition', 'General View', 'generalView'); + explorer.createRepresentation('ActionDefinition1', 'General View', 'generalView'); diagram.getDiagram('generalView').should('exist'); }); it('Then we can create an Interconnection View diagram', () => { - explorer.createRepresentation('ActionDefinition', 'Interconnection View', 'interconnectionView'); + explorer.createRepresentation('ActionDefinition1', 'Interconnection View', 'interconnectionView'); diagram.getDiagram('interconnectionView').should('exist'); }); it('Then we can create an Action Flow View diagram', () => { - explorer.createRepresentation('ActionDefinition', 'Action Flow View', 'actionFlowView'); + explorer.createRepresentation('ActionDefinition1', 'Action Flow View', 'actionFlowView'); diagram.getDiagram('actionFlowView').should('exist'); }); }); diff --git a/integration-tests/cypress/e2e/project/explorer/semanticElementCreationTests.cy.ts b/integration-tests/cypress/e2e/project/explorer/semanticElementCreationTests.cy.ts index bf2e2026f..c9af4a2e2 100644 --- a/integration-tests/cypress/e2e/project/explorer/semanticElementCreationTests.cy.ts +++ b/integration-tests/cypress/e2e/project/explorer/semanticElementCreationTests.cy.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -45,14 +45,14 @@ describe('Semantic Element Creation Tests', () => { explorer.createRootObject(sysmlv2.getProjectLabel(), 'SysMLv2EditService-PartUsage'); // Check that the created part is a child of the root document explorer - .getTreeItemByLabel('part') + .getTreeItemByLabel('part1') .parents('ul') .first() .siblings() .contains(sysmlv2.getProjectLabel()) .should('exist'); // Check that the created part is a sibling of Package 1 - explorer.getTreeItemByLabel('part').parents('li').first().siblings().contains('Package 1').should('exist'); + explorer.getTreeItemByLabel('part1').parents('li').first().siblings().contains('Package 1').should('exist'); }); it('Then we can create a PartUsage in it and it will be set as a child of the root namespace (when root namespaces are visible)', () => { @@ -61,14 +61,14 @@ describe('Semantic Element Creation Tests', () => { explorer.createRootObject(sysmlv2.getProjectLabel(), 'SysMLv2EditService-PartUsage'); // Check that the created part is a child of the root Namespace explorer - .getTreeItemByLabel('part') + .getTreeItemByLabel('part1') .parents('ul') .first() .siblings() .contains(sysmlv2.getRootNamespaceLabel()) .should('exist'); // Check that the created part is a sibling of Package 1 - explorer.getTreeItemByLabel('part').parents('li').first().siblings().contains('Package 1').should('exist'); + explorer.getTreeItemByLabel('part1').parents('li').first().siblings().contains('Package 1').should('exist'); }); }); diff --git a/integration-tests/cypress/workbench/Explorer.ts b/integration-tests/cypress/workbench/Explorer.ts index b4730a464..6ce43ac57 100644 --- a/integration-tests/cypress/workbench/Explorer.ts +++ b/integration-tests/cypress/workbench/Explorer.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 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 @@ -95,6 +95,7 @@ export class Explorer { public select(treeItemLabel: string, multiSelection: boolean = false): void { this.getTreeItemByLabel(treeItemLabel).should('exist'); this.getTreeItemByLabel(treeItemLabel).click({ ctrlKey: multiSelection }); + this.getTreeItemByLabel(treeItemLabel).should('have.attr', 'data-testid', 'selected'); } public createRepresentation(