diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 9bed7118c..334647bd9 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -30,6 +30,7 @@ As a result, the following GraphQL mutations have been removed `exposeRequiremen === Improvements +- https://github.com/eclipse-syson/syson/issues/2105[#2105] [explorer] In the _Explorer_ view, `Expression` elements now display their full textual representation === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java index 6b813fe08..f01ccd452 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java @@ -49,6 +49,7 @@ import org.eclipse.syson.application.controller.explorer.testers.ExpandAllTreeItemTester; import org.eclipse.syson.application.controller.explorer.testers.TreeItemContextMenuTester; import org.eclipse.syson.application.controller.explorer.testers.TreePathTester; +import org.eclipse.syson.application.data.ActionTransitionUsagesProjectData; import org.eclipse.syson.application.data.GeneralViewEmptyTestProjectData; import org.eclipse.syson.application.data.ProjectWithLibraryDependencyContainingCommentAndLibraryPackageTestProjectData; import org.eclipse.syson.application.data.ProjectWithLibraryDependencyContainingLibraryPackageTestProjectData; @@ -871,4 +872,70 @@ public void sysONExplorerTreeItemContextMenuEntriesTest() { .verify(Duration.ofSeconds(10)); } + + @DisplayName("GIVEN the SysON Explorer, WHEN displaying an Expression item, THEN the item's label shows the textual representation of the expression") + @Sql(scripts = { ActionTransitionUsagesProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + @Test + public void sysONExplorerTreeExpressionLabelTest() { + + var optionalEditingContext = this.editingContextSearchService.findById(ActionTransitionUsagesProjectData.EDITING_CONTEXT_ID); + TreeDescription treeDescription = optionalEditingContext + .flatMap(editingContext -> this.representationDescriptionSearchService.findById(editingContext, this.sysONExplorerTreeDescriptionId)) + .filter(TreeDescription.class::isInstance) + .map(TreeDescription.class::cast) + .orElse(null); + List defaultFilters = this.sysonTreeFilterProvider.get(null, treeDescription).stream() + .filter(TreeFilter::defaultState) + .map(TreeFilter::id) + .toList(); + + var expandedItemIds = List.of( + ActionTransitionUsagesProjectData.SemanticIds.DOCUMENT_ID, + ActionTransitionUsagesProjectData.SemanticIds.PACKAGE_1_ID, + ActionTransitionUsagesProjectData.SemanticIds.A0_ID, + ActionTransitionUsagesProjectData.SemanticIds.S1_ID, + ActionTransitionUsagesProjectData.SemanticIds.S2_ID); + + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, expandedItemIds, defaultFilters); + var input = new ExplorerEventInput(UUID.randomUUID(), ActionTransitionUsagesProjectData.EDITING_CONTEXT_ID, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(input).flux(); + TestTransaction.flagForCommit(); + TestTransaction.end(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + assertThat(tree.getChildren()).hasSize(2); + var documentItem = tree.getChildren().get(0); + assertThat(documentItem.getChildren()).hasSize(1); + assertThat(documentItem.getLabel().toString()).isEqualTo("ActionTransitionUsage.sysml"); + var packageItem = documentItem.getChildren().get(0); + assertThat(packageItem.getLabel().toString()).isEqualTo("Package 1"); + assertThat(packageItem.getChildren()).hasSize(3); + var a0Item = packageItem.getChildren().get(0); + assertThat(a0Item.getLabel().toString()).isEqualTo("a0"); + assertThat(a0Item.getChildren()).hasSize(7); + var s1Item = a0Item.getChildren().get(5); + assertThat(s1Item.getLabel().toString()).isEqualTo("S1"); + assertThat(s1Item.getChildren()).hasSize(3); + var expr1Item = s1Item.getChildren().get(0); + assertThat(expr1Item.getKind()).isEqualTo("siriusComponents://semantic?domain=sysml&entity=OperatorExpression"); + assertThat(expr1Item.getLabel().toString()).isEqualTo("attr1 < 1"); + + var s2Item = a0Item.getChildren().get(6); + assertThat(s2Item.getLabel().toString()).isEqualTo("S2"); + assertThat(s2Item.getChildren()).hasSize(3); + var expr2Item = s2Item.getChildren().get(0); + assertThat(expr2Item.getKind()).isEqualTo("siriusComponents://semantic?domain=sysml&entity=OperatorExpression"); + assertThat(expr2Item.getLabel().toString()).isEqualTo("attr1 < 0"); + + }); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10_000)); + } } diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionTransitionUsagesProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionTransitionUsagesProjectData.java index 7b79a3338..ef794b9d4 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionTransitionUsagesProjectData.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ActionTransitionUsagesProjectData.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 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 @@ -42,6 +42,8 @@ public static final class GraphicalIds { */ public static final class SemanticIds { + public static final String DOCUMENT_ID = "26a78eef-bfa1-4de0-9d0a-c1f86a1b97d5"; + public static final String PACKAGE_1_ID = "2264f62c-b223-4b9b-ae41-96dc2ba0070e"; public static final String A0_ID = "82a2fa10-e736-4c3e-b27a-e94f76d7456d"; diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/EMFUtils.java b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/EMFUtils.java index 121a73629..0abfb9328 100644 --- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/EMFUtils.java +++ b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/EMFUtils.java @@ -282,7 +282,6 @@ public static List getAncestors(Class type, EObject ob */ public static Optional getFirstAncestor(Class type, EObject object, Predicate ancestorPredicate) { var current = object; - List results = new ArrayList<>(); while (current != null) { if (type.isInstance(current) && (ancestorPredicate == null || ancestorPredicate.test(current))) { return Optional.of((T) current); diff --git a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java index 5b9157880..749b0caa3 100644 --- a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java +++ b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java @@ -38,8 +38,13 @@ import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataSearchService; import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; import org.eclipse.syson.sysml.Type; +import org.eclipse.syson.sysml.Usage; import org.eclipse.syson.sysml.ViewUsage; +import org.eclipse.syson.sysml.textual.SysMLElementSerializer; +import org.eclipse.syson.sysml.textual.SysMLSerializingOptions; +import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver; import org.eclipse.syson.sysml.util.ElementUtil; import org.eclipse.syson.tree.explorer.fragments.KerMLStandardLibraryDirectory; import org.eclipse.syson.tree.explorer.fragments.LibrariesDirectory; @@ -148,6 +153,8 @@ public String getLabel(Object self) { String label = ""; if (self instanceof ISysONExplorerFragment fragment) { label = fragment.getLabel(); + } else if (self instanceof Expression expression && !(self instanceof Usage)) { + label = this.getValueExpressionTextualRepresentation(expression); } else if (self instanceof Type type) { String name = type.getName(); if (name != null) { @@ -159,6 +166,25 @@ public String getLabel(Object self) { return label; } + private String getValueExpressionTextualRepresentation(Expression value) { + String result = ""; + if (value != null) { + SysMLSerializingOptions options = new SysMLSerializingOptions.Builder() + .lineSeparator("\n") + .nameDeresolver(new FileNameDeresolver()) + .indentation("\t") + .needEscapeCharacter(false) + .build(); + String textualFormat = new SysMLElementSerializer(options, s -> { + // Do nothing for now + }).doSwitch(value); + if (textualFormat != null) { + result = textualFormat; + } + } + return result; + } + private String getFallbackLabel(Object self) { StyledString styledLabel = this.labelService.getStyledLabel(self); if (styledLabel != null) { diff --git a/doc/content/modules/user-manual/assets/images/explorer-expression-text.png b/doc/content/modules/user-manual/assets/images/explorer-expression-text.png new file mode 100644 index 000000000..14e3c1ef5 Binary files /dev/null and b/doc/content/modules/user-manual/assets/images/explorer-expression-text.png differ diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc index b6d556387..d7aeaaa98 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc @@ -25,6 +25,12 @@ ** Add tools to create timeslices and snapshots, available on `OccurrenceUsage`, `ItemUsage`, and `PartUsage` graphical nodes. These tools leverage a selection dialog to either create a specific timeslice/snapshot graphical node or, if no selection is made, default to a timeslice/snapshot `OccurrenceUsage` graphical node. +* In the _Explorer_ view: + +** `Expression` elements now display their full textual representation: + +image::explorer-expression-text.png[Expression textual representation] + == Technical details * For technical details on this {product} release (including breaking changes), please refer to https://github.com/eclipse-syson/syson/blob/main/CHANGELOG.adoc[changelog].