From 6658e34e77adc883c1a12937e0b6a455a93f82af Mon Sep 17 00:00:00 2001 From: Pierre-Charles David Date: Tue, 30 Jun 2026 17:44:51 +0200 Subject: [PATCH] [2318] Direct edit on a constraint label should ignore the expression part Bug: https://github.com/eclipse-syson/syson/issues/2318 Signed-off-by: Pierre-Charles David --- CHANGELOG.adoc | 2 + ...xpressionsControllersIntegrationTests.java | 101 +++++++++++++++++- .../services/DiagramMutationLabelService.java | 13 ++- 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 8d29c4039..7bac2220a 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -34,8 +34,10 @@ It has long been unused by Sirius Web itself (since the transition to MUI). - https://github.com/eclipse-syson/syson/issues/1808[#1808] [diagrams] Remove multiplicity displayed on feature membership edge between Part Definition and Part Usage. - https://github.com/eclipse-syson/syson/issues/2194[#2194] [diagrams] Properly report feedback messages to user when using _ISysMLMoveElementService_. - https://github.com/eclipse-syson/syson/issues/2306[#2306] [diagrams] Fixed an error when trying to rename a constraint displayed as a list item through direct edit. +- https://github.com/eclipse-syson/syson/issues/2318[#2318] [diagrams] Direct edit on a constraint label now ignores the expression part (`{ ... }`) if present in the new text. === Improvements + - https://github.com/eclipse-syson/syson/issues/2198[#2198] [diagrams] Improve diagram-to-diagram drag and drop to support dropping multiple graphical nodes at once, leveraging Sirius Web's `droppedNodes` and `droppedElements` variables. - https://github.com/eclipse-syson/syson/issues/2182[#2182] [services] Provide a way for downstream applications to extend _ISysMLMoveElementService_. - https://github.com/eclipse-syson/syson/issues/2240[#2240] [diagrams] Update the choice of _timeslice_ and _snapshot_ elements that can be created in the selection dialog of tools creating _timeslice_ and _snapshot_ elements. diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java index 3446b4c65..a46a12f49 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java @@ -651,7 +651,7 @@ public void topLevelConstraintNodeLabelIncludesExpressionInLabel() { .check(diagram.get(), newDiagram); String nodeLabel = newDiagram.getNodes().get(0).getInsideLabel().getText(); - assertThat(nodeLabel).isEqualTo("«constraint»\npressureLimit\n{ pressure <= maxPressure }"); + assertThat(nodeLabel).isEqualTo(this.multiLine(this.quoted("constraint"), "pressureLimit", "{ pressure <= maxPressure }")); }); StepVerifier.create(flux) @@ -662,6 +662,70 @@ public void topLevelConstraintNodeLabelIncludesExpressionInLabel() { .verify(Duration.ofSeconds(10)); } + @DisplayName("GIVEN a SysML model with expressions, WHEN direct editing a constraint node's label with an expression part, THEN the direct edit succeeds but ignores the expression part") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void directEditTopLevelConstraintNodeIgnoresExpressionPart() { + String initialConstraintName = "pressureLimit"; + String constraintExpression = "pressure <= maxPressure"; + String newConstraintName = "newConstraintName"; + String newExpressionIgnored = "false"; + + var flux = this.givenSubscriptionToNewDiagram(); + + AtomicReference diagram = new AtomicReference<>(); + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + AtomicReference constraintNodeId = new AtomicReference<>(); + AtomicReference constraintNodeLabelId = new AtomicReference<>(); + + Runnable dropFromExplorerRunnable = () -> { + assertThat(diagram.get().getNodes()).hasSize(0); + this.dropFromExplorerTester.dropFromExplorerOnDiagram(ExpressionSamplesProjectData.EDITING_CONTEXT_ID, diagram, ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_LIMIT_CONSTRAINT_ID); + }; + + Consumer updatedDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator) + .hasNewEdgeCount(0) + .hasNewNodeCount(5) + .check(diagram.get(), newDiagram); + + var constraintNode = newDiagram.getNodes().get(0); + String nodeLabel = constraintNode.getInsideLabel().getText(); + assertThat(nodeLabel).isEqualTo(this.multiLine(this.quoted("constraint"), initialConstraintName, "{ " + constraintExpression + " }")); + constraintNodeId.set(constraintNode.getId()); + constraintNodeLabelId.set(constraintNode.getInsideLabel().getId()); + }); + + var checkDirectEditInitialTextShowsNameOnly = this.directEditInitialLabelTester.checkDirectEditInitialLabel(diagram, constraintNodeLabelId::get, + initialConstraintName); + + Runnable editConstraintName = () -> { + String labelId = constraintNodeLabelId.get(); + String newLabelWIthExpressionPart = newConstraintName + " {" + newExpressionIgnored + " }"; + EditLabelInput input = new EditLabelInput(UUID.randomUUID(), ExpressionSamplesProjectData.EDITING_CONTEXT_ID, diagram.get().getId(), labelId, newLabelWIthExpressionPart); + var result = this.editLabelMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.editLabel.__typename"); + assertThat(typename).isEqualTo(EditLabelSuccessPayload.class.getSimpleName()); + }; + + Consumer updatedDiagramAfterDirectEditConsumer = assertRefreshedDiagramThat(newDiagram -> { + var constraintNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_LIMIT_CONSTRAINT_ID) + .getNode(); + assertThat(constraintNode).isNotNull(); + assertThat(constraintNode.getInsideLabel().getText()).isEqualTo(this.multiLine(this.quoted("constraint"), newConstraintName, "{ " + constraintExpression + " }")); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(dropFromExplorerRunnable) + .consumeNextWith(updatedDiagramConsumer) + .then(checkDirectEditInitialTextShowsNameOnly) + .then(editConstraintName) + .consumeNextWith(updatedDiagramAfterDirectEditConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + @DisplayName("GIVEN a SysML model an assumed constraint visible as a list item in a requirement node, WHEN using direct-edit on the constraint, THEN only its name is shown in the initial text and impacted by the edition") @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) @Test @@ -689,7 +753,7 @@ public void directEditNamedAssumedConstraintListItem() { .check(diagram.get(), newDiagram); String nodeLabel = newDiagram.getNodes().get(0).getInsideLabel().getText(); - assertThat(nodeLabel).isEqualTo(this.quoted("requirement def") + "\nSensorOperability"); + assertThat(nodeLabel).isEqualTo(this.multiLine(this.quoted("requirement def"), "SensorOperability")); var constraintNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(ExpressionSamplesProjectData.SemanticIds.SENSOR_OPERABILITY_ENVIRONMENTAL_PRECONDITION_ASSUMED_CONSTRAINT_ID) .getNode(); @@ -717,7 +781,7 @@ public void directEditNamedAssumedConstraintListItem() { .check(diagram.get(), newDiagram); String nodeLabel = newDiagram.getNodes().get(0).getInsideLabel().getText(); - assertThat(nodeLabel).isEqualTo(this.quoted("requirement def") + "\nSensorOperability"); + assertThat(nodeLabel).isEqualTo(this.multiLine(this.quoted("requirement def"), "SensorOperability")); var constraintNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(ExpressionSamplesProjectData.SemanticIds.SENSOR_OPERABILITY_ENVIRONMENTAL_PRECONDITION_ASSUMED_CONSTRAINT_ID) .getNode(); @@ -727,6 +791,31 @@ public void directEditNamedAssumedConstraintListItem() { constraintNodeLabelId.set(constraintNode.getInsideLabel().getId()); }); + Runnable editConstraintNameWithExpressionPart = () -> { + String labelId = constraintNodeLabelId.get(); + EditLabelInput input = new EditLabelInput(UUID.randomUUID(), ExpressionSamplesProjectData.EDITING_CONTEXT_ID, diagram.get().getId(), labelId, initialConstraintName + "{ false }"); + var result = this.editLabelMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.editLabel.__typename"); + assertThat(typename).isEqualTo(EditLabelSuccessPayload.class.getSimpleName()); + }; + + Consumer updatedDiagramAfterDirectEditWithExpressionPartConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator) + .hasNewEdgeCount(0) + .hasNewNodeCount(15) + .check(diagram.get(), newDiagram); + + String nodeLabel = newDiagram.getNodes().get(0).getInsideLabel().getText(); + assertThat(nodeLabel).isEqualTo(this.multiLine(this.quoted("requirement def"), "SensorOperability")); + + var constraintNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(ExpressionSamplesProjectData.SemanticIds.SENSOR_OPERABILITY_ENVIRONMENTAL_PRECONDITION_ASSUMED_CONSTRAINT_ID) + .getNode(); + assertThat(constraintNode).isNotNull(); + assertThat(constraintNode.getInsideLabel().getText()).isEqualTo(initialConstraintName + " { " + constraintExpression + " }"); + constraintNodeId.set(constraintNode.getId()); + constraintNodeLabelId.set(constraintNode.getInsideLabel().getId()); + }); + StepVerifier.create(flux) .consumeNextWith(initialDiagramContentConsumer) .then(dropFromExplorerRunnable) @@ -734,6 +823,8 @@ public void directEditNamedAssumedConstraintListItem() { .then(checkDirectEditInitialTextShowsNameOnly) .then(editConstraintName) .consumeNextWith(updatedDiagramAfterDirectEditConsumer) + .then(editConstraintNameWithExpressionPart) + .consumeNextWith(updatedDiagramAfterDirectEditWithExpressionPartConsumer) .thenCancel() .verify(Duration.ofSeconds(10)); } @@ -742,6 +833,10 @@ private String quoted(String text) { return LabelConstants.OPEN_QUOTE + text + LabelConstants.CLOSE_QUOTE; } + private String multiLine(String... lines) { + return String.join(LabelConstants.CR, lines); + } + /** * Executes a function in the editing context with the specified id (which is assumed to be loaded). The function * can perform JUnit assertions to verify the state of the semantic model in the editing context. diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java index 8231127c1..8b62043bd 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java @@ -28,6 +28,7 @@ import org.eclipse.syson.services.LabelService; import org.eclipse.syson.services.api.DiagramDirectEditErrorListener; import org.eclipse.syson.services.api.IDirectEditNamespaceProvider; +import org.eclipse.syson.sysml.ConstraintUsage; import org.eclipse.syson.sysml.Element; import org.springframework.stereotype.Service; @@ -120,7 +121,11 @@ public Element directEdit(Element element, String newLabel, boolean isCompartmen * @return the given {@link Element}. */ public Element directEditNode(Element element, String newLabel) { - return this.directEdit(element, newLabel, false, (String[]) null); + String cleanNewLabel = newLabel; + if (element instanceof ConstraintUsage) { + cleanNewLabel = newLabel.replaceAll("\\{.*$", ""); + } + return this.directEdit(element, cleanNewLabel, false, (String[]) null); } /** @@ -133,7 +138,11 @@ public Element directEditNode(Element element, String newLabel) { * @return the given {@link Element}. */ public Element directEditListItem(Element element, String newLabel) { - return this.directEdit(element, newLabel, true, LabelService.VALUE_OFF); + String cleanNewLabel = newLabel; + if (element instanceof ConstraintUsage) { + cleanNewLabel = newLabel.replaceAll("\\{.*$", ""); + } + return this.directEdit(element, cleanNewLabel, true, LabelService.VALUE_OFF); } public Element editMultiplicityRangeCenterLabel(Element element, String newLabel) {