diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 3a889b0240..44eba8ed30 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -95,6 +95,11 @@ As a result, the following maven modules have been deleted: `sirius-web-sample-a - https://github.com/eclipse-sirius/sirius-web/issues/3437[#3437] [core] Migrate frontend to MUI 5, if you contributed React componenent that use MUI, you should upgrade them to use MUI 5. - https://github.com/eclipse-sirius/sirius-web/issues/2204[#2204] [core] Added `getStyledLabel` in `IDefaultLabelService` and `IObjectService`, you must implement this method if you have custom implementation of these interface. - https://github.com/eclipse-sirius/sirius-web/issues/3815[#3815] [core] Make child creation descriptions locale independent +- https://github.com/eclipse-sirius/sirius-web/issues/2204[#2204] [core, tree] Added `getStyledLabel` in `IDefaultLabelService` and `IObjectService`, you must implement this method if you have custom implementation of these interface. +- 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. + === Dependency update @@ -160,7 +165,7 @@ A migration participant has been added to automatically keep compatible all diag - https://github.com/eclipse-sirius/sirius-web/issues/3776[#3776] [trees] Remove unwanted dependency from the reference widget in the explorer - https://github.com/eclipse-sirius/sirius-web/issues/3777[#3777] [sirius-web] Add support for any kind of object as semantic element in the tree representation - https://github.com/eclipse-sirius/sirius-web/issues/3392[#3392] [diagram] Prevent edge from passing through another node -- https://github.com/eclipse-sirius/sirius-web/issues/3763[#3763] [diagram] Split the SelectionDialogDescription to prepare the Tree support +- 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 - https://github.com/eclipse-sirius/sirius-web/issues/3793[#3793] [sirius-web] Add mechanism to retrieve the parent object of an element in the tree representation - https://github.com/eclipse-sirius/sirius-web/issues/3880[#3880] [sirius-web] Make some tests methods more generic - https://github.com/eclipse-sirius/sirius-web/issues/3834[#3834] [deck] Add more variables for DeckDescription @@ -173,6 +178,7 @@ A migration participant has been added to automatically keep compatible all diag - 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/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/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/ISelectionInput.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionInput.java deleted file mode 100644 index edb738b09e..0000000000 --- a/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/api/ISelectionInput.java +++ /dev/null @@ -1,24 +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.core.api.IRepresentationInput; - -/** - * Represents an input sent by a client which concerns a selection representation. - * - * @author arichard - */ -public interface ISelectionInput extends IRepresentationInput { - -} 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/dto/SelectionEventInput.java b/packages/selection/backend/sirius-components-collaborative-selection/src/main/java/org/eclipse/sirius/components/collaborative/selection/dto/GetSelectionDescriptionMessageInput.java similarity index 54% 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/GetSelectionDescriptionMessageInput.java index 94e46521c4..63d9487a5a 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/GetSelectionDescriptionMessageInput.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,21 @@ *******************************************************************************/ 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 of the selection event subscription. + * The input for the Selection Description message query. * - * @author arichard + * @author fbarbin */ -public record SelectionEventInput(UUID id, String editingContextId, String selectionId, String targetObjectId) implements IInput { +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/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..d559081e15 --- /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,66 @@ +/******************************************************************************* + * 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.selection.dto.GetSelectionDescriptionMessageInput; +import org.eclipse.sirius.components.collaborative.selection.dto.GetSelectionDescriptionMessagePayload; +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 IObjectService objectService; + + public SelectionDescriptionMessageEventHandler(IObjectService objectService) { + 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)); + } + } +} 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/resources/schema/selection.graphqls b/packages/selection/backend/sirius-components-collaborative-selection/src/main/resources/schema/selection.graphqls index 35ff956306..a1d7472c57 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,6 @@ -extend type Subscription { - selectionEvent(input: SelectionEventInput!): SelectionEventPayload! -} - -input SelectionEventInput { - id: ID! - 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!]! -} - 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/SubscriptionSelectionEventDataFetcher.java deleted file mode 100644 index a6a6537fe5..0000000000 --- a/packages/selection/backend/sirius-components-selection-graphql/src/main/java/org/eclipse/sirius/components/selection/graphql/datafetchers/subscription/SubscriptionSelectionEventDataFetcher.java +++ /dev/null @@ -1,73 +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.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.core.api.IPayload; -import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; -import org.eclipse.sirius.components.graphql.api.IEventProcessorSubscriptionProvider; -import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; -import org.eclipse.sirius.components.graphql.api.LocalContextConstants; -import org.reactivestreams.Publisher; - -import graphql.execution.DataFetcherResult; -import graphql.schema.DataFetchingEnvironment; - -/** - * The data fetcher used to send the refreshed tree to a subscription. - * - * @author arichard - */ -@SubscriptionDataFetcher(type = "Subscription", field = "selectionEvent") -public class SubscriptionSelectionEventDataFetcher implements IDataFetcherWithFieldCoordinates>> { - - private static final String INPUT_ARGUMENT = "input"; - - private final ObjectMapper objectMapper; - - private final IExceptionWrapper exceptionWrapper; - - private final IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider; - - public SubscriptionSelectionEventDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEventProcessorSubscriptionProvider eventProcessorSubscriptionProvider) { - this.objectMapper = Objects.requireNonNull(objectMapper); - this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); - this.eventProcessorSubscriptionProvider = Objects.requireNonNull(eventProcessorSubscriptionProvider); - } - - @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()); - - Map localContext = new HashMap<>(); - localContext.put(LocalContextConstants.EDITING_CONTEXT_ID, input.editingContextId()); - localContext.put(LocalContextConstants.REPRESENTATION_ID, selectionConfiguration.getId()); - - return this.exceptionWrapper.wrapFlux(() -> this.eventProcessorSubscriptionProvider.getSubscription(input.editingContextId(), selectionConfiguration, 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/src/SelectionDialog.tsx b/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx index bf4a16a518..2aca1c4fb0 100644 --- a/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx +++ b/packages/selection/frontend/sirius-components-selection/src/SelectionDialog.tsx @@ -10,66 +10,20 @@ * 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 { TreeItemActionProps, TreeView } from '@eclipse-sirius/sirius-components-trees'; +import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; 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 IconButton from '@mui/material/IconButton'; +import { useEffect, useState } from 'react'; +import { DiagramDialogComponentState } from './SelectionDialog.types'; +import { useSelectionDescription } from './useSelectionDescription'; export const SELECTION_DIALOG_TYPE: string = 'selectionDialogDescription'; @@ -80,59 +34,54 @@ 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 initialState: DiagramDialogComponentState = { + treeDescriptionId: '', + message: '', + selectedObjects: [], + }; + const [state, setState] = useState(initialState); - 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 { loading, selectionDescription } = useSelectionDescription({ + editingContextId, + selectionDescriptionId: dialogDescriptionId, + targetObjectId, }); - useEffect(() => { - if (error) { - const { message } = error; - const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message }; - dispatch(showToastEvent); - } - }, [error, dispatch]); + const setDialogSelection = (selection: Selection) => { + setState((prevState) => ({ ...prevState, selectedObjects: [...selection.entries] })); + }; useEffect(() => { - if (selectionDialog === 'complete') { - onClose(); + if (!loading && selectionDescription) { + setState((prevState) => ({ + ...prevState, + message: selectionDescription.message, + treeDescriptionId: selectionDescription.treeDescription.id, + })); } - }, [selectionDialog, onClose]); - - const handleListItemClick = (selectedObjectId: string) => { - dispatch({ type: 'HANDLE_SELECTION_UPDATED', selectedObjectId } as HandleSelectionUpdatedEvent); - }; + }, [loading, selectionDescription]); + let content: JSX.Element | null = null; + if (state.treeDescriptionId) { + content = ( + } + /> + ); + } return ( - <> + Selection Dialog - {selection?.message} - - {selection?.objects.map((selectionObject) => ( - handleListItemClick(selectionObject.id)} - data-testid={selectionObject.label}> - - {selectionObject.iconURL.length > 0 ? ( - - ) : ( - - )} - - - - ))} - + {state.message} + {content} - dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)} - /> - + + ); +}; + +const SelectionDialogTreeItemAction = ({ onExpandAll, item, isHovered }: TreeItemActionProps) => { + if (!onExpandAll || !item || !item.hasChildren || !isHovered) { + return null; + } + return ( + { + onExpandAll(item); + }}> + + ); }; 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..e599f4d8cb 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,14 @@ export interface SelectionDialogProps { onClose: () => void; onFinish: (selectedObjectId: string) => void; } + +export interface DiagramDialogComponentState { + treeDescriptionId: string; + message: string; + selectedObjects: DiagramDialogComponentSelectedObject[]; +} + +export interface DiagramDialogComponentSelectedObject { + 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/useSelectionDescription.ts b/packages/selection/frontend/sirius-components-selection/src/useSelectionDescription.ts new file mode 100644 index 0000000000..49de2b0a50 --- /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, + SelectionDescription, + 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: SelectionDescription | 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..53423391cd --- /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: SelectionDescription | null; +} + +export interface GetSelectionDescriptionVariables { + editingContextId: string; + representationId: string; + targetObjectId: string; +} + +export interface SelectionDescription { + message: string; + treeDescription: TreeDescription; +} + +export interface TreeDescription { + id: string; +} + +export interface GetSelectionDescriptionData { + viewer: GetSelectionDescriptionViewer; +} + +export interface GetSelectionDescriptionViewer { + editingContext: GetSelectionDescriptionEditingContext; +} + +export interface GetSelectionDescriptionEditingContext { + representation: GetSelectionDescriptionRepresentation; +} + +export interface GetSelectionDescriptionRepresentation { + description: SelectionDescription; +} 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..8cc2c63dbc 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 @@ -13,36 +13,29 @@ package org.eclipse.sirius.web.application.controllers.selection; 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 static org.junit.jupiter.api.Assertions.fail; 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.trees.dto.TreeEventInput; +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.tests.graphql.TreeEventSubscriptionRunner; 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.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -54,7 +47,6 @@ import org.springframework.transaction.annotation.Transactional; import graphql.execution.DataFetcherResult; -import reactor.core.publisher.Flux; import reactor.test.StepVerifier; /** @@ -67,15 +59,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 treeEvent($input: TreeEventInput!) { + treeEvent(input: $input) { __typename - ... on SelectionRefreshedEventPayload { - selection { - objects { - id - label + ... on TreeRefreshedEventPayload { + id + tree { + id + children { iconURL } } @@ -84,6 +95,7 @@ subscription selectionEvent($input: SelectionEventInput!) { } """; + @Autowired private IGraphQLRequestor graphQLRequestor; @@ -91,16 +103,7 @@ subscription selectionEvent($input: SelectionEventInput!) { private IGivenInitialServerState givenInitialServerState; @Autowired - private DetailsEventSubscriptionRunner detailsEventSubscriptionRunner; - - @Autowired - private EditTextfieldMutationRunner editTextfieldMutationRunner; - - @Autowired - private ModelOperationDiagramDescriptionProvider modelOperationDiagramDescriptionProvider; - - @Autowired - private IGivenCreatedDiagramSubscription givenCreatedDiagramSubscription; + private TreeEventSubscriptionRunner treeEventSubscriptionRunner; @BeforeEach public void beforeEach() { @@ -108,147 +111,73 @@ public void beforeEach() { } @Test - @DisplayName("Given a semantic object, when we subscribe to its selection events, then the selection 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")); - - StepVerifier.create(flux) - .consumeNextWith(selectionContentConsumer) - .thenCancel() - .verify(Duration.ofSeconds(10)); + @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(SelectionDescriptionProvider.REPRESENTATION_DESCRIPTION_ID, 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("siriusComponents://selectionDialogTreeDescription"); } @Test - @DisplayName("Given a selection representation, when we edit the details of an object, then the selection representation is updated") + @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 givenSelectionRepresentationWhenWeEditTheDetailsOfAnObjectThenTheSelectionRepresentationIsUpdated() { - this.testSelectionRepresentationUpdate(PapayaIdentifiers.PROJECT_OBJECT.toString()); + public void givenSemanticObjectWhenWeSubscribeToItsSelectionEventsThenTheSelectionIsSent() { + var treeId = "selection://?treeDescriptionId=" + SelectionDescriptionProvider.TREE_DESCRIPTION_ID + "&targetObjectId=" + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new TreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of(), List.of()); + var flux = this.treeEventSubscriptionRunner.run(input); + var hasResourceRootContent = this.getTreeRefreshedEventPayloadMatcher(); + StepVerifier.create(flux) + .expectNextMatches(hasResourceRootContent) + .thenCancel() + .verify(); } - 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 detailsEventInput = new DetailsEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), List.of(PapayaIdentifiers.SIRIUS_WEB_DOMAIN_OBJECT.toString())); - var detailsFlux = this.detailsEventSubscriptionRunner.run(detailsEventInput); - - var formId = new AtomicReference(); - var textareaId = new AtomicReference(); - - 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()); - 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")); - 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")); - - 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); - - String typename = JsonPath.read(result, "$.data.editTextfield.__typename"); - assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + 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"); }; - 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); - - assertThat(textarea).hasValue("sirius-web-domain-EDITED"); - }, () -> fail("Missing form")); - - 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")); - - StepVerifier.create(Flux.merge(selectionFlux, detailsFlux)) - .consumeNextWith(selectionContentConsumer) - .consumeNextWith(initialFormContentConsumer) - .then(editTextfield) - .consumeNextWith(updatedFormContentConsumer) - .consumeNextWith(updatedSelectionContentConsumer) - .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); + 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=" + SelectionDescriptionProvider.TREE_DESCRIPTION_ID + "&targetObjectId=" + PapayaIdentifiers.PROJECT_OBJECT.toString(); + var input = new TreeEventInput(UUID.randomUUID(), PapayaIdentifiers.PAPAYA_PROJECT.toString(), treeId, List.of(), 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.treeEvent.__typename"); + assertThat(typename).isEqualTo(TreeRefreshedEventPayload.class.getSimpleName()); - List> objectIconURLs = JsonPath.read(body, "$.data.selectionEvent.selection.objects[*].iconURL"); + List> objectIconURLs = JsonPath.read(body, "$.data.treeEvent.tree.children[*].iconURL"); assertThat(objectIconURLs) .isNotEmpty() .allSatisfy(iconURLs -> { @@ -260,10 +189,9 @@ public void givenSemanticObjectWhenWeSubscribeToItsSelectionEventsThenTheURLOfIt }, () -> fail("Missing selection")); StepVerifier.create(flux) - .consumeNextWith(selectionContentConsumer) + .consumeNextWith(treeContentConsumer) .thenCancel() .verify(Duration.ofSeconds(10)); } - } 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..faa2f01815 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 @@ -15,16 +15,21 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; +import java.util.function.Predicate; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.sirius.components.core.URLParser; 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.core.api.IObjectService; +import org.eclipse.sirius.components.core.api.labels.StyledString; +import org.eclipse.sirius.components.representations.Failure; 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.trees.description.TreeDescription; import org.eclipse.sirius.web.application.editingcontext.EditingContext; +import org.eclipse.sirius.web.data.PapayaIdentifiers; import org.springframework.stereotype.Service; /** @@ -35,59 +40,121 @@ @Service 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 final IIdentityService identityService; + public static final String TREE_DESCRIPTION_ID = "siriusComponents://selectionDialogTreeDescription"; + + private static final String DIALOG_MESSAGE = "Select the objects to consider"; + + private static final String SELECTION_PREFIX = "selection://"; + + private static final String TREE_ID = "treeId"; + + private static final String TARGET_OBJECT_ID = "targetObjectId"; - private final ILabelService labelService; + private final IObjectService objectService; - public SelectionDescriptionProvider(IIdentityService identityService, ILabelService labelService) { - this.identityService = Objects.requireNonNull(identityService); - this.labelService = Objects.requireNonNull(labelService); + private final URLParser urlParser; + + public SelectionDescriptionProvider(IObjectService objectService, URLParser urlParser) { + this.objectService = Objects.requireNonNull(objectService); + this.urlParser = Objects.requireNonNull(urlParser); } @Override public void preProcess(IEditingContext editingContext) { if (editingContext instanceof EditingContext siriusWebEditingContext) { - siriusWebEditingContext.getRepresentationDescriptions().put(REPRESENTATION_DESCRIPTION_ID, this.getSelectionDescription()); + SelectionDescription selectionDescription = this.getSelectionDescription(); + siriusWebEditingContext.getRepresentationDescriptions().put(REPRESENTATION_DESCRIPTION_ID, selectionDescription); + siriusWebEditingContext.getRepresentationDescriptions().put(TREE_DESCRIPTION_ID, selectionDescription.getTreeDescription()); } } private SelectionDescription getSelectionDescription() { + Function labelProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.labelService::getLabel) + .map(this.objectService::getFullLabel) + .orElse(""); + + Function treeLabelProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getStyledLabel) + .orElse(StyledString.of("")); + + Function kindProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) + .map(this.objectService::getKind) .orElse(""); Function> iconURLProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.labelService::getImagePath) + .map(this.objectService::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> objectsProvider = variableManager -> { + IEditingContext editingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class).get(); + String projectId = PapayaIdentifiers.PROJECT_OBJECT.toString(); + Resource rootResource = this.objectService.getObject(editingContext, projectId) + .map(EObject.class::cast) + .map(EObject::eResource) + .get(); + return List.of(rootResource); + }; - Function targetObjectIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.identityService::getId) - .orElse(null); + Function> childrenProvider = variableManager -> { + List children = List.of(); + Object currentElement = variableManager.get(VariableManager.SELF, Object.class).get(); + if (currentElement instanceof Resource resource) { + children = resource.getContents(); + } else if (currentElement instanceof EObject eObject) { + children = eObject.eContents(); + } + return children; + }; - Function selectionObjectsIdProvider = variableManager -> variableManager.get(VariableManager.SELF, Object.class) - .map(this.identityService::getId) - .orElse(null); + Function targetObjectIdProvider = variableManager -> + variableManager.get(TREE_ID, String.class).map(this.urlParser::getParameterValues) + .map(parameters -> parameters.get( + TARGET_OBJECT_ID).get(0)) + .orElse(""); + + TreeDescription treeDescription = TreeDescription.newTreeDescription(TREE_DESCRIPTION_ID) + .canCreatePredicate(this.canCreateTreeRepresentation()) + .childrenProvider(childrenProvider) + .deletableProvider(variableManager -> false) + .deleteHandler(variableManager -> new Failure("")) + .editableProvider(variableManager -> false) + .elementsProvider(objectsProvider) + .hasChildrenProvider(variableManager -> true) + .iconURLProvider(iconURLProvider) + .idProvider(variableManager -> SELECTION_PREFIX) + .labelProvider(treeLabelProvider) + .kindProvider(kindProvider) + .label(LABEL) + .renameHandler((variableManager, newName) -> new Failure("")) + .selectableProvider(variableManager -> true) + .targetObjectIdProvider(targetObjectIdProvider) + .treeItemIdProvider(targetObjectIdProvider) + .treeItemObjectProvider(variableManager -> null) + .parentObjectProvider(variableManager -> null) + .build(); return SelectionDescription.newSelectionDescription(REPRESENTATION_DESCRIPTION_ID) .label(LABEL) - .idProvider(variableManager -> Selection.PREFIX) + .idProvider(variableManager -> SELECTION_PREFIX) .labelProvider(labelProvider) - .iconURLProvider(iconURLProvider) - .messageProvider(variableManager -> "Select the objects to consider") - .objectsProvider(objectsProvider) + .messageProvider(variableManager -> DIALOG_MESSAGE) .targetObjectIdProvider(targetObjectIdProvider) - .selectionObjectsIdProvider(selectionObjectsIdProvider) - .canCreatePredicate(variableManager -> false) + .canCreatePredicate(this.canCreateTreeRepresentation()) + .treeDescription(treeDescription) .build(); } + + private Predicate canCreateTreeRepresentation() { + return variableManager -> { + return variableManager.get(TREE_ID, String.class) + .filter(id -> id.startsWith(SELECTION_PREFIX) && id.contains(TREE_DESCRIPTION_ID)) + .isPresent(); + }; + } } 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..c23034a652 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) + 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.SelectionDialogDescription.class::isInstance) - .map(org.eclipse.sirius.components.view.diagram.SelectionDialogDescription.class::cast) - .map(selectionDescription -> this.convertSelectionDialog(selectionDescription, interpreter)) - .toList(); + .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) { + Optional optionalConverter = this.dialogDescriptionConverts.stream() + .filter(converter -> converter.canConvert(dialogDescription)) + .findFirst(); + optionalConverter.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/api/SelectionDialogDescriptionConverter.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/api/SelectionDialogDescriptionConverter.java new file mode 100644 index 0000000000..f636b6119f --- /dev/null +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/api/SelectionDialogDescriptionConverter.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * 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.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.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.interpreter.AQLInterpreter; +import org.eclipse.sirius.components.interpreter.Result; +import org.eclipse.sirius.components.representations.Failure; +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.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.diagram.IDiagramIdProvider; +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 static final String TREE_ID = "treeId"; + + 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> childrenProvider = this.getChildrenProvider(interpreter, selectionDialogTreeDescription); + + Function> elementsProvider = this.getElementProvider(interpreter, selectionDialogTreeDescription); + + Function hasChildrenProvider = variableManager -> { + String childrenExpression = selectionDialogTreeDescription.getChildrenExpression(); + String safeExpression = Optional.ofNullable(childrenExpression).orElse(""); + if (safeExpression.isBlank()) { + return false; + } else { + Result result = interpreter.evaluateExpression(variableManager.getVariables(), safeExpression); + return !result.asObjects().orElse(List.of()).isEmpty(); + } + }; + + 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); + }; + + Predicate canCreatePredicate = variableManager -> { + return variableManager.get(TREE_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(); + }; + + return TreeDescription.newTreeDescription(treeDescriptionId) + .label(DIALOG_DESCRIPTION_TREE_REPRESENTATION_NAME) + .idProvider(variableManager -> variableManager.get(TREE_ID, String.class).orElse(null)) + .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(variableManager -> null) + .build(); + } + + private Object getTreeItemObject(VariableManager variableManager) { + var optionalEditingContext = variableManager.get(IEditingContext.EDITING_CONTEXT, IEditingContext.class); + var optionalId = variableManager.get(TreeDescription.ID, String.class); + if (optionalId.isPresent() && optionalEditingContext.isPresent()) { + return this.objectService.getObject(optionalEditingContext.get(), optionalId.get()); + } + return null; + } + + private Function> getChildrenProvider(AQLInterpreter interpreter, SelectionDialogTreeDescription selectionDialogTreeDescription) { + return variableManager -> { + String childrenExpression = selectionDialogTreeDescription.getChildrenExpression(); + String safeExpression = Optional.ofNullable(childrenExpression).orElse(""); + if (safeExpression.isBlank()) { + return List.of(); + } + else { + Result result = interpreter.evaluateExpression(variableManager.getVariables(), safeExpression); + return result.asObjects() + .orElse(List.of()) + .stream() + .filter(Objects::nonNull) + .toList(); + } + }; + } + + 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(TREE_ID, String.class) + .map(this.urlParser::getParameterValues) + .map(parameters -> parameters.get(TARGET_OBJECT_ID).get(0)); + } + +} 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/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);