diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 615ced674..6f7864da5 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -6,15 +6,24 @@ === Breaking changes +- https://github.com/eclipse-syson/syson/issues/893[#893] [explorer] Fix _Expand All_ tool in SysON _Explorer_ view +* `ISysONExplorerFragment` implementations now need to implement `getKind()` and `getParent()`. +* The constructors of `KerMLStandardLibraryDirectory`, `LibrariesDirectory`, `SysMLStandardLibraryDirectory`, and `UserLibrariesDirectory` now have a new `parent` parameter. +* `ISysONExplorerService` implementations now need to implement `canExpandAll(TreeItem, IEditingContext)` +* `ISysONExplorerServiceDelegate` implementations now need to implement `canExpandAll(TreeItem, IEditingContext)` + === Dependency update - [releng] Switch to https://github.com/spring-projects/spring-boot/releases/tag/v3.5.0[Spring Boot 3.5.0] - [releng] Switch to https://github.com/eclipse-sirius/sirius-web[Sirius Web 2025.6.1] - https://github.com/eclipse-syson/syson/issues/1385[#1385] [releng] Switch to SysIDE 0.9.0 - [releng] Switch to Sirius EMF-JSON 2.5.2 +- [releng] Switch to Node 22.16.0 === Bug fixes +- https://github.com/eclipse-syson/syson/issues/893[#893] [explorer] Fix _Expand All_ tool in SysON _Explorer_ view + === Improvements === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/testers/TreeItemContextMenuTester.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/testers/TreeItemContextMenuTester.java new file mode 100644 index 000000000..a6badcc2a --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/testers/TreeItemContextMenuTester.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.application.controller.explorer.testers; + +import com.jayway.jsonpath.JsonPath; + +import java.util.List; +import java.util.Map; + +import org.eclipse.sirius.web.tests.graphql.TreeItemContextMenuQueryRunner; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Gets the context menu of a tree item in the explorer. + * + * @author gdaniel + */ +@Service +public class TreeItemContextMenuTester { + + @Autowired + private TreeItemContextMenuQueryRunner treeItemContextMenuQueryRunner; + + public List getContextMenuEntries(String editingContextId, String representationId, String treeItemId) { + Map contextMenuVariables = Map.of( + "editingContextId", editingContextId, + "representationId", representationId, + "treeItemId", treeItemId); + String result = this.treeItemContextMenuQueryRunner.run(contextMenuVariables); + List contextMenuIds = JsonPath.read(result, "$.data.viewer.editingContext.representation.description.contextMenu[*].id"); + return contextMenuIds; + } + +} 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 1622b110b..ef9d4086b 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 @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; @@ -41,8 +42,10 @@ import org.eclipse.sirius.web.tests.services.representation.RepresentationIdBuilder; import org.eclipse.syson.AbstractIntegrationTests; import org.eclipse.syson.application.controller.explorer.testers.ExpandTreeItemTester; +import org.eclipse.syson.application.controller.explorer.testers.TreeItemContextMenuTester; import org.eclipse.syson.application.data.GeneralViewEmptyTestProjectData; import org.eclipse.syson.application.data.SysonStudioTestProjectData; +import org.eclipse.syson.tree.explorer.view.SysONExplorerTreeDescriptionProvider; import org.eclipse.syson.tree.explorer.view.SysONTreeViewDescriptionProvider; import org.eclipse.syson.tree.explorer.view.filters.SysONTreeFilterProvider; import org.junit.jupiter.api.BeforeEach; @@ -87,6 +90,9 @@ public class SysONExplorerTests extends AbstractIntegrationTests { @Autowired private ExpandTreeItemTester expandTreeItemTester; + @Autowired + private TreeItemContextMenuTester treeItemContextMenuTester; + @Autowired private SysONTreeFilterProvider sysonTreeFilterProvider; @@ -244,6 +250,66 @@ public void getRootContentWithHideMembershipsAndHideKerMLStandardLibraries() { .verify(Duration.ofSeconds(10)); } + @DisplayName("GIVEN an empty SysML Project, WHEN context menu is queried, THEN the menu is returned") + @Sql(scripts = { GeneralViewEmptyTestProjectData.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 getContextMenuOfModelAndLibraryDirectories() { + // Expand the Libraries directory when building the explorer, we want to check the context menu of elements + // under it. + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.treeDescriptionId, + List.of(UUID.nameUUIDFromBytes("SysON_Libraries_Directory".getBytes()).toString()), this.defaultFilters); + var input = new ExplorerEventInput(UUID.randomUUID(), GeneralViewEmptyTestProjectData.EDITING_CONTEXT, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(input); + + AtomicReference sysmlModelTreeItemId = new AtomicReference<>(); + AtomicReference librariesDirectoryTreeItemId = new AtomicReference<>(); + AtomicReference kermlDirectoryTreeItemId = new AtomicReference<>(); + AtomicReference sysmlDirectoryTreeItemId = new AtomicReference<>(); + + var initialExplorerContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(2); + TreeItem sysmlv2Model = tree.getChildren().get(0); + this.assertThatTreeItemHasLabel(sysmlv2Model, "SysMLv2"); + sysmlModelTreeItemId.set(sysmlv2Model.getId()); + assertThat(sysmlv2Model.isHasChildren()).isTrue(); + TreeItem librariesDirectory = tree.getChildren().get(1); + this.assertThatTreeItemHasLabel(librariesDirectory, "Libraries"); + librariesDirectoryTreeItemId.set(librariesDirectory.getId()); + assertThat(librariesDirectory.isHasChildren()).isTrue(); + assertThat(librariesDirectory.getChildren()).hasSize(2); + TreeItem kermlDirectory = librariesDirectory.getChildren().get(0); + this.assertThatTreeItemHasLabel(kermlDirectory, "KerML"); + kermlDirectoryTreeItemId.set(kermlDirectory.getId()); + assertThat(kermlDirectory.isHasChildren()).isTrue(); + TreeItem sysmlDirectory = librariesDirectory.getChildren().get(1); + this.assertThatTreeItemHasLabel(sysmlDirectory, "SysML"); + sysmlDirectoryTreeItemId.set(sysmlDirectory.getId()); + assertThat(sysmlDirectory.isHasChildren()).isTrue(); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialExplorerContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + + List sysmlModelContextMenu = this.treeItemContextMenuTester.getContextMenuEntries(GeneralViewEmptyTestProjectData.EDITING_CONTEXT, explorerRepresentationId, + sysmlModelTreeItemId.get()); + // The expected size is 1: there is only one entry contributed from the backend, the other entries (new object, + // download, etc) are contributed from the frontend. + assertThat(sysmlModelContextMenu).hasSize(1).anyMatch(entry -> Objects.equals(entry, SysONExplorerTreeDescriptionProvider.EXPAND_ALL_MENU_ENTRY_CONTRIBUTION_ID)); + List librariesDirectoryContextMenu = this.treeItemContextMenuTester.getContextMenuEntries(GeneralViewEmptyTestProjectData.EDITING_CONTEXT, explorerRepresentationId, + librariesDirectoryTreeItemId.get()); + assertThat(librariesDirectoryContextMenu).isEmpty(); + List kermlDirectoryContextMenu = this.treeItemContextMenuTester.getContextMenuEntries(GeneralViewEmptyTestProjectData.EDITING_CONTEXT, explorerRepresentationId, + kermlDirectoryTreeItemId.get()); + assertThat(kermlDirectoryContextMenu).isEmpty(); + List sysmlDirectoryContextMenu = this.treeItemContextMenuTester.getContextMenuEntries(GeneralViewEmptyTestProjectData.EDITING_CONTEXT, explorerRepresentationId, + sysmlDirectoryTreeItemId.get()); + assertThat(sysmlDirectoryContextMenu).isEmpty(); + } + private void assertThatTreeItemHasLabel(TreeItem treeItem, String label) { assertThat(treeItem.getLabel().styledStringFragments().stream().map(StyledStringFragment::text).collect(Collectors.joining())).isEqualTo(label); } diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/SysONExplorerTreeDescriptionProvider.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/SysONExplorerTreeDescriptionProvider.java index 77d29e9da..dca8f5f1b 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/SysONExplorerTreeDescriptionProvider.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/SysONExplorerTreeDescriptionProvider.java @@ -12,15 +12,18 @@ *******************************************************************************/ package org.eclipse.syson.tree.explorer.view; +import java.util.List; import java.util.UUID; import org.eclipse.sirius.components.emf.ResourceMetadataAdapter; import org.eclipse.sirius.components.emf.services.JSONResourceFactory; +import org.eclipse.sirius.components.trees.TreeItem; import org.eclipse.sirius.components.view.View; import org.eclipse.sirius.components.view.builder.generated.tree.TreeBuilders; import org.eclipse.sirius.components.view.builder.generated.tree.TreeDescriptionBuilder; import org.eclipse.sirius.components.view.builder.generated.view.ViewBuilders; import org.eclipse.sirius.components.view.tree.TreeDescription; +import org.eclipse.sirius.components.view.tree.TreeItemContextMenuEntry; import org.eclipse.sirius.components.view.tree.TreeItemLabelDescription; import org.eclipse.sirius.components.view.tree.TreeItemLabelFragmentDescription; import org.eclipse.sirius.emfjson.resource.JsonResource; @@ -34,6 +37,8 @@ public class SysONExplorerTreeDescriptionProvider { public static final String SYSON_EXPLORER = "SysON Explorer"; + public static final String EXPAND_ALL_MENU_ENTRY_CONTRIBUTION_ID = "expandAll"; + public View createView() { var sysonDefaultTreeView = new ViewBuilders() @@ -70,6 +75,7 @@ private TreeDescription build() { .treeItemIdExpression("aql:self.getTreeItemId()") .treeItemObjectExpression("aql:id.getTreeItemObject(editingContext)") .treeItemLabelDescriptions(this.createDefaultStyle()) + .contextMenuEntries(this.getContextMenuEntries().toArray(TreeItemContextMenuEntry[]::new)) .build(); return description; } @@ -88,4 +94,12 @@ private TreeItemLabelFragmentDescription getDefaultLabelFragmentDescription() { .labelExpression("aql:self.getLabel()") .build(); } + + private List getContextMenuEntries() { + var expandAllMenuEntry = new TreeBuilders().newCustomTreeItemContextMenuEntry() + .contributionId(EXPAND_ALL_MENU_ENTRY_CONTRIBUTION_ID) + .preconditionExpression("aql:" + TreeItem.SELECTED_TREE_ITEM + ".canExpandAll(editingContext)") + .build(); + return List.of(expandAllMenuEntry); + } } diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/KerMLStandardLibraryDirectory.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/KerMLStandardLibraryDirectory.java index f8aa66278..ad77c03e8 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/KerMLStandardLibraryDirectory.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/KerMLStandardLibraryDirectory.java @@ -20,8 +20,8 @@ import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; -import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFragment; import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFilterService; +import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFragment; /** * The KerML directory displayed in the explorer. @@ -30,11 +30,14 @@ */ public class KerMLStandardLibraryDirectory implements ISysONExplorerFragment { - private final String id = UUID.nameUUIDFromBytes("KERML".getBytes()).toString(); + private final String id = UUID.nameUUIDFromBytes("SysON_KerML_Directory".getBytes()).toString(); + + private final Object parent; private final ISysONExplorerFilterService filterService; - public KerMLStandardLibraryDirectory(ISysONExplorerFilterService filterService) { + public KerMLStandardLibraryDirectory(Object parent, ISysONExplorerFilterService filterService) { + this.parent = Objects.requireNonNull(parent); this.filterService = Objects.requireNonNull(filterService); } @@ -48,6 +51,11 @@ public String getLabel() { return "KerML"; } + @Override + public String getKind() { + return this.getClass().getSimpleName(); + } + @Override public List getIconURL() { return List.of("icons/LibraryResource.svg"); @@ -63,6 +71,11 @@ public boolean hasChildren(IEditingContext editingContext, List getChildren(IEditingContext editingContext, List existingRepresentations, List expandedIds, List activeFilterIds) { List result = new ArrayList<>(); diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/LibrariesDirectory.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/LibrariesDirectory.java index f1cc95ad2..68937ea3e 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/LibrariesDirectory.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/LibrariesDirectory.java @@ -32,12 +32,15 @@ public class LibrariesDirectory implements ISysONExplorerFragment { private final String id = UUID.nameUUIDFromBytes("SysON_Libraries_Directory".getBytes()).toString(); - private final ISysONExplorerFilterService filterService; - private final String label; - public LibrariesDirectory(String label, ISysONExplorerFilterService filterService) { + private final Object parent; + + private final ISysONExplorerFilterService filterService; + + public LibrariesDirectory(String label, Object parent, ISysONExplorerFilterService filterService) { this.label = Objects.requireNonNull(label); + this.parent = Objects.requireNonNull(parent); this.filterService = Objects.requireNonNull(filterService); } @@ -51,6 +54,16 @@ public String getLabel() { return this.label; } + @Override + public String getKind() { + return this.getClass().getSimpleName(); + } + + @Override + public Object getParent() { + return this.parent; + } + @Override public List getIconURL() { return List.of("icons/LibraryResource.svg"); @@ -63,7 +76,7 @@ public boolean hasChildren(IEditingContext editingContext, List getChildren(IEditingContext editingContext, List existingRepresentations, List expandedIds, List activeFilterIds) { List result = new ArrayList<>(); if (!activeFilterIds.contains(SysONTreeFilterProvider.HIDE_KERML_STANDARD_LIBRARIES_TREE_FILTER_ID)) { - result.add(new KerMLStandardLibraryDirectory(this.filterService)); + result.add(new KerMLStandardLibraryDirectory(this, this.filterService)); } if (!activeFilterIds.contains(SysONTreeFilterProvider.HIDE_SYSML_STANDARD_LIBRARIES_TREE_FILTER_ID)) { - result.add(new SysMLStandardLibraryDirectory(this.filterService)); + result.add(new SysMLStandardLibraryDirectory(this, this.filterService)); } if (!activeFilterIds.contains(SysONTreeFilterProvider.HIDE_USER_LIBRARIES_TREE_FILTER_ID)) { - UserLibrariesDirectory userLibrariesDirectory = new UserLibrariesDirectory("User libraries", this.filterService); + UserLibrariesDirectory userLibrariesDirectory = new UserLibrariesDirectory("User libraries", this, this.filterService); if (userLibrariesDirectory.hasChildren(editingContext, existingRepresentations, expandedIds, activeFilterIds)) { // Add the user libraries directory only if it contains children. result.add(userLibrariesDirectory); diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/SysMLStandardLibraryDirectory.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/SysMLStandardLibraryDirectory.java index 98271c236..8d2afcf7a 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/SysMLStandardLibraryDirectory.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/SysMLStandardLibraryDirectory.java @@ -20,8 +20,8 @@ import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; -import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFragment; import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFilterService; +import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFragment; /** * The SysML directory displayed in the explorer. @@ -30,11 +30,14 @@ */ public class SysMLStandardLibraryDirectory implements ISysONExplorerFragment { - private final String id = UUID.nameUUIDFromBytes("SYSML".getBytes()).toString(); + private final String id = UUID.nameUUIDFromBytes("SysON_SysML_Directory".getBytes()).toString(); + + private final Object parent; private final ISysONExplorerFilterService filterService; - public SysMLStandardLibraryDirectory(ISysONExplorerFilterService filterService) { + public SysMLStandardLibraryDirectory(Object parent, ISysONExplorerFilterService filterService) { + this.parent = Objects.requireNonNull(parent); this.filterService = Objects.requireNonNull(filterService); } @@ -48,6 +51,16 @@ public String getLabel() { return "SysML"; } + @Override + public String getKind() { + return this.getClass().getSimpleName(); + } + + @Override + public Object getParent() { + return this.parent; + } + @Override public List getIconURL() { return List.of("icons/LibraryResource.svg"); diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/UserLibrariesDirectory.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/UserLibrariesDirectory.java index 7b8f2ec70..0c6a9c8bd 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/UserLibrariesDirectory.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/fragments/UserLibrariesDirectory.java @@ -35,10 +35,13 @@ public class UserLibrariesDirectory implements ISysONExplorerFragment { private String label; + private final Object parent; + private final ISysONExplorerFilterService filterService; - public UserLibrariesDirectory(String label, ISysONExplorerFilterService filterService) { + public UserLibrariesDirectory(String label, Object parent, ISysONExplorerFilterService filterService) { this.label = Objects.requireNonNull(label); + this.parent = Objects.requireNonNull(parent); this.filterService = Objects.requireNonNull(filterService); } @@ -52,6 +55,16 @@ public String getLabel() { return this.label; } + @Override + public String getKind() { + return this.getClass().getSimpleName(); + } + + @Override + public Object getParent() { + return this.parent; + } + @Override public List getIconURL() { return List.of("icons/LibraryResource.svg"); diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java index 44c06fa9b..9c1e4c382 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java @@ -12,9 +12,11 @@ *******************************************************************************/ package org.eclipse.syson.tree.explorer.view.menu.context; +import java.util.List; import java.util.Objects; import org.eclipse.sirius.components.collaborative.trees.api.ITreeItemContextMenuEntryProvider; +import org.eclipse.sirius.components.collaborative.trees.dto.ITreeItemContextMenuEntry; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IObjectSearchService; import org.eclipse.sirius.components.trees.Tree; @@ -51,5 +53,14 @@ public boolean canHandle(IEditingContext editingContext, TreeDescription treeDes return tree.getId().startsWith(ExplorerDescriptionProvider.PREFIX) && Objects.equals(tree.getDescriptionId(), this.sysONTreeViewDescriptionProvider.getDescriptionId()); } + + @Override + public List getTreeItemContextMenuEntries(IEditingContext editingContext, TreeDescription treeDescription, Tree tree, TreeItem treeItem) { + // Remove the expandAll entry: it is handled at the view level in SysON + // "expandAll" is the ID from Sirius-Web ExplorerTreeItemContextMenuEntryProvider service class + return super.getTreeItemContextMenuEntries(editingContext, treeDescription, tree, treeItem).stream() + .filter(entry -> !Objects.equals(entry.id(), "expandAll")) + .toList(); + } } diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/ComposedSysONExplorerService.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/ComposedSysONExplorerService.java index a9c236a33..f59ded2ca 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/ComposedSysONExplorerService.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/ComposedSysONExplorerService.java @@ -17,6 +17,7 @@ import java.util.Optional; import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.trees.TreeItem; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; import org.eclipse.syson.tree.explorer.view.services.api.ISysONDefaultExplorerService; import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerService; @@ -119,6 +120,13 @@ public List getChildren(Object self, IEditingContext editingContext, Lis .orElseGet(() -> this.defaultExplorerService.getChildren(self, editingContext, existingRepresentations, expandedIds, activeFilterIds)); } + @Override + public boolean canExpandAll(TreeItem treeItem, IEditingContext editingContext) { + return this.getDelegate(editingContext) + .map(delegate -> delegate.canExpandAll(treeItem, editingContext)) + .orElseGet(() -> this.defaultExplorerService.canExpandAll(treeItem, editingContext)); + } + @Override public List getElements(IEditingContext editingContext, List activeFilterIds) { return this.getDelegate(editingContext) diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/SysONDefaultExplorerServices.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/SysONDefaultExplorerServices.java index 0503de684..e569fbd6b 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/SysONDefaultExplorerServices.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/SysONDefaultExplorerServices.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EObject; @@ -24,6 +25,8 @@ import org.eclipse.sirius.components.core.api.IIdentityService; import org.eclipse.sirius.components.core.api.ILabelService; import org.eclipse.sirius.components.core.api.labels.StyledString; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.trees.TreeItem; import org.eclipse.sirius.web.application.UUIDParser; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.eclipse.sirius.web.application.views.explorer.services.api.IExplorerServices; @@ -34,7 +37,10 @@ import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.Namespace; import org.eclipse.syson.sysml.util.ElementUtil; +import org.eclipse.syson.tree.explorer.view.fragments.KerMLStandardLibraryDirectory; import org.eclipse.syson.tree.explorer.view.fragments.LibrariesDirectory; +import org.eclipse.syson.tree.explorer.view.fragments.SysMLStandardLibraryDirectory; +import org.eclipse.syson.tree.explorer.view.fragments.UserLibrariesDirectory; import org.eclipse.syson.tree.explorer.view.services.api.ISysONDefaultExplorerService; import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFilterService; import org.eclipse.syson.tree.explorer.view.services.api.ISysONExplorerFragment; @@ -86,7 +92,7 @@ public List getElements(IEditingContext editingContext, List act .filter(r -> !this.filterService.isKerMLStandardLibrary(r)) .filter(r -> !this.sysONResourceService.isImported(r) || this.utilService.getLibraries(r, false).isEmpty()) .forEach(results::add); - LibrariesDirectory librariesDirectory = new LibrariesDirectory("Libraries", this.filterService); + LibrariesDirectory librariesDirectory = new LibrariesDirectory("Libraries", editingContext, this.filterService); if (librariesDirectory.hasChildren(editingContext, List.of(), List.of(), activeFilterIds)) { // Do not display the libraries directory if is has no children. results.add(librariesDirectory); @@ -108,7 +114,13 @@ public String getTreeItemId(Object self) { @Override public String getKind(Object self) { - return this.explorerServices.getKind(self); + final String result; + if (self instanceof ISysONExplorerFragment fragment) { + result = fragment.getKind(); + } else { + result = this.explorerServices.getKind(self); + } + return result; } @Override @@ -182,14 +194,43 @@ public List getChildren(Object self, IEditingContext editingContext, Lis .toList(); } + @Override + public boolean canExpandAll(TreeItem treeItem, IEditingContext editingContext) { + List nonExpandableDirectories = List.of(KerMLStandardLibraryDirectory.class, LibrariesDirectory.class, SysMLStandardLibraryDirectory.class, UserLibrariesDirectory.class) + .stream() + .map(Class::getSimpleName) + .toList(); + return treeItem.isHasChildren() && !nonExpandableDirectories.contains(treeItem.getKind()); + } + @Override public Object getParent(Object self, String treeItemId, IEditingContext editingContext) { - return this.explorerServices.getParent(self, treeItemId, editingContext); + final Object result; + if (self instanceof ISysONExplorerFragment fragment) { + result = fragment.getParent(); + } else { + result = this.explorerServices.getParent(self, treeItemId, editingContext); + } + return result; } @Override public Object getTreeItemObject(String treeItemId, IEditingContext editingContext) { - return this.explorerServices.getTreeItemObject(treeItemId, editingContext); + final Object result; + Optional optionalResource = Optional.ofNullable(editingContext) + .filter(IEMFEditingContext.class::isInstance) + .map(IEMFEditingContext.class::cast) + .flatMap(emfEditingContext -> { + return emfEditingContext.getDomain().getResourceSet().getResources().stream() + .filter(resource -> resource.getURI().toString().contains(treeItemId)) + .findFirst(); + }); + if (optionalResource.isPresent()) { + result = optionalResource.get(); + } else { + result = this.explorerServices.getTreeItemObject(treeItemId, editingContext); + } + return result; } @Override diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONDefaultExplorerService.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONDefaultExplorerService.java index 7b891c248..617d0a7d5 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONDefaultExplorerService.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONDefaultExplorerService.java @@ -15,6 +15,7 @@ import java.util.List; import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.trees.TreeItem; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; /** @@ -46,6 +47,8 @@ public interface ISysONDefaultExplorerService { List getChildren(Object self, IEditingContext editingContext, List existingRepresentations, List expandedIds, List activeFilterIds); + boolean canExpandAll(TreeItem treeItem, IEditingContext editingContext); + List getElements(IEditingContext editingContext, List activeFilterIds); } diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerFragment.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerFragment.java index 25591a78a..a11c350cf 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerFragment.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerFragment.java @@ -32,6 +32,10 @@ public interface ISysONExplorerFragment { String getLabel(); + String getKind(); + + Object getParent(); + List getIconURL(); boolean hasChildren(IEditingContext editingContext, List existingRepresentations, List expandedIds, List activeFilterIds); diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerService.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerService.java index f7aba0a09..61244c9df 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerService.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerService.java @@ -15,6 +15,7 @@ import java.util.List; import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.trees.TreeItem; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; /** @@ -46,6 +47,8 @@ public interface ISysONExplorerService { List getChildren(Object self, IEditingContext editingContext, List existingRepresentations, List expandedIds, List activeFilterIds); + boolean canExpandAll(TreeItem treeItem, IEditingContext editingContext); + List getElements(IEditingContext editingContext, List activeFilterIds); } diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerServiceDelegate.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerServiceDelegate.java index a1d6dc464..1f7540f74 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerServiceDelegate.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/services/api/ISysONExplorerServiceDelegate.java @@ -15,6 +15,7 @@ import java.util.List; import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.trees.TreeItem; import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata; /** @@ -48,6 +49,8 @@ public interface ISysONExplorerServiceDelegate { List getChildren(Object self, IEditingContext editingContext, List existingRepresentations, List expandedIds, List activeFilterIds); + boolean canExpandAll(TreeItem treeItem, IEditingContext editingContext); + List getElements(IEditingContext editingContext, List activeFilterIds); } diff --git a/doc/content/modules/user-manual/pages/release-notes/2025.8.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2025.8.0.adoc index 13e44cc8f..125161007 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2025.8.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2025.8.0.adoc @@ -4,10 +4,18 @@ == Breaking changes +- Fix an issue that made the _Expand All_ tool not work consistently in SysON _Explorer_ view +* `ISysONExplorerFragment` implementations now need to implement `getKind()` and `getParent()`. +* The constructors of `KerMLStandardLibraryDirectory`, `LibrariesDirectory`, `SysMLStandardLibraryDirectory`, and `UserLibrariesDirectory` now have a new `parent` parameter. +* `ISysONExplorerService` implementations now need to implement `canExpandAll(TreeItem, IEditingContext)` +* `ISysONExplorerServiceDelegate` implementations now need to implement `canExpandAll(TreeItem, IEditingContext)` + == New features == Bug fixes +- Fix an issue that made the _Expand All_ tool not work consistently in SysON _Explorer_ view + == Improvements == Dependency update @@ -16,6 +24,7 @@ - Switch to https://github.com/eclipse-sirius/sirius-web[Sirius Web 2025.6.1] - Switch to SysIDE 0.9.0 - Switch to Sirius EMF-JSON 2.5.2 +- Switch to Node 22.16.0 == Technical details diff --git a/frontend/syson/src/index.tsx b/frontend/syson/src/index.tsx index 4f9612f5c..2c306a1ab 100644 --- a/frontend/syson/src/index.tsx +++ b/frontend/syson/src/index.tsx @@ -37,6 +37,7 @@ import { NodeTypeRegistry, SiriusWebApplication, UpdateLibraryTreeItemContextMenuContribution, + ExpandAllTreeItemContextMenuContribution, } from '@eclipse-sirius/sirius-web-application'; import { InsertTextualSysMLMenuContribution, @@ -158,6 +159,12 @@ const treeItemContextMenuOverrideContributions: TreeItemContextMenuOverrideContr }, component: UpdateLibraryTreeItemContextMenuContribution, }, + { + canHandle: (entry: GQLTreeItemContextMenuEntry) => { + return entry.id.includes('expandAll'); + }, + component: ExpandAllTreeItemContextMenuContribution, + }, ]; extensionRegistry.putData(treeItemContextMenuEntryOverrideExtensionPoint, {