From 23946980ccb8c48e74913a63cd0c8812764f2ca7 Mon Sep 17 00:00:00 2001 From: Florian Barbin Date: Wed, 14 Aug 2024 11:23:48 +0200 Subject: [PATCH] [3763] Update the Selection Dialog to display its content using a TreeView Bug: https://github.com/eclipse-sirius/sirius-web/issues/3763 Signed-off-by: Florian Barbin --- CHANGELOG.adoc | 9 +- .../SelectModelElementVariableConverter.java | 20 +- .../emf/services/DefaultContentService.java | 14 +- .../emf/services/DefaultIdentityService.java | 2 + .../emf/services/DefaultLabelService.java | 22 + .../emf/services/EMFImagePathService.java | 2 +- .../src/main/resources/icons/Resource.svg | 8 + .../pom.xml | 5 + .../selection/SelectionEventProcessor.java | 173 -------- .../SelectionEventProcessorFactory.java | 84 ---- .../api/ISelectionEventProcessor.java | 31 -- .../selection/api/SelectionConfiguration.java | 51 --- .../SelectionDialogTreeConfiguration.java | 53 +++ .../GetSelectionDescriptionMessageInput.java | 32 ++ ...etSelectionDescriptionMessagePayload.java} | 15 +- ...ava => SelectionDialogTreeEventInput.java} | 9 +- ...lectionDescriptionMessageEventHandler.java | 74 ++++ ...ectionDialogExpandAllTreePathProvider.java | 112 +++++ .../SelectionDialogNavigationService.java | 91 ++++ ...ectionDialogTreeEventProcessorFactory.java | 102 +++++ ...lectionRepresentationMetadataProvider.java | 54 +++ .../ISelectionDialogNavigationService.java | 44 ++ .../main/resources/schema/selection.graphqls | 31 +- ...electionDescriptionMessageDataFetcher.java | 62 +++ .../SelectionObjectIconURLDataFetcher.java | 47 -- ...eshedEventPayloadSelectionDataFetcher.java | 38 -- ...nSelectionDialogTreeEventDataFetcher.java} | 27 +- .../sirius-components-selection/pom.xml | 5 + .../components/selection/Selection.java | 159 ------- .../components/selection/SelectionObject.java | 99 ----- .../description/SelectionDescription.java | 56 +-- .../selection/renderer/SelectionRenderer.java | 76 ---- .../sirius-components-selection/package.json | 2 + .../src/SelectionDialog.tsx | 177 ++------ .../src/SelectionDialog.types.ts | 11 +- .../src/SelectionDialogMachine.ts | 175 -------- .../src/SelectionDialogTreeView.tsx | 89 ++++ .../src/SelectionDialogTreeView.types.ts} | 19 +- .../src/useSelectionDescription.ts | 76 ++++ .../src/useSelectionDescription.types.ts | 54 +++ .../useSelectionDialogTreeSubscription.tsx | 87 ++++ ...seSelectionDialogTreeSubscription.types.ts | 49 +++ ...tionDialogTreeEventSubscriptionRunner.java | 51 +++ ...DetailsViewControllerIntegrationTests.java | 8 +- .../SelectionControllerIntegrationTests.java | 412 ++++++++++++------ .../SelectionDescriptionProvider.java | 175 ++++++-- .../src/views/TreeView.tsx | 6 +- .../provider/NodeToolItemProvider.java | 10 +- .../components/view/emf/ViewConverter.java | 71 +-- .../emf/api/IDialogDescriptionConverter.java | 31 ++ .../view/emf/diagram/DiagramIdProvider.java | 12 + .../view/emf/diagram/IDiagramIdProvider.java | 10 + .../SelectionDialogDescriptionConverter.java | 325 ++++++++++++++ .../view/emf/view/DynamicDiagramsTests.java | 2 +- .../view/emf/view/DynamicFormsTests.java | 3 +- scripts/check-coverage.jsh | 26 +- 56 files changed, 2032 insertions(+), 1456 deletions(-) create mode 100644 packages/emf/backend/sirius-components-emf/src/main/resources/icons/Resource.svg delete mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessor.java delete mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessorFactory.java delete mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionEventProcessor.java delete mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/SelectionConfiguration.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/configurations/SelectionDialogTreeConfiguration.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessageInput.java rename packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/{SelectionRefreshedEventPayload.java => GetSelectionDescriptionMessagePayload.java} (66%) rename packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/{SelectionEventInput.java => SelectionDialogTreeEventInput.java} (70%) create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/handlers/SelectionDescriptionMessageEventHandler.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogExpandAllTreePathProvider.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogNavigationService.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogTreeEventProcessorFactory.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionRepresentationMetadataProvider.java create mode 100644 packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/api/ISelectionDialogNavigationService.java create mode 100644 packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionDescriptionMessageDataFetcher.java delete mode 100644 packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionObjectIconURLDataFetcher.java delete mode 100644 packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SelectionRefreshedEventPayloadSelectionDataFetcher.java rename packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/{SubscriptionSelectionEventDataFetcher.java => SubscriptionSelectionDialogTreeEventDataFetcher.java} (74%) delete mode 100644 packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/Selection.java delete mode 100644 packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/SelectionObject.java delete mode 100644 packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/renderer/SelectionRenderer.java delete mode 100644 packages/selection/frontend/sirius-components-selection/src/SelectionDialogMachine.ts create mode 100644 packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.tsx rename packages/selection/{backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionInput.java => frontend/sirius-components-selection/src/SelectionDialogTreeView.types.ts} (59%) create mode 100644 packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.ts create mode 100644 packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.types.ts create mode 100644 packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx create mode 100644 packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.types.ts create mode 100644 packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/services/selection/SelectionDialogTreeEventSubscriptionRunner.java create mode 100644 packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/api/IDialogDescriptionConverter.java create mode 100644 packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/SelectionDialogDescriptionConverter.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 3df2aafa10..a2f19a3fd1 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -23,6 +23,11 @@ - https://github.com/eclipse-sirius/sirius-web/issues/3875[#3875] [sirius-web] The `ExplorerView` component has been moved from `sirius-components-trees` to `sirius-web-application` module - https://github.com/eclipse-sirius/sirius-web/issues/3155[#3155] [core] All the paths in the code involving the modification of some data (project, semantic data, representation data, etc.) will now require a new parameter `ICause cause` which is the cause of this change. Both `IInput` and `IDomainEvent` implement `ICause` and will thus be used to indicate which action has triggered the change. +- https://github.com/eclipse-sirius/sirius-web/issues/3763[#3763] [core] updates some services behavior: +** The `org.eclipse.sirius.components.emf.services.DefaultIdentityService#getId(Object object)` Can now provides the Id if the Object is of type `org.eclipse.emf.ecore.resource.Resource`. Before this change, it would have returned null. +** The `org.eclipse.sirius.components.emf.services.DefaultLabelService` Can now provides the label if the Object is of type `org.eclipse.emf.ecore.resource.Resource`. Before this change, it would have returned an empty string. +** The `org.eclipse.sirius.components.emf.services.DefaultContentService#getContents(Object)` Can now provides the content if the Object is of type `org.eclipse.emf.ecore.resource.Resource`. Before this change, it would have returned an empty list. + === Dependency update @@ -39,6 +44,8 @@ Both `IInput` and `IDomainEvent` implement `ICause` and will thus be used to ind === New Features +- https://github.com/eclipse-sirius/sirius-web/issues/3763[#3763] [diagram] Make it possible to display semantic candidates in the selection dialog using a tree + === Improvements @@ -51,7 +58,6 @@ Both `IInput` and `IDomainEvent` implement `ICause` and will thus be used to ind The interfaces `IInput` and `IDomainEvent` are now both extending `ICause` which let us capture the causes of a domain event recursively. An event may have been caused by another event or caused by an input received from the frontend for example. - == v2024.9.0 === Shapes @@ -186,6 +192,7 @@ A migration participant has been added to automatically keep compatible all diag - https://github.com/eclipse-sirius/sirius-web/issues/3951[#3951] [sirius-web] Provide an error page to redirect users in case of error - https://github.com/eclipse-sirius/sirius-web/issues/3974[#3974] [diagram] Add support for `<` and `>` to trigger direct edit + == v2024.7.0 === Shapes diff --git a/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/diagrams/SelectModelElementVariableConverter.java b/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/diagrams/SelectModelElementVariableConverter.java index d0b051aaf1..fb374bf0db 100644 --- a/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/diagrams/SelectModelElementVariableConverter.java +++ b/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/diagrams/SelectModelElementVariableConverter.java @@ -12,17 +12,12 @@ *******************************************************************************/ package org.eclipse.sirius.components.compatibility.diagrams; -import java.util.List; import java.util.Objects; -import org.eclipse.sirius.components.compatibility.api.IAQLInterpreterFactory; import org.eclipse.sirius.components.compatibility.api.IIdentifierProvider; import org.eclipse.sirius.components.compatibility.services.selection.api.ISelectModelElementVariableConverter; import org.eclipse.sirius.components.core.api.IObjectService; -import org.eclipse.sirius.components.interpreter.AQLInterpreter; -import org.eclipse.sirius.components.interpreter.Result; import org.eclipse.sirius.components.representations.VariableManager; -import org.eclipse.sirius.components.selection.Selection; import org.eclipse.sirius.components.selection.description.SelectionDescription; import org.eclipse.sirius.viewpoint.description.tool.SelectModelElementVariable; import org.springframework.stereotype.Service; @@ -41,24 +36,15 @@ public class SelectModelElementVariableConverter implements ISelectModelElementV private final IIdentifierProvider identifierProvider; - private final IAQLInterpreterFactory interpreterFactory; - public SelectModelElementVariableConverter(IObjectService objectService, IIdentifierProvider identifierProvider, IAQLInterpreterFactory interpreterFactory) { + public SelectModelElementVariableConverter(IObjectService objectService, IIdentifierProvider identifierProvider) { this.objectService = Objects.requireNonNull(objectService); this.identifierProvider = Objects.requireNonNull(identifierProvider); - this.interpreterFactory = Objects.requireNonNull(interpreterFactory); } @Override public SelectionDescription convert(SelectModelElementVariable selectModelElementVariable, org.eclipse.sirius.diagram.description.DiagramDescription diagramDescription) { - AQLInterpreter interpreter = this.interpreterFactory.create(diagramDescription); return SelectionDescription.newSelectionDescription(this.identifierProvider.getIdentifier(selectModelElementVariable)) - .objectsProvider(variableManager -> { - Result result = interpreter.evaluateExpression(variableManager.getVariables(), selectModelElementVariable.getCandidatesExpression()); - return result.asObjects().orElse(List.of()).stream() - .filter(Objects::nonNull) - .toList(); - }) .messageProvider(variableManager -> { String message = selectModelElementVariable.getMessage(); if (message == null) { @@ -66,11 +52,9 @@ public SelectionDescription convert(SelectModelElementVariable selectModelElemen } return message; }) - .idProvider(variableManager -> Selection.PREFIX) + .idProvider(variableManager -> "selection://") .labelProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getLabel).orElse(null)) - .iconURLProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getImagePath).orElse(List.of())) .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) - .selectionObjectsIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) .label("Selection Description") .canCreatePredicate(variableManager -> false) .build(); diff --git a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultContentService.java b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultContentService.java index a235bfa652..1603a60e34 100644 --- a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultContentService.java +++ b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultContentService.java @@ -12,17 +12,17 @@ *******************************************************************************/ package org.eclipse.sirius.components.emf.services; -import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.IEditingDomainItemProvider; import org.eclipse.sirius.components.core.api.IDefaultContentService; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import org.springframework.stereotype.Service; /** * Default implementation of {@link IDefaultContentService}. @@ -48,6 +48,10 @@ public List getContents(Object object) { contents.addAll(eObject.eContents()); } } + else if (object instanceof Resource resource) { + // The object may be a document + contents.addAll(resource.getContents()); + } return contents; } } diff --git a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultIdentityService.java b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultIdentityService.java index c87997e0d5..38afaa5be6 100644 --- a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultIdentityService.java +++ b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultIdentityService.java @@ -54,6 +54,8 @@ public String getId(Object object) { id = representation.getId(); } else if (object instanceof IEditingContext editingContext) { id = editingContext.getId(); + } else if (object instanceof Resource resource) { + id = resource.getURI().path().substring(1); } return id; } diff --git a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultLabelService.java b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultLabelService.java index 00c5e323bf..5db73b64ec 100644 --- a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultLabelService.java +++ b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/DefaultLabelService.java @@ -24,6 +24,7 @@ import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.ComposedImage; import org.eclipse.emf.edit.provider.IItemLabelProvider; @@ -31,6 +32,7 @@ import org.eclipse.sirius.components.collaborative.api.IRepresentationImageProvider; import org.eclipse.sirius.components.core.api.IDefaultLabelService; import org.eclipse.sirius.components.core.api.labels.StyledString; +import org.eclipse.sirius.components.emf.ResourceMetadataAdapter; import org.eclipse.sirius.components.representations.IRepresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,11 +47,15 @@ public class DefaultLabelService implements IDefaultLabelService { public static final String DEFAULT_ICON_PATH = "/icons/svg/Default.svg"; + private static final String DEFAULT_LABEL_FEATURE = "name"; + private final LabelFeatureProviderRegistry labelFeatureProviderRegistry; + private final ComposedAdapterFactory composedAdapterFactory; private final List representationImageProviders; + private final Logger logger = LoggerFactory.getLogger(LabelFeatureProviderRegistry.class); public DefaultLabelService(LabelFeatureProviderRegistry labelFeatureProviderRegistry, ComposedAdapterFactory composedAdapterFactory, List representationImageProviders) { @@ -57,11 +63,13 @@ public DefaultLabelService(LabelFeatureProviderRegistry labelFeatureProviderRegi this.composedAdapterFactory = Objects.requireNonNull(composedAdapterFactory); this.representationImageProviders = Objects.requireNonNull(representationImageProviders); } + @Override public String getLabel(Object object) { return this.getStyledLabel(object).toString(); } + @Override public StyledString getStyledLabel(Object object) { String label = ""; if (object instanceof EObject eObject) { @@ -71,11 +79,21 @@ public StyledString getStyledLabel(Object object) { .orElse(""); } else if (object instanceof IRepresentation representation) { label = representation.getLabel(); + } else if (object instanceof Resource resource) { + label = this.getResourceLabel(resource); } return StyledString.of(label); } + private String getResourceLabel(Resource resource) { + return resource.eAdapters().stream() + .filter(ResourceMetadataAdapter.class::isInstance) + .map(ResourceMetadataAdapter.class::cast).findFirst() + .map(ResourceMetadataAdapter::getName) + .orElse(resource.getURI().lastSegment()); + } + @Override public String getFullLabel(Object object) { String fullLabel = ""; @@ -87,6 +105,8 @@ public String getFullLabel(Object object) { } } else if (object instanceof IRepresentation representation) { fullLabel = representation.getLabel(); + } else if (object instanceof Resource resource) { + fullLabel = this.getResourceLabel(resource); } else { fullLabel = this.getLabel(object); } @@ -143,6 +163,8 @@ public List getImagePath(Object object) { .map(provider -> provider.getImageURL(representation.getKind())) .flatMap(Optional::stream) .toList(); + } else if (object instanceof Resource) { + result = List.of("/icons/Resource.svg"); } return result; } diff --git a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/EMFImagePathService.java b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/EMFImagePathService.java index 5e67d44aab..3ed377987d 100644 --- a/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/EMFImagePathService.java +++ b/packages/emf/backend/sirius-components-emf/src/main/java/org/eclipse/sirius/components/emf/services/EMFImagePathService.java @@ -25,7 +25,7 @@ @Service public class EMFImagePathService implements IImagePathService { - private static final List IMAGES_PATHS = List.of("/icons/full/obj16", "/icons/full/ovr16"); + private static final List IMAGES_PATHS = List.of("/icons/full/obj16", "/icons/full/ovr16", "/icons"); @Override public List getPaths() { diff --git a/packages/emf/backend/sirius-components-emf/src/main/resources/icons/Resource.svg b/packages/emf/backend/sirius-components-emf/src/main/resources/icons/Resource.svg new file mode 100644 index 0000000000..1a76ef6435 --- /dev/null +++ b/packages/emf/backend/sirius-components-emf/src/main/resources/icons/Resource.svg @@ -0,0 +1,8 @@ + + + + diff --git a/packages/selection/backend/sirius-components-collaborative-selection/pom.xml b/packages/selection/backend/sirius-components-collaborative-selection/pom.xml index fbc2fb3032..d72b6ed4c3 100644 --- a/packages/selection/backend/sirius-components-collaborative-selection/pom.xml +++ b/packages/selection/backend/sirius-components-collaborative-selection/pom.xml @@ -75,6 +75,11 @@ assertj-core test + + org.eclipse.sirius + sirius-components-collaborative-trees + 2024.9.0 + diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessor.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessor.java deleted file mode 100644 index 5fd7ef82ac..0000000000 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessor.java +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2024 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.sirius.components.collaborative.selection; - -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; - -import org.eclipse.sirius.components.collaborative.api.ChangeDescription; -import org.eclipse.sirius.components.collaborative.api.ChangeKind; -import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicy; -import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyRegistry; -import org.eclipse.sirius.components.collaborative.api.ISubscriptionManager; -import org.eclipse.sirius.components.collaborative.selection.api.ISelectionEventProcessor; -import org.eclipse.sirius.components.collaborative.selection.dto.SelectionRefreshedEventPayload; -import org.eclipse.sirius.components.core.api.IEditingContext; -import org.eclipse.sirius.components.core.api.IInput; -import org.eclipse.sirius.components.core.api.IObjectService; -import org.eclipse.sirius.components.core.api.IPayload; -import org.eclipse.sirius.components.core.api.IRepresentationInput; -import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider; -import org.eclipse.sirius.components.representations.IRepresentation; -import org.eclipse.sirius.components.representations.VariableManager; -import org.eclipse.sirius.components.selection.Selection; -import org.eclipse.sirius.components.selection.description.SelectionDescription; -import org.eclipse.sirius.components.selection.renderer.SelectionRenderer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.publisher.Sinks; -import reactor.core.publisher.Sinks.EmitResult; -import reactor.core.publisher.Sinks.Many; -import reactor.core.publisher.Sinks.One; - -/** - * Reacts to the input that target the selection and publishes updated versions of the {@link Selection} to interested - * subscribers. - * - * @author arichard - */ -public class SelectionEventProcessor implements ISelectionEventProcessor { - - private final Logger logger = LoggerFactory.getLogger(SelectionEventProcessor.class); - - private final IEditingContext editingContext; - - private final SelectionDescription selectionDescription; - - private final IObjectService objectService; - - private final String id; - - private final String objectId; - - private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry; - - private final ISubscriptionManager subscriptionManager; - - private final Many sink = Sinks.many().multicast().directBestEffort(); - - private final AtomicReference currentSelection = new AtomicReference<>(); - - public SelectionEventProcessor(IEditingContext editingContext, IObjectService objectService, SelectionDescription selectionDescription, String id, String objectId, ISubscriptionManager subscriptionManager, - IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry) { - this.logger.trace("Creating the selection event processor {}", id); - this.objectService = Objects.requireNonNull(objectService); - this.selectionDescription = Objects.requireNonNull(selectionDescription); - this.editingContext = Objects.requireNonNull(editingContext); - this.id = Objects.requireNonNull(id); - this.objectId = Objects.requireNonNull(objectId); - this.subscriptionManager = Objects.requireNonNull(subscriptionManager); - this.representationRefreshPolicyRegistry = Objects.requireNonNull(representationRefreshPolicyRegistry); - - Selection selection = this.refreshSelection(); - this.currentSelection.set(selection); - - } - - @Override - public IRepresentation getRepresentation() { - return this.currentSelection.get(); - } - - @Override - public ISubscriptionManager getSubscriptionManager() { - return this.subscriptionManager; - } - - @Override - public void handle(One payloadSink, Many changeDescriptionSink, IRepresentationInput representationInput) { - // Do nothing - } - - @Override - public void refresh(ChangeDescription changeDescription) { - if (this.shouldRefresh(changeDescription)) { - Selection selection = this.refreshSelection(); - - this.currentSelection.set(selection); - if (this.sink.currentSubscriberCount() > 0) { - EmitResult emitResult = this.sink.tryEmitNext(new SelectionRefreshedEventPayload(changeDescription.getInput().id(), selection)); - if (emitResult.isFailure()) { - String pattern = "An error has occurred while emitting a SelectionRefreshedEventPayload: {}"; - this.logger.warn(pattern, emitResult); - } - } - } - } - - private boolean shouldRefresh(ChangeDescription changeDescription) { - // @formatter:off - return this.representationRefreshPolicyRegistry.getRepresentationRefreshPolicy(this.selectionDescription) - .orElseGet(this::getDefaultRefreshPolicy) - .shouldRefresh(changeDescription); - // @formatter:on - } - - private IRepresentationRefreshPolicy getDefaultRefreshPolicy() { - return (changeDescription) -> ChangeKind.SEMANTIC_CHANGE.equals(changeDescription.getKind()); - } - - private Selection refreshSelection() { - VariableManager variableManager = new VariableManager(); - var optionalObject = this.objectService.getObject(this.editingContext, this.objectId); - variableManager.put(VariableManager.SELF, optionalObject.orElse(null)); - variableManager.put(IEditingContext.EDITING_CONTEXT, this.editingContext); - variableManager.put(GetOrCreateRandomIdProvider.PREVIOUS_REPRESENTATION_ID, this.id); - - Selection selection = new SelectionRenderer(variableManager, this.selectionDescription).render(); - - this.logger.trace("Selection refreshed: {}", selection.getId()); - - return selection; - } - - @Override - public Flux getOutputEvents(IInput input) { - var initialRefresh = Mono.fromCallable(() -> new SelectionRefreshedEventPayload(input.id(), this.currentSelection.get())); - var refreshEventFlux = Flux.concat(initialRefresh, this.sink.asFlux()); - - // @formatter:off - return Flux.merge( - refreshEventFlux, - this.subscriptionManager.getFlux(input) - ); - // @formatter:on - } - - @Override - public void dispose() { - this.logger.trace("Disposing the selection event processor {}", this.id); - - this.subscriptionManager.dispose(); - - EmitResult emitResult = this.sink.tryEmitComplete(); - if (emitResult.isFailure()) { - String pattern = "An error has occurred while marking the publisher as complete: {}"; - this.logger.warn(pattern, emitResult); - } - } - -} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessorFactory.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessorFactory.java deleted file mode 100644 index 5cb65314a8..0000000000 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/SelectionEventProcessorFactory.java +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2024 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.sirius.components.collaborative.selection; - -import java.util.Objects; -import java.util.Optional; - -import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; -import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; -import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessorFactory; -import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyRegistry; -import org.eclipse.sirius.components.collaborative.api.ISubscriptionManagerFactory; -import org.eclipse.sirius.components.collaborative.api.RepresentationEventProcessorFactoryConfiguration; -import org.eclipse.sirius.components.collaborative.selection.api.SelectionConfiguration; -import org.eclipse.sirius.components.core.api.IEditingContext; -import org.eclipse.sirius.components.core.api.IObjectService; -import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; -import org.eclipse.sirius.components.selection.description.SelectionDescription; -import org.springframework.stereotype.Service; - -/** - * Used to create the selection event processors. - * - * @author arichard - */ -@Service -public class SelectionEventProcessorFactory implements IRepresentationEventProcessorFactory { - - private final IRepresentationDescriptionSearchService representationDescriptionSearchService; - - private final IObjectService objectService; - - private final ISubscriptionManagerFactory subscriptionManagerFactory; - - private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry; - - public SelectionEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IObjectService objectService) { - this.representationDescriptionSearchService = Objects.requireNonNull(configuration.getRepresentationDescriptionSearchService()); - this.objectService = Objects.requireNonNull(objectService); - this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory()); - this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry()); - } - - @Override - public boolean canHandle(IRepresentationConfiguration configuration) { - return configuration instanceof SelectionConfiguration; - } - - @Override - public Optional createRepresentationEventProcessor(IRepresentationConfiguration configuration, IEditingContext editingContext) { - if (configuration instanceof SelectionConfiguration selectionConfiguration) { - - // @formatter:off - Optional optionalSelectionDescription = this.representationDescriptionSearchService - .findById(editingContext, selectionConfiguration.getSelectionId()) - .filter(SelectionDescription.class::isInstance) - .map(SelectionDescription.class::cast); - // @formatter:on - - Optional optionalObject = this.objectService.getObject(editingContext, selectionConfiguration.getTargetObjectId()); - if (optionalSelectionDescription.isPresent() && optionalObject.isPresent()) { - SelectionDescription selectionDescription = optionalSelectionDescription.get(); - String objectId = this.objectService.getId(optionalObject.get()); - - IRepresentationEventProcessor selectionEventProcessor = new SelectionEventProcessor(editingContext, this.objectService, selectionDescription, selectionConfiguration.getId(), objectId, - this.subscriptionManagerFactory.create(), this.representationRefreshPolicyRegistry); - - return Optional.of(selectionEventProcessor); - } - } - return Optional.empty(); - } - -} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionEventProcessor.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionEventProcessor.java deleted file mode 100644 index 173bcff214..0000000000 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionEventProcessor.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021 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.sirius.components.collaborative.selection.api; - -import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; - -/** - * Interface implemented by the selection event processor. - * - * @author arichard - */ -public interface ISelectionEventProcessor extends IRepresentationEventProcessor { - /** - * Implementation which does nothing, used for mocks in unit tests. - * - * @author sbegaudeau - */ - class NoOp extends IRepresentationEventProcessor.NoOp implements ISelectionEventProcessor { - - } -} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/SelectionConfiguration.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/SelectionConfiguration.java deleted file mode 100644 index 34ef60fb98..0000000000 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/SelectionConfiguration.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2022 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.sirius.components.collaborative.selection.api; - -import java.util.Objects; - -import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; - -/** - * The configuration used to create a selection event processor. - * - * @author arichard - */ -public class SelectionConfiguration implements IRepresentationConfiguration { - - private final String id; - - private final String selectionId; - - private final String targetObjectId; - - public SelectionConfiguration(String id, String selectionId, String targetObjectId) { - this.id = Objects.requireNonNull(id); - this.selectionId = Objects.requireNonNull(selectionId); - this.targetObjectId = Objects.requireNonNull(targetObjectId); - } - - @Override - public String getId() { - return this.id; - } - - public String getSelectionId() { - return this.selectionId; - } - - public String getTargetObjectId() { - return this.targetObjectId; - } - -} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/configurations/SelectionDialogTreeConfiguration.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/configurations/SelectionDialogTreeConfiguration.java new file mode 100644 index 0000000000..c37b6d51ad --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/configurations/SelectionDialogTreeConfiguration.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.configurations; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; + +/** + * The configuration of the selection dialog event processor. + * + * @author fbarbin + */ +public class SelectionDialogTreeConfiguration implements IRepresentationConfiguration { + + private final String treeId; + + private final List expanded; + + public SelectionDialogTreeConfiguration(String editingContextId, String treeId, List expanded) { + this.expanded = Objects.requireNonNull(expanded); + + StringBuilder idBuilder = new StringBuilder(treeId); + + List expandedObjectIds = expanded.stream().map(id -> URLEncoder.encode(id, StandardCharsets.UTF_8)).toList(); + idBuilder.append("&expandedIds=[").append(String.join(",", expandedObjectIds)).append("]"); + + this.treeId = idBuilder.toString(); + } + + @Override + public String getId() { + return this.treeId; + } + + public List getExpanded() { + return this.expanded; + } + +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessageInput.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessageInput.java new file mode 100644 index 0000000000..63d9487a5a --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessageInput.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.dto; + +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.selection.description.SelectionDescription; + +/** + * The input for the Selection Description message query. + * + * @author fbarbin + */ +public record GetSelectionDescriptionMessageInput(UUID id, String targetObjectId, SelectionDescription selectionDescription) implements IInput { + public GetSelectionDescriptionMessageInput { + Objects.requireNonNull(id); + Objects.requireNonNull(targetObjectId); + Objects.requireNonNull(selectionDescription); + } +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionRefreshedEventPayload.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessagePayload.java similarity index 66% rename from packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionRefreshedEventPayload.java rename to packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessagePayload.java index 6a5a9e4453..f7aef4479f 100644 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionRefreshedEventPayload.java +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessagePayload.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2023 Obeo. + * Copyright (c) 2024 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,16 +16,15 @@ import java.util.UUID; import org.eclipse.sirius.components.core.api.IPayload; -import org.eclipse.sirius.components.selection.Selection; /** - * Payload used to indicate that the selection has been refreshed. - * - * @author arichard + * The input for the Selection Description message query. + * @author fbarbin */ -public record SelectionRefreshedEventPayload(UUID id, Selection selection) implements IPayload { - public SelectionRefreshedEventPayload { +public record GetSelectionDescriptionMessagePayload(UUID id, String message) implements IPayload { + + public GetSelectionDescriptionMessagePayload { Objects.requireNonNull(id); - Objects.requireNonNull(selection); + Objects.requireNonNull(message); } } diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionEventInput.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionDialogTreeEventInput.java similarity index 70% rename from packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionEventInput.java rename to packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionDialogTreeEventInput.java index 94e46521c4..c132e39a06 100644 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionEventInput.java +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/SelectionDialogTreeEventInput.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2023 Obeo. + * Copyright (c) 2024 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 @@ -12,14 +12,15 @@ *******************************************************************************/ package org.eclipse.sirius.components.collaborative.selection.dto; +import java.util.List; import java.util.UUID; import org.eclipse.sirius.components.core.api.IInput; /** - * The input of the selection event subscription. + * The input of the selection dialog tree event subscription. * - * @author arichard + * @author fbarbin */ -public record SelectionEventInput(UUID id, String editingContextId, String selectionId, String targetObjectId) implements IInput { +public record SelectionDialogTreeEventInput(UUID id, String editingContextId, String treeId, List expanded) implements IInput { } diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/handlers/SelectionDescriptionMessageEventHandler.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/handlers/SelectionDescriptionMessageEventHandler.java new file mode 100644 index 0000000000..782116d89a --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/handlers/SelectionDescriptionMessageEventHandler.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.handlers; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventHandler; +import org.eclipse.sirius.components.collaborative.messages.ICollaborativeMessageService; +import org.eclipse.sirius.components.collaborative.selection.dto.GetSelectionDescriptionMessageInput; +import org.eclipse.sirius.components.collaborative.selection.dto.GetSelectionDescriptionMessagePayload; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.selection.description.SelectionDescription; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Sinks.Many; +import reactor.core.publisher.Sinks.One; + +/** + * Handler for the GetSelectionDescriptionMessageInput. + * + * @author fbarbin + */ +@Service +public class SelectionDescriptionMessageEventHandler implements IEditingContextEventHandler { + + private final ICollaborativeMessageService messageService; + + private final IObjectService objectService; + + public SelectionDescriptionMessageEventHandler(ICollaborativeMessageService messageService, IObjectService objectService) { + this.messageService = Objects.requireNonNull(messageService); + this.objectService = Objects.requireNonNull(objectService); + } + + @Override + public boolean canHandle(IEditingContext editingContext, IInput input) { + return input instanceof GetSelectionDescriptionMessageInput; + } + + @Override + public void handle(One payloadSink, Many changeDescriptionSink, IEditingContext editingContext, IInput input) { + if (input instanceof GetSelectionDescriptionMessageInput getSelectionDescriptionMessageInput) { + String targetObjectId = getSelectionDescriptionMessageInput.targetObjectId(); + Optional optionalTargetObject = this.objectService.getObject(editingContext, targetObjectId); + VariableManager variableManager = new VariableManager(); + if (optionalTargetObject.isPresent()) { + variableManager.put(VariableManager.SELF, optionalTargetObject.get()); + } + SelectionDescription selectionDescription = getSelectionDescriptionMessageInput.selectionDescription(); + String message = selectionDescription.getMessageProvider().apply(variableManager); + payloadSink.tryEmitValue(new GetSelectionDescriptionMessagePayload(input.id(), message)); + } else { + String message = this.messageService.invalidInput(input.getClass().getSimpleName(), GetSelectionDescriptionMessageInput.class.getSimpleName()); + payloadSink.tryEmitValue(new ErrorPayload(input.id(), message)); + } + } +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogExpandAllTreePathProvider.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogExpandAllTreePathProvider.java new file mode 100644 index 0000000000..69c146d0ce --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogExpandAllTreePathProvider.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.services; + +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.sirius.components.collaborative.selection.services.api.ISelectionDialogNavigationService; +import org.eclipse.sirius.components.collaborative.trees.api.IExpandAllTreePathProvider; +import org.eclipse.sirius.components.collaborative.trees.dto.ExpandAllTreePathInput; +import org.eclipse.sirius.components.collaborative.trees.dto.ExpandAllTreePathSuccessPayload; +import org.eclipse.sirius.components.collaborative.trees.dto.TreePath; +import org.eclipse.sirius.components.core.api.IContentService; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IIdentityService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.trees.Tree; +import org.eclipse.sirius.components.trees.description.TreeDescription; +import org.springframework.stereotype.Service; + +/** + * Implementation of {@link IExpandAllTreePathProvider} for the Selection Dialog Tree. + * + * @author frouene + * @author fbarbin + */ +@Service +public class SelectionDialogExpandAllTreePathProvider implements IExpandAllTreePathProvider { + + private final IIdentityService identityService; + + private final IContentService contentService; + + private final ISelectionDialogNavigationService selectionDialogNavigationService; + + private final IRepresentationDescriptionSearchService representationDescriptionSearchService; + + public SelectionDialogExpandAllTreePathProvider(IIdentityService identityService, IContentService contentService, ISelectionDialogNavigationService selectionDialogNavigationService, IRepresentationDescriptionSearchService representationDescriptionSearchService) { + this.identityService = Objects.requireNonNull(identityService); + this.contentService = Objects.requireNonNull(contentService); + this.selectionDialogNavigationService = Objects.requireNonNull(selectionDialogNavigationService); + this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService); + } + + @Override + public boolean canHandle(Tree tree) { + return tree.getDescriptionId().startsWith(ISelectionDialogNavigationService.SELECTION_DIALOG_TREE_DESCRIPTION_KIND); + } + + @Override + public IPayload handle(IEditingContext editingContext, Tree tree, ExpandAllTreePathInput input) { + int maxDepth = 0; + String treeItemId = input.treeItemId(); + Set treeItemIdsToExpand = new LinkedHashSet<>(); + var object = this.getTreeItemObject(editingContext, treeItemId, tree); + if (object != null) { + // We need to get the current depth of the tree item + var itemAncestors = this.selectionDialogNavigationService.getAncestors(editingContext, treeItemId, tree); + maxDepth = itemAncestors.size(); + maxDepth = this.addAllContents(editingContext, treeItemId, maxDepth, treeItemIdsToExpand, tree); + } + return new ExpandAllTreePathSuccessPayload(input.id(), new TreePath(treeItemIdsToExpand.stream().toList(), maxDepth)); + } + + private int addAllContents(IEditingContext editingContext, String treeItemId, int depth, Set treeItemIdsToExpand, Tree tree) { + var depthConsidered = depth; + var object = this.getTreeItemObject(editingContext, treeItemId, tree); + if (object != null) { + var contents = this.contentService.getContents(object); + if (!contents.isEmpty()) { + treeItemIdsToExpand.add(treeItemId); + + for (var child : contents) { + String childId = this.identityService.getId(child); + treeItemIdsToExpand.add(childId); + var childTreePathMaxDepth = depth + 1; + childTreePathMaxDepth = this.addAllContents(editingContext, childId, childTreePathMaxDepth, treeItemIdsToExpand, tree); + depthConsidered = Math.max(depthConsidered, childTreePathMaxDepth); + } + } + } + + return depthConsidered; + } + + private Object getTreeItemObject(IEditingContext editingContext, String treeItemId, Tree tree) { + var optionalTreeDescription = this.representationDescriptionSearchService.findById(editingContext, tree.getDescriptionId()) + .filter(TreeDescription.class::isInstance) + .map(TreeDescription.class::cast); + + if (optionalTreeDescription.isPresent()) { + var variableManager = new VariableManager(); + variableManager.put(IEditingContext.EDITING_CONTEXT, editingContext); + variableManager.put(TreeDescription.ID, treeItemId); + return optionalTreeDescription.get().getTreeItemObjectProvider().apply(variableManager); + } + return null; + } +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogNavigationService.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogNavigationService.java new file mode 100644 index 0000000000..0206beb01c --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogNavigationService.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.services; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.selection.services.api.ISelectionDialogNavigationService; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.trees.Tree; +import org.eclipse.sirius.components.trees.description.TreeDescription; +import org.springframework.stereotype.Service; + +/** + * Services for the navigation through the Selection Dialog Tree. + * + * @author frouene + * @author fbarbin + */ +@Service +public class SelectionDialogNavigationService implements ISelectionDialogNavigationService { + + private final IObjectService objectService; + + private final IRepresentationDescriptionSearchService representationDescriptionSearchService; + + public SelectionDialogNavigationService(IObjectService objectService, IRepresentationDescriptionSearchService representationDescriptionSearchService) { + this.objectService = Objects.requireNonNull(objectService); + this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService); + } + + @Override + public List getAncestors(IEditingContext editingContext, String treeItemId, Tree tree) { + List ancestorsIds = new ArrayList<>(); + + Optional optionalObject = this.getParentSemanticObject(treeItemId, ancestorsIds, editingContext, tree); + while (optionalObject.isPresent()) { + String parentId = this.objectService.getId(optionalObject.get()); + ancestorsIds.add(parentId); + optionalObject = this.getParentSemanticObject(parentId, ancestorsIds, editingContext, tree); + } + return ancestorsIds; + } + + private Optional getParentSemanticObject(String elementId, List ancestorsIds, IEditingContext editingContext, Tree tree) { + Optional result = Optional.empty(); + + var variableManager = new VariableManager(); + var optionalSemanticObject = this.getTreeItemObject(editingContext, elementId, tree); + var optionalTreeDescription = this.representationDescriptionSearchService.findById(editingContext, tree.getDescriptionId()) + .filter(TreeDescription.class::isInstance) + .map(TreeDescription.class::cast); + + if (optionalSemanticObject.isPresent() && optionalTreeDescription.isPresent()) { + variableManager.put(VariableManager.SELF, optionalSemanticObject.get()); + variableManager.put(TreeDescription.ID, elementId); + variableManager.put(IEditingContext.EDITING_CONTEXT, editingContext); + result = Optional.ofNullable(optionalTreeDescription.get().getParentObjectProvider().apply(variableManager)); + } + return result; + } + + private Optional getTreeItemObject(IEditingContext editingContext, String id, Tree tree) { + var optionalTreeDescription = this.representationDescriptionSearchService.findById(editingContext, tree.getDescriptionId()) + .filter(TreeDescription.class::isInstance) + .map(TreeDescription.class::cast); + + if (optionalTreeDescription.isPresent()) { + var variableManager = new VariableManager(); + variableManager.put(IEditingContext.EDITING_CONTEXT, editingContext); + variableManager.put(TreeDescription.ID, id); + return Optional.ofNullable(optionalTreeDescription.get().getTreeItemObjectProvider().apply(variableManager)); + } + return Optional.empty(); + } +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogTreeEventProcessorFactory.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogTreeEventProcessorFactory.java new file mode 100644 index 0000000000..f8b9fcd6b2 --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionDialogTreeEventProcessorFactory.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.services; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.IRepresentationConfiguration; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessor; +import org.eclipse.sirius.components.collaborative.api.IRepresentationEventProcessorFactory; +import org.eclipse.sirius.components.collaborative.api.IRepresentationRefreshPolicyRegistry; +import org.eclipse.sirius.components.collaborative.api.ISubscriptionManagerFactory; +import org.eclipse.sirius.components.collaborative.selection.configurations.SelectionDialogTreeConfiguration; +import org.eclipse.sirius.components.collaborative.trees.TreeEventProcessor; +import org.eclipse.sirius.components.collaborative.trees.api.ITreeEventHandler; +import org.eclipse.sirius.components.collaborative.trees.api.ITreeService; +import org.eclipse.sirius.components.collaborative.trees.api.TreeCreationParameters; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; +import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.trees.description.TreeDescription; +import org.springframework.stereotype.Service; + +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +/** + * Used to create the tree event processors in the context of a selection dialog tree. + * + * @author fbarbin + */ +@Service +public class SelectionDialogTreeEventProcessorFactory implements IRepresentationEventProcessorFactory { + + private final IRepresentationDescriptionSearchService representationDescriptionSearchService; + + private final ITreeService treeService; + + private final List treeEventHandlers; + + private final ISubscriptionManagerFactory subscriptionManagerFactory; + + private final IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry; + + public SelectionDialogTreeEventProcessorFactory(IRepresentationDescriptionSearchService representationDescriptionSearchService, List treeEventHandlers, ITreeService treeService, IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry, ISubscriptionManagerFactory subscriptionManagerFactory) { + this.representationDescriptionSearchService = Objects.requireNonNull(representationDescriptionSearchService); + this.treeService = Objects.requireNonNull(treeService); + this.treeEventHandlers = Objects.requireNonNull(treeEventHandlers); + this.subscriptionManagerFactory = Objects.requireNonNull(subscriptionManagerFactory); + this.representationRefreshPolicyRegistry = Objects.requireNonNull(representationRefreshPolicyRegistry); + } + + @Override + public boolean canHandle(IRepresentationConfiguration configuration) { + return configuration instanceof SelectionDialogTreeConfiguration; + } + + @Override + public Optional createRepresentationEventProcessor(IRepresentationConfiguration configuration, IEditingContext editingContext) { + if (configuration instanceof SelectionDialogTreeConfiguration selectionDialogTreeConfiguration) { + + Optional optionalTreeDescription = this.findTreeDescription(editingContext, selectionDialogTreeConfiguration); + if (optionalTreeDescription.isPresent()) { + var treeDescription = optionalTreeDescription.get(); + + TreeCreationParameters treeCreationParameters = TreeCreationParameters.newTreeCreationParameters(selectionDialogTreeConfiguration.getId()) + .treeDescription(treeDescription) + .activeFilterIds(List.of()) + .expanded(selectionDialogTreeConfiguration.getExpanded()) + .editingContext(editingContext) + .build(); + + IRepresentationEventProcessor treeEventProcessor = new TreeEventProcessor(editingContext, this.treeService, treeCreationParameters, this.treeEventHandlers, + this.subscriptionManagerFactory.create(), new SimpleMeterRegistry(), this.representationRefreshPolicyRegistry); + return Optional.of(treeEventProcessor); + } + } + return Optional.empty(); + } + + private Optional findTreeDescription(IEditingContext editingContext, SelectionDialogTreeConfiguration treeConfiguration) { + VariableManager variableManager = new VariableManager(); + variableManager.put(GetOrCreateRandomIdProvider.PREVIOUS_REPRESENTATION_ID, treeConfiguration.getId()); + return this.representationDescriptionSearchService + .findAll(editingContext).values().stream() + .filter(TreeDescription.class::isInstance) + .map(TreeDescription.class::cast) + .filter(treeDescription -> treeDescription.getCanCreatePredicate().test(variableManager)) + .findFirst(); + } +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionRepresentationMetadataProvider.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionRepresentationMetadataProvider.java new file mode 100644 index 0000000000..18182fd6cd --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/SelectionRepresentationMetadataProvider.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.services; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.core.RepresentationMetadata; +import org.eclipse.sirius.components.core.URLParser; +import org.eclipse.sirius.components.core.api.IRepresentationMetadataProvider; +import org.springframework.stereotype.Service; + +/** + * A Service to provide metadata for the Selection Dialog Representation. + * + * @author fbarbin + */ +@Service +public class SelectionRepresentationMetadataProvider implements IRepresentationMetadataProvider { + + private static final String REPRESENTATION_DESCRIPTION_PARAMETER = "representationDescription"; + + private static final String PREFIX = "selectionDialog://"; + + private final URLParser urlParser; + + public SelectionRepresentationMetadataProvider(URLParser urlParser) { + this.urlParser = Objects.requireNonNull(urlParser); + } + + @Override + public Optional getMetadata(String representationId) { + if (representationId.startsWith(PREFIX)) { + Optional optionalRepresentationDescriptionId = Optional.ofNullable(this.urlParser.getParameterValues(representationId).get(REPRESENTATION_DESCRIPTION_PARAMETER)) + .filter(values -> !values.isEmpty()) + .map(values -> values.get(0)); + if (optionalRepresentationDescriptionId.isPresent()) { + return Optional.of(new RepresentationMetadata(representationId, "SelectionRepresentation", "Selection Representation", optionalRepresentationDescriptionId.get())); + } + } + return Optional.empty(); + } + +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/api/ISelectionDialogNavigationService.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/api/ISelectionDialogNavigationService.java new file mode 100644 index 0000000000..db3f579435 --- /dev/null +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/services/api/ISelectionDialogNavigationService.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.collaborative.selection.services.api; + +import java.util.List; + +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.trees.Tree; + +/** + * Interface of the service for the navigation through the Selection Dialog Tree. + * + * @author fbarbin + */ +public interface ISelectionDialogNavigationService { + + String SELECTION_DIALOG_TREE_DESCRIPTION_KIND = "siriusComponents://selectionDialogTreeDescription"; + + List getAncestors(IEditingContext editingContext, String treeItemId, Tree tree); + + /** + * Implementation which does nothing, used for mocks in unit tests. + * + * @author fbarbin + */ + class NoOp implements ISelectionDialogNavigationService { + + @Override + public List getAncestors(IEditingContext editingContext, String treeItemId, Tree tree) { + return List.of(); + } + } + +} diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/resources/schema/selection.graphqls b/packages/selection/backend/sirius-components-collaborative-selection/src/main/resources/schema/selection.graphqls index 35ff956306..f4ad38f86c 100644 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/resources/schema/selection.graphqls +++ b/packages/selection/backend/sirius-components-collaborative-selection/src/main/resources/schema/selection.graphqls @@ -1,36 +1,17 @@ extend type Subscription { - selectionEvent(input: SelectionEventInput!): SelectionEventPayload! + selectionDialogTreeEvent(input: SelectionDialogTreeEventInput!): TreeEventPayload! } -input SelectionEventInput { +input SelectionDialogTreeEventInput { id: ID! + treeId: String! editingContextId: ID! - selectionId: ID! - targetObjectId: ID! -} - -union SelectionEventPayload = ErrorPayload | SelectionRefreshedEventPayload - -type SelectionRefreshedEventPayload { - id: ID! - selection: Selection! -} - -type Selection implements Representation { - id: ID! - metadata: RepresentationMetadata! - targetObjectId: String! - message: String - objects: [SelectionObject!]! -} - -type SelectionObject { - id: ID! - label: String! - iconURL: [String!]! + expanded: [String!]! } type SelectionDescription implements RepresentationDescription { id: ID! label: String! + message(targetObjectId: ID!): String! + treeDescription: TreeDescription! } diff --git a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionDescriptionMessageDataFetcher.java b/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionDescriptionMessageDataFetcher.java new file mode 100644 index 0000000000..0ae1ba0e92 --- /dev/null +++ b/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionDescriptionMessageDataFetcher.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.selection.graphql.datafetchers.selection; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry; +import org.eclipse.sirius.components.collaborative.selection.dto.GetSelectionDescriptionMessageInput; +import org.eclipse.sirius.components.collaborative.selection.dto.GetSelectionDescriptionMessagePayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.LocalContextConstants; +import org.eclipse.sirius.components.selection.description.SelectionDescription; + +import graphql.schema.DataFetchingEnvironment; + +/** + * The data fetcher use to compute the message defined in the Selection Dialog Description. + * @author fbarbin + */ +@QueryDataFetcher(type = "SelectionDescription", field = "message") +public class SelectionDescriptionMessageDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private static final String TARGET_OBJECT_ID = "targetObjectId"; + + private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry; + + public SelectionDescriptionMessageDataFetcher(IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry) { + this.editingContextEventProcessorRegistry = Objects.requireNonNull(editingContextEventProcessorRegistry); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + SelectionDescription selectionDescription = environment.getSource(); + + Map localContext = environment.getLocalContext(); + var editingContextId = Optional.of(localContext.get(LocalContextConstants.EDITING_CONTEXT_ID)).map(Object::toString); + String targetObjectId = environment.getArgument(TARGET_OBJECT_ID); + + var input = new GetSelectionDescriptionMessageInput(UUID.randomUUID(), targetObjectId, selectionDescription); + return this.editingContextEventProcessorRegistry.dispatchEvent(editingContextId.get(), input) + .filter(GetSelectionDescriptionMessagePayload.class::isInstance) + .map(GetSelectionDescriptionMessagePayload.class::cast) + .map(GetSelectionDescriptionMessagePayload::message) + .toFuture(); + } + +} diff --git a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionObjectIconURLDataFetcher.java b/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionObjectIconURLDataFetcher.java deleted file mode 100644 index d0d3f69ef1..0000000000 --- a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/selection/SelectionObjectIconURLDataFetcher.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022, 2024 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.sirius.components.selection.graphql.datafetchers.selection; - -import java.util.List; -import java.util.Objects; - -import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; -import org.eclipse.sirius.components.core.api.IImageURLSanitizer; -import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; -import org.eclipse.sirius.components.graphql.api.URLConstants; -import org.eclipse.sirius.components.selection.SelectionObject; - -import graphql.schema.DataFetchingEnvironment; - -/** - * The data fetcher used to concatenate the server image URL to the SelectionObject icon path. - * - * @author arichard - */ -@QueryDataFetcher(type = "SelectionObject", field = "iconURL") -public class SelectionObjectIconURLDataFetcher implements IDataFetcherWithFieldCoordinates> { - - private final IImageURLSanitizer imageURLSanitizer; - - public SelectionObjectIconURLDataFetcher(IImageURLSanitizer imageURLSanitizer) { - this.imageURLSanitizer = Objects.requireNonNull(imageURLSanitizer); - } - - @Override - public List get(DataFetchingEnvironment environment) throws Exception { - SelectionObject selectionObject = environment.getSource(); - return selectionObject.getIconURL().stream() - .map(url -> this.imageURLSanitizer.sanitize(URLConstants.IMAGE_BASE_PATH, url)) - .toList(); - } -} diff --git a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SelectionRefreshedEventPayloadSelectionDataFetcher.java b/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SelectionRefreshedEventPayloadSelectionDataFetcher.java deleted file mode 100644 index 5b96718432..0000000000 --- a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SelectionRefreshedEventPayloadSelectionDataFetcher.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2023 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.sirius.components.selection.graphql.datafetchers.subscription; - -import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; -import org.eclipse.sirius.components.collaborative.selection.dto.SelectionRefreshedEventPayload; -import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; -import org.eclipse.sirius.components.selection.Selection; - -import graphql.execution.DataFetcherResult; -import graphql.schema.DataFetchingEnvironment; - -/** - * Used to retrieve the selection from its payload. - * - * @author sbegaudeau - */ -@QueryDataFetcher(type = "SelectionRefreshedEventPayload", field = "selection") -public class SelectionRefreshedEventPayloadSelectionDataFetcher implements IDataFetcherWithFieldCoordinates> { - @Override - public DataFetcherResult get(DataFetchingEnvironment environment) throws Exception { - SelectionRefreshedEventPayload payload = environment.getSource(); - return DataFetcherResult.newResult() - .data(payload.selection()) - .localContext(environment.getLocalContext()) - .build(); - } -} diff --git a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionEventDataFetcher.java b/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionDialogTreeEventDataFetcher.java similarity index 74% rename from packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionEventDataFetcher.java rename to packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionDialogTreeEventDataFetcher.java index a6a6537fe5..511a37a734 100644 --- a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionEventDataFetcher.java +++ b/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionDialogTreeEventDataFetcher.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022, 2024 Obeo. + * Copyright (c) 2024 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 @@ -13,14 +13,14 @@ package org.eclipse.sirius.components.selection.graphql.datafetchers.subscription; import com.fasterxml.jackson.databind.ObjectMapper; + import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.UUID; import org.eclipse.sirius.components.annotations.spring.graphql.SubscriptionDataFetcher; -import org.eclipse.sirius.components.collaborative.selection.api.SelectionConfiguration; -import org.eclipse.sirius.components.collaborative.selection.dto.SelectionEventInput; +import org.eclipse.sirius.components.collaborative.selection.configurations.SelectionDialogTreeConfiguration; +import org.eclipse.sirius.components.collaborative.selection.dto.SelectionDialogTreeEventInput; import org.eclipse.sirius.components.core.api.IPayload; import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; import org.eclipse.sirius.components.graphql.api.IEventProcessorSubscriptionProvider; @@ -32,12 +32,12 @@ import graphql.schema.DataFetchingEnvironment; /** - * The data fetcher used to send the refreshed tree to a subscription. + * The data fetcher used to send the refreshed tree to a selection dialog tree subscription . * - * @author arichard + * @author fbarbin */ -@SubscriptionDataFetcher(type = "Subscription", field = "selectionEvent") -public class SubscriptionSelectionEventDataFetcher implements IDataFetcherWithFieldCoordinates>> { +@SubscriptionDataFetcher(type = "Subscription", field = "selectionDialogTreeEvent") +public class SubscriptionSelectionDialogTreeEventDataFetcher implements IDataFetcherWithFieldCoordinates>> { private static final String INPUT_ARGUMENT = "input"; @@ -47,7 +47,7 @@ public class SubscriptionSelectionEventDataFetcher implements IDataFetcherWithFi private final IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider; - public SubscriptionSelectionEventDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider) { + public SubscriptionSelectionDialogTreeEventDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider) { this.objectMapper = Objects.requireNonNull(objectMapper); this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); this.eventProcessorSubscriptionProvider = Objects.requireNonNull(eventProcessorSubscriptionProvider); @@ -56,18 +56,17 @@ public SubscriptionSelectionEventDataFetcher(ObjectMapper objectMapper, IExcepti @Override public Publisher> get(DataFetchingEnvironment environment) throws Exception { Object argument = environment.getArgument(INPUT_ARGUMENT); - var input = this.objectMapper.convertValue(argument, SelectionEventInput.class); - var selectionConfiguration = new SelectionConfiguration(UUID.randomUUID().toString(), input.selectionId(), input.targetObjectId()); + var input = this.objectMapper.convertValue(argument, SelectionDialogTreeEventInput.class); + var selectionDialogTreeConfiguration = new SelectionDialogTreeConfiguration(input.editingContextId(), input.treeId(), input.expanded()); Map localContext = new HashMap<>(); localContext.put(LocalContextConstants.EDITING_CONTEXT_ID, input.editingContextId()); - localContext.put(LocalContextConstants.REPRESENTATION_ID, selectionConfiguration.getId()); + localContext.put(LocalContextConstants.REPRESENTATION_ID, selectionDialogTreeConfiguration.getId()); - return this.exceptionWrapper.wrapFlux(() -> this.eventProcessorSubscriptionProvider.getSubscription(input.editingContextId(), selectionConfiguration, input), input) + return this.exceptionWrapper.wrapFlux(() -> this.eventProcessorSubscriptionProvider.getSubscription(input.editingContextId(), selectionDialogTreeConfiguration, input), input) .map(payload -> DataFetcherResult.newResult() .data(payload) .localContext(localContext) .build()); } - } diff --git a/packages/selection/backend/sirius-components-selection/pom.xml b/packages/selection/backend/sirius-components-selection/pom.xml index 6756dd9f51..ccc0380a7e 100644 --- a/packages/selection/backend/sirius-components-selection/pom.xml +++ b/packages/selection/backend/sirius-components-selection/pom.xml @@ -51,6 +51,11 @@ 2024.9.0 test + + org.eclipse.sirius + sirius-components-trees + 2024.9.0 + diff --git a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/Selection.java b/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/Selection.java deleted file mode 100644 index 269f2560b0..0000000000 --- a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/Selection.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2024 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.sirius.components.selection; - -import java.text.MessageFormat; -import java.util.List; -import java.util.Objects; - -import org.eclipse.sirius.components.annotations.Immutable; -import org.eclipse.sirius.components.representations.IRepresentation; - -/** - * The root concept of the selection representation. - * - * @author arichard - */ -@Immutable -public final class Selection implements IRepresentation { - - public static final String KIND = IRepresentation.KIND_PREFIX + "?type=Selection"; - - public static final String PREFIX = "selection://"; - - public static final String TITLE = "Selection"; - - private String id; - - private String kind; - - private String descriptionId; - - private String label; - - private String targetObjectId; - - private String message; - - private List objects; - - private Selection() { - // Prevent instantiation - } - - @Override - public String getId() { - return this.id; - } - - @Override - public String getDescriptionId() { - return this.descriptionId; - } - - @Override - public String getKind() { - return this.kind; - } - - @Override - public String getLabel() { - return this.label; - } - - @Override - public String getTargetObjectId() { - return this.targetObjectId; - } - - public String getMessage() { - return this.message; - } - - public List getObjects() { - return this.objects; - } - - public static Builder newSelection(String id) { - return new Builder(id); - } - - @Override - public String toString() { - String pattern = "{0} '{'id: {1}, descriptionId: {2}, targetObjectId: {3}, label: {4}, message: {5}, objectsCount: {6}'}'"; - return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.descriptionId, this.targetObjectId, this.label, this.message, this.objects.size()); - } - - /** - * The builder used to create the selection. - * - * @author arichard - */ - @SuppressWarnings("checkstyle:HiddenField") - public static final class Builder { - private String id; - - private String kind = KIND; - - private String descriptionId; - - private String label; - - private String targetObjectId; - - private String message; - - private List objects; - - private Builder(String id) { - this.id = Objects.requireNonNull(id); - } - - public Builder descriptionId(String descriptionId) { - this.descriptionId = Objects.requireNonNull(descriptionId); - return this; - } - - public Builder label(String label) { - this.label = Objects.requireNonNull(label); - return this; - } - - public Builder targetObjectId(String targetObjectId) { - this.targetObjectId = Objects.requireNonNull(targetObjectId); - return this; - } - - public Builder message(String message) { - this.message = message; - return this; - } - - public Builder objects(List objects) { - this.objects = Objects.requireNonNull(objects); - return this; - } - - public Selection build() { - Selection selection = new Selection(); - selection.id = Objects.requireNonNull(this.id); - selection.kind = Objects.requireNonNull(this.kind); - selection.descriptionId = Objects.requireNonNull(this.descriptionId); - selection.label = Objects.requireNonNull(this.label); - selection.targetObjectId = Objects.requireNonNull(this.targetObjectId); - selection.message = this.message; - selection.objects = Objects.requireNonNull(this.objects); - return selection; - } - } -} diff --git a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/SelectionObject.java b/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/SelectionObject.java deleted file mode 100644 index aa8b78d3b3..0000000000 --- a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/SelectionObject.java +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 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.sirius.components.selection; - -import java.text.MessageFormat; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -import org.eclipse.sirius.components.annotations.Immutable; - -/** - * An object of the selection. - * - * @author arichard - */ -@Immutable - -public final class SelectionObject { - - private UUID id; - - private String label; - - private List iconURL; - - private SelectionObject() { - // Prevent instantiation - } - - public static Builder newSelectionObject(UUID id) { - return new Builder(id); - } - - public UUID getId() { - return this.id; - } - - public String getLabel() { - return this.label; - } - - public List getIconURL() { - return this.iconURL; - } - - @Override - public String toString() { - String pattern = "{0} '{'id: {1}, label: {2}, imageURL: {3}'}'"; - return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.label, this.iconURL); - } - - /** - * The builder used to create the selection object. - * - * @author arichard - */ - @SuppressWarnings("checkstyle:HiddenField") - public static final class Builder { - - private final UUID id; - - private String label; - - private List iconURL; - - private Builder(UUID id) { - this.id = Objects.requireNonNull(id); - } - - public Builder label(String label) { - this.label = Objects.requireNonNull(label); - return this; - } - - public Builder iconURL(List iconURL) { - this.iconURL = Objects.requireNonNull(iconURL); - return this; - } - - public SelectionObject build() { - SelectionObject selectionObject = new SelectionObject(); - selectionObject.id = Objects.requireNonNull(this.id); - selectionObject.label = Objects.requireNonNull(this.label); - selectionObject.iconURL = Objects.requireNonNull(this.iconURL); - return selectionObject; - } - } -} diff --git a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/description/SelectionDescription.java b/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/description/SelectionDescription.java index f1b2c8a4ab..d325d18c26 100644 --- a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/description/SelectionDescription.java +++ b/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/description/SelectionDescription.java @@ -13,7 +13,6 @@ package org.eclipse.sirius.components.selection.description; import java.text.MessageFormat; -import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; @@ -21,6 +20,7 @@ import org.eclipse.sirius.components.annotations.Immutable; import org.eclipse.sirius.components.representations.IRepresentationDescription; import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.trees.description.TreeDescription; /** * The root concept of the description of a selection representation. @@ -40,16 +40,12 @@ public final class SelectionDescription implements IRepresentationDescription { private Function targetObjectIdProvider; - private Function> iconURLProvider; - private Function messageProvider; - private Function> objectsProvider; - - private Function selectionObjectsIdProvider; - private Predicate canCreatePredicate; + private TreeDescription treeDescription; + private SelectionDescription() { // Prevent instantiation } @@ -80,27 +76,20 @@ public Function getTargetObjectIdProvider() { return this.targetObjectIdProvider; } - public Function> getIconURLProvider() { - return this.iconURLProvider; - } public Function getMessageProvider() { return this.messageProvider; } - public Function> getObjectsProvider() { - return this.objectsProvider; - } - - public Function getSelectionObjectsIdProvider() { - return this.selectionObjectsIdProvider; - } - @Override public Predicate getCanCreatePredicate() { return this.canCreatePredicate; } + public TreeDescription getTreeDescription() { + return this.treeDescription; + } + @Override public String toString() { String pattern = "{0} '{'id: {1}, label: {2}'}'"; @@ -125,16 +114,12 @@ public static final class Builder { private Function targetObjectIdProvider; - private Function> iconURLProvider; - private Function messageProvider; - private Function> objectsProvider; - - private Function selectionObjectsIdProvider; - private Predicate canCreatePredicate; + private TreeDescription treeDescription; + private Builder(String id) { this.id = Objects.requireNonNull(id); } @@ -159,28 +144,18 @@ public Builder targetObjectIdProvider(Function targetOb return this; } - public Builder iconURLProvider(Function> iconURLProvider) { - this.iconURLProvider = Objects.requireNonNull(iconURLProvider); - return this; - } - public Builder messageProvider(Function messageProvider) { this.messageProvider = Objects.requireNonNull(messageProvider); return this; } - public Builder objectsProvider(Function> objectsProvider) { - this.objectsProvider = Objects.requireNonNull(objectsProvider); - return this; - } - - public Builder selectionObjectsIdProvider(Function selectionObjectsIdProvider) { - this.selectionObjectsIdProvider = Objects.requireNonNull(selectionObjectsIdProvider); + public Builder canCreatePredicate(Predicate canCreatePredicate) { + this.canCreatePredicate = Objects.requireNonNull(canCreatePredicate); return this; } - public Builder canCreatePredicate(Predicate canCreatePredicate) { - this.canCreatePredicate = Objects.requireNonNull(canCreatePredicate); + public Builder treeDescription(TreeDescription treeDescription) { + this.treeDescription = Objects.requireNonNull(treeDescription); return this; } @@ -191,13 +166,12 @@ public SelectionDescription build() { selectionDescription.idProvider = Objects.requireNonNull(this.idProvider); selectionDescription.labelProvider = Objects.requireNonNull(this.labelProvider); selectionDescription.targetObjectIdProvider = Objects.requireNonNull(this.targetObjectIdProvider); - selectionDescription.iconURLProvider = Objects.requireNonNull(this.iconURLProvider); selectionDescription.messageProvider = Objects.requireNonNull(this.messageProvider); - selectionDescription.objectsProvider = Objects.requireNonNull(this.objectsProvider); - selectionDescription.selectionObjectsIdProvider = Objects.requireNonNull(this.selectionObjectsIdProvider); selectionDescription.canCreatePredicate = Objects.requireNonNull(this.canCreatePredicate); + selectionDescription.treeDescription = Objects.requireNonNull(this.treeDescription); return selectionDescription; } } + } diff --git a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/renderer/SelectionRenderer.java b/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/renderer/SelectionRenderer.java deleted file mode 100644 index df62719d1b..0000000000 --- a/packages/selection/backend/sirius-components-selection/src/main/java/org/eclipse/sirius/components/selection/renderer/SelectionRenderer.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 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.sirius.components.selection.renderer; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -import org.eclipse.sirius.components.representations.VariableManager; -import org.eclipse.sirius.components.selection.Selection; -import org.eclipse.sirius.components.selection.SelectionObject; -import org.eclipse.sirius.components.selection.description.SelectionDescription; - -/** - * Renderer used to create the selection representation from its description and some variables. - * - * @author arichard - */ -public class SelectionRenderer { - - private final VariableManager variableManager; - - private final SelectionDescription selectionDescription; - - public SelectionRenderer(VariableManager variableManager, SelectionDescription selectionDescription) { - this.variableManager = Objects.requireNonNull(variableManager); - this.selectionDescription = Objects.requireNonNull(selectionDescription); - } - - public Selection render() { - String id = this.selectionDescription.getIdProvider().apply(this.variableManager); - String label = this.selectionDescription.getLabelProvider().apply(this.variableManager); - String message = this.selectionDescription.getMessageProvider().apply(this.variableManager); - String targetObjectId = this.selectionDescription.getTargetObjectIdProvider().apply(this.variableManager); - - List selectionObjects = this.selectionDescription.getObjectsProvider().apply(this.variableManager); - List objects = new ArrayList<>(selectionObjects.size()); - for (Object selectionObject : selectionObjects) { - VariableManager selectionObjectVariableManager = this.variableManager.createChild(); - selectionObjectVariableManager.put(VariableManager.SELF, selectionObject); - objects.add(this.renderSelectionObject(selectionObjectVariableManager)); - } - - // @formatter:off - return Selection.newSelection(id) - .descriptionId(this.selectionDescription.getId()) - .label(label) - .targetObjectId(targetObjectId) - .message(message) - .objects(objects) - .build(); - // @formatter:on - } - - private SelectionObject renderSelectionObject(VariableManager selectionObjectVariableManager) { - String id = this.selectionDescription.getSelectionObjectsIdProvider().apply(selectionObjectVariableManager); - String label = this.selectionDescription.getLabelProvider().apply(selectionObjectVariableManager); - List iconURL = this.selectionDescription.getIconURLProvider().apply(selectionObjectVariableManager); - - return SelectionObject.newSelectionObject(UUID.fromString(id)) - .label(label) - .iconURL(iconURL) - .build(); - } -} diff --git a/packages/selection/frontend/sirius-components-selection/package.json b/packages/selection/frontend/sirius-components-selection/package.json index b187d6a6a8..cfb9bfa082 100644 --- a/packages/selection/frontend/sirius-components-selection/package.json +++ b/packages/selection/frontend/sirius-components-selection/package.json @@ -33,6 +33,7 @@ "@apollo/client": "3.10.4", "@eclipse-sirius/sirius-components-core": "*", "@eclipse-sirius/sirius-components-diagrams": "*", + "@eclipse-sirius/sirius-components-trees": "*", "@mui/material": "5.15.19", "@mui/icons-material": "5.15.19", "@xstate/react": "3.0.0", @@ -46,6 +47,7 @@ "@apollo/client": "3.10.4", "@eclipse-sirius/sirius-components-core": "*", "@eclipse-sirius/sirius-components-diagrams": "*", + "@eclipse-sirius/sirius-components-trees": "*", "@eclipse-sirius/sirius-components-tsconfig": "*", "@mui/material": "5.15.19", "@mui/icons-material": "5.15.19", diff --git a/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx b/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx index bf4a16a518..a1a99aa53a 100644 --- a/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx +++ b/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx @@ -10,66 +10,18 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useSubscription } from '@apollo/client'; -import { IconOverlay, Toast } from '@eclipse-sirius/sirius-components-core'; +import { Selection, SelectionContext } from '@eclipse-sirius/sirius-components-core'; import { DiagramDialogComponentProps } from '@eclipse-sirius/sirius-components-diagrams'; -import CropDinIcon from '@mui/icons-material/CropDin'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; -import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import ListItemText from '@mui/material/ListItemText'; -import { useMachine } from '@xstate/react'; -import { useEffect } from 'react'; -import { makeStyles } from 'tss-react/mui'; -import { StateMachine } from 'xstate'; -import { - HandleCompleteEvent, - HandleSelectionUpdatedEvent, - HandleSubscriptionResultEvent, - HideToastEvent, - SchemaValue, - SelectionDialogContext, - SelectionDialogEvent, - SelectionDialogStateSchema, - ShowToastEvent, - selectionDialogMachine, -} from './SelectionDialogMachine'; -import { GQLSelectionEventSubscription } from './SelectionEvent.types'; - -const selectionEventSubscription = gql` - subscription selectionEvent($input: SelectionEventInput!) { - selectionEvent(input: $input) { - __typename - ... on SelectionRefreshedEventPayload { - selection { - id - targetObjectId - message - objects { - id - label - iconURL - } - } - } - } - } -`; - -const useSelectionObjectModalStyles = makeStyles()((_theme) => ({ - root: { - width: '100%', - position: 'relative', - overflow: 'auto', - maxHeight: 300, - }, -})); +import { useState } from 'react'; +import { SelectionDialogState } from './SelectionDialog.types'; +import { SelectionDialogTreeView } from './SelectionDialogTreeView'; +import { useSelectionDescription } from './useSelectionDescription'; export const SELECTION_DIALOG_TYPE: string = 'selectionDialogDescription'; @@ -80,114 +32,67 @@ export const SelectionDialog = ({ onClose, onFinish, }: DiagramDialogComponentProps) => { - const { classes } = useSelectionObjectModalStyles(); - - const [{ value, context }, dispatch] = - useMachine>( - selectionDialogMachine - ); - const { toast, selectionDialog } = value as SchemaValue; - const { id, selection, message, selectedObjectId } = context; + const [state, setState] = useState({ + selectedObjects: [], + }); - const { error } = useSubscription(selectionEventSubscription, { - variables: { - input: { - id, - editingContextId, - selectionId: dialogDescriptionId, - targetObjectId, - }, - }, - fetchPolicy: 'no-cache', - skip: selectionDialog === 'complete', - onData: ({ data }) => { - const handleDataEvent: HandleSubscriptionResultEvent = { - type: 'HANDLE_SUBSCRIPTION_RESULT', - result: data, - }; - dispatch(handleDataEvent); - }, - onComplete: () => { - const completeEvent: HandleCompleteEvent = { type: 'HANDLE_COMPLETE' }; - dispatch(completeEvent); - }, + const { selectionDescription } = useSelectionDescription({ + editingContextId, + selectionDescriptionId: dialogDescriptionId, + targetObjectId, }); - useEffect(() => { - if (error) { - const { message } = error; - const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message }; - dispatch(showToastEvent); - } - }, [error, dispatch]); + const message: string = selectionDescription?.message ?? ''; + const treeDescriptionId: string | null = selectionDescription?.treeDescription.id ?? null; - useEffect(() => { - if (selectionDialog === 'complete') { - onClose(); - } - }, [selectionDialog, onClose]); + const setDialogSelection = (selection: Selection) => { + setState((prevState) => ({ ...prevState, selectedObjects: [...selection.entries] })); + }; - const handleListItemClick = (selectedObjectId: string) => { - dispatch({ type: 'HANDLE_SELECTION_UPDATED', selectedObjectId } as HandleSelectionUpdatedEvent); + const handleClick = () => { + if (state.selectedObjects.length > 0) { + const selectedObjectId = state.selectedObjects[0]?.id ?? ''; + onFinish([{ name: 'selectedObject', value: selectedObjectId, type: 'OBJECT_ID' }]); + } }; + let content: JSX.Element | null = null; + if (treeDescriptionId !== null) { + content = ( + + ); + } + return ( - <> + Selection Dialog - {selection?.message} - - {selection?.objects.map((selectionObject) => ( - handleListItemClick(selectionObject.id)} - data-testid={selectionObject.label}> - - {selectionObject.iconURL.length > 0 ? ( - - ) : ( - - )} - - - - ))} - + {message} + {content} - dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)} - /> - + ); }; diff --git a/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.types.ts b/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.types.ts index b833448a9a..4a70002ab0 100644 --- a/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.types.ts +++ b/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.types.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Obeo. + * Copyright (c) 2021, 2024 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 @@ -18,3 +18,12 @@ export interface SelectionDialogProps { onClose: () => void; onFinish: (selectedObjectId: string) => void; } + +export interface SelectionDialogState { + selectedObjects: SelectionDialogSelectedObject[]; +} + +export interface SelectionDialogSelectedObject { + id: string; + kind: string; +} diff --git a/packages/selection/frontend/sirius-components-selection/src/SelectionDialogMachine.ts b/packages/selection/frontend/sirius-components-selection/src/SelectionDialogMachine.ts deleted file mode 100644 index 5b1ed61ec7..0000000000 --- a/packages/selection/frontend/sirius-components-selection/src/SelectionDialogMachine.ts +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 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 - *******************************************************************************/ -import { SubscriptionResult } from '@apollo/client'; -import { assign, Machine } from 'xstate'; -import { Selection } from './Selection.types'; -import { - GQLSelectionEventPayload, - GQLSelectionEventSubscription, - GQLSelectionRefreshedEventPayload, -} from './SelectionEvent.types'; - -export interface SelectionDialogStateSchema { - states: { - toast: { - states: { - visible: {}; - hidden: {}; - }; - }; - selectionDialog: { - states: { - idle: {}; - ready: {}; - complete: {}; - }; - }; - }; -} - -export type SchemaValue = { - toast: 'visible' | 'hidden'; - selectionDialog: 'idle' | 'ready' | 'complete'; -}; - -export interface SelectionDialogContext { - id: string; - selection: Selection | null; - message: string | null; - selectedObjectId: string | null; -} - -export type ShowToastEvent = { type: 'SHOW_TOAST'; message: string }; -export type HideToastEvent = { type: 'HIDE_TOAST' }; -export type HandleSelectionUpdatedEvent = { type: 'HANDLE_SELECTION_UPDATED'; selectedObjectId: string }; -export type HandleCompleteEvent = { type: 'HANDLE_COMPLETE' }; -export type HandleSubscriptionResultEvent = { - type: 'HANDLE_SUBSCRIPTION_RESULT'; - result: SubscriptionResult; -}; - -export type SelectionDialogEvent = - | HandleCompleteEvent - | HandleSelectionUpdatedEvent - | HandleSubscriptionResultEvent - | ShowToastEvent - | HideToastEvent; - -const isSelectionRefreshedEventPayload = ( - payload: GQLSelectionEventPayload -): payload is GQLSelectionRefreshedEventPayload => payload.__typename === 'SelectionRefreshedEventPayload'; - -export const selectionDialogMachine = Machine( - { - type: 'parallel', - context: { - id: crypto.randomUUID(), - selection: null, - message: null, - selectedObjectId: null, - }, - states: { - toast: { - initial: 'hidden', - states: { - hidden: { - on: { - SHOW_TOAST: { - target: 'visible', - actions: 'setMessage', - }, - }, - }, - visible: { - on: { - HIDE_TOAST: { - target: 'hidden', - actions: 'clearMessage', - }, - }, - }, - }, - }, - selectionDialog: { - initial: 'idle', - states: { - idle: { - on: { - HANDLE_SUBSCRIPTION_RESULT: [ - { - cond: 'isSelectionRefreshedEventPayload', - target: 'ready', - actions: 'handleSubscriptionResult', - }, - ], - }, - }, - ready: { - on: { - HANDLE_SELECTION_UPDATED: [ - { - target: 'ready', - actions: 'handleSelectionUpdated', - }, - ], - HANDLE_SUBSCRIPTION_RESULT: [ - { - cond: 'isSelectionRefreshedEventPayload', - target: 'ready', - actions: 'handleSubscriptionResult', - }, - ], - HANDLE_COMPLETE: { - target: 'complete', - }, - }, - }, - complete: { - type: 'final', - }, - }, - }, - }, - }, - { - guards: { - isSelectionRefreshedEventPayload: (_, event) => { - const { result } = event as HandleSubscriptionResultEvent; - const { data } = result; - return !!data && isSelectionRefreshedEventPayload(data.selectionEvent); - }, - }, - actions: { - handleSelectionUpdated: assign((_, event) => { - const { selectedObjectId } = event as HandleSelectionUpdatedEvent; - return { selectedObjectId }; - }), - handleSubscriptionResult: assign((_, event) => { - const { result } = event as HandleSubscriptionResultEvent; - const { data } = result; - if (data && isSelectionRefreshedEventPayload(data.selectionEvent)) { - const { selection } = data.selectionEvent; - return { selection }; - } - return {}; - }), - setMessage: assign((_, event) => { - const { message } = event as ShowToastEvent; - return { message }; - }), - clearMessage: assign((_) => { - return { message: null }; - }), - }, - } -); diff --git a/packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.tsx b/packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.tsx new file mode 100644 index 0000000000..04189d5bf9 --- /dev/null +++ b/packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.tsx @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + *******************************************************************************/ +import { TreeItemActionProps, TreeView } from '@eclipse-sirius/sirius-components-trees'; +import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; +import IconButton from '@mui/material/IconButton'; +import { useState } from 'react'; +import { makeStyles } from 'tss-react/mui'; +import { SelectionDialogTreeViewProps, SelectionDialogTreeViewState } from './SelectionDialogTreeView.types'; +import { useSelectionDialogTreeSubscription } from './useSelectionDialogTreeSubscription'; +export const SELECTION_DIALOG_TYPE: string = 'selectionDialogDescription'; + +const useTreeStyle = makeStyles()((theme) => ({ + borderStyle: { + border: '1px solid', + borderColor: theme.palette.divider, + height: 600, + overflow: 'auto', + }, +})); + +export const SelectionDialogTreeView = ({ + editingContextId, + treeDescriptionId, + targetObjectId, +}: SelectionDialogTreeViewProps) => { + const initialState: SelectionDialogTreeViewState = { + expanded: [], + maxDepth: 1, + }; + + const { classes } = useTreeStyle(); + const [state, setState] = useState(initialState); + + const treeId = `selection://?treeDescriptionId=${encodeURIComponent( + treeDescriptionId + )}&targetObjectId=${encodeURIComponent(targetObjectId)}`; + + const { tree } = useSelectionDialogTreeSubscription(editingContextId, treeId, state.expanded, state.maxDepth); + + const onExpandedElementChange = (expanded: string[], maxDepth: number) => { + setState((prevState) => ({ ...prevState, expanded, maxDepth })); + }; + + return ( +
+ {tree ? ( + } + onExpandedElementChange={onExpandedElementChange} + /> + ) : null} +
+ ); +}; + +const SelectionDialogTreeItemAction = ({ onExpandAll, item, isHovered }: TreeItemActionProps) => { + if (!onExpandAll || !item || !item.hasChildren || !isHovered) { + return null; + } + return ( + { + onExpandAll(item); + }}> + + + ); +}; diff --git a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionInput.java b/packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.types.ts similarity index 59% rename from packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionInput.java rename to packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.types.ts index edb738b09e..aba103cf1b 100644 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionInput.java +++ b/packages/selection/frontend/sirius-components-selection/src/SelectionDialogTreeView.types.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 Obeo. + * Copyright (c) 2024 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 @@ -10,15 +10,14 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -package org.eclipse.sirius.components.collaborative.selection.api; -import org.eclipse.sirius.components.core.api.IRepresentationInput; - -/** - * Represents an input sent by a client which concerns a selection representation. - * - * @author arichard - */ -public interface ISelectionInput extends IRepresentationInput { +export interface SelectionDialogTreeViewProps { + editingContextId: string; + treeDescriptionId: string; + targetObjectId: string; +} +export interface SelectionDialogTreeViewState { + expanded: string[]; + maxDepth: number; } diff --git a/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.ts b/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.ts new file mode 100644 index 0000000000..e3fa0a578c --- /dev/null +++ b/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.ts @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + *******************************************************************************/ +import { gql, useQuery } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useEffect } from 'react'; +import { + GetSelectionDescriptionData, + GetSelectionDescriptionVariables, + GQLSelectionDescription, + UseSelectionDescriptionProps, + UseSelectionDescriptionValue, +} from './useSelectionDescription.types'; +const getSelectionDescription = gql` + query getSelectionDescription($editingContextId: ID!, $representationId: ID!, $targetObjectId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + representation(representationId: $representationId) { + description { + ... on SelectionDescription { + message(targetObjectId: $targetObjectId) + treeDescription { + id + } + } + } + } + } + } + } +`; + +export const useSelectionDescription = ({ + editingContextId, + selectionDescriptionId, + targetObjectId, +}: UseSelectionDescriptionProps): UseSelectionDescriptionValue => { + //Since the SelectionDialogRepresentation does not really exist, the representationId just contains the description id + const representationId = `selectionDialog://?representationDescription=${encodeURIComponent(selectionDescriptionId)}`; + const { loading, data, error } = useQuery( + getSelectionDescription, + { + variables: { + editingContextId, + representationId, + targetObjectId, + }, + } + ); + + const { addErrorMessage } = useMultiToast(); + + useEffect(() => { + if (error) { + const { message } = error; + addErrorMessage(message); + } + }, [error]); + + const selectionDescription: GQLSelectionDescription | null = + data?.viewer.editingContext.representation.description || null; + + return { + selectionDescription, + loading, + }; +}; diff --git a/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.types.ts b/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.types.ts new file mode 100644 index 0000000000..2889d150d8 --- /dev/null +++ b/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.types.ts @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + *******************************************************************************/ + +export interface UseSelectionDescriptionProps { + editingContextId: string; + selectionDescriptionId: string; + targetObjectId: string; +} + +export interface UseSelectionDescriptionValue { + loading: boolean; + selectionDescription: GQLSelectionDescription | null; +} + +export interface GetSelectionDescriptionVariables { + editingContextId: string; + representationId: string; + targetObjectId: string; +} + +export interface GQLSelectionDescription { + message: string; + treeDescription: GQLTreeDescription; +} + +export interface GQLTreeDescription { + id: string; +} + +export interface GetSelectionDescriptionData { + viewer: GetSelectionDescriptionViewer; +} + +export interface GetSelectionDescriptionViewer { + editingContext: GetSelectionDescriptionEditingContext; +} + +export interface GetSelectionDescriptionEditingContext { + representation: GetSelectionDescriptionRepresentation; +} + +export interface GetSelectionDescriptionRepresentation { + description: GQLSelectionDescription; +} diff --git a/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx b/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx new file mode 100644 index 0000000000..8e22f6ea0a --- /dev/null +++ b/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + *******************************************************************************/ + +import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { getTreeEventSubscription } from '@eclipse-sirius/sirius-components-trees'; +import { useEffect, useState } from 'react'; +import { + GQLSelectionDialogTreeEventData, + GQLSelectionDialogTreeEventInput, + GQLSelectionDialogTreeEventVariables, + GQLTreeEventPayload, + GQLTreeRefreshedEventPayload, + UseSelectionDialogTreeSubscriptionState, + UseSelectionDialogTreeSubscriptionValue, +} from './useSelectionDialogTreeSubscription.types'; + +const isTreeRefreshedEventPayload = (payload: GQLTreeEventPayload): payload is GQLTreeRefreshedEventPayload => + payload.__typename === 'TreeRefreshedEventPayload'; + +export const useSelectionDialogTreeSubscription = ( + editingContextId: string, + treeId: string, + expanded: string[], + maxDepth: number +): UseSelectionDialogTreeSubscriptionValue => { + const [state, setState] = useState({ + id: crypto.randomUUID(), + tree: null, + complete: false, + }); + + const input: GQLSelectionDialogTreeEventInput = { + id: state.id, + editingContextId, + treeId, + expanded, + }; + + const variables: GQLSelectionDialogTreeEventVariables = { input }; + + const onData = ({ data }: OnDataOptions) => { + const { data: gqlTreeData } = data; + if (gqlTreeData) { + const { selectionDialogTreeEvent: payload } = gqlTreeData; + if (isTreeRefreshedEventPayload(payload)) { + const { tree } = payload; + setState((prevState) => ({ ...prevState, tree })); + } + } + }; + + const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); + + const { error, loading } = useSubscription( + gql(getTreeEventSubscription(maxDepth, 'selectionDialogTreeEvent', 'SelectionDialogTreeEventInput')), + { + variables, + fetchPolicy: 'no-cache', + onData, + onComplete, + } + ); + + const { addErrorMessage } = useMultiToast(); + useEffect(() => { + if (error) { + addErrorMessage('An unexpected error has occurred, please refresh the page'); + } + }, [error]); + + return { + loading, + tree: state.tree, + complete: state.complete, + }; +}; diff --git a/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.types.ts b/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.types.ts new file mode 100644 index 0000000000..fb1c3d8748 --- /dev/null +++ b/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.types.ts @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2024 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 + *******************************************************************************/ +import { GQLTree } from '@eclipse-sirius/sirius-components-trees'; + +export interface UseSelectionDialogTreeSubscriptionValue { + loading: boolean; + tree: GQLTree | null; + complete: boolean; +} + +export interface UseSelectionDialogTreeSubscriptionState { + id: string; + tree: GQLTree | null; + complete: boolean; +} + +export interface GQLSelectionDialogTreeEventInput { + id: string; + treeId: string; + editingContextId: string; + expanded: string[]; +} + +export interface GQLSelectionDialogTreeEventVariables { + input: GQLSelectionDialogTreeEventInput; +} + +export interface GQLSelectionDialogTreeEventData { + selectionDialogTreeEvent: GQLTreeEventPayload; +} + +export interface GQLTreeEventPayload { + __typename: string; +} + +export interface GQLTreeRefreshedEventPayload extends GQLTreeEventPayload { + id: string; + tree: GQLTree; +} diff --git a/packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/services/selection/SelectionDialogTreeEventSubscriptionRunner.java b/packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/services/selection/SelectionDialogTreeEventSubscriptionRunner.java new file mode 100644 index 0000000000..f16732e31d --- /dev/null +++ b/packages/sirius-web/backend/sirius-web-tests/src/main/java/org/eclipse/sirius/web/tests/services/selection/SelectionDialogTreeEventSubscriptionRunner.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.web.tests.services.selection; + +import java.util.Objects; + +import org.eclipse.sirius.components.collaborative.selection.dto.SelectionDialogTreeEventInput; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.ISubscriptionRunner; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Flux; + +/** + * Used to get the selection dialog tree event subscription with the GraphQL API. + * + * @author fbarbin + */ +@Service +public class SelectionDialogTreeEventSubscriptionRunner implements ISubscriptionRunner { + + private static final String SELECTION_DIALOG_TREE_EVENT_SUBSCRIPTION = """ + subscription selectionDialogTreeEvent($input: SelectionDialogTreeEventInput!) { + selectionDialogTreeEvent(input: $input) { + __typename + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public SelectionDialogTreeEventSubscriptionRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public Flux run(SelectionDialogTreeEventInput input) { + return this.graphQLRequestor.subscribe(SELECTION_DIALOG_TREE_EVENT_SUBSCRIPTION, input); + } + +} diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/forms/DetailsViewControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/forms/DetailsViewControllerIntegrationTests.java index d49e15bab3..f9ec3b431b 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/forms/DetailsViewControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/forms/DetailsViewControllerIntegrationTests.java @@ -19,10 +19,10 @@ import java.util.function.Predicate; import org.eclipse.sirius.components.collaborative.forms.dto.FormRefreshedEventPayload; +import org.eclipse.sirius.web.AbstractIntegrationTests; import org.eclipse.sirius.web.application.views.details.dto.DetailsEventInput; +import org.eclipse.sirius.web.data.PapayaIdentifiers; import org.eclipse.sirius.web.tests.graphql.DetailsEventSubscriptionRunner; -import org.eclipse.sirius.web.AbstractIntegrationTests; -import org.eclipse.sirius.web.data.TestIdentifiers; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -59,10 +59,10 @@ public void beforeEach() { @Test @DisplayName("Given a semantic object, when we subscribe to its properties events, then the form is sent") - @Sql(scripts = {"/scripts/initialize.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) public void givenSemanticObjectWhenWeSubscribeToItsPropertiesEventsThenTheFormIsSent() { - var input = new DetailsEventInput(UUID.randomUUID(), TestIdentifiers.ECORE_SAMPLE_PROJECT.toString(), List.of(TestIdentifiers.EPACKAGE_OBJECT.toString())); + var input = new DetailsEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), List.of(PapayaIdentifiers.SIRIUS_WEB_DOMAIN_OBJECT.toString())); var flux = this.detailsEventSubscriptionRunner.run(input); Predicate formContentMatcher = object -> Optional.of(object) diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/selection/SelectionControllerIntegrationTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/selection/SelectionControllerIntegrationTests.java index 54fe568a62..12556f9701 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/selection/SelectionControllerIntegrationTests.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/selection/SelectionControllerIntegrationTests.java @@ -14,36 +14,32 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -import static org.eclipse.sirius.components.forms.tests.assertions.FormAssertions.assertThat; import com.jayway.jsonpath.JsonPath; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Predicate; -import org.eclipse.sirius.components.collaborative.dto.CreateRepresentationInput; -import org.eclipse.sirius.components.collaborative.forms.dto.EditTextfieldInput; -import org.eclipse.sirius.components.collaborative.forms.dto.FormRefreshedEventPayload; -import org.eclipse.sirius.web.application.views.details.dto.DetailsEventInput; -import org.eclipse.sirius.components.collaborative.selection.dto.SelectionEventInput; -import org.eclipse.sirius.components.collaborative.selection.dto.SelectionRefreshedEventPayload; -import org.eclipse.sirius.components.core.api.SuccessPayload; -import org.eclipse.sirius.components.forms.Textarea; -import org.eclipse.sirius.components.forms.tests.graphql.EditTextfieldMutationRunner; -import org.eclipse.sirius.web.tests.graphql.DetailsEventSubscriptionRunner; -import org.eclipse.sirius.components.forms.tests.navigation.FormNavigator; +import org.eclipse.sirius.components.collaborative.selection.dto.SelectionDialogTreeEventInput; +import org.eclipse.sirius.components.collaborative.trees.dto.TreeRefreshedEventPayload; import org.eclipse.sirius.components.graphql.api.URLConstants; import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.trees.Tree; +import org.eclipse.sirius.components.trees.TreeItem; +import org.eclipse.sirius.components.trees.tests.graphql.ExpandAllTreePathQueryRunner; import org.eclipse.sirius.web.AbstractIntegrationTests; import org.eclipse.sirius.web.data.PapayaIdentifiers; -import org.eclipse.sirius.web.services.diagrams.ModelOperationDiagramDescriptionProvider; import org.eclipse.sirius.web.services.selection.SelectionDescriptionProvider; -import org.eclipse.sirius.web.tests.services.api.IGivenCreatedDiagramSubscription; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; +import org.eclipse.sirius.web.tests.services.selection.SelectionDialogTreeEventSubscriptionRunner; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -54,7 +50,6 @@ import org.springframework.transaction.annotation.Transactional; import graphql.execution.DataFetcherResult; -import reactor.core.publisher.Flux; import reactor.test.StepVerifier; /** @@ -67,15 +62,34 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "sirius.web.test.enabled=studio" }) public class SelectionControllerIntegrationTests extends AbstractIntegrationTests { - private static final String GET_SELECTION_EVENT_SUBSCRIPTION = """ - subscription selectionEvent($input: SelectionEventInput!) { - selectionEvent(input: $input) { + private static final String GET_SELECTION_DESCRIPTION = """ + query getSelectionDescription($editingContextId: ID!, $representationId: ID!, $targetObjectId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + representation(representationId: $representationId) { + description { + ... on SelectionDescription { + message(targetObjectId: $targetObjectId) + treeDescription { + id + } + } + } + } + } + } + } + """; + + private static final String GET_TREE_EVENT_SUBSCRIPTION = """ + subscription selectionDialogTreeEvent($input: SelectionDialogTreeEventInput!) { + selectionDialogTreeEvent(input: $input) { __typename - ... on SelectionRefreshedEventPayload { - selection { - objects { - id - label + ... on TreeRefreshedEventPayload { + id + tree { + id + children { iconURL } } @@ -84,6 +98,7 @@ subscription selectionEvent($input: SelectionEventInput!) { } """; + @Autowired private IGraphQLRequestor graphQLRequestor; @@ -91,16 +106,13 @@ subscription selectionEvent($input: SelectionEventInput!) { private IGivenInitialServerState givenInitialServerState; @Autowired - private DetailsEventSubscriptionRunner detailsEventSubscriptionRunner; - - @Autowired - private EditTextfieldMutationRunner editTextfieldMutationRunner; + private SelectionDialogTreeEventSubscriptionRunner selectionDialogTreeEventSubscriptionRunner; @Autowired - private ModelOperationDiagramDescriptionProvider modelOperationDiagramDescriptionProvider; + private SelectionDescriptionProvider selectionDescriptionProvider; @Autowired - private IGivenCreatedDiagramSubscription givenCreatedDiagramSubscription; + private ExpandAllTreePathQueryRunner expandAllTreePathQueryRunner; @BeforeEach public void beforeEach() { @@ -108,147 +120,264 @@ public void beforeEach() { } @Test - @DisplayName("Given a semantic object, when we subscribe to its selection events, then the selection is sent") + @DisplayName("Given a semantic object and a dialog description id, when requesting the Selection Description, then the Selection Description is sent") + @Sql(scripts = { "/scripts/papaya.sql" }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenSemanticObjectWhenRequestingSelectionDescriptionThenTheSelectionDescriptionIsSent() { + String representationId = "selectionDialog://?representationDescription=" + URLEncoder.encode(this.selectionDescriptionProvider.getSelectionDialogDescriptionId(), StandardCharsets.UTF_8); + Map variables = Map.of("editingContextId", PapayaIdentifiers.PAPAYA_PROJECT.toString(), "representationId", representationId, "targetObjectId", + PapayaIdentifiers.PROJECT_OBJECT.toString()); + var result = this.graphQLRequestor.execute(GET_SELECTION_DESCRIPTION, variables); + String message = JsonPath.read(result, "$.data.viewer.editingContext.representation.description.message"); + String treeDescriptionId = JsonPath.read(result, "$.data.viewer.editingContext.representation.description.treeDescription.id"); + + assertThat(message).isEqualTo("Select the objects to consider"); + assertThat(treeDescriptionId).isEqualTo(this.selectionDescriptionProvider.getSelectionDialogTreeDescriptionId()); + } + + @Test + @DisplayName("Given a semantic object, when we subscribe to its selection dialog tree, then the tree is sent") @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) public void givenSemanticObjectWhenWeSubscribeToItsSelectionEventsThenTheSelectionIsSent() { - var input = new SelectionEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), SelectionDescriptionProvider.REPRESENTATION_DESCRIPTION_ID, PapayaIdentifiers.PROJECT_OBJECT.toString()); - var flux = this.graphQLRequestor.subscribe(GET_SELECTION_EVENT_SUBSCRIPTION, input) - .filter(DataFetcherResult.class::isInstance) - .map(DataFetcherResult.class::cast) - .map(DataFetcherResult::getData) - .filter(SelectionRefreshedEventPayload.class::isInstance) - .map(SelectionRefreshedEventPayload.class::cast); - - Consumer selectionContentConsumer = payload -> Optional.of(payload) - .map(SelectionRefreshedEventPayload::selection) - .ifPresentOrElse(selection -> { - assertThat(selection.getObjects()).hasSizeGreaterThanOrEqualTo(5); - }, () -> fail("Missing selection")); - + var treeId = "selection://?treeDescriptionId=" + URLEncoder.encode(this.selectionDescriptionProvider.getSelectionDialogTreeDescriptionId(), StandardCharsets.UTF_8) + "&targetObjectId=" + + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of()); + var flux = this.selectionDialogTreeEventSubscriptionRunner.run(input); + var hasResourceRootContent = this.getTreeRefreshedEventPayloadMatcher(); StepVerifier.create(flux) - .consumeNextWith(selectionContentConsumer) - .thenCancel() - .verify(Duration.ofSeconds(10)); + .expectNextMatches(hasResourceRootContent) + .thenCancel() + .verify(); } @Test - @DisplayName("Given a selection representation, when we edit the details of an object, then the selection representation is updated") + @DisplayName("Given the selection dialog tree, when we expand the first item, then the children are sent") @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) - public void givenSelectionRepresentationWhenWeEditTheDetailsOfAnObjectThenTheSelectionRepresentationIsUpdated() { - this.testSelectionRepresentationUpdate(PapayaIdentifiers.PROJECT_OBJECT.toString()); - } + public void givenSelectionDialogTreeWhenWeExpandTheFirstItemThenChildrenAreSent() { + var treeId = "selection://?treeDescriptionId=" + URLEncoder.encode(this.selectionDescriptionProvider.getSelectionDialogTreeDescriptionId(), StandardCharsets.UTF_8) + "&targetObjectId=" + + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of()); + var flux = this.selectionDialogTreeEventSubscriptionRunner.run(input); - private void testSelectionRepresentationUpdate(String targetObjectId) throws AssertionError { - var selectionEventInput = new SelectionEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), SelectionDescriptionProvider.REPRESENTATION_DESCRIPTION_ID, targetObjectId); - var selectionFlux = this.graphQLRequestor.subscribe(GET_SELECTION_EVENT_SUBSCRIPTION, selectionEventInput); + var treeItemId = new AtomicReference(); - var detailsEventInput = new DetailsEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), List.of(PapayaIdentifiers.SIRIUS_WEB_DOMAIN_OBJECT.toString())); - var detailsFlux = this.detailsEventSubscriptionRunner.run(detailsEventInput); + Consumer initialTreeContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(1); + assertThat(tree.getChildren()).allSatisfy(treeItem -> assertThat(treeItem.getChildren()).isEmpty()); - var formId = new AtomicReference(); - var textareaId = new AtomicReference(); + treeItemId.set(tree.getChildren().get(0).getId()); + }); - Consumer initialFormContentConsumer = payload -> Optional.of(payload) - .filter(DataFetcherResult.class::isInstance) - .map(DataFetcherResult.class::cast) - .map(DataFetcherResult::getData) - .filter(FormRefreshedEventPayload.class::isInstance) - .map(FormRefreshedEventPayload.class::cast) - .map(FormRefreshedEventPayload::form) - .ifPresentOrElse(form -> { - formId.set(form.getId()); + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .thenCancel() + .verify(); + + var expandedTreeInput = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of(treeItemId.get())); + var expandedTreeFlux = this.selectionDialogTreeEventSubscriptionRunner.run(expandedTreeInput); + var treeRefreshedEventPayloadExpandMatcher = this.getTreeRefreshedEventPayloadExpandMatcher(); + StepVerifier.create(expandedTreeFlux) + .expectNextMatches(treeRefreshedEventPayloadExpandMatcher) + .thenCancel() + .verify(); + } - var groupNavigator = new FormNavigator(form).page("sirius-web-domain").group("Core Properties"); - var textarea = groupNavigator.findWidget("Name", Textarea.class); - textareaId.set(textarea.getId()); - }, () -> fail("Missing form")); + @Test + @DisplayName("Given the selection dialog tree, when we perform expand all on the first item, then the children are sent") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenSelectionDialogTreeWhenWePerformExpandAllOnTheFirstItemThenChildrenAreSent() { + var treeId = "selection://?treeDescriptionId=" + URLEncoder.encode(this.selectionDescriptionProvider.getSelectionDialogTreeDescriptionId(), StandardCharsets.UTF_8) + "&targetObjectId=" + + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of()); + var flux = this.selectionDialogTreeEventSubscriptionRunner.run(input); + + var treeItemId = new AtomicReference(); + var treeInstanceId = new AtomicReference(); + + Consumer initialTreeContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(1); + assertThat(tree.getChildren()).allSatisfy(treeItem -> assertThat(treeItem.getChildren()).isEmpty()); + treeInstanceId.set(tree.getId()); + treeItemId.set(tree.getChildren().get(0).getId()); + }); + + var treeItemIds = new AtomicReference>(); + + Runnable getTreePath = () -> { + Map variables = Map.of( + "editingContextId", PapayaIdentifiers.PAPAYA_PROJECT.toString(), + "treeId", treeInstanceId.get(), + "treeItemId", treeItemId.get() + ); + var result = this.expandAllTreePathQueryRunner.run(variables); + List treeItemIdsToExpand = JsonPath.read(result, "$.data.viewer.editingContext.expandAllTreePath.treeItemIdsToExpand"); + assertThat(treeItemIdsToExpand).isNotEmpty(); + + treeItemIds.set(treeItemIdsToExpand); + }; - Consumer selectionContentConsumer = object -> Optional.of(object) - .filter(DataFetcherResult.class::isInstance) - .map(DataFetcherResult.class::cast) - .map(DataFetcherResult::getData) - .filter(SelectionRefreshedEventPayload.class::isInstance) - .map(SelectionRefreshedEventPayload.class::cast) - .map(SelectionRefreshedEventPayload::selection) - .ifPresentOrElse(selection -> { - assertThat(selection.getObjects()) - .isNotEmpty() - .anyMatch(selectionObject -> selectionObject.getLabel().contains("sirius-web-domain")); - }, () -> fail("Missing selection")); + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(getTreePath) + .thenCancel() + .verify(Duration.ofSeconds(10)); - Runnable editTextfield = () -> { - var input = new EditTextfieldInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), formId.get(), textareaId.get(), "sirius-web-domain-EDITED"); - var result = this.editTextfieldMutationRunner.run(input); + Consumer initialExpandedTreeContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(1); + assertThat(tree.getChildren()).anySatisfy(treeItem -> assertThat(treeItem.getChildren()).isNotEmpty()); - String typename = JsonPath.read(result, "$.data.editTextfield.__typename"); - assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); - }; + }); - Consumer updatedFormContentConsumer = payload -> Optional.of(payload) - .filter(DataFetcherResult.class::isInstance) - .map(DataFetcherResult.class::cast) - .map(DataFetcherResult::getData) - .filter(FormRefreshedEventPayload.class::isInstance) - .map(FormRefreshedEventPayload.class::cast) - .map(FormRefreshedEventPayload::form) - .ifPresentOrElse(form -> { - var groupNavigator = new FormNavigator(form).page("sirius-web-domain-EDITED").group("Core Properties"); - var textarea = groupNavigator.findWidget("Name", Textarea.class); + var expandedTreeInput = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, treeItemIds.get()); + var expandedTreeFlux = this.selectionDialogTreeEventSubscriptionRunner.run(expandedTreeInput); - assertThat(textarea).hasValue("sirius-web-domain-EDITED"); - }, () -> fail("Missing form")); + StepVerifier.create(expandedTreeFlux) + .consumeNextWith(initialExpandedTreeContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } - Consumer updatedSelectionContentConsumer = object -> Optional.of(object) - .filter(DataFetcherResult.class::isInstance) - .map(DataFetcherResult.class::cast) - .map(DataFetcherResult::getData) - .filter(SelectionRefreshedEventPayload.class::isInstance) - .map(SelectionRefreshedEventPayload.class::cast) - .map(SelectionRefreshedEventPayload::selection) - .ifPresentOrElse(selection -> { - assertThat(selection.getObjects()) - .isNotEmpty() - .anyMatch(selectionObject -> selectionObject.getLabel().contains("sirius-web-domain-EDITED")); - }, () -> fail("Missing selection")); + @Test + @DisplayName("Given the selection dialog tree, when we perform expand all on the second item (the root project), then the children are sent") + @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) + public void givenSelectionDialogTreeWhenWePerformExpandAllOnTheSecondItemThenChildrenAreSent() { + var treeId = "selection://?treeDescriptionId=" + URLEncoder.encode(this.selectionDescriptionProvider.getSelectionDialogTreeDescriptionId(), StandardCharsets.UTF_8) + "&targetObjectId=" + + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of()); + var flux = this.selectionDialogTreeEventSubscriptionRunner.run(input); + + var rootTreeItemId = new AtomicReference(); + var treeInstanceId = new AtomicReference(); + + Consumer initialTreeContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(1); + assertThat(tree.getChildren()).allSatisfy(treeItem -> assertThat(treeItem.getChildren()).isEmpty()); + treeInstanceId.set(tree.getId()); + rootTreeItemId.set(tree.getChildren().get(0).getId()); + }); - StepVerifier.create(Flux.merge(selectionFlux, detailsFlux)) - .consumeNextWith(selectionContentConsumer) - .consumeNextWith(initialFormContentConsumer) - .then(editTextfield) - .consumeNextWith(updatedFormContentConsumer) - .consumeNextWith(updatedSelectionContentConsumer) + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + + //We expand the first tree item (representing the resource) + var expandedFirstTreeItemInput = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of(rootTreeItemId.get())); + var expandedFirstTreeItemFlux = this.selectionDialogTreeEventSubscriptionRunner.run(expandedFirstTreeItemInput); + + //Used to retrieve the Papaya Root Project tree item + var rootProjectTreeItemId = new AtomicReference(); + Consumer firstTreeItemExpandedContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(1); + var rootResourceTreeItem = tree.getChildren().get(0); + assertThat(tree.getChildren()).hasSize(1); + rootProjectTreeItemId.set(rootResourceTreeItem.getChildren().get(0).getId()); + }); + + var treeItemIds = new AtomicReference>(); + + Runnable getTreePath = () -> { + Map variables = Map.of( + "editingContextId", PapayaIdentifiers.PAPAYA_PROJECT.toString(), + "treeId", treeInstanceId.get(), + "treeItemId", rootProjectTreeItemId.get() + ); + var result = this.expandAllTreePathQueryRunner.run(variables); + List treeItemIdsToExpand = JsonPath.read(result, "$.data.viewer.editingContext.expandAllTreePath.treeItemIdsToExpand"); + assertThat(treeItemIdsToExpand).isNotEmpty(); + treeItemIdsToExpand.add(0, rootTreeItemId.get()); + treeItemIds.set(treeItemIdsToExpand); + }; + + StepVerifier.create(expandedFirstTreeItemFlux) + .consumeNextWith(firstTreeItemExpandedContentConsumer) + //Now that we have expand and retrieve the Papaya Root Project tree item, we can perform the expandAll from it + .then(getTreePath) + .thenCancel() + .verify(Duration.ofSeconds(10)); + + + Consumer initialExpandedTreeContentConsumer = this.getTreeSubscriptionConsumer(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getChildren()).hasSize(1); + TreeItem rootProjectTreeItem = tree.getChildren().get(0); + assertThat(rootProjectTreeItem.getChildren()).isNotEmpty(); + }); + + var expandedTreeInput = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, treeItemIds.get()); + var expandedTreeFlux = this.selectionDialogTreeEventSubscriptionRunner.run(expandedTreeInput); + // We now verify the expandAll result + StepVerifier.create(expandedTreeFlux) + .consumeNextWith(initialExpandedTreeContentConsumer) .thenCancel() .verify(Duration.ofSeconds(10)); + + } - private Flux givenSubscriptionToModelOperationDiagram() { - var input = new CreateRepresentationInput( - UUID.randomUUID(), - PapayaIdentifiers.PAPAYA_PROJECT.toString(), - this.modelOperationDiagramDescriptionProvider.getRepresentationDescriptionId(), - PapayaIdentifiers.PROJECT_OBJECT.toString(), - "ModelOperationDiagram" - ); - return this.givenCreatedDiagramSubscription.createAndSubscribe(input); + private Predicate getTreeRefreshedEventPayloadMatcher() { + Predicate treeMatcher = tree -> { + return tree.getChildren().size() == 1 && tree.getChildren().get(0).getIconURL().get(0).equals("/icons/Resource.svg") + && tree.getChildren().get(0).getLabel().toString().equals("Sirius Web Architecture") && tree.getChildren().get(0).getChildren().isEmpty(); + }; + + return object -> { + return Optional.of(object) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(TreeRefreshedEventPayload.class::isInstance) + .map(TreeRefreshedEventPayload.class::cast) + .map(TreeRefreshedEventPayload::tree) + .filter(treeMatcher) + .isPresent(); + }; + } + + private Predicate getTreeRefreshedEventPayloadExpandMatcher() { + Predicate treeMatcher = tree -> { + return tree.getChildren().size() == 1 && !tree.getChildren().get(0).getChildren().isEmpty(); + }; + + return object -> { + return Optional.of(object) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(TreeRefreshedEventPayload.class::isInstance) + .map(TreeRefreshedEventPayload.class::cast) + .map(TreeRefreshedEventPayload::tree) + .filter(treeMatcher) + .isPresent(); + }; } @Test - @DisplayName("Given a semantic object, when we subscribe to its selection events, then the URL of its objects is valid") + @DisplayName("Given a semantic object, when we subscribe to its selection dialog tree, then the URL of its treeItems is valid") @Sql(scripts = {"/scripts/papaya.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = {"/scripts/cleanup.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)) public void givenSemanticObjectWhenWeSubscribeToItsSelectionEventsThenTheURLOfItsObjectsIsValid() { - var input = new SelectionEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), SelectionDescriptionProvider.REPRESENTATION_DESCRIPTION_ID, PapayaIdentifiers.PROJECT_OBJECT.toString()); - var flux = this.graphQLRequestor.subscribeToSpecification(GET_SELECTION_EVENT_SUBSCRIPTION, input); + var treeId = "selection://?treeDescriptionId=" + URLEncoder.encode(this.selectionDescriptionProvider.getSelectionDialogTreeDescriptionId(), StandardCharsets.UTF_8) + "&targetObjectId=" + + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new SelectionDialogTreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of()); + var flux = this.graphQLRequestor.subscribeToSpecification(GET_TREE_EVENT_SUBSCRIPTION, input); - Consumer selectionContentConsumer = payload -> Optional.of(payload) + Consumer treeContentConsumer = payload -> Optional.of(payload) .ifPresentOrElse(body -> { - String typename = JsonPath.read(body, "$.data.selectionEvent.__typename"); - assertThat(typename).isEqualTo(SelectionRefreshedEventPayload.class.getSimpleName()); + String typename = JsonPath.read(body, "$.data.selectionDialogTreeEvent.__typename"); + assertThat(typename).isEqualTo(TreeRefreshedEventPayload.class.getSimpleName()); - List> objectIconURLs = JsonPath.read(body, "$.data.selectionEvent.selection.objects[*].iconURL"); + List> objectIconURLs = JsonPath.read(body, "$.data.selectionDialogTreeEvent.tree.children[*].iconURL"); assertThat(objectIconURLs) .isNotEmpty() .allSatisfy(iconURLs -> { @@ -260,10 +389,19 @@ public void givenSemanticObjectWhenWeSubscribeToItsSelectionEventsThenTheURLOfIt }, () -> fail("Missing selection")); StepVerifier.create(flux) - .consumeNextWith(selectionContentConsumer) + .consumeNextWith(treeContentConsumer) .thenCancel() .verify(Duration.ofSeconds(10)); } - + private Consumer getTreeSubscriptionConsumer(Consumer treeConsumer) { + return object -> Optional.of(object) + .filter(DataFetcherResult.class::isInstance) + .map(DataFetcherResult.class::cast) + .map(DataFetcherResult::getData) + .filter(TreeRefreshedEventPayload.class::isInstance) + .map(TreeRefreshedEventPayload.class::cast) + .map(TreeRefreshedEventPayload::tree) + .ifPresentOrElse(treeConsumer, () -> fail("Missing tree")); + } } diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/selection/SelectionDescriptionProvider.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/selection/SelectionDescriptionProvider.java index f801fe2247..9a8be5f677 100644 --- a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/selection/SelectionDescriptionProvider.java +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/selection/SelectionDescriptionProvider.java @@ -12,18 +12,35 @@ *******************************************************************************/ package org.eclipse.sirius.web.services.selection; -import java.util.List; import java.util.Objects; -import java.util.function.Function; +import java.util.UUID; -import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IEditingContextProcessor; -import org.eclipse.sirius.components.core.api.IIdentityService; -import org.eclipse.sirius.components.core.api.ILabelService; -import org.eclipse.sirius.components.representations.VariableManager; -import org.eclipse.sirius.components.selection.Selection; -import org.eclipse.sirius.components.selection.description.SelectionDescription; +import org.eclipse.sirius.components.emf.ResourceMetadataAdapter; +import org.eclipse.sirius.components.emf.services.IDAdapter; +import org.eclipse.sirius.components.emf.services.JSONResourceFactory; +import org.eclipse.sirius.components.view.View; +import org.eclipse.sirius.components.view.builder.generated.SelectionDialogTreeDescriptionBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.DiagramDescriptionBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.DiagramPaletteBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.InsideLabelDescriptionBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.NodeDescriptionBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.NodeToolBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.RectangularNodeStyleDescriptionBuilder; +import org.eclipse.sirius.components.view.builder.generated.diagram.SelectionDialogDescriptionBuilder; +import org.eclipse.sirius.components.view.builder.generated.view.ChangeContextBuilder; +import org.eclipse.sirius.components.view.builder.generated.view.ViewBuilder; +import org.eclipse.sirius.components.view.diagram.DiagramDescription; +import org.eclipse.sirius.components.view.diagram.DiagramFactory; +import org.eclipse.sirius.components.view.diagram.InsideLabelPosition; +import org.eclipse.sirius.components.view.diagram.NodeTool; +import org.eclipse.sirius.components.view.diagram.SelectionDialogDescription; +import org.eclipse.sirius.components.view.diagram.SelectionDialogTreeDescription; +import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; +import org.eclipse.sirius.emfjson.resource.JsonResource; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.springframework.stereotype.Service; @@ -37,57 +54,119 @@ public class SelectionDescriptionProvider implements IEditingContextProcessor { public static final String LABEL = "Selection"; - public static final String REPRESENTATION_DESCRIPTION_ID = "siriusComponents://representationDescription?kind=selectionDescription&sourceElementId=Test"; + private static final String DIALOG_MESSAGE = "Select the objects to consider"; - private final IIdentityService identityService; + private final View view; - private final ILabelService labelService; + private DiagramDescription diagramDescription; - public SelectionDescriptionProvider(IIdentityService identityService, ILabelService labelService) { - this.identityService = Objects.requireNonNull(identityService); - this.labelService = Objects.requireNonNull(labelService); + private NodeTool createNodeTool; + + private final IDiagramIdProvider diagramIdProvider; + + private SelectionDialogTreeDescription selectionDialogTreeDescription; + + private SelectionDialogDescription selectionDialog; + + + public SelectionDescriptionProvider(IDiagramIdProvider diagramIdProvider) { + this.diagramIdProvider = Objects.requireNonNull(diagramIdProvider); + this.view = this.createView(); } @Override public void preProcess(IEditingContext editingContext) { if (editingContext instanceof EditingContext siriusWebEditingContext) { - siriusWebEditingContext.getRepresentationDescriptions().put(REPRESENTATION_DESCRIPTION_ID, this.getSelectionDescription()); + siriusWebEditingContext.getViews().add(this.view); } } - private SelectionDescription getSelectionDescription() { - Function labelProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.labelService::getLabel) - .orElse(""); - - Function> iconURLProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.labelService::getImagePath) - .orElse(null); - - Function> objectsProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .filter(EObject.class::isInstance) - .map(EObject.class::cast) - .map(eObject -> (List) eObject.eContents()) - .orElse(List.of()); - - Function targetObjectIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.identityService::getId) - .orElse(null); - - Function selectionObjectsIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.identityService::getId) - .orElse(null); - - return SelectionDescription.newSelectionDescription(REPRESENTATION_DESCRIPTION_ID) - .label(LABEL) - .idProvider(variableManager -> Selection.PREFIX) - .labelProvider(labelProvider) - .iconURLProvider(iconURLProvider) - .messageProvider(variableManager -> "Select the objects to consider") - .objectsProvider(objectsProvider) - .targetObjectIdProvider(targetObjectIdProvider) - .selectionObjectsIdProvider(selectionObjectsIdProvider) - .canCreatePredicate(variableManager -> false) + public String getSelectionDialogTreeDescriptionId() { + return this.diagramIdProvider.getId(this.selectionDialogTreeDescription); + } + + public String getSelectionDialogDescriptionId() { + return this.diagramIdProvider.getId(this.selectionDialog); + } + + private View createView() { + ViewBuilder viewBuilder = new ViewBuilder(); + View unsynchronizedView = viewBuilder.build(); + unsynchronizedView.getDescriptions().add(this.createDiagramDescription()); + + unsynchronizedView.eAllContents().forEachRemaining(eObject -> { + eObject.eAdapters().add(new IDAdapter(UUID.nameUUIDFromBytes(EcoreUtil.getURI(eObject).toString().getBytes()))); + }); + + String resourcePath = UUID.nameUUIDFromBytes("SelectionDescriptionDiagramDescription".getBytes()).toString(); + JsonResource resource = new JSONResourceFactory().createResourceFromPath(resourcePath); + resource.eAdapters().add(new ResourceMetadataAdapter("SelectionDescriptionDiagramDescription")); + resource.getContents().add(unsynchronizedView); + + return unsynchronizedView; + } + + private DiagramDescription createDiagramDescription() { + var nodeStyle = new RectangularNodeStyleDescriptionBuilder() + .build(); + + var insideLabel = new InsideLabelDescriptionBuilder() + .labelExpression("aql:self.name") + .style(DiagramFactory.eINSTANCE.createInsideLabelStyle()) + .position(InsideLabelPosition.TOP_CENTER) + .build(); + + var nodeDescription = new NodeDescriptionBuilder() + .name("Component") + .domainType("papaya:Component") + .semanticCandidatesExpression("aql:self.eContents()") + .insideLabel(insideLabel) + .synchronizationPolicy(SynchronizationPolicy.SYNCHRONIZED) + .style(nodeStyle) + .build(); + + this.createCreateNodeTool(); + + var diagramPalette = new DiagramPaletteBuilder() + .nodeTools(this.createNodeTool) + .build(); + + this.diagramDescription = new DiagramDescriptionBuilder() + .name("Diagram") + .titleExpression("aql:'SelectionDescriptionDiagram'") + .domainType("papaya:Project") + .nodeDescriptions(nodeDescription) + .edgeDescriptions() + .palette(diagramPalette) + .autoLayout(false) + .build(); + + return this.diagramDescription; + } + + private void createCreateNodeTool() { + this.selectionDialog = this.createSelectionDialog(); + this.createNodeTool = new NodeToolBuilder() + .name("Create Component") + .body( + new ChangeContextBuilder() + .expression("aql:self") + .build() + ) + .dialogDescription(this.selectionDialog) + .build(); + } + + private SelectionDialogDescription createSelectionDialog() { + this.selectionDialogTreeDescription = new SelectionDialogTreeDescriptionBuilder() + .elementsExpression("aql:self.eResource()") + .childrenExpression("aql:if self.oclIsKindOf(papaya::NamedElement) then self.eContents() else self.getContents() endif ") + .isSelectableExpression("aql:self.oclIsKindOf(papaya::Component)") .build(); + return new SelectionDialogDescriptionBuilder() + .selectionMessage(DIALOG_MESSAGE) + .selectionDialogTreeDescription(this.selectionDialogTreeDescription) + .build(); + } } diff --git a/packages/trees/frontend/sirius-components-trees/src/views/TreeView.tsx b/packages/trees/frontend/sirius-components-trees/src/views/TreeView.tsx index d5720945b0..9ea8b91b1e 100644 --- a/packages/trees/frontend/sirius-components-trees/src/views/TreeView.tsx +++ b/packages/trees/frontend/sirius-components-trees/src/views/TreeView.tsx @@ -146,15 +146,15 @@ export const TreeView = ({ } }, [expandAllTreePathLoading, expandAllTreePathData]); - const { addMessages } = useMultiToast(); + const { addErrorMessage } = useMultiToast(); useEffect(() => { if (expandAllTreePathError) { - addMessages(expandAllTreePathError.message); + addErrorMessage(expandAllTreePathError.message); } }, [expandAllTreePathError]); useEffect(() => { if (treePathError) { - addMessages(treePathError.message); + addErrorMessage(treePathError.message); } }, [treePathError]); diff --git a/packages/view/backend/sirius-components-view-diagram-edit/src/main/java/org/eclipse/sirius/components/view/diagram/provider/NodeToolItemProvider.java b/packages/view/backend/sirius-components-view-diagram-edit/src/main/java/org/eclipse/sirius/components/view/diagram/provider/NodeToolItemProvider.java index cfc52e3843..9606bd154e 100644 --- a/packages/view/backend/sirius-components-view-diagram-edit/src/main/java/org/eclipse/sirius/components/view/diagram/provider/NodeToolItemProvider.java +++ b/packages/view/backend/sirius-components-view-diagram-edit/src/main/java/org/eclipse/sirius/components/view/diagram/provider/NodeToolItemProvider.java @@ -25,6 +25,8 @@ import org.eclipse.sirius.components.view.diagram.DiagramFactory; import org.eclipse.sirius.components.view.diagram.DiagramPackage; import org.eclipse.sirius.components.view.diagram.NodeTool; +import org.eclipse.sirius.components.view.diagram.SelectionDialogDescription; +import org.eclipse.sirius.components.view.diagram.SelectionDialogTreeDescription; /** * This is the item provider adapter for a {@link org.eclipse.sirius.components.view.diagram.NodeTool} object. * - * @generated + * @generated NOT */ @Override protected void collectNewChildDescriptors(Collection newChildDescriptors, Object object) { super.collectNewChildDescriptors(newChildDescriptors, object); - - newChildDescriptors.add(this.createChildParameter(DiagramPackage.Literals.NODE_TOOL__DIALOG_DESCRIPTION, DiagramFactory.eINSTANCE.createSelectionDialogDescription())); + SelectionDialogDescription selectionDialogDescription = DiagramFactory.eINSTANCE.createSelectionDialogDescription(); + SelectionDialogTreeDescription selectionDialogTreeDescription = DiagramFactory.eINSTANCE.createSelectionDialogTreeDescription(); + selectionDialogDescription.setSelectionDialogTreeDescription(selectionDialogTreeDescription); + newChildDescriptors.add(this.createChildParameter(DiagramPackage.Literals.NODE_TOOL__DIALOG_DESCRIPTION, selectionDialogDescription)); } } diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/ViewConverter.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/ViewConverter.java index 50cb375c68..26e52e2c22 100644 --- a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/ViewConverter.java +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/ViewConverter.java @@ -27,19 +27,14 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.sirius.components.collaborative.diagrams.DiagramServices; -import org.eclipse.sirius.components.core.api.IObjectService; import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.interpreter.AQLInterpreter; -import org.eclipse.sirius.components.interpreter.Result; import org.eclipse.sirius.components.representations.IRepresentationDescription; -import org.eclipse.sirius.components.representations.VariableManager; -import org.eclipse.sirius.components.selection.Selection; -import org.eclipse.sirius.components.selection.description.SelectionDescription; import org.eclipse.sirius.components.view.RepresentationDescription; import org.eclipse.sirius.components.view.View; import org.eclipse.sirius.components.view.diagram.DiagramDescription; -import org.eclipse.sirius.components.view.diagram.SelectionDialogTreeDescription; -import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; +import org.eclipse.sirius.components.view.diagram.DialogDescription; +import org.eclipse.sirius.components.view.emf.api.IDialogDescriptionConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -63,19 +58,16 @@ public class ViewConverter implements IViewConverter { private final ApplicationContext applicationContext; - private final IObjectService objectService; + private final List dialogDescriptionConverts; - private final IDiagramIdProvider diagramIdProvider; - - public ViewConverter(List javaServiceProviders, List representationDescriptionConverters, ApplicationContext applicationContext, IObjectService objectService, IDiagramIdProvider diagramIdProvider) { + public ViewConverter(List javaServiceProviders, List representationDescriptionConverters, ApplicationContext applicationContext, List dialogDescriptionConverts) { this.javaServiceProviders = new ArrayList<>(); this.javaServiceProviders.addAll(Objects.requireNonNull(javaServiceProviders)); IServiceProvider nodeServiceProvider = (IReadOnlyQueryEnvironment queryEnvironment) -> ServiceUtils.getReceiverServices(null, Node.class).stream().toList(); this.javaServiceProviders.add((View view) -> List.of(CanonicalServices.class, DiagramServices.class, nodeServiceProvider.getClass())); this.representationDescriptionConverters = Objects.requireNonNull(representationDescriptionConverters); this.applicationContext = Objects.requireNonNull(applicationContext); - this.objectService = Objects.requireNonNull(objectService); - this.diagramIdProvider = Objects.requireNonNull(diagramIdProvider); + this.dialogDescriptionConverts = Objects.requireNonNull(dialogDescriptionConverts); } /** @@ -94,7 +86,7 @@ public List convert(List views, List .flatMap(Optional::stream) .toList()); - result.addAll(this.convertSelectionsDialogs(view, interpreter)); + result.addAll(this.convertDialogDescriptions(view, interpreter)); } catch (NullPointerException exception) { // Can easily happen if the View model is currently invalid/inconsistent, typically because it is @@ -105,42 +97,25 @@ public List convert(List views, List return result; } - private List convertSelectionsDialogs(View view, AQLInterpreter interpreter) { - return view.getDescriptions().stream().filter(DiagramDescription.class::isInstance) - .flatMap(this::getAllContent) - .filter(org.eclipse.sirius.components.view.diagram.SelectionDialogDescription.class::isInstance) - .map(org.eclipse.sirius.components.view.diagram.SelectionDialogDescription.class::cast) - .map(selectionDescription -> this.convertSelectionDialog(selectionDescription, interpreter)) - .toList(); + private List convertDialogDescriptions(View view, AQLInterpreter interpreter) { + List representationDescriptions = new ArrayList<>(); + view.getDescriptions().stream() + .filter(DiagramDescription.class::isInstance) + .flatMap(this::getAllContent) + .filter(org.eclipse.sirius.components.view.diagram.DialogDescription.class::isInstance) + .map(org.eclipse.sirius.components.view.diagram.DialogDescription.class::cast) + .forEach(dialogDescription -> this.convertDialogDescription(dialogDescription, interpreter, representationDescriptions)); + return representationDescriptions; } - private IRepresentationDescription convertSelectionDialog(org.eclipse.sirius.components.view.diagram.SelectionDialogDescription selectionDescription, AQLInterpreter interpreter) { - String selectionDescriptionId = this.diagramIdProvider.getId(selectionDescription); - return SelectionDescription.newSelectionDescription(selectionDescriptionId) - .objectsProvider(variableManager -> { - String expression = Optional.ofNullable(selectionDescription.getSelectionDialogTreeDescription()) - .map(SelectionDialogTreeDescription::getElementsExpression) - .orElse(""); - Result result = interpreter.evaluateExpression(variableManager.getVariables(), expression); - return result.asObjects().orElse(List.of()).stream() - .filter(Objects::nonNull) - .toList(); - }) - .messageProvider(variableManager -> { - String message = selectionDescription.getSelectionMessage(); - if (message == null) { - message = ""; - } - return message; - }) - .idProvider(variableManager -> Selection.PREFIX) - .labelProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getLabel).orElse(null)) - .iconURLProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getImagePath).orElse(null)) - .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) - .selectionObjectsIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) - .label("Selection Description") - .canCreatePredicate(variableManager -> false) - .build(); + private void convertDialogDescription(DialogDescription dialogDescription, AQLInterpreter interpreter, List representationDescriptions) { + this.dialogDescriptionConverts.stream() + .filter(converter -> converter.canConvert(dialogDescription)) + .findFirst() + .map(converter -> converter.convert(dialogDescription, interpreter)) + .stream() + .flatMap(List::stream) + .forEach(representationDescriptions::add); } private Stream getAllContent(EObject representationDescription) { diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/api/IDialogDescriptionConverter.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/api/IDialogDescriptionConverter.java new file mode 100644 index 0000000000..234dd62d02 --- /dev/null +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/api/IDialogDescriptionConverter.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2024 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.sirius.components.view.emf.api; + +import java.util.List; + +import org.eclipse.sirius.components.interpreter.AQLInterpreter; +import org.eclipse.sirius.components.representations.IRepresentationDescription; +import org.eclipse.sirius.components.view.diagram.DialogDescription; + +/** + * Common interface for services dedicated to the {@link DialogDescription} conversion. + * + * @author fbarbin + */ +public interface IDialogDescriptionConverter { + + boolean canConvert(DialogDescription dialogDescription); + + List convert(DialogDescription dialogDescription, AQLInterpreter interpreter); +} diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/DiagramIdProvider.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/DiagramIdProvider.java index ecc3211ed9..49e85eb6d9 100644 --- a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/DiagramIdProvider.java +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/DiagramIdProvider.java @@ -20,6 +20,7 @@ import org.eclipse.sirius.components.view.diagram.DiagramElementDescription; import org.eclipse.sirius.components.view.diagram.DialogDescription; import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.sirius.components.view.diagram.SelectionDialogTreeDescription; import org.springframework.stereotype.Service; /** @@ -66,6 +67,17 @@ public String getId(DialogDescription dialogDescription) { return null; } + @Override + public String getId(SelectionDialogTreeDescription treeDescription) { + if (treeDescription != null) { + String sourceId = this.getSourceIdFromElementDescription(treeDescription); + String sourceElementId = this.objectService.getId(treeDescription); + return SELECTION_DIALOG_TREE_DESCRIPTION_KIND + "?" + SOURCE_KIND + "=" + VIEW_SOURCE_KIND + "&" + SOURCE_ID + "=" + sourceId + "&" + SOURCE_ELEMENT_ID + "=" + + sourceElementId; + } + return null; + } + private String getDialogDescriptionTypeName(DialogDescription dialogDescription) { String name = dialogDescription.eClass().getName(); String typeName = "siriusComponents://"; diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/IDiagramIdProvider.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/IDiagramIdProvider.java index ddfeef03f7..87a004fb9c 100644 --- a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/IDiagramIdProvider.java +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/IDiagramIdProvider.java @@ -16,6 +16,7 @@ import org.eclipse.sirius.components.view.diagram.DiagramDescription; import org.eclipse.sirius.components.view.diagram.DiagramElementDescription; import org.eclipse.sirius.components.view.diagram.DialogDescription; +import org.eclipse.sirius.components.view.diagram.SelectionDialogTreeDescription; import org.eclipse.sirius.components.view.emf.IRepresentationDescriptionIdProvider; /** @@ -31,6 +32,8 @@ public interface IDiagramIdProvider extends IRepresentationDescriptionIdProvider String EDGE_DESCRIPTION_KIND = "siriusComponents://edgeDescription"; + String SELECTION_DIALOG_TREE_DESCRIPTION_KIND = "siriusComponents://selectionDialogTreeDescription"; + @Override String getId(DiagramDescription diagramDescription); @@ -38,6 +41,8 @@ public interface IDiagramIdProvider extends IRepresentationDescriptionIdProvider String getId(DialogDescription dialogDescription); + String getId(SelectionDialogTreeDescription selectionDialogTreeDescription); + /** * Implementation which does nothing, used for mocks in unit tests. * @@ -60,6 +65,11 @@ public String getId(DialogDescription dialogDescription) { return ""; } + @Override + public String getId(SelectionDialogTreeDescription selectionDialogTreeDescription) { + return ""; + } + } } diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/SelectionDialogDescriptionConverter.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/SelectionDialogDescriptionConverter.java new file mode 100644 index 0000000000..53d61ebdf8 --- /dev/null +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/SelectionDialogDescriptionConverter.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2024, 2024 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.sirius.components.view.emf.diagram; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IObjectService; +import org.eclipse.sirius.components.core.api.IURLParser; +import org.eclipse.sirius.components.core.api.labels.StyledString; +import org.eclipse.sirius.components.emf.services.JSONResourceFactory; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.interpreter.AQLInterpreter; +import org.eclipse.sirius.components.interpreter.Result; +import org.eclipse.sirius.components.representations.Failure; +import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider; +import org.eclipse.sirius.components.representations.IRepresentationDescription; +import org.eclipse.sirius.components.representations.IStatus; +import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.sirius.components.selection.description.SelectionDescription; +import org.eclipse.sirius.components.trees.description.TreeDescription; +import org.eclipse.sirius.components.trees.renderer.TreeRenderer; +import org.eclipse.sirius.components.view.diagram.DialogDescription; +import org.eclipse.sirius.components.view.diagram.SelectionDialogDescription; +import org.eclipse.sirius.components.view.diagram.SelectionDialogTreeDescription; +import org.eclipse.sirius.components.view.emf.api.IDialogDescriptionConverter; +import org.springframework.stereotype.Service; + +/** + * An implementation of the {@link IDialogDescriptionConverter} to convert the {@link SelectionDialogDescription}. + * + * @author fbarbin + */ +@Service +public class SelectionDialogDescriptionConverter implements IDialogDescriptionConverter { + + private static final String TARGET_OBJECT_ID = "targetObjectId"; + + private static final String TREE_DESCRIPTION_ID = "treeDescriptionId"; + + private static final String DIALOG_DESCRIPTION_TREE_REPRESENTATION_NAME = "Selection Dialog Tree Representation"; + + private static final String SELECTION_PREFIX = "selection://"; + + private final IObjectService objectService; + + private final IDiagramIdProvider diagramIdProvider; + + private final IURLParser urlParser; + + public SelectionDialogDescriptionConverter(IObjectService objectService, IDiagramIdProvider diagramIdProvider, IURLParser urlParser) { + this.objectService = Objects.requireNonNull(objectService); + this.diagramIdProvider = Objects.requireNonNull(diagramIdProvider); + this.urlParser = Objects.requireNonNull(urlParser); + } + @Override + public List convert(DialogDescription dialogDescription, AQLInterpreter interpreter) { + List representationDescriptions = new ArrayList<>(); + if (dialogDescription instanceof SelectionDialogDescription selectionDialogDescription) { + SelectionDescription selectionDescription = this.convertSelectionDialog(selectionDialogDescription, interpreter); + representationDescriptions.add(selectionDescription); + representationDescriptions.add(selectionDescription.getTreeDescription()); + } + return representationDescriptions; + } + + @Override + public boolean canConvert(DialogDescription dialogDescription) { + return dialogDescription instanceof SelectionDialogDescription; + } + + private SelectionDescription convertSelectionDialog(org.eclipse.sirius.components.view.diagram.SelectionDialogDescription selectionDescription, AQLInterpreter interpreter) { + String selectionDescriptionId = this.diagramIdProvider.getId(selectionDescription); + TreeDescription treeDescription = this.createTreeDescription(selectionDescription, interpreter); + return SelectionDescription.newSelectionDescription(selectionDescriptionId) + .messageProvider(variableManager -> { + String message = selectionDescription.getSelectionMessage(); + if (message == null) { + message = ""; + } + return message; + }) + .idProvider(variableManager -> SELECTION_PREFIX) + .labelProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getLabel).orElse(null)) + .targetObjectIdProvider(variableManager -> variableManager.get(VariableManager.SELF, Object.class).map(this.objectService::getId).orElse(null)) + .label("Selection Description") + .canCreatePredicate(variableManager -> false) + .treeDescription(treeDescription) + .build(); + } + + /** + * Create the TreeDescription attached to the SelectionDescription. + * + * @param selectionDescription + * the SelectionDialogDescription. + * @param interpreter + * the AQL interpreter. + * @return The {@link TreeDescription} + */ + private TreeDescription createTreeDescription(SelectionDialogDescription selectionDescription, AQLInterpreter interpreter) { + SelectionDialogTreeDescription selectionDialogTreeDescription = selectionDescription.getSelectionDialogTreeDescription(); + final String treeDescriptionId = this.diagramIdProvider.getId(selectionDialogTreeDescription); + + Function> elementsProvider = this.getElementProvider(interpreter, selectionDialogTreeDescription); + + Function deleteHandler = variableManager -> new Failure("Unexecutable delete handler"); + + BiFunction renameHandler = (variableManager, newValue) -> new Failure("Unexecutable rename handler"); + + Function treeItemIdProvider = variableManager -> { + return variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getId) + .orElse(""); + }; + + Function kindProvider = variableManager -> { + return variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getKind) + .orElse(""); + }; + + Function labelProvider = variableManager -> { + return variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getFullLabel) + .map(StyledString::of) + .orElse(StyledString.of("")); + }; + + Function targetObjectIdProvider = variableManager -> { + return this.getTargetObjectId(variableManager).orElse(""); + }; + + Function> imageURLProvider = variableManager -> { + return variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getImagePath) + .orElse(List.of()); + }; + + Function isSelectableProvider = variableManager -> { + String isSelectableExpression = selectionDialogTreeDescription.getIsSelectableExpression(); + String safeExpression = Optional.ofNullable(isSelectableExpression).orElse(""); + if (safeExpression.isBlank()) { + return true; + } + Result result = interpreter.evaluateExpression(variableManager.getVariables(), safeExpression); + return result.asBoolean() + .orElse(false); + }; + + + Function> childrenProvider = variableManager -> this.getChildren(variableManager, interpreter, selectionDialogTreeDescription); + + Function hasChildrenProvider = variableManager -> this.hasChildren(interpreter, selectionDialogTreeDescription, variableManager); + + Predicate canCreatePredicate = variableManager -> { + return variableManager.get(GetOrCreateRandomIdProvider.PREVIOUS_REPRESENTATION_ID, String.class) + .filter(id -> id.startsWith(SELECTION_PREFIX)) + .map(this.urlParser::getParameterValues) + .map(parameters -> parameters.get(TREE_DESCRIPTION_ID).get(0)) + .filter(treeDescriptionIdParameter -> treeDescriptionId.equals(treeDescriptionIdParameter)) + .isPresent(); + }; + String treeId = SELECTION_PREFIX + "?" + TREE_DESCRIPTION_ID + "=" + treeDescriptionId; + return TreeDescription.newTreeDescription(treeDescriptionId) + .label(DIALOG_DESCRIPTION_TREE_REPRESENTATION_NAME) + .idProvider(variableManager -> variableManager.get(GetOrCreateRandomIdProvider.PREVIOUS_REPRESENTATION_ID, String.class).orElse(treeId)) + .canCreatePredicate(canCreatePredicate) + .childrenProvider(childrenProvider) + .deletableProvider(variableManager -> false) + .deleteHandler(deleteHandler) + .renameHandler(renameHandler) + .editableProvider(variableManager -> false) + .elementsProvider(elementsProvider) + .hasChildrenProvider(hasChildrenProvider) + .treeItemIdProvider(treeItemIdProvider) + .kindProvider(kindProvider) + .labelProvider(labelProvider) + .targetObjectIdProvider(targetObjectIdProvider) + .iconURLProvider(imageURLProvider) + .selectableProvider(isSelectableProvider) + .treeItemObjectProvider(this::getTreeItemObject) + .parentObjectProvider(this::getParentObject) + .build(); + } + + private Boolean hasChildren(AQLInterpreter interpreter, SelectionDialogTreeDescription selectionDialogTreeDescription, VariableManager variableManager) { + return !this.computeChildrenFromExpression(variableManager, interpreter, selectionDialogTreeDescription).isEmpty(); + } + + private Object getTreeItemObject(VariableManager variableManager) { + Object result = null; + var optionalEditingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class); + var optionalId = variableManager.get(TreeDescription.ID, String.class); + if (optionalId.isPresent() && optionalEditingContext.isPresent()) { + var optionalObject = this.objectService.getObject(optionalEditingContext.get(), optionalId.get()); + if (optionalObject.isPresent()) { + result = optionalObject.get(); + } else { + var optionalEditingDomain = Optional.of(optionalEditingContext.get()) + .filter(IEMFEditingContext.class::isInstance) + .map(IEMFEditingContext.class::cast) + .map(IEMFEditingContext::getDomain); + + if (optionalEditingDomain.isPresent()) { + var editingDomain = optionalEditingDomain.get(); + ResourceSet resourceSet = editingDomain.getResourceSet(); + URI uri = new JSONResourceFactory().createResourceURI(optionalId.get()); + + result = resourceSet.getResources().stream() + .filter(resource -> resource.getURI().equals(uri)). + findFirst() + .orElse(null); + } + } + } + + return result; + } + + + private Object getParentObject(VariableManager variableManager) { + Object result = null; + Object self = variableManager.getVariables().get(VariableManager.SELF); + if (self instanceof EObject eObject) { + Object semanticContainer = eObject.eContainer(); + if (semanticContainer == null) { + semanticContainer = eObject.eResource(); + } + result = semanticContainer; + } + return result; + } + + private List getChildren(VariableManager variableManager, AQLInterpreter interpreter, SelectionDialogTreeDescription selectionDialogTreeDescription) { + List result = new ArrayList<>(); + + List expandedIds = new ArrayList<>(); + Object objects = variableManager.getVariables().get(TreeRenderer.EXPANDED); + if (objects instanceof List list) { + expandedIds = list.stream() + .filter(String.class::isInstance) + .map(String.class::cast) + .toList(); + } + + String id = this.getTreeItemId(variableManager); + if (expandedIds.contains(id)) { + result.addAll(this.computeChildrenFromExpression(variableManager, interpreter, selectionDialogTreeDescription)); + } + return result; + } + + private List computeChildrenFromExpression(VariableManager variableManager, AQLInterpreter interpreter, SelectionDialogTreeDescription selectionDialogTreeDescription) { + List result = new ArrayList<>(); + String childrenExpression = selectionDialogTreeDescription.getChildrenExpression(); + String safeExpression = Optional.ofNullable(childrenExpression).orElse(""); + if (!safeExpression.isBlank()) { + Result interpreterResult = interpreter.evaluateExpression(variableManager.getVariables(), safeExpression); + result = interpreterResult.asObjects() + .orElse(List.of()) + .stream() + .filter(Objects::nonNull) + .toList(); + } + return result; + } + + private Function> getElementProvider(AQLInterpreter interpreter, SelectionDialogTreeDescription selectionDialogTreeDescription) { + return variableManager -> { + Optional optionalEditingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class); + if (optionalEditingContext.isPresent()) { + //Set the targetObject as the SELF value. + //The targetObjectId is provided by the frontend in the treeId. + this.getTargetObjectId(variableManager) + .flatMap(targetObjectId -> this.objectService.getObject(optionalEditingContext.get(), targetObjectId)) + .ifPresent(targetObject -> variableManager.put(VariableManager.SELF, targetObject)); + + String elementsExpression = selectionDialogTreeDescription.getElementsExpression(); + Result result = interpreter.evaluateExpression(variableManager.getVariables(), elementsExpression); + return result.asObjects() + .orElse(List.of()) + .stream() + .filter(Objects::nonNull) + .toList(); + } + return List.of(); + }; + } + + private Optional getTargetObjectId(VariableManager variableManager) { + return variableManager.get(GetOrCreateRandomIdProvider.PREVIOUS_REPRESENTATION_ID, String.class) + .map(this.urlParser::getParameterValues) + .map(parameters -> parameters.get(TARGET_OBJECT_ID).get(0)); + } + + private String getTreeItemId(VariableManager variableManager) { + Object self = variableManager.getVariables().get(VariableManager.SELF); + String id = null; + if (self != null) { + id = this.objectService.getId(self); + } + return id; + } + +} diff --git a/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicDiagramsTests.java b/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicDiagramsTests.java index 380634761c..5127ec5bcd 100644 --- a/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicDiagramsTests.java +++ b/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicDiagramsTests.java @@ -151,7 +151,7 @@ private Diagram render(DiagramDescription diagramDescription, Object target) { ViewDiagramDescriptionConverter diagramDescriptionConverter = new ViewDiagramDescriptionConverter(new IObjectService.NoOp(), new IEditService.NoOp(), List.of(), new IDiagramIdProvider.NoOp(), new IViewToolImageProvider.NoOp(), new IFeedbackMessageService.NoOp()); - var viewConverter = new ViewConverter(List.of(), List.of(diagramDescriptionConverter), new StaticApplicationContext(), new IObjectService.NoOp(), new IDiagramIdProvider.NoOp()); + var viewConverter = new ViewConverter(List.of(), List.of(diagramDescriptionConverter), new StaticApplicationContext(), List.of()); List conversionResult = viewConverter.convert(List.of(view), List.of(EcorePackage.eINSTANCE)); assertThat(conversionResult).hasSize(1); assertThat(conversionResult.get(0)).isInstanceOf(org.eclipse.sirius.components.diagrams.description.DiagramDescription.class); diff --git a/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicFormsTests.java b/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicFormsTests.java index af9927d7cf..275617ac05 100644 --- a/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicFormsTests.java +++ b/packages/view/backend/sirius-components-view-emf/src/test/java/org/eclipse/sirius/components/view/emf/view/DynamicFormsTests.java @@ -78,7 +78,6 @@ import org.eclipse.sirius.components.view.View; import org.eclipse.sirius.components.view.ViewFactory; import org.eclipse.sirius.components.view.emf.ViewConverter; -import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; import org.eclipse.sirius.components.view.emf.form.IFormIdProvider; import org.eclipse.sirius.components.view.emf.form.ViewFormDescriptionConverter; import org.eclipse.sirius.components.view.form.BarChartDescription; @@ -1365,7 +1364,7 @@ public Optional getObject(IEditingContext editingContext, String objectI }; ViewFormDescriptionConverter formDescriptionConverter = new ViewFormDescriptionConverter(objectService, editService, new IFormIdProvider.NoOp(), List.of(), new IFeedbackMessageService.NoOp()); - var viewConverter = new ViewConverter(List.of(), List.of(formDescriptionConverter), new StaticApplicationContext(), new IObjectService.NoOp(), new IDiagramIdProvider.NoOp()); + var viewConverter = new ViewConverter(List.of(), List.of(formDescriptionConverter), new StaticApplicationContext(), List.of()); List conversionResult = viewConverter.convert(List.of(view), List.of(EcorePackage.eINSTANCE)); assertThat(conversionResult).hasSize(1); assertThat(conversionResult.get(0)).isInstanceOf(org.eclipse.sirius.components.forms.description.FormDescription.class); diff --git a/scripts/check-coverage.jsh b/scripts/check-coverage.jsh index 32b7d772b3..4f9181d6bc 100644 --- a/scripts/check-coverage.jsh +++ b/scripts/check-coverage.jsh @@ -51,15 +51,15 @@ var moduleCoverageData = List.of( new ModuleCoverage("sirius-components-collaborative-forms", 92.0), new ModuleCoverage("sirius-components-forms-graphql", 83.0), new ModuleCoverage("sirius-components-widget-reference", 83.0), - new ModuleCoverage("sirius-components-collaborative-widget-reference", 65.0), - new ModuleCoverage("sirius-components-widget-reference-graphql", 44.0), + new ModuleCoverage("sirius-components-collaborative-widget-reference", 72.0), + new ModuleCoverage("sirius-components-widget-reference-graphql", 52.0), new ModuleCoverage("sirius-components-formdescriptioneditors", 76.0), new ModuleCoverage("sirius-components-collaborative-formdescriptioneditors", 88.0), new ModuleCoverage("sirius-components-formdescriptioneditors-graphql", 55.0), new ModuleCoverage("sirius-components-trees", 84.0), - new ModuleCoverage("sirius-components-collaborative-trees", 89.0), - new ModuleCoverage("sirius-components-trees-graphql", 92.0), - new ModuleCoverage("sirius-components-selection", 80.0), + new ModuleCoverage("sirius-components-collaborative-trees", 91.0), + new ModuleCoverage("sirius-components-trees-graphql", 98.0), + new ModuleCoverage("sirius-components-selection", 92.0), new ModuleCoverage("sirius-components-collaborative-selection", 95.0), new ModuleCoverage("sirius-components-selection-graphql", 100.0), new ModuleCoverage("sirius-components-validation", 86.0), @@ -75,25 +75,25 @@ var moduleCoverageData = List.of( new ModuleCoverage("sirius-components-domain", 72.0), new ModuleCoverage("sirius-components-domain-edit", 64.0), new ModuleCoverage("sirius-components-domain-emf", 95.0), - new ModuleCoverage("sirius-components-view", 63.0), - new ModuleCoverage("sirius-components-view-edit", 41.0), - new ModuleCoverage("sirius-components-view-diagram", 56.0), - new ModuleCoverage("sirius-components-view-diagram-edit", 19.0), + new ModuleCoverage("sirius-components-view", 64.0), + new ModuleCoverage("sirius-components-view-edit", 43.0), + new ModuleCoverage("sirius-components-view-diagram", 57.0), + new ModuleCoverage("sirius-components-view-diagram-edit", 20.0), new ModuleCoverage("sirius-components-view-form", 48.0), new ModuleCoverage("sirius-components-view-form-edit", 0.0), new ModuleCoverage("sirius-components-widget-reference-view", 34.0), new ModuleCoverage("sirius-components-widget-reference-view-edit", 4.0), new ModuleCoverage("sirius-components-view-gantt", 63.0), - new ModuleCoverage("sirius-components-view-gantt-edit", 31.0), + new ModuleCoverage("sirius-components-view-gantt-edit", 34.0), new ModuleCoverage("sirius-components-view-deck", 50.0), - new ModuleCoverage("sirius-components-view-deck-edit", 26.0), - new ModuleCoverage("sirius-components-view-emf", 71.0), + new ModuleCoverage("sirius-components-view-deck-edit", 28.0), + new ModuleCoverage("sirius-components-view-emf", 73.0), new ModuleCoverage("sirius-components-view-builder", 43.0), new ModuleCoverage("sirius-components-task", 37.0), new ModuleCoverage("sirius-components-task-edit", 3.0), new ModuleCoverage("sirius-components-view-diagram-customnodes", 57.0), new ModuleCoverage("sirius-components-view-diagram-customnodes-edit", 62.0), - new ModuleCoverage("sirius-web-domain", 91.0), + new ModuleCoverage("sirius-web-domain", 92.0), new ModuleCoverage("sirius-web-application", 93.0), new ModuleCoverage("sirius-web-infrastructure", 93.0), new ModuleCoverage("sirius-web-starter", 98.0),