From de374b8711e2dca1e6ac81b5aeb102d7f4d55592 Mon Sep 17 00:00:00 2001 From: Camille Letavernier Date: Tue, 26 Sep 2023 15:17:04 +0200 Subject: [PATCH] 211: Revise TypeHints and server side feedback for creation actions (#210) - Add a new parameter to EdgeTypeHint, 'dynamic', indicating that new edges need to check with the server before allowing creation - Make source/target element type ids in `EdgeTypeHint` optional If not defined, all potential element types are considered to be valid sources/targets - Add `RequestEdgeCheckAction` and `EdgeCheckResultAction` response to implement the dynamic check - Add a optional `EdegeCreationchecker` component that can be implemented by adopters to provide dynamic typehints - Adapt workflow example to use dynamic edge hints for weighted edges - Move typehints related components into feature subpackage - Move progress actions into progress feature subpackage Co-authored-by: Camille Letavernier --- .../WorkflowDiagramConfiguration.java | 29 +++---- .../workflow/WorkflowDiagramModule.java | 7 ++ .../WorkflowEdgeCreationChecker.java | 46 +++++++++++ .../META-INF/MANIFEST.MF | 2 + .../eclipse/glsp/server/di/DiagramModule.java | 11 ++- .../server/diagram/DiagramConfiguration.java | 4 +- .../progress/DefaultProgressService.java | 2 - .../progress}/EndProgressAction.java | 4 +- .../progress}/StartProgressAction.java | 4 +- .../typehints/CheckEdgeResultAction.java | 79 ++++++++++++++++++ .../typehints/EdgeCreationChecker.java | 47 +++++++++++ .../typehints/RequestCheckEdgeAction.java | 64 +++++++++++++++ .../RequestCheckEdgeTargetActionHandler.java | 81 +++++++++++++++++++ .../typehints}/RequestTypeHintsAction.java | 4 +- .../RequestTypeHintsActionHandler.java | 11 ++- .../typehints}/SetTypeHintsAction.java | 4 +- .../glsp/server/types/EdgeTypeHint.java | 62 +++++++++++++- .../glsp/server/types/ElementTypeHint.java | 14 +++- .../glsp/server/types/ShapeTypeHint.java | 14 +++- 19 files changed, 451 insertions(+), 38 deletions(-) create mode 100644 examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/typehints/WorkflowEdgeCreationChecker.java rename plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/{actions => features/progress}/EndProgressAction.java (94%) rename plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/{actions => features/progress}/StartProgressAction.java (96%) create mode 100644 plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/CheckEdgeResultAction.java create mode 100644 plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/EdgeCreationChecker.java create mode 100644 plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeAction.java create mode 100644 plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeTargetActionHandler.java rename plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/{diagram => features/typehints}/RequestTypeHintsAction.java (90%) rename plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/{diagram => features/typehints}/RequestTypeHintsActionHandler.java (81%) rename plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/{diagram => features/typehints}/SetTypeHintsAction.java (94%) diff --git a/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramConfiguration.java b/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramConfiguration.java index f3c0c5fe..116d6ae4 100644 --- a/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramConfiguration.java +++ b/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2019-2021 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -72,7 +72,7 @@ public List getShapeTypeHints() { nodeHints.add(new ShapeTypeHint(AUTOMATED_TASK, true, true, true, true)); ShapeTypeHint catHint = new ShapeTypeHint(CATEGORY, true, true, true, true); catHint.setContainableElementTypeIds( - Arrays.asList(DECISION_NODE, MERGE_NODE, FORK_NODE, JOIN_NODE, AUTOMATED_TASK, MANUAL_TASK, CATEGORY)); + Arrays.asList(TASK, ACTIVITY_NODE, CATEGORY)); nodeHints.add(catHint); nodeHints.add(createDefaultShapeTypeHint(FORK_NODE)); nodeHints.add(createDefaultShapeTypeHint(JOIN_NODE)); @@ -91,24 +91,21 @@ public ShapeTypeHint createDefaultShapeTypeHint(final String elementId) { @Override public List getEdgeTypeHints() { List edgeHints = new ArrayList<>(); - edgeHints.add(createDefaultEdgeTypeHint(EDGE)); - EdgeTypeHint weightedEdgeHint = super.createDefaultEdgeTypeHint(WEIGHTED_EDGE); - weightedEdgeHint.setSourceElementTypeIds(Arrays.asList(DECISION_NODE)); - weightedEdgeHint.setTargetElementTypeIds(Arrays.asList(MANUAL_TASK, AUTOMATED_TASK, FORK_NODE, JOIN_NODE)); + + EdgeTypeHint hint = super.createDefaultEdgeTypeHint(EDGE); + hint.addSourceElementTypeId(TASK, ACTIVITY_NODE, CATEGORY); + hint.addTargetElementTypeId(TASK, ACTIVITY_NODE, CATEGORY); + edgeHints.add(hint); + + EdgeTypeHint weightedEdgeHint = createDefaultEdgeTypeHint(WEIGHTED_EDGE); + weightedEdgeHint.addSourceElementTypeId(ACTIVITY_NODE); + weightedEdgeHint.addTargetElementTypeId(TASK, ACTIVITY_NODE); + weightedEdgeHint.setDynamic(true); edgeHints.add(weightedEdgeHint); return edgeHints; } - @Override - public EdgeTypeHint createDefaultEdgeTypeHint(final String elementId) { - EdgeTypeHint hint = super.createDefaultEdgeTypeHint(elementId); - hint.setSourceElementTypeIds( - Arrays.asList(MANUAL_TASK, AUTOMATED_TASK, DECISION_NODE, MERGE_NODE, FORK_NODE, JOIN_NODE, CATEGORY)); - hint.setTargetElementTypeIds( - Arrays.asList(MANUAL_TASK, AUTOMATED_TASK, DECISION_NODE, MERGE_NODE, FORK_NODE, JOIN_NODE, CATEGORY)); - return hint; - } - + @Override public ServerLayoutKind getLayoutKind() { return ServerLayoutKind.MANUAL; } diff --git a/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramModule.java b/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramModule.java index 3d981894..f9f8bc5e 100644 --- a/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramModule.java +++ b/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/WorkflowDiagramModule.java @@ -38,6 +38,7 @@ import org.eclipse.glsp.example.workflow.taskedit.EditTaskOperationHandler; import org.eclipse.glsp.example.workflow.taskedit.TaskEditContextActionProvider; import org.eclipse.glsp.example.workflow.taskedit.TaskEditValidator; +import org.eclipse.glsp.example.workflow.typehints.WorkflowEdgeCreationChecker; import org.eclipse.glsp.graph.GraphExtension; import org.eclipse.glsp.server.actions.ActionHandler; import org.eclipse.glsp.server.di.MultiBinding; @@ -54,6 +55,7 @@ import org.eclipse.glsp.server.features.popup.PopupModelFactory; import org.eclipse.glsp.server.features.sourcemodelwatcher.FileWatcher; import org.eclipse.glsp.server.features.sourcemodelwatcher.SourceModelWatcher; +import org.eclipse.glsp.server.features.typehints.EdgeCreationChecker; import org.eclipse.glsp.server.features.validation.ModelValidator; import org.eclipse.glsp.server.gmodel.GModelDiagramModule; import org.eclipse.glsp.server.gmodel.GModelStorage; @@ -159,6 +161,11 @@ protected Class bindNavigationTargetResolver return WorkflowNavigationTargetResolver.class; } + @Override + protected Class bindEdgeCreationChecker() { + return WorkflowEdgeCreationChecker.class; + } + @Override public String getDiagramType() { return "workflow-diagram"; } diff --git a/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/typehints/WorkflowEdgeCreationChecker.java b/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/typehints/WorkflowEdgeCreationChecker.java new file mode 100644 index 00000000..f74c2dc9 --- /dev/null +++ b/examples/org.eclipse.glsp.example.workflow/src/org/eclipse/glsp/example/workflow/typehints/WorkflowEdgeCreationChecker.java @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.example.workflow.typehints; + +import static org.eclipse.glsp.example.workflow.utils.ModelTypes.DECISION_NODE; +import static org.eclipse.glsp.example.workflow.utils.ModelTypes.FORK_NODE; +import static org.eclipse.glsp.example.workflow.utils.ModelTypes.JOIN_NODE; +import static org.eclipse.glsp.example.workflow.utils.ModelTypes.WEIGHTED_EDGE; + +import org.eclipse.glsp.example.workflow.wfgraph.TaskNode; +import org.eclipse.glsp.graph.GModelElement; +import org.eclipse.glsp.server.features.typehints.EdgeCreationChecker; + +public class WorkflowEdgeCreationChecker implements EdgeCreationChecker { + + @Override + public boolean isValidSource(final String edgeType, final GModelElement sourceElement) { + return edgeType.equals(WEIGHTED_EDGE) && sourceElement.getType().equals(DECISION_NODE); + } + + @Override + public boolean isValidTarget(final String edgeType, final GModelElement sourceElement, + final GModelElement targetElement) { + + if (!edgeType.equals(WEIGHTED_EDGE)) { + return false; + } + String targetType = targetElement.getType(); + return targetElement instanceof TaskNode || targetType.equals(FORK_NODE) + || targetType.equals(JOIN_NODE); + + } +} diff --git a/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF b/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF index a869994d..b5d0b306 100644 --- a/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF @@ -26,8 +26,10 @@ Export-Package: org.eclipse.glsp.server.actions, org.eclipse.glsp.server.features.directediting, org.eclipse.glsp.server.features.navigation, org.eclipse.glsp.server.features.popup, + org.eclipse.glsp.server.features.progress, org.eclipse.glsp.server.features.sourcemodelwatcher, org.eclipse.glsp.server.features.toolpalette, + org.eclipse.glsp.server.features.typehints, org.eclipse.glsp.server.features.undoredo, org.eclipse.glsp.server.features.validation, org.eclipse.glsp.server.gmodel, diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java index e790aa5f..f4dc135e 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java @@ -27,7 +27,6 @@ import org.eclipse.glsp.server.di.scope.DiagramGlobalScope; import org.eclipse.glsp.server.di.scope.DiagramGlobalSingleton; import org.eclipse.glsp.server.diagram.DiagramConfiguration; -import org.eclipse.glsp.server.diagram.RequestTypeHintsActionHandler; import org.eclipse.glsp.server.diagram.ServerConfigurationContribution; import org.eclipse.glsp.server.features.commandpalette.CommandPaletteActionProvider; import org.eclipse.glsp.server.features.contextactions.ContextActionsProvider; @@ -53,6 +52,9 @@ import org.eclipse.glsp.server.features.progress.ProgressService; import org.eclipse.glsp.server.features.sourcemodelwatcher.SourceModelWatcher; import org.eclipse.glsp.server.features.toolpalette.ToolPaletteItemProvider; +import org.eclipse.glsp.server.features.typehints.EdgeCreationChecker; +import org.eclipse.glsp.server.features.typehints.RequestCheckEdgeTargetActionHandler; +import org.eclipse.glsp.server.features.typehints.RequestTypeHintsActionHandler; import org.eclipse.glsp.server.features.undoredo.UndoRedoActionHandler; import org.eclipse.glsp.server.features.validation.ModelValidator; import org.eclipse.glsp.server.features.validation.RequestMarkersHandler; @@ -127,6 +129,7 @@ *
  • {@link PopupModelFactory} as {@link Optional} *
  • {@link LayoutEngine} as {@link Optional} *
  • {@link GraphExtension} as {@link Optional} + *
  • {@link EdgeCreationChecker} as {@link Optional} * * * @@ -185,6 +188,7 @@ protected void configureBase() { bindOptionally(PopupModelFactory.class, bindPopupModelFactory()); bindOptionally(LayoutEngine.class, bindLayoutEngine()); bindOptionally(GraphExtension.class, bindGraphExtension()); + bindOptionally(EdgeCreationChecker.class, bindEdgeCreationChecker()); } protected void bindDiagramType() { @@ -271,6 +275,7 @@ protected void configureActionHandlers(final MultiBinding binding binding.add(ComputedBoundsActionHandler.class); binding.add(SaveModelActionHandler.class); binding.add(UndoRedoActionHandler.class); + binding.add(RequestCheckEdgeTargetActionHandler.class); } protected Class bindActionHandlerRegistry() { @@ -319,5 +324,9 @@ protected Class bindGraphExtension() { return null; } + protected Class bindEdgeCreationChecker() { + return null; + } + public abstract String getDiagramType(); } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/DiagramConfiguration.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/DiagramConfiguration.java index 0fffbc20..e2a1d223 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/DiagramConfiguration.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/DiagramConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2019-2021 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -71,7 +71,7 @@ public interface DiagramConfiguration { Optional getGraphExtension(); default EdgeTypeHint createDefaultEdgeTypeHint(final String elementId) { - return new EdgeTypeHint(elementId, true, true, true, null, null); + return new EdgeTypeHint(elementId, true, true, true, false, null, null); } default ShapeTypeHint createDefaultShapeTypeHint(final String elementId) { diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/DefaultProgressService.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/DefaultProgressService.java index e304ad32..9fc639c4 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/DefaultProgressService.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/DefaultProgressService.java @@ -18,8 +18,6 @@ import java.util.UUID; import org.eclipse.glsp.server.actions.ActionDispatcher; -import org.eclipse.glsp.server.actions.EndProgressAction; -import org.eclipse.glsp.server.actions.StartProgressAction; import org.eclipse.glsp.server.actions.UpdateProgressAction; import com.google.inject.Inject; diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/EndProgressAction.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/EndProgressAction.java similarity index 94% rename from plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/EndProgressAction.java rename to plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/EndProgressAction.java index 70c52a68..d6dc5cf8 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/EndProgressAction.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/EndProgressAction.java @@ -13,10 +13,12 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -package org.eclipse.glsp.server.actions; +package org.eclipse.glsp.server.features.progress; import java.util.Optional; +import org.eclipse.glsp.server.actions.Action; + /** * Sent by the server to the client to end the reporting of a progress. */ diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/StartProgressAction.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/StartProgressAction.java similarity index 96% rename from plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/StartProgressAction.java rename to plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/StartProgressAction.java index 47efc0a6..69c1f5b3 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/StartProgressAction.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/progress/StartProgressAction.java @@ -13,10 +13,12 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -package org.eclipse.glsp.server.actions; +package org.eclipse.glsp.server.features.progress; import java.util.Optional; +import org.eclipse.glsp.server.actions.Action; + /** * Sent by the server to the client to request presenting the progress of a long running process in the UI. */ diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/CheckEdgeResultAction.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/CheckEdgeResultAction.java new file mode 100644 index 00000000..822f6b9e --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/CheckEdgeResultAction.java @@ -0,0 +1,79 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.features.typehints; + +import java.util.Optional; + +import org.eclipse.glsp.server.actions.ResponseAction; + +/** + * Response Action for a {@link RequestCheckEdgeAction}. It provides + * a boolean indicating whether the requested element is a valid target + * for the edge being created and the context edge context information (type, source, target). + */ +public class CheckEdgeResultAction extends ResponseAction { + + public static final String KIND = "checkEdgeResult"; + + /** + * true if the selected element is a valid target for this edge, + * false otherwise. + */ + private boolean isValid; + + /** + * The element type of the Edge that has been checked. + */ + private String edgeType; + + /** + * The ID of the source element of the edge that has been checked. + */ + private String sourceElementId; + /** + * The ID of the target element of the edge that has been checked. + */ + private String targetElementId; + + public CheckEdgeResultAction() { + super(KIND); + } + + public CheckEdgeResultAction(final boolean isValid, final RequestCheckEdgeAction requestAction) { + super(KIND); + this.setValid(isValid); + this.setEdgeType(requestAction.getEdgeType()); + this.setSourceElementId(requestAction.getSourceElementId()); + this.setTargetElementId(requestAction.getTargetElementId().orElse(null)); + } + + public String getEdgeType() { return edgeType; } + + public void setEdgeType(final String edgeType) { this.edgeType = edgeType; } + + public String getSourceElementId() { return sourceElementId; } + + public void setSourceElementId(final String sourceElementId) { this.sourceElementId = sourceElementId; } + + public Optional getTargetElementId() { return Optional.ofNullable(this.targetElementId); } + + public void setTargetElementId(final String targetElementId) { this.targetElementId = targetElementId; } + + public boolean isValid() { return isValid; } + + public void setValid(final boolean isValid) { this.isValid = isValid; } + +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/EdgeCreationChecker.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/EdgeCreationChecker.java new file mode 100644 index 00000000..dfc5cecc --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/EdgeCreationChecker.java @@ -0,0 +1,47 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.features.typehints; + +import org.eclipse.glsp.graph.GModelElement; +import org.eclipse.glsp.server.types.EdgeTypeHint; + +/** + * Optional service used to check the validity of an edge being created. Used in combination with `dynamic` + * {@link EdgeTypeHint}s. + * A dynamic edge type hint is used for cases where a plain list of allowed source and target element ids is not enough + * to determine + * wether an edge beeing created is valid. In this cases the client will query the server to determine wether the edge + * is valid. + * The `EdegeCreationChecker` then checks the given edge information and returns wether the edge beeing created is + * valid. + */ +public interface EdgeCreationChecker { + /** + * Checks wether the given source element for an edge beeing created is valid i.e. if the + * given source is and allowed source element for the given edge type. + * + * @return `true` if the edge source is valid, `false` otherwise + */ + boolean isValidSource(String edgeType, GModelElement sourceElement); + + /** + * Checks wether the given information for an edge beeing created is valid i.e. if the + * given target is an allowed target for the given source and edge type. + * + * @return `true` if the edge target is valid, `false` otherwise + */ + boolean isValidTarget(String edgeType, GModelElement sourceElement, GModelElement targetElement); +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeAction.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeAction.java new file mode 100644 index 00000000..2b8d9d71 --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeAction.java @@ -0,0 +1,64 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.features.typehints; + +import java.util.Optional; + +import org.eclipse.glsp.server.actions.RequestAction; +import org.eclipse.glsp.server.types.EdgeTypeHint; + +/** + * A {@link RequestAction} used to check the validity of an edge being created. This is used + * to update creation feedback on the client side, for edges configured with a dynamic {@link EdgeTypeHint}. + * + * @see EdgeTypeHint#isDynamic() + * @see CheckEdgeResultAction + */ +public class RequestCheckEdgeAction extends RequestAction { + + public static final String KIND = "requestCheckEdge"; + + /** + * The element type of the edge whose target should be checked. + */ + private String edgeType; + + /** + * The ID of the source element of the edge whose target should be checked. + */ + private String sourceElementId; + /** + * The (optional) ID of the target element of the edge whose target should be checked. + */ + private String targetElementId; + + public RequestCheckEdgeAction() { + super(KIND); + } + + public String getEdgeType() { return edgeType; } + + public void setEdgeType(final String edgeType) { this.edgeType = edgeType; } + + public String getSourceElementId() { return sourceElementId; } + + public void setSourceElementId(final String sourceElementId) { this.sourceElementId = sourceElementId; } + + public Optional getTargetElementId() { return Optional.ofNullable(targetElementId); } + + public void setTargetElementId(final String targetElementId) { this.targetElementId = targetElementId; } + +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeTargetActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeTargetActionHandler.java new file mode 100644 index 00000000..c4cb257f --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestCheckEdgeTargetActionHandler.java @@ -0,0 +1,81 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +package org.eclipse.glsp.server.features.typehints; + +import static org.eclipse.glsp.server.types.GLSPServerException.getOrThrow; + +import java.util.List; +import java.util.Optional; + +import org.eclipse.glsp.graph.GModelElement; +import org.eclipse.glsp.server.actions.AbstractActionHandler; +import org.eclipse.glsp.server.actions.Action; +import org.eclipse.glsp.server.diagram.DiagramConfiguration; +import org.eclipse.glsp.server.model.GModelState; +import org.eclipse.glsp.server.types.GLSPServerException; + +import com.google.inject.Inject; + +/** + * Default handler implementation for {@link RequestCheckEdgeAction}. + * Delegates the edge check to an {@link EdgeCreationChecker} if the given edge information is applicable + * (i.e. a checker is bound,the given edge type has a dynamic type hint and source/target elemnent are present in the + * diagram). + * Returns a valid {@link CheckEdgeResultAction} if no edge creation checker is bound or the type hint associated with + * the given edge information is not dynamic. + */ +public class RequestCheckEdgeTargetActionHandler extends AbstractActionHandler { + + @Inject + protected Optional edgeTargetChecker; + + @Inject + protected DiagramConfiguration diagramConfiguration; + + @Inject + protected GModelState modelState; + + @Override + protected List executeAction(final RequestCheckEdgeAction action) { + boolean hasDynamicHint = diagramConfiguration.getEdgeTypeHints().stream() + .filter(hint -> hint.getElementTypeId().equals(action.getEdgeType()) && hint.isDynamic()).findAny() + .isPresent(); + + if (!edgeTargetChecker.isPresent() || !hasDynamicHint) { + return listOf(new CheckEdgeResultAction(true, action)); + } + return listOf(new CheckEdgeResultAction(validate(action), action)); + } + + protected boolean validate(final RequestCheckEdgeAction action) { + GModelElement sourceElement = getOrThrow(modelState.getIndex().get(action.getSourceElementId()), + "Invalid `RequestCheckEdgeTargetAction`!. Could not find a source elemment with id: " + + action.getSourceElementId()); + Optional targetElement = action.getTargetElementId() + .flatMap(targetId -> modelState.getIndex().get(targetId)); + + if (action.getTargetElementId().isPresent() && targetElement.isEmpty()) { + throw new GLSPServerException( + "Invalid `RequestCheckEdgeTargetAction`! Could not find a target element with id: " + + action.getTargetElementId().get()); + } + return targetElement.isPresent() + ? edgeTargetChecker.get().isValidTarget(action.getEdgeType(), sourceElement, targetElement.get()) + : edgeTargetChecker.get().isValidSource(action.getEdgeType(), sourceElement); + + } + +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/RequestTypeHintsAction.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestTypeHintsAction.java similarity index 90% rename from plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/RequestTypeHintsAction.java rename to plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestTypeHintsAction.java index b6f56d5f..aa2ed050 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/RequestTypeHintsAction.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestTypeHintsAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019-2021 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,7 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ******************************************************************************/ -package org.eclipse.glsp.server.diagram; +package org.eclipse.glsp.server.features.typehints; import org.eclipse.glsp.server.actions.RequestAction; diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/RequestTypeHintsActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestTypeHintsActionHandler.java similarity index 81% rename from plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/RequestTypeHintsActionHandler.java rename to plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestTypeHintsActionHandler.java index 263ff53d..1a8c939c 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/RequestTypeHintsActionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/RequestTypeHintsActionHandler.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2019-2021 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,15 +13,20 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -package org.eclipse.glsp.server.diagram; +package org.eclipse.glsp.server.features.typehints; import java.util.List; -import org.eclipse.glsp.server.actions.Action; import org.eclipse.glsp.server.actions.AbstractActionHandler; +import org.eclipse.glsp.server.actions.Action; +import org.eclipse.glsp.server.diagram.DiagramConfiguration; import com.google.inject.Inject; +/** + * Default handler for {@link RequestTypeHintsAction}s. + * Queries the type hints provided by the {@link DiagramConfiguration} + */ public class RequestTypeHintsActionHandler extends AbstractActionHandler { @Inject protected DiagramConfiguration diagramConfiguration; diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/SetTypeHintsAction.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/SetTypeHintsAction.java similarity index 94% rename from plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/SetTypeHintsAction.java rename to plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/SetTypeHintsAction.java index edca9399..74b2df26 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/diagram/SetTypeHintsAction.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/typehints/SetTypeHintsAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019-2021 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,7 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ******************************************************************************/ -package org.eclipse.glsp.server.diagram; +package org.eclipse.glsp.server.features.typehints; import java.util.ArrayList; import java.util.List; diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/EdgeTypeHint.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/EdgeTypeHint.java index 5d9b6a34..0f8de24f 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/EdgeTypeHint.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/EdgeTypeHint.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2019-2022 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,22 +15,53 @@ ********************************************************************************/ package org.eclipse.glsp.server.types; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.eclipse.glsp.server.features.typehints.RequestCheckEdgeAction; /** * {@link ElementTypeHint Element type hints} for edges. */ public class EdgeTypeHint extends ElementTypeHint { + /** + * Specifies whether the routing points of the edge can be changed + * i.e. edited by the user. + */ private boolean routable; + /** + * Allowed source element types for this edge type. + * If not defined any element can be used as source element for this edge. + */ private List sourceElementTypeIds; + /** + * Allowed target element types for this edge type + * If not defined any element can be used as target element for this edge. + */ private List targetElementTypeIds; + /** + * Indicates whether this type hint is dynamic or not. Dynamic edge type hints + * require an additional runtime check before creating an edge, when checking + * source and target element types is not sufficient. + * + * @see {@link RequestCheckEdgeAction} + */ + private boolean dynamic; public EdgeTypeHint() {} public EdgeTypeHint(final String elementTypeId, final boolean repositionable, final boolean deletable, final boolean routable, final List sourceElementTypeIds, final List targetElementTypeIds) { + this(elementTypeId, repositionable, deletable, routable, false, sourceElementTypeIds, targetElementTypeIds); + } + + public EdgeTypeHint(final String elementTypeId, final boolean repositionable, final boolean deletable, + final boolean routable, final boolean dynamic, + final List sourceElementTypeIds, final List targetElementTypeIds) { super(elementTypeId, repositionable, deletable); this.routable = routable; this.sourceElementTypeIds = sourceElementTypeIds; @@ -41,16 +72,41 @@ public EdgeTypeHint(final String elementTypeId, final boolean repositionable, fi public void setRoutable(final boolean routable) { this.routable = routable; } - public List getSourceElementTypeIds() { return sourceElementTypeIds; } + public Optional> getSourceElementTypeIds() { return Optional.ofNullable(sourceElementTypeIds); } public void setSourceElementTypeIds(final List sourceElementTypeIds) { this.sourceElementTypeIds = sourceElementTypeIds; } - public List getTargetElementTypeIds() { return targetElementTypeIds; } + public void addSourceElementTypeId(final String... elementTypeIds) { + if (this.sourceElementTypeIds == null) { + this.sourceElementTypeIds = new ArrayList<>(); + } + Stream.of(elementTypeIds).forEach(id -> this.sourceElementTypeIds.add(id)); + } + + public Optional> getTargetElementTypeIds() { return Optional.ofNullable(targetElementTypeIds); } public void setTargetElementTypeIds(final List targetElementTypeIds) { this.targetElementTypeIds = targetElementTypeIds; } + public void addTargetElementTypeId(final String... elementTypeIds) { + if (this.targetElementTypeIds == null) { + this.targetElementTypeIds = new ArrayList<>(); + } + Stream.of(elementTypeIds).forEach(id -> this.targetElementTypeIds.add(id)); + } + + /** + * Indicates whether this type hint is dynamic or not. Dynamic edge type hints + * require an additional runtime check before creating an edge, when checking + * source and target element types is not sufficient. + * + * @see RequestCheckEdgeAction + */ + public boolean isDynamic() { return dynamic; } + + public void setDynamic(final boolean isDynamic) { this.dynamic = isDynamic; } + } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ElementTypeHint.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ElementTypeHint.java index 0cc67370..d6558539 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ElementTypeHint.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ElementTypeHint.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2019-2022 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,7 +15,7 @@ ********************************************************************************/ package org.eclipse.glsp.server.types; -import org.eclipse.glsp.server.diagram.RequestTypeHintsAction; +import org.eclipse.glsp.server.features.typehints.RequestTypeHintsAction; /** * Type hints are sent from the server to the client to provide information on which edit operations are possible for @@ -27,9 +27,17 @@ * @see RequestTypeHintsAction */ public abstract class ElementTypeHint { - + /** + * The id of the element. + */ private String elementTypeId; + /** + * Specifies whether the element can be relocated. + */ private boolean repositionable; + /** + * Specifies whether the element can be deleted. + */ private boolean deletable; public ElementTypeHint() {} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ShapeTypeHint.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ShapeTypeHint.java index e5e73e60..f3434447 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ShapeTypeHint.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/types/ShapeTypeHint.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019-2022 EclipseSource and others. + * Copyright (c) 2019-2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -15,15 +15,24 @@ ******************************************************************************/ package org.eclipse.glsp.server.types; +import java.util.ArrayList; import java.util.List; /** * {@link ElementTypeHint Element type hints} for shapes (nodes). */ public class ShapeTypeHint extends ElementTypeHint { - + /** + * Specifies whether the element can be resized. + */ private boolean resizable; + /** + * Specifies whether the element can be moved to another parent. + */ private boolean reparentable; + /** + * The types of elements that can be contained by this element (if any). + */ private List containableElementTypeIds; public ShapeTypeHint() {} @@ -44,6 +53,7 @@ public ShapeTypeHint(final String elementTypeId, final boolean repositionable, f super(elementTypeId, repositionable, deletable); this.reparentable = reparentable; this.resizable = resizable; + this.containableElementTypeIds = new ArrayList<>(); } public boolean isResizable() { return resizable; }