diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 9cbbf3d4d..50ad5c534 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -57,6 +57,8 @@ The `org.eclipse.syson.services.grammars` package in `syson-direct-edit-grammar`
As a consequence, the `syson-table-requirements-view` and `syson-common-view` modules has been created.
`IViewDescriptionProvider.java` and `SysONViewDescriptionProvider.java` have been moved from `syson-diagram-common-view` to `syson-common-view`.
`GetIntermediateContainerCreationSwitch.java` has been moved from `syson-application-configuration` to `syson-services`.
+- https://github.com/eclipse-syson/syson/issues/1245[#1245] [syson] Standardize read-only computation.
+The class `SysMLReadOnlyService` and the interface `ISysMLReadOnlyService` have been removed, use `IReadOnlyObjectPredicate` instead.
=== Dependency update
@@ -91,6 +93,13 @@ Consumers may override this by providing an implementation of `org.eclipse.syson
With this enhancement, users can now easily identify which `ViewDefinition` is used by a `ViewUsage`.
- https://github.com/eclipse-syson/syson/issues/1535[#1535] [explorer] Since it is now possible to switch from a _Standard Diagram View_ to another in SysON (for example from _General View_ to _Interconnection View_), the `ViewUsages` default name does not contain the name of the `ViewDefinition` anymore.
With this enhancement, users avoid confusion when switching from one _ViewDefinition_ to another one in a `ViewUsage`.
+- https://github.com/eclipse-syson/syson/issues/1245[#1245] [syson] Standardize read-only computation.
+We removed the assumptions SysON made on whether a resource is read-only.
+Resources are now considered read-only if:
+* They are Sirius Web libraries imported by reference
+* They are textual SysML files imported as read-only
+* They are standard libraries (SysML and KerML)
+All the other resources are read-write.
=== New features
diff --git a/backend/application/pom.xml b/backend/application/pom.xml
index f2a3bdfe9..f15142c1d 100644
--- a/backend/application/pom.xml
+++ b/backend/application/pom.xml
@@ -17,7 +17,7 @@
org.eclipse.syson
syson-application-parent
- 2025.8.5
+ 2025.8.8
syson-application-parent
SysON Application Parent
diff --git a/backend/application/syson-application-configuration/pom.xml b/backend/application/syson-application-configuration/pom.xml
index 1fe799eed..089098fa2 100644
--- a/backend/application/syson-application-configuration/pom.xml
+++ b/backend/application/syson-application-configuration/pom.xml
@@ -23,7 +23,7 @@
org.eclipse.syson
syson-application-configuration
- 2025.8.5
+ 2025.8.8
syson-application-configuration
SysON Application Configuration
@@ -69,27 +69,27 @@
org.eclipse.syson
syson-sysml-metamodel
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-sysml-metamodel-edit
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-services
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-siriusweb-customnodes-metamodel
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-siriusweb-customnodes-metamodel-edit
- 2025.8.5
+ 2025.8.8
@@ -105,7 +105,7 @@
org.eclipse.syson
syson-tests
- 2025.8.5
+ 2025.8.8
test
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java
index 78444e849..52b9ff519 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java
@@ -12,15 +12,11 @@
*******************************************************************************/
package org.eclipse.syson.application.services;
-import java.util.Objects;
-
import org.eclipse.emf.ecore.EAnnotation;
-import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicate;
import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicateDelegate;
-import org.eclipse.syson.services.UtilService;
-import org.eclipse.syson.services.api.ISysONResourceService;
+import org.eclipse.sirius.components.emf.ResourceMetadataAdapter;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.util.ElementUtil;
import org.springframework.stereotype.Service;
@@ -42,12 +38,6 @@
@Service
public class SysONReadOnlyObjectPredicateDelegate implements IReadOnlyObjectPredicateDelegate {
- private final ISysONResourceService sysONResourceService;
-
- public SysONReadOnlyObjectPredicateDelegate(final ISysONResourceService sysONResourceService) {
- this.sysONResourceService = Objects.requireNonNull(sysONResourceService);
- }
-
@Override
public boolean canHandle(Object candidate) {
return candidate instanceof Element || candidate instanceof Resource || candidate instanceof EAnnotation;
@@ -60,12 +50,18 @@ public boolean test(Object candidate) {
if (candidate instanceof Resource resource) {
isReadOnly = ElementUtil.isStandardLibraryResource(resource)
- || this.sysONResourceService.isImported(resource) && !new UtilService().getLibraries(resource, false).isEmpty();
- } else if (candidate instanceof Element || candidate instanceof EAnnotation) {
- // Derive editability from the editability of the containing resource.
- final EObject eObject = (EObject) candidate;
- final Resource resource = eObject.eResource();
- isReadOnly = this.test(resource);
+ || resource.eAdapters().stream()
+ .filter(ResourceMetadataAdapter.class::isInstance)
+ .map(ResourceMetadataAdapter.class::cast)
+ .map(ResourceMetadataAdapter::isReadOnly)
+ .findFirst()
+ .orElse(false);
+ } else if (candidate instanceof Element element) {
+ // An element is read-only if it is contained in a standard LibraryPackage, regardless of whether its
+ // containing resource is read-only or not.
+ isReadOnly = this.test(element.eResource()) || ElementUtil.isFromStandardLibrary(element);
+ } else if (candidate instanceof EAnnotation eAnnotation) {
+ isReadOnly = this.test(eAnnotation.eResource());
}
return isReadOnly;
diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java
index 0e9f6d252..affc5b52c 100644
--- a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java
+++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java
@@ -21,6 +21,7 @@
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.sirius.components.core.api.IFeedbackMessageService;
import org.eclipse.sirius.components.core.services.ComposedReadOnlyObjectPredicate;
+import org.eclipse.sirius.components.emf.ResourceMetadataAdapter;
import org.eclipse.sirius.components.emf.services.JSONResourceFactory;
import org.eclipse.sirius.web.application.object.services.DefaultReadOnlyObjectPredicate;
import org.eclipse.syson.sysml.LibraryPackage;
@@ -46,7 +47,7 @@ public class DetailsViewServiceTest {
public void setUp() {
// Use a dummy CompsedAdapterFactory, we don't test methods that require the one used by SysON for the moment.
this.detailsViewService = new DetailsViewService(new ComposedAdapterFactory(), new IFeedbackMessageService.NoOp(),
- new ComposedReadOnlyObjectPredicate(List.of(new SysONReadOnlyObjectPredicateDelegate(new SysONResourceService())), new DefaultReadOnlyObjectPredicate()));
+ new ComposedReadOnlyObjectPredicate(List.of(new SysONReadOnlyObjectPredicateDelegate()), new DefaultReadOnlyObjectPredicate()));
}
@Test
@@ -95,6 +96,20 @@ public void isReadOnlyElementInImportedLibrary() {
namespace.getOwnedRelationship().add(owningMembership);
owningMembership.getOwnedRelatedElement().add(libraryPackage);
ElementUtil.setIsImported(resource, true);
+ assertThat(this.detailsViewService.isReadOnly(libraryPackage)).isFalse();
+ }
+
+ @Test
+ public void isReadOnlyElementInImportedLibraryFlaggedAsReadOnly() {
+ Resource resource = new JSONResourceFactory().createResourceFromPath("testResource");
+ Namespace namespace = SysmlFactory.eINSTANCE.createNamespace();
+ resource.getContents().add(namespace);
+ LibraryPackage libraryPackage = SysmlFactory.eINSTANCE.createLibraryPackage();
+ OwningMembership owningMembership = SysmlFactory.eINSTANCE.createOwningMembership();
+ namespace.getOwnedRelationship().add(owningMembership);
+ owningMembership.getOwnedRelatedElement().add(libraryPackage);
+ ElementUtil.setIsImported(resource, true);
+ resource.eAdapters().add(new ResourceMetadataAdapter("test", true));
assertThat(this.detailsViewService.isReadOnly(libraryPackage)).isTrue();
}
diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java
index ae79d872a..7822b9758 100644
--- a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java
+++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java
@@ -26,6 +26,7 @@
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicateDelegate;
import org.eclipse.sirius.components.core.services.ComposedReadOnlyObjectPredicate;
+import org.eclipse.sirius.components.emf.ResourceMetadataAdapter;
import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext;
import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter;
import org.eclipse.syson.application.configuration.SysONDefaultLibrariesConfiguration;
@@ -57,7 +58,7 @@
*/
public class SysONReadOnlyObjectPredicateDelegateTests {
- private final IReadOnlyObjectPredicateDelegate readOnlyObjectPredicateDelegate = new SysONReadOnlyObjectPredicateDelegate(new SysONResourceService());
+ private final IReadOnlyObjectPredicateDelegate readOnlyObjectPredicateDelegate = new SysONReadOnlyObjectPredicateDelegate();
@SafeVarargs
private static Resource createResource(final URI uri, final Consumer... postTreatments) {
@@ -282,6 +283,8 @@ public class ImportedResources {
private final Resource importedResourceWithSysmlLibraryPackage;
+ private final Resource importedResourceWithSysmlLibraryPackageAndFlaggedAsReadOnly;
+
private final Resource importedResourceWithSysmlMixedPackages;
public ImportedResources() {
@@ -295,6 +298,13 @@ public ImportedResources() {
ElementUtil.setIsImported(resource, true);
});
+ this.importedResourceWithSysmlLibraryPackageAndFlaggedAsReadOnly = createResource(createEmfUri(), resource -> {
+ resource.getContents().addAll(EcoreUtil.copyAll(SysMLResources.this.resourceWithSysmlLibraryPackage.getContents()));
+ ElementUtil.setIsImported(resource, true);
+ ResourceMetadataAdapter resourceMetadataAdapter = new ResourceMetadataAdapter("test", true);
+ resource.eAdapters().add(resourceMetadataAdapter);
+ });
+
this.importedResourceWithSysmlMixedPackages = createResource(createEmfUri(), resource -> {
resource.getContents().addAll(EcoreUtil.copyAll(SysMLResources.this.resourceWithSysmlMixedPackages.getContents()));
ElementUtil.setIsImported(resource, true);
@@ -308,15 +318,21 @@ public void testResourceWithSysmlPackageImported() {
}
@Test
- @DisplayName("Imported resource containing LibraryPackage is read-only (both the resource and all of its contents)")
+ @DisplayName("Imported resource containing LibraryPackage is not read-only (both the resource and all of its contents)")
public void testResourceWithSysmlLibraryPackageImported() {
- SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.importedResourceWithSysmlLibraryPackage, true);
+ SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.importedResourceWithSysmlLibraryPackage, false);
+ }
+
+ @Test
+ @DisplayName("Imported resource containing LibraryPackage and flagged as read-only is read-only (both the resource and all of its contents)")
+ public void testResourceWithSysmlLibraryPackageImportedAndFlaggedAsReadOnly() {
+ SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.importedResourceWithSysmlLibraryPackageAndFlaggedAsReadOnly, true);
}
@Test
- @DisplayName("Imported resource containing Package and LibraryPackage is read-only (both the resource and all of its contents)")
+ @DisplayName("Imported resource containing Package and LibraryPackage is not read-only (both the resource and all of its contents)")
public void testResourceWithSysmlMixedPackagesImported() {
- SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.importedResourceWithSysmlMixedPackages, true);
+ SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.importedResourceWithSysmlMixedPackages, false);
}
}
@@ -339,26 +355,26 @@ public class ReferencedResources {
public ReferencedResources() {
this.referencedResourceWithSysmlPackage = createResource(createEmfUri(), resource -> {
resource.getContents().addAll(EcoreUtil.copyAll(SysMLResources.this.resourceWithSysmlPackage.getContents()));
- }, resource -> resource.eAdapters().add(createLibraryMetadataAdapter()));
+ }, ReferencedResources::addReferencedResourceAdapters);
this.referencedResourceWithSysmlLibraryPackage = createResource(createEmfUri(), resource -> {
resource.getContents().addAll(EcoreUtil.copyAll(SysMLResources.this.resourceWithSysmlLibraryPackage.getContents()));
- }, resource -> resource.eAdapters().add(createLibraryMetadataAdapter()));
+ }, ReferencedResources::addReferencedResourceAdapters);
this.referencedResourceWithSysmlMixedPackages = createResource(createEmfUri(), resource -> {
resource.getContents().addAll(EcoreUtil.copyAll(SysMLResources.this.resourceWithSysmlMixedPackages.getContents()));
- }, resource -> resource.eAdapters().add(createLibraryMetadataAdapter()));
+ }, ReferencedResources::addReferencedResourceAdapters);
}
- private static LibraryMetadataAdapter createLibraryMetadataAdapter() {
- return new LibraryMetadataAdapter("namespace", "name", "version");
+ private static void addReferencedResourceAdapters(Resource resource) {
+ resource.eAdapters().add(new LibraryMetadataAdapter("namespace", "name", "version"));
+ resource.eAdapters().add(new ResourceMetadataAdapter("test", true));
}
@Test
@DisplayName("Resource from referenced library containing Package is read-only (both the resource and all of its contents)")
public void testResourceWithSysmlPackageImported() {
- // Note that this might be a bug, see https://github.com/eclipse-syson/syson/issues/1342.
- SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.referencedResourceWithSysmlPackage, false);
+ SysONReadOnlyObjectPredicateDelegateTests.this.assertResourceAndAllContentsIsReadOnly(this.referencedResourceWithSysmlPackage, true);
}
@Test
diff --git a/backend/application/syson-application/pom.xml b/backend/application/syson-application/pom.xml
index c59147278..a4049d27a 100644
--- a/backend/application/syson-application/pom.xml
+++ b/backend/application/syson-application/pom.xml
@@ -23,7 +23,7 @@
org.eclipse.syson
syson-application
- 2025.8.5
+ 2025.8.8
syson-application
SysON Application
@@ -82,62 +82,62 @@
org.eclipse.syson
syson-sysml-metamodel
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-sysml-metamodel-edit
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-frontend
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-application-configuration
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-sysml-rest-api-services
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-diagram-common-view
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-standard-diagrams-view
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-table-requirements-view
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-tree-explorer-view
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-sysml-import
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-sysml-export
- 2025.8.5
+ 2025.8.8
org.eclipse.syson
syson-sysml-validation
- 2025.8.5
+ 2025.8.8
@@ -194,7 +194,7 @@
org.eclipse.syson
syson-sysml-metamodel
- 2025.8.5
+ 2025.8.8
test-jar
test
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 a23d0453a..be41a4d73 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
@@ -236,6 +236,52 @@ public void getExplorerContentWithDefaultFilters() {
}
+ @DisplayName("GIVEN an empty SysML Project, WHEN the explorer is displayed with KerML and SysML libraries expanded, THEN the library models are visible")
+ @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 getExplorerContentWithKerMLAndSysMLExpanded() {
+ String librariesTreeItemId = UUID.nameUUIDFromBytes("SysON_Libraries_Directory".getBytes()).toString();
+ String sysmlLibrariesTreeItemId = UUID.nameUUIDFromBytes("SysON_SysML_Directory".getBytes()).toString();
+ String kermlLibrariesTreeItemId = UUID.nameUUIDFromBytes("SysON_KerML_Directory".getBytes()).toString();
+ var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.treeDescriptionId,
+ List.of(librariesTreeItemId, sysmlLibrariesTreeItemId, kermlLibrariesTreeItemId), this.defaultFilters);
+ var input = new ExplorerEventInput(UUID.randomUUID(), GeneralViewEmptyTestProjectData.EDITING_CONTEXT, explorerRepresentationId);
+ var flux = this.explorerEventSubscriptionRunner.run(input);
+ this.givenCommittedTransaction.commit();
+
+ var initialExplorerContentConsumer = assertRefreshedTreeThat(tree -> {
+ assertThat(tree).isNotNull();
+ assertThat(tree.getChildren()).hasSize(2);
+ TreeItem librariesDirectory = tree.getChildren().get(1);
+ assertThat(librariesDirectory.getLabel().toString()).isEqualTo("Libraries");
+ assertThat(librariesDirectory.isDeletable()).isFalse();
+ assertThat(librariesDirectory.isEditable()).isFalse();
+ assertThat(librariesDirectory.isSelectable()).isTrue();
+ assertThat(librariesDirectory.isHasChildren()).isTrue();
+ assertThat(librariesDirectory.getChildren()).hasSize(2);
+ TreeItem kermlDirectory = librariesDirectory.getChildren().get(0);
+ assertThat(kermlDirectory.getLabel().toString()).isEqualTo("KerML");
+ assertThat(kermlDirectory.isDeletable()).isFalse();
+ assertThat(kermlDirectory.isEditable()).isFalse();
+ assertThat(kermlDirectory.isSelectable()).isTrue();
+ assertThat(kermlDirectory.isHasChildren()).isTrue();
+ assertThat(kermlDirectory.getChildren()).allMatch(children -> !children.isDeletable() && !children.isEditable());
+ TreeItem sysmlDirectory = librariesDirectory.getChildren().get(1);
+ assertThat(sysmlDirectory.getLabel().toString()).isEqualTo("SysML");
+ assertThat(sysmlDirectory.isDeletable()).isFalse();
+ assertThat(sysmlDirectory.isEditable()).isFalse();
+ assertThat(sysmlDirectory.isSelectable()).isTrue();
+ assertThat(sysmlDirectory.isHasChildren()).isTrue();
+ assertThat(sysmlDirectory.getChildren()).allMatch(children -> !children.isDeletable() && !children.isEditable());
+ });
+
+ StepVerifier.create(flux)
+ .consumeNextWith(initialExplorerContentConsumer)
+ .thenCancel()
+ .verify(Duration.ofSeconds(10));
+ }
+
@DisplayName("GIVEN an empty SysML Project, WHEN the explorer is displayed with its root model expanded and the hide memberships and hide KerML libraries filters, THEN the root model is visible and is expanded")
@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))
diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java
new file mode 100644
index 000000000..7c12f32de
--- /dev/null
+++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/explorer/view/ExplorerViewControllerIntegrationTests.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * 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.controllers.explorer.view;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.eclipse.sirius.components.trees.tests.TreeEventPayloadConsumer.assertRefreshedTreeThat;
+
+import com.jayway.jsonpath.JsonPath;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.sirius.components.core.api.IEditingContextSearchService;
+import org.eclipse.sirius.components.core.api.IIdentityService;
+import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext;
+import org.eclipse.sirius.components.trees.Tree;
+import org.eclipse.sirius.components.trees.TreeItem;
+import org.eclipse.sirius.components.trees.tests.graphql.InitialDirectEditTreeItemLabelQueryRunner;
+import org.eclipse.sirius.web.application.views.explorer.ExplorerEventInput;
+import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
+import org.eclipse.sirius.web.tests.services.explorer.ExplorerEventSubscriptionRunner;
+import org.eclipse.sirius.web.tests.services.representation.RepresentationIdBuilder;
+import org.eclipse.syson.AbstractIntegrationTests;
+import org.eclipse.syson.application.data.ExplorerViewDirectEditTestProjectData;
+import org.eclipse.syson.tree.explorer.view.SysONTreeViewDescriptionProvider;
+import org.eclipse.syson.tree.explorer.view.filters.SysONTreeFilterProvider;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.jdbc.SqlConfig;
+import org.springframework.transaction.annotation.Transactional;
+
+import reactor.test.StepVerifier;
+
+/**
+ * Integration tests of the Explorer view.
+ *
+ * @author arichard
+ */
+@Transactional
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class ExplorerViewControllerIntegrationTests extends AbstractIntegrationTests {
+
+ /**
+ * Record used to contain both a way to find a tree item and some predicate to validate on said tree item in order
+ * to simplify the design of some advanced tests.
+ *
+ * @author arichard
+ */
+ private record TreeItemMatcher(Function treeItemFinder, Predicate treeItemPredicate) {
+ }
+
+ private final TreeItemMatcher view1GV = new TreeItemMatcher(
+ tree -> tree.getChildren().get(0).getChildren().get(0).getChildren().get(2),
+ treeItem -> treeItem.getLabel().toString().equals("view1 [GeneralView]"));
+
+ private final TreeItemMatcher view2AFV = new TreeItemMatcher(
+ tree -> tree.getChildren().get(0).getChildren().get(0).getChildren().get(3),
+ treeItem -> treeItem.getLabel().toString().equals("view2 [ActionFlowView]"));
+
+ private final TreeItemMatcher view3STV = new TreeItemMatcher(
+ tree -> tree.getChildren().get(0).getChildren().get(0).getChildren().get(4),
+ treeItem -> treeItem.getLabel().toString().equals("view3 [StateTransitionView]"));
+
+ private final TreeItemMatcher view4IV = new TreeItemMatcher(
+ tree -> tree.getChildren().get(0).getChildren().get(0).getChildren().get(1).getChildren().get(1),
+ treeItem -> treeItem.getLabel().toString().equals("view4 [InterconnectionView]"));
+
+ @Autowired
+ private IGivenInitialServerState givenInitialServerState;
+
+ @Autowired
+ private IEditingContextSearchService editingContextSearchService;
+
+ @Autowired
+ private IIdentityService identityService;
+
+ @Autowired
+ private ExplorerEventSubscriptionRunner treeEventSubscriptionRunner;
+
+ @Autowired
+ private InitialDirectEditTreeItemLabelQueryRunner initialDirectEditTreeItemLabelQueryRunner;
+
+ @Autowired
+ private RepresentationIdBuilder representationIdBuilder;
+
+ @Autowired
+ private SysONTreeViewDescriptionProvider sysONTreeViewDescriptionProvider;
+
+ @BeforeEach
+ public void beforeEach() {
+ this.givenInitialServerState.initialize();
+ }
+
+ @DisplayName("GIVEN the SysON Explorer View, WHEN we direct edit a ViewUsage typed with a standard diagram, THEN the type of ViewUsage is not part of the initial value of the direct edit")
+ @Sql(scripts = { ExplorerViewDirectEditTestProjectData.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 testDirectEditOnViewUsage() {
+ var expandedIds = this.getAllTreeItemIds();
+ var activatedFilters = List.of(SysONTreeFilterProvider.HIDE_ROOT_NAMESPACES_ID, SysONTreeFilterProvider.HIDE_MEMBERSHIPS_TREE_ITEM_FILTER_ID);
+ var treeRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONTreeViewDescriptionProvider.getDescriptionId(), expandedIds, activatedFilters);
+
+ var treeEventInput = new ExplorerEventInput(UUID.randomUUID(), ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, treeRepresentationId);
+ var treeFlux = this.treeEventSubscriptionRunner.run(treeEventInput);
+
+ var hasProjectContent = this.getTreeRefreshedEventPayloadMatcher(List.of(this.view1GV, this.view2AFV, this.view3STV, this.view4IV));
+
+ StepVerifier.create(treeFlux)
+ .consumeNextWith(hasProjectContent)
+ .then(this.triggerDirectEditTreeItemLabel(ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, treeRepresentationId, UUID.fromString(ExplorerViewDirectEditTestProjectData.SemanticIds.VIEW_1_GV_ID), "view1"))
+ .then(this.triggerDirectEditTreeItemLabel(ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, treeRepresentationId,
+ UUID.fromString(ExplorerViewDirectEditTestProjectData.SemanticIds.VIEW_2_AFV_ID), "view2"))
+ .then(this.triggerDirectEditTreeItemLabel(ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, treeRepresentationId,
+ UUID.fromString(ExplorerViewDirectEditTestProjectData.SemanticIds.VIEW_3_STV_ID), "view3"))
+ .then(this.triggerDirectEditTreeItemLabel(ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID, treeRepresentationId,
+ UUID.fromString(ExplorerViewDirectEditTestProjectData.SemanticIds.VIEW_4_IV_ID), "view4"))
+ .thenCancel()
+ .verify(Duration.ofSeconds(100));
+
+ }
+
+ private List getAllTreeItemIds() {
+ var optionalEditingContext = this.editingContextSearchService.findById(ExplorerViewDirectEditTestProjectData.EDITING_CONTEXT_ID)
+ .filter(IEMFEditingContext.class::isInstance)
+ .map(IEMFEditingContext.class::cast);
+ assertThat(optionalEditingContext).isPresent();
+
+ var editingContext = optionalEditingContext.get();
+ var expandedIds = new ArrayList();
+ editingContext.getDomain().getResourceSet().getAllContents().forEachRemaining(notifier -> {
+ if (notifier instanceof Resource || notifier instanceof EObject) {
+ expandedIds.add(this.identityService.getId(notifier));
+ }
+ });
+ return expandedIds;
+ }
+
+ private Consumer