diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c4d8a76b9..0853c9bf5 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -22,6 +22,8 @@ - https://github.com/eclipse-syson/syson/issues/298[#298] [syson] Add root Namespace to SysON models and libraries - https://github.com/eclipse-syson/syson/issues/324[#324] [diagrams] Improve support for whitespaces, quotes, and special characters in direct edit - https://github.com/eclipse-syson/syson/issues/307[#307] [diagrams] Fix parallel states tooling conditions +- https://github.com/eclipse-syson/syson/issues/269[#269] [diagrams] Handle action start and done nodes in Action Flow View diagram + === New features diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ToolService.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ToolService.java index 90abc9d12..dfc772647 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ToolService.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/ToolService.java @@ -204,22 +204,22 @@ protected void createView(Element element, IEditingContext editingContext, IDiag protected void createView(Element element, IEditingContext editingContext, IDiagramContext diagramContext, Object selectedNode, Map convertedNodes, NodeContainmentKind nodeKind) { - var parentElementId = this.getParentElementId(element, diagramContext, selectedNode, convertedNodes); - var descriptionId = this.getDescriptionId(element, editingContext, diagramContext, selectedNode, convertedNodes); - - if (descriptionId.isPresent()) { - var request = ViewCreationRequest.newViewCreationRequest() - .containmentKind(nodeKind) - .descriptionId(descriptionId.get()) - .parentElementId(parentElementId) - .targetObjectId(this.objectService.getId(element)) - .build(); - diagramContext.getViewCreationRequests().add(request); - } + var parentElementId = this.getParentElementId(selectedNode, diagramContext); + this.getDescriptionId(element, editingContext, diagramContext, selectedNode, convertedNodes) + .ifPresent(descriptionId -> this.createView(element, parentElementId, descriptionId, diagramContext, nodeKind)); } - protected String getParentElementId(Element element, IDiagramContext diagramContext, Object selectedNode, - Map convertedNodes) { + protected void createView(Element element, String parentElementId, String descriptionId, IDiagramContext diagramContext, NodeContainmentKind nodeKind) { + var request = ViewCreationRequest.newViewCreationRequest() + .containmentKind(nodeKind) + .descriptionId(descriptionId) + .parentElementId(parentElementId) + .targetObjectId(this.objectService.getId(element)) + .build(); + diagramContext.getViewCreationRequests().add(request); + } + + protected String getParentElementId(Object selectedNode, IDiagramContext diagramContext) { if (selectedNode instanceof Node node) { return node.getId(); } @@ -260,7 +260,7 @@ protected void moveElement(Element droppedElement, Node droppedNode, Element tar } else { return; } - this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes, NodeContainmentKind.CHILD_NODE); + this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes); diagramContext.getViewDeletionRequests().add(ViewDeletionRequest.newViewDeletionRequest().elementId(droppedNode.getId()).build()); } diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java index 6e847c2b1..44cc6e213 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/UtilService.java @@ -28,17 +28,21 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.syson.sysml.AcceptActionUsage; +import org.eclipse.syson.sysml.ActionUsage; import org.eclipse.syson.sysml.Definition; import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Membership; import org.eclipse.syson.sysml.Namespace; import org.eclipse.syson.sysml.Package; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.PortUsage; +import org.eclipse.syson.sysml.Relationship; import org.eclipse.syson.sysml.TransitionFeatureKind; import org.eclipse.syson.sysml.TransitionFeatureMembership; import org.eclipse.syson.sysml.TransitionUsage; import org.eclipse.syson.sysml.Type; import org.eclipse.syson.sysml.Usage; +import org.eclipse.syson.sysml.helper.NameHelper; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.SysONEContentAdapter; @@ -142,6 +146,32 @@ public List getAllReachable(EObject eObject, EClass eClass) { return allReachable; } + /** + * Find an {@link Element} that match the given name in the ResourceSet of the given element. + * + * @param object + * the object for which to find a corresponding type. + * @param elementName + * the element name to match. + * @return the found element or null. + */ + public T findByName(EObject object, String elementName) { + T result = null; + Namespace namespace = null; + if (object instanceof Element element) { + namespace = element.getOwningNamespace(); + } else if (object instanceof Relationship relationship && relationship.getOwner() != null) { + namespace = relationship.getOwner().getOwningNamespace(); + } + if (namespace != null) { + var membership = namespace.resolve(elementName); + if (membership != null) { + result = (T) membership.getMemberElement(); + } + } + return result; + } + /** * Find an {@link Element} that match the given name and type in the ResourceSet of the given element. * @@ -320,12 +350,8 @@ private boolean nameMatches(Element element, String name) { * @return true if the given element name is a qualified name, false otherwise. */ public boolean isQualifiedName(String elementName) { - boolean isQualifiedName = false; - if (elementName != null) { - String[] splitElementName = elementName.split("::"); - isQualifiedName = splitElementName.length > 1; - } - return isQualifiedName; + List segments = NameHelper.parseQualifiedName(elementName); + return segments.size() > 1; } /** @@ -344,4 +370,34 @@ public void removeTransitionFeaturesOfSpecificKind(TransitionUsage transition, T .toList(); EcoreUtil.removeAll(elementsToDelete); } + + /** + * Retrieve the start action defined inside the standard library Actions. + * + * @param eObject + * an object to access to the library resources. + * + * @return the standard start ActionUsage defined in the Actions library. + */ + public ActionUsage retrieveStandardStartAction(Element eObject) { + return this.findByName(eObject, "Actions::Action::start"); + } + + /** + * Check if the given element is actually the standard start action defined by Actions::Action::start. + * + * @param element + * an element that could be the standard start action. + * @return true if the given element is the standard start action and false otherwise. + */ + public boolean isStandardStartAction(Element element) { + var elt = element; + if (element instanceof Membership membership) { + elt = membership.getMemberElement(); + } + if (elt instanceof ActionUsage au) { + return "9a0d2905-0f9c-5bb4-af74-9780d6db1817".equals(au.getElementId()); + } + return false; + } } diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/util/AQLUtils.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/util/AQLUtils.java index 9d5e02374..a5343760e 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/util/AQLUtils.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/util/AQLUtils.java @@ -21,6 +21,17 @@ */ public class AQLUtils { + /** + * Return an AQL string from the given string. + * + * @param string + * the string to transform to an AQL string + * @return the AQL string built upon the given string. + */ + public static String aqlString(String string) { + return '\'' + string + '\''; + } + /** * Returns the AQL expression for calling a service without any parameter using self as the * instance.
diff --git a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/ActionFlowViewDiagramDescriptionProvider.java b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/ActionFlowViewDiagramDescriptionProvider.java index bd471d4a8..7ef300b64 100644 --- a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/ActionFlowViewDiagramDescriptionProvider.java +++ b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/ActionFlowViewDiagramDescriptionProvider.java @@ -46,6 +46,7 @@ import org.eclipse.syson.diagram.common.view.diagram.AbstractDiagramDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.CompartmentItemNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.tools.ToolSectionDescription; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.IDescriptionNameGenerator; @@ -116,6 +117,7 @@ public RepresentationDescription create(IColorProvider colorProvider) { diagramElementDescriptionProviders.add(new FakeNodeDescriptionProvider(colorProvider)); diagramElementDescriptionProviders.add(new ActionFlowViewEmptyDiagramNodeDescriptionProvider(colorProvider)); diagramElementDescriptionProviders.add(new PackageNodeDescriptionProvider(colorProvider)); + diagramElementDescriptionProviders.add(new StartActionNodeDescriptionProvider(colorProvider, this.getNameGenerator())); DEFINITIONS.forEach(definition -> { diagramElementDescriptionProviders.add(new DefinitionNodeDescriptionProvider(definition, colorProvider)); @@ -173,7 +175,7 @@ private NodeTool[] createElementsOfToolSection(IViewDiagramElementFinder cache, var nodeTools = new ArrayList(); elements.forEach(definition -> { - nodeTools.add(this.createNodeToolFromDiagramBackground(cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(definition)).get(), definition)); + nodeTools.add(this.createNodeToolFromDiagramBackground(cache.getNodeDescription(this.getNameGenerator().getNodeName(definition)).get(), definition)); }); nodeTools.sort((nt1, nt2) -> nt1.getName().compareTo(nt2.getName())); diff --git a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/edges/SuccessionEdgeDescriptionProvider.java b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/edges/SuccessionEdgeDescriptionProvider.java index 0ddfa4b59..1c81d645d 100644 --- a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/edges/SuccessionEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/edges/SuccessionEdgeDescriptionProvider.java @@ -21,6 +21,7 @@ import org.eclipse.syson.diagram.actionflow.view.AFVDescriptionNameGenerator; import org.eclipse.syson.diagram.actionflow.view.ActionFlowViewDiagramDescriptionProvider; import org.eclipse.syson.diagram.common.view.edges.AbstractSuccessionEdgeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; /** * Used to create a Succession Edge provider in Action Flow View diagram. @@ -33,7 +34,7 @@ public SuccessionEdgeDescriptionProvider(IColorProvider colorProvider) { super(colorProvider, new AFVDescriptionNameGenerator()); } - private List getSourceAndTargetNodes(IViewDiagramElementFinder cache) { + private List getAllUsages(IViewDiagramElementFinder cache) { var sourcesAndTargets = new ArrayList(); ActionFlowViewDiagramDescriptionProvider.USAGES.forEach(usage -> { @@ -45,11 +46,14 @@ private List getSourceAndTargetNodes(IViewDiagramElementFinder @Override protected List getSourceNodes(IViewDiagramElementFinder cache) { - return this.getSourceAndTargetNodes(cache); + var sources = this.getAllUsages(cache); + // the start node can be the source of a succession + cache.getNodeDescription(this.nameGenerator.getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME)).ifPresent(sources::add); + return sources; } @Override protected List getTargetNodes(IViewDiagramElementFinder cache) { - return this.getSourceAndTargetNodes(cache); + return this.getAllUsages(cache); } } diff --git a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/DefinitionNodeDescriptionProvider.java b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/DefinitionNodeDescriptionProvider.java index 75015eb1e..dca3d0a01 100644 --- a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/DefinitionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/DefinitionNodeDescriptionProvider.java @@ -66,7 +66,7 @@ protected List getAllNodeDescriptions(IViewDiagramElementFinder @Override protected List getToolSections(NodeDescription nodeDescription, IViewDiagramElementFinder cache) { - ActionFlowViewNodeToolSectionSwitch toolSectionSwitch = new ActionFlowViewNodeToolSectionSwitch(this.getAllNodeDescriptions(cache)); + ActionFlowViewNodeToolSectionSwitch toolSectionSwitch = new ActionFlowViewNodeToolSectionSwitch(cache, this.getAllNodeDescriptions(cache)); return toolSectionSwitch.doSwitch(this.eClass); } } diff --git a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/UsageNodeDescriptionProvider.java b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/UsageNodeDescriptionProvider.java index 480521dcc..869e88f78 100644 --- a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/UsageNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/nodes/UsageNodeDescriptionProvider.java @@ -66,7 +66,7 @@ protected List getAllNodeDescriptions(IViewDiagramElementFinder @Override protected List getToolSections(NodeDescription nodeDescription, IViewDiagramElementFinder cache) { - ActionFlowViewNodeToolSectionSwitch toolSectionSwitch = new ActionFlowViewNodeToolSectionSwitch(this.getAllNodeDescriptions(cache)); + ActionFlowViewNodeToolSectionSwitch toolSectionSwitch = new ActionFlowViewNodeToolSectionSwitch(cache, this.getAllNodeDescriptions(cache)); return toolSectionSwitch.doSwitch(this.eClass); } } diff --git a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/services/ActionFlowViewNodeToolSectionSwitch.java b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/services/ActionFlowViewNodeToolSectionSwitch.java index c40294f26..05f674769 100644 --- a/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/services/ActionFlowViewNodeToolSectionSwitch.java +++ b/backend/views/syson-diagram-actionflow-view/src/main/java/org/eclipse/syson/diagram/actionflow/view/services/ActionFlowViewNodeToolSectionSwitch.java @@ -17,6 +17,7 @@ import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EReference; +import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.diagram.NodeToolSection; @@ -27,6 +28,7 @@ import org.eclipse.syson.diagram.common.view.tools.AcceptActionPortUsageReceiverToolNodeProvider; import org.eclipse.syson.diagram.common.view.tools.ActionFlowCompartmentNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.CompartmentNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.StartActionNodeToolProvider; import org.eclipse.syson.sysml.AcceptActionUsage; import org.eclipse.syson.sysml.ActionDefinition; import org.eclipse.syson.sysml.ActionUsage; @@ -44,8 +46,11 @@ public class ActionFlowViewNodeToolSectionSwitch extends AbstractViewNodeToolSec private final List allNodeDescriptions; - public ActionFlowViewNodeToolSectionSwitch(List allNodeDescriptions) { + private final IViewDiagramElementFinder cache; + + public ActionFlowViewNodeToolSectionSwitch(IViewDiagramElementFinder cache, List allNodeDescriptions) { super(new AFVDescriptionNameGenerator()); + this.cache = Objects.requireNonNull(cache); this.allNodeDescriptions = Objects.requireNonNull(allNodeDescriptions); } @@ -75,16 +80,16 @@ public List caseAcceptActionUsage(AcceptActionUsage object) { @Override public List caseActionUsage(ActionUsage object) { - var createSection = this.buildCreateSection(); - createSection.getNodeTools().add(new CompartmentNodeToolProvider(SysmlPackage.eINSTANCE.getUsage_NestedItem(), this.descriptionNameGenerator).create(null)); - createSection.getNodeTools().add(new ActionFlowCompartmentNodeToolProvider(SysmlPackage.eINSTANCE.getActionUsage(), this.descriptionNameGenerator).create(null)); + var createSection = this.buildCreateSection(new StartActionNodeToolProvider(SysmlPackage.eINSTANCE.getActionUsage(), this.descriptionNameGenerator).create(this.cache)); + createSection.getNodeTools().add(new CompartmentNodeToolProvider(SysmlPackage.eINSTANCE.getUsage_NestedItem(), this.descriptionNameGenerator).create(this.cache)); + createSection.getNodeTools().add(new ActionFlowCompartmentNodeToolProvider(SysmlPackage.eINSTANCE.getActionUsage(), this.descriptionNameGenerator).create(this.cache)); return List.of(createSection, this.addElementsToolSection()); } @Override public List caseActionDefinition(ActionDefinition object) { - var createSection = this.buildCreateSection(); - createSection.getNodeTools().add(new ActionFlowCompartmentNodeToolProvider(SysmlPackage.eINSTANCE.getActionDefinition(), this.descriptionNameGenerator).create(null)); + var createSection = this.buildCreateSection(new StartActionNodeToolProvider(SysmlPackage.eINSTANCE.getActionDefinition(), this.descriptionNameGenerator).create(this.cache)); + createSection.getNodeTools().add(new ActionFlowCompartmentNodeToolProvider(SysmlPackage.eINSTANCE.getActionDefinition(), this.descriptionNameGenerator).create(this.cache)); return List.of(createSection, this.addElementsToolSection()); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java index 925249f0c..f7603ae9c 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java @@ -17,8 +17,10 @@ import java.util.Objects; import org.eclipse.emf.common.util.EList; +import org.eclipse.sirius.components.view.UserColor; import org.eclipse.sirius.components.view.builder.providers.IColorProvider; import org.eclipse.sirius.components.view.builder.providers.INodeDescriptionProvider; +import org.eclipse.sirius.components.view.diagram.ImageNodeStyleDescription; import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.diagram.NodeToolSection; import org.eclipse.sirius.components.view.diagram.provider.DefaultToolsFactory; @@ -56,4 +58,17 @@ protected void orderToolSectionsTools(List toolSections) { toolSection.getNodeTools().addAll(sortedNodeTools); }); } + + protected ImageNodeStyleDescription createImageNodeStyleDescription(String shapeId) { + return this.createImageNodeStyleDescription(shapeId, this.colorProvider.getColor("transparent")); + } + + protected ImageNodeStyleDescription createImageNodeStyleDescription(String imagePath, UserColor borderColor) { + return this.diagramBuilderHelper.newImageNodeStyleDescription() + .borderColor(borderColor) + .borderRadius(0) + .positionDependentRotation(true) + .shape(imagePath) + .build(); + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ActionFlowCompartmentNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ActionFlowCompartmentNodeDescriptionProvider.java index a2539e56c..b0e836663 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ActionFlowCompartmentNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ActionFlowCompartmentNodeDescriptionProvider.java @@ -21,14 +21,19 @@ import org.eclipse.sirius.components.view.builder.providers.IColorProvider; import org.eclipse.sirius.components.view.builder.providers.INodeToolProvider; import org.eclipse.sirius.components.view.diagram.DiagramDescription; +import org.eclipse.sirius.components.view.diagram.DiagramFactory; import org.eclipse.sirius.components.view.diagram.InsideLabelDescription; import org.eclipse.sirius.components.view.diagram.InsideLabelPosition; import org.eclipse.sirius.components.view.diagram.InsideLabelStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.sirius.components.view.diagram.NodePalette; +import org.eclipse.sirius.components.view.diagram.NodeToolSection; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.syson.diagram.common.view.tools.ActionFlowCompartmentNodeToolProvider; +import org.eclipse.syson.diagram.common.view.tools.StartActionNodeToolProvider; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -40,6 +45,8 @@ */ public class ActionFlowCompartmentNodeDescriptionProvider extends AbstractCompartmentNodeDescriptionProvider { + public static final String COMPARTMENT_LABEL = "action flow"; + private final String name; public ActionFlowCompartmentNodeDescriptionProvider(EClass eClass, EReference eReference, IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { @@ -67,11 +74,34 @@ public NodeDescription create() { @Override public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { cache.getNodeDescription(this.name).ifPresent(nodeDescription -> { + cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME)).ifPresent(nodeDescription.getReusedChildNodeDescriptions()::add); cache.getNodeDescription(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionUsage())).ifPresent(nodeDescription.getReusedChildNodeDescriptions()::add); nodeDescription.setPalette(this.createCompartmentPalette(cache)); }); } + @Override + protected NodePalette createCompartmentPalette(IViewDiagramElementFinder cache) { + var palette = this.diagramBuilderHelper.newNodePalette() + .dropNodeTool(this.createCompartmentDropFromDiagramTool(cache)); + + return palette.toolSections(this.createCreationToolSection(cache), + this.defaultToolsFactory.createDefaultHideRevealNodeToolSection()) + .build(); + } + + private NodeToolSection createCreationToolSection(IViewDiagramElementFinder cache) { + NodeToolSection nodeToolSection = DiagramFactory.eINSTANCE.createNodeToolSection(); + nodeToolSection.setName("Create Section"); + INodeToolProvider compartmentNodeToolProvider = this.getItemCreationToolProvider(); + + if (compartmentNodeToolProvider != null) { + nodeToolSection.getNodeTools().add(compartmentNodeToolProvider.create(cache)); + } + nodeToolSection.getNodeTools().add(new StartActionNodeToolProvider(this.eClass, this.descriptionNameGenerator).create(cache)); + return nodeToolSection; + } + @Override protected List getDroppableNodes(IViewDiagramElementFinder cache) { List droppableNodes = new ArrayList<>(); @@ -83,7 +113,7 @@ protected List getDroppableNodes(IViewDiagramElementFinder cach @Override protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression("action flow") + .labelExpression(COMPARTMENT_LABEL) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .build(); @@ -108,6 +138,15 @@ protected INodeToolProvider getItemCreationToolProvider() { @Override protected String getDropElementFromDiagramExpression() { - return "aql:droppedElement.dropActionUsageFromDiagram(droppedNode, targetElement, targetNode, editingContext, diagramContext, convertedNodes)"; + var actionUsageDescriptionName = this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionUsage()); + var params = List.of( + "droppedNode", + "targetElement", + "targetNode", + AQLUtils.aqlString(actionUsageDescriptionName), + "diagramContext", + "convertedNodes" + ); + return AQLUtils.getServiceCallExpression("droppedElement", "dropActionUsageFromDiagram", params); } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java new file mode 100644 index 000000000..c82d704ae --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StartActionNodeDescriptionProvider.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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.syson.diagram.common.view.nodes; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.diagrams.description.EdgeDescription; +import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; +import org.eclipse.sirius.components.view.builder.providers.IColorProvider; +import org.eclipse.sirius.components.view.diagram.DiagramDescription; +import org.eclipse.sirius.components.view.diagram.EdgeTool; +import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.sirius.components.view.diagram.NodePalette; +import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.AQLUtils; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.SysMLMetamodelHelper; + +/** + * Used to create the starting node description of an Action. + * + * @author Jerome Gout + */ +public class StartActionNodeDescriptionProvider extends AbstractNodeDescriptionProvider { + + public static final String START_ACTION_NAME = "StartAction"; + + private final IDescriptionNameGenerator nameGenerator; + + public StartActionNodeDescriptionProvider(IColorProvider colorProvider, IDescriptionNameGenerator nameGenerator) { + super(colorProvider); + this.nameGenerator = Objects.requireNonNull(nameGenerator); + } + + @Override + public NodeDescription create() { + String domainType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getMembership()); + return this.diagramBuilderHelper.newNodeDescription() + .collapsible(false) + .domainType(domainType) + .defaultWidthExpression("36") + .defaultHeightExpression("36") + .name(this.nameGenerator.getNodeName(START_ACTION_NAME)) + .semanticCandidatesExpression(AQLUtils.getSelfServiceCallExpression("getStandardStartAction")) + .style(this.createImageNodeStyleDescription("2005db7d-7239-39d2-8206-3277c79fdea6")) + .userResizable(false) + .synchronizationPolicy(SynchronizationPolicy.UNSYNCHRONIZED) + .build(); + } + + @Override + public void link(DiagramDescription diagramDescription, IViewDiagramElementFinder cache) { + cache.getNodeDescription(this.nameGenerator.getNodeName(START_ACTION_NAME)).ifPresent(nodeDescription -> { + nodeDescription.setPalette(this.createNodePalette(cache)); + diagramDescription.getNodeDescriptions().add(nodeDescription); + }); + } + + private NodePalette createNodePalette(IViewDiagramElementFinder cache) { + var changeContext = this.viewBuilderHelper.newChangeContext() + .expression(AQLUtils.getSelfServiceCallExpression("deleteFromModel")); + + var deleteTool = this.diagramBuilderHelper.newDeleteTool() + .name("Remove Start") + .body(changeContext.build()); + + var edgeTools = new ArrayList(); + edgeTools.addAll(this.getEdgeTools(cache)); + + return this.diagramBuilderHelper.newNodePalette() + .deleteTool(deleteTool.build()) + .edgeTools(edgeTools.toArray(EdgeTool[]::new)) + .toolSections(this.defaultToolsFactory.createDefaultHideRevealNodeToolSection()) + .build(); + } + + private List getEdgeTools(IViewDiagramElementFinder cache) { + var targetElementDescriptions = this.getStartTargetDescriptions(cache); + + var builder = this.diagramBuilderHelper.newEdgeTool(); + var body = this.viewBuilderHelper.newChangeContext() + .expression(AQLUtils.getServiceCallExpression(EdgeDescription.SEMANTIC_EDGE_SOURCE, "createSuccessionEdge", EdgeDescription.SEMANTIC_EDGE_TARGET)); + + var createStartEdgeTool = builder.name(this.nameGenerator.getCreationToolName(SysmlPackage.eINSTANCE.getSuccession())) + .iconURLsExpression("/icons/full/obj16/Succession.svg") + .body(body.build()) + .targetElementDescriptions(targetElementDescriptions.toArray(NodeDescription[]::new)) + .build(); + + return List.of(createStartEdgeTool); + } + + private List getStartTargetDescriptions(IViewDiagramElementFinder cache) { + var targets = new ArrayList(); + cache.getNodeDescription(this.nameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionUsage())).ifPresent(targets::add); + cache.getNodeDescription(this.nameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionDefinition())).ifPresent(targets::add); + return targets; + } + +} diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java index bfc74ae89..707ba36f9 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java @@ -17,8 +17,10 @@ import java.util.Objects; import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramContext; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramService; import org.eclipse.sirius.components.core.api.IEditingContext; @@ -29,6 +31,7 @@ import org.eclipse.sirius.components.view.emf.diagram.api.IViewDiagramDescriptionSearchService; import org.eclipse.syson.services.DeleteService; import org.eclipse.syson.services.ElementInitializerSwitch; +import org.eclipse.syson.services.UtilService; import org.eclipse.syson.sysml.AcceptActionUsage; import org.eclipse.syson.sysml.ActionUsage; import org.eclipse.syson.sysml.AllocationDefinition; @@ -73,11 +76,14 @@ public class ViewCreateService { private final DeleteService deleteService; + private final UtilService utilService; + public ViewCreateService(IViewDiagramDescriptionSearchService viewDiagramDescriptionSearchService, IObjectService objectService) { this.viewDiagramDescriptionSearchService = Objects.requireNonNull(viewDiagramDescriptionSearchService); this.objectService = Objects.requireNonNull(objectService); this.elementInitializerSwitch = new ElementInitializerSwitch(); this.deleteService = new DeleteService(); + this.utilService = new UtilService(); } /** @@ -565,17 +571,83 @@ public boolean isEmptyAcceptActionUsagePayload(Element element) { return result; } - public Element createSuccessionEdge(ActionUsage sourceAction, ActionUsage targetAction) { - Element sourceParentElement = sourceAction.getOwner(); + public Element createSuccessionEdge(Element successionSource, Element successionTarget) { + if (this.utilService.isStandardStartAction(successionSource)) { + return this.createSuccessionEdge(successionSource, successionTarget, successionSource.eContainer()); + } + return this.createSuccessionEdge(successionSource, successionTarget, successionSource.getOwner()); + } + + private Element createSuccessionEdge(Element successionSource, Element successionTarget, EObject successionOwner) { + if (successionOwner instanceof Element ownerElement) { + Succession succession = SysmlFactory.eINSTANCE.createSuccession(); + this.elementInitializerSwitch.doSwitch(succession); + var featureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); + featureMembership.getOwnedRelatedElement().add(succession); + succession.getSource().add(successionSource); + succession.getTarget().add(successionTarget); + ownerElement.getOwnedRelationship().add(featureMembership); + } + return successionSource; + } + + /** + * Add the standard start action as the child of the given element. + * + * @param ownerElement + * an element that will own the standard start action. + * @return the {@link Membership} element containing the start action in its memberElement feature. + */ + public Membership addStartAction(Element ownerElement) { + var standardStartAction = this.utilService.retrieveStandardStartAction(ownerElement); + if (standardStartAction != null) { + var membership = SysmlFactory.eINSTANCE.createMembership(); + membership.setMemberElement(standardStartAction); + ownerElement.getOwnedRelationship().add(membership); + return membership; + } + return null; + } - Succession succession = SysmlFactory.eINSTANCE.createSuccession(); - this.elementInitializerSwitch.doSwitch(succession); + /** + * Create a new action {@link ActionUsage} inside the given element which should be an {@link ActionUsage} or an + * {@link ActionDefintion}. + * + * @param ownerElement + * the owner of the new action usage. + * @return the newly created action usage. + */ + public ActionUsage createSubActionUsage(Element ownerElement) { + var newActionUsage = SysmlFactory.eINSTANCE.createActionUsage(); + this.elementInitializerSwitch.doSwitch(newActionUsage); var featureMembership = SysmlFactory.eINSTANCE.createFeatureMembership(); - featureMembership.getOwnedRelatedElement().add(succession); - succession.getSource().add(sourceAction); - succession.getTarget().add(targetAction); - sourceParentElement.getOwnedRelationship().add(featureMembership); + featureMembership.getOwnedRelatedElement().add(newActionUsage); + ownerElement.getOwnedRelationship().add(featureMembership); + return newActionUsage; + } - return sourceAction; + /** + * Removal service for Start action inside an action usage or definition. + * + * @param selectedNode + * the node element that represents the start action in the diagram. + * @param editingContext + * @param diagramService + * @return the element that owned the start action. + */ + public Element removeStartAction(Node selectedNode, IEditingContext editingContext, IDiagramService diagramService) { + Element owner = this.getSourceOwner(selectedNode, editingContext, diagramService); + var membership = owner.getOwnedRelationship().stream() + .filter(Membership.class::isInstance) + .map(Membership.class::cast) + .filter(m -> { + return m.getMemberElement() instanceof ActionUsage au && this.utilService.isStandardStartAction(au); + }) + .findFirst() + .orElse(null); + if (membership != null) { + EcoreUtil.remove(membership); + } + return owner; } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java index 3d1254800..4ed1c07ad 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java @@ -28,7 +28,12 @@ import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.diagrams.description.NodeDescription; import org.eclipse.sirius.components.representations.VariableManager; +import org.eclipse.syson.services.UtilService; +import org.eclipse.syson.sysml.ActionDefinition; +import org.eclipse.syson.sysml.ActionUsage; import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Membership; +import org.eclipse.syson.sysml.Namespace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,8 +48,11 @@ public class ViewNodeService { private final IObjectService objectService; + private final UtilService utilService; + public ViewNodeService(IObjectService objectService) { this.objectService = Objects.requireNonNull(objectService); + this.utilService = new UtilService(); } /** @@ -145,4 +153,34 @@ public boolean isHiddenByDefault(Element self, String referenceName) { } return isHiddenByDefault; } + + /** + * Check if a Start Action can be added to the given element. + * + * @param self + * a {@link Namespace} element + * @return true if the Start action can be added to the given element or false otherwise. + */ + public boolean canAddStartAction(Namespace self) { + // 1. Check that self is an Action usage or definition + if (self instanceof ActionUsage || self instanceof ActionDefinition) { + // 2. Check that there is not yet the start action displayed. + return this.getStandardStartAction(self) == null; + } + return false; + } + + public Membership getStandardStartAction(Namespace self) { + if (self instanceof ActionUsage || self instanceof ActionDefinition) { + return self.getOwnedRelationship().stream() + .filter(Membership.class::isInstance) + .map(Membership.class::cast) + .filter(m -> { + return m.getMemberElement() instanceof ActionUsage au && this.utilService.isStandardStartAction(au); + }) + .findFirst() + .orElse(null); + } + return null; + } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java index 3cb4ea4ab..7441e3bcc 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewToolService.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import org.eclipse.emf.ecore.EClass; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramContext; @@ -447,7 +446,7 @@ public Element dropElementFromDiagramInRequirementAssumeConstraintCompartment(El Map convertedNodes) { if (droppedElement instanceof ConstraintUsage droppedConstraint && (targetElement instanceof RequirementUsage || targetElement instanceof RequirementDefinition)) { this.moveContraintInRequirementConstraintCompartment(droppedConstraint, targetElement, RequirementConstraintKind.ASSUMPTION); - this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes, NodeContainmentKind.CHILD_NODE); + this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes); diagramContext.getViewDeletionRequests().add(ViewDeletionRequest.newViewDeletionRequest().elementId(droppedNode.getId()).build()); } return droppedElement; @@ -458,7 +457,7 @@ public Element dropElementFromDiagramInRequirementRequireConstraintCompartment(E Map convertedNodes) { if (droppedElement instanceof ConstraintUsage droppedConstraint && (targetElement instanceof RequirementUsage || targetElement instanceof RequirementDefinition)) { this.moveContraintInRequirementConstraintCompartment(droppedConstraint, targetElement, RequirementConstraintKind.REQUIREMENT); - this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes, NodeContainmentKind.CHILD_NODE); + this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes); diagramContext.getViewDeletionRequests().add(ViewDeletionRequest.newViewDeletionRequest().elementId(droppedNode.getId()).build()); } return droppedElement; @@ -487,7 +486,7 @@ public Element dropElementFromDiagramInConstraintCompartment(Element droppedElem if (oldMembership instanceof OwningMembership owningMembership) { this.deleteService.deleteFromModel(owningMembership); } - this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes, NodeContainmentKind.CHILD_NODE); + this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes); diagramContext.getViewDeletionRequests().add(ViewDeletionRequest.newViewDeletionRequest().elementId(droppedNode.getId()).build()); } } @@ -564,56 +563,63 @@ private Package getClosestContainingPackageFrom(Element element) { } /** - * Called by "New Action" tool from free-form for both nested and owned ActionUsage . + * Create a new graphical view for an element inside a compartment given its label. * - * @param actionUsage - * the {@link ActionUsage} corresponding to the target object on which the tool has been called. - * @param nodeName - * the name of the node description corresponding to the ActionUsage in the context of the tool. + * @param childElement + * the semantic object for which the view is created. + * @param nodeDescriptionName + * the childElement node description name. + * @param parentNodeDescriptionName + * the name of the node description that owns the compartment in which the view should be created. * @param compartmentName - * the name of the free-form compartment - * @param editingContext - * the {@link IEditingContext} of the tool. It corresponds to a variable accessible from the variable - * manager. + * the label of the compartment in which the view should be created. + * @param selectedNode + * the {@link Node} where the tool was triggered. It can be an element or the compartment itself. * @param diagramContext * the {@link IDiagramContext} of the tool. It corresponds to a variable accessible from the variable * manager. - * @param selectedNode - * the selected node on which the tool has been called. It corresponds to a variable accessible from the - * variable manager. * @param convertedNodes * the map of all existing node descriptions in the DiagramDescription of this Diagram. It corresponds to * a variable accessible from the variable manager. - * @return the created PartUsage */ - public ActionUsage createSubActionUsageAndView(Element actionUsage, String nodeName, String compartmentName, IEditingContext editingContext, IDiagramContext diagramContext, Node selectedNode, + public Element createViewInFreeFormCompartment(Element childElement, String nodeDescriptionName, String parentNodeDescriptionName, String compartmentName, Node selectedNode, + IDiagramContext diagramContext, Map convertedNodes) { - var membership = SysmlFactory.eINSTANCE.createFeatureMembership(); - actionUsage.getOwnedRelationship().add(membership); - ActionUsage childAction = SysmlFactory.eINSTANCE.createActionUsage(); - membership.getOwnedRelatedElement().add(childAction); - this.elementInitializerSwitch.doSwitch(childAction); - // get the children action usage compartment - Optional nodeChildActionUsage = convertedNodes.keySet().stream() - .filter(n -> nodeName.equals(n.getName())) - .findFirst(); - if (nodeChildActionUsage.isPresent()) { - NodeDescription nodeDescription = convertedNodes.get(nodeChildActionUsage.get()); - if (nodeDescription != null && nodeDescription.getId().equals(selectedNode.getDescriptionId()) && compartmentName != null) { + + var childNodeDescription = this.getNodeDescriptionFromViewName(nodeDescriptionName, convertedNodes); + var parentNodeDescription = this.getNodeDescriptionFromViewName(parentNodeDescriptionName, convertedNodes); + + if (childNodeDescription != null && parentNodeDescription != null) { + // Is selectedNode the compartment owner or the compartment itself? + if (parentNodeDescription.getId().equals(selectedNode.getDescriptionId()) && compartmentName != null) { + // compartment owner => need to search the compartment selectedNode.getChildNodes().stream() .filter(child -> compartmentName.equals(child.getInsideLabel().getText())) .findFirst() .ifPresent(compartmentNode -> { - this.createView(childAction, editingContext, diagramContext, compartmentNode, convertedNodes); + var parentElementId = this.getParentElementId(compartmentNode, diagramContext); + this.createView(childElement, parentElementId, childNodeDescription.getId(), diagramContext, NodeContainmentKind.CHILD_NODE); }); } else { - this.createView(childAction, editingContext, diagramContext, selectedNode, convertedNodes); + var parentElementId = this.getParentElementId(selectedNode, diagramContext); + this.createView(childElement, parentElementId, childNodeDescription.getId(), diagramContext, NodeContainmentKind.CHILD_NODE); } } - return childAction; + return childElement; + } + + private NodeDescription getNodeDescriptionFromViewName(String nodeDescriptionName, Map convertedNodes) { + var viewNodeDescription = convertedNodes.keySet().stream() + .filter(n -> nodeDescriptionName.equals(n.getName())) + .findFirst() + .orElse(null); + if (viewNodeDescription != null) { + return convertedNodes.get(viewNodeDescription); + } + return null; } - public Element dropActionUsageFromDiagram(Element droppedElement, Node droppedNode, Element targetElement, Node targetNode, IEditingContext editingContext, IDiagramContext diagramContext, + public Element dropActionUsageFromDiagram(Element droppedElement, Node droppedNode, Element targetElement, Node targetNode, String dropNodeDescriptionName, IDiagramContext diagramContext, Map convertedNodes) { var eContainer = droppedElement.eContainer(); if (eContainer instanceof FeatureMembership featureMembership) { @@ -630,7 +636,9 @@ public Element dropActionUsageFromDiagram(Element droppedElement, Node droppedNo oldParent.getOwnedRelationship().remove(owningMembership); } } - this.createView(droppedElement, editingContext, diagramContext, targetNode, convertedNodes, NodeContainmentKind.CHILD_NODE); + var parentElementId = this.getParentElementId(targetNode, diagramContext); + var droppedNodeDescription = this.getNodeDescriptionFromViewName(dropNodeDescriptionName, convertedNodes); + this.createView(droppedElement, parentElementId, droppedNodeDescription.getId(), diagramContext, NodeContainmentKind.CHILD_NODE); diagramContext.getViewDeletionRequests().add(ViewDeletionRequest.newViewDeletionRequest().elementId(droppedNode.getId()).build()); return droppedElement; } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java index 676efb867..321afaa61 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/ActionFlowCompartmentNodeToolProvider.java @@ -12,13 +12,17 @@ *******************************************************************************/ package org.eclipse.syson.diagram.common.view.tools; +import java.util.List; + import org.eclipse.emf.ecore.EClass; import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; import org.eclipse.sirius.components.view.builder.generated.DiagramBuilders; import org.eclipse.sirius.components.view.builder.generated.ViewBuilders; import org.eclipse.sirius.components.view.builder.providers.INodeToolProvider; import org.eclipse.sirius.components.view.diagram.NodeTool; -import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; /** @@ -34,35 +38,42 @@ public class ActionFlowCompartmentNodeToolProvider implements INodeToolProvider private final ViewBuilders viewBuilderHelper = new ViewBuilders(); - private final EClass eClass; + private final EClass ownerEClass; - public ActionFlowCompartmentNodeToolProvider(EClass eClass, IDescriptionNameGenerator nameGenerator) { + public ActionFlowCompartmentNodeToolProvider(EClass ownerEClass, IDescriptionNameGenerator nameGenerator) { super(); - this.eClass = eClass; + this.ownerEClass = ownerEClass; this.nameGenerator = nameGenerator; } @Override public NodeTool create(IViewDiagramElementFinder cache) { var builder = this.diagramBuilderHelper.newNodeTool(); - var nodeName = this.nameGenerator.getNodeName(this.eClass); + var parentNodeName = this.nameGenerator.getNodeName(this.ownerEClass); + var params = List.of( + AQLUtils.aqlString(this.nameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionUsage())), + AQLUtils.aqlString(parentNodeName), + AQLUtils.aqlString(ActionFlowCompartmentNodeDescriptionProvider.COMPARTMENT_LABEL), + "selectedNode", + "diagramContext", + "convertedNodes"); var creationServiceCall = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL_SELF + ".createSubActionUsageAndView('" + nodeName + "', 'action flow', editingContext, diagramContext, selectedNode, convertedNodes)") + .expression(AQLUtils.getSelfServiceCallExpression("createSubActionUsage")); + + var createViewOperation = this.viewBuilderHelper.newChangeContext() + .expression(AQLUtils.getSelfServiceCallExpression("createViewInFreeFormCompartment", params)) .build(); var revealOperation = this.viewBuilderHelper.newChangeContext() .expression("aql:selectedNode.revealCompartment(self, diagramContext, editingContext, convertedNodes)") .build(); - var rootChangContext = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL_SELF) - .children(creationServiceCall, revealOperation) - .build(); + creationServiceCall.children(createViewOperation, revealOperation); return builder.name("New Action") .iconURLsExpression("/icons/full/obj16/ActionUsage.svg") - .body(rootChangContext) + .body(creationServiceCall.build()) .build(); } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java new file mode 100644 index 000000000..dc545d583 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/tools/StartActionNodeToolProvider.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.syson.diagram.common.view.tools; + +import java.util.List; +import java.util.Objects; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.sirius.components.view.builder.IViewDiagramElementFinder; +import org.eclipse.sirius.components.view.builder.generated.DiagramBuilders; +import org.eclipse.sirius.components.view.builder.generated.ViewBuilders; +import org.eclipse.sirius.components.view.builder.providers.INodeToolProvider; +import org.eclipse.sirius.components.view.diagram.NodeTool; +import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.StartActionNodeDescriptionProvider; +import org.eclipse.syson.util.AQLUtils; +import org.eclipse.syson.util.IDescriptionNameGenerator; + +/** + * Used to add the standard start action inside diagrams. + * + * @author Jerome Gout + */ +public class StartActionNodeToolProvider implements INodeToolProvider { + + private final DiagramBuilders diagramBuilderHelper = new DiagramBuilders(); + + private final ViewBuilders viewBuilderHelper = new ViewBuilders(); + + private final IDescriptionNameGenerator nameGenerator; + + /** + * the ECLass of the owner of the tool. + */ + private final EClass ownerEClass; + + public StartActionNodeToolProvider(EClass ownerEClass, IDescriptionNameGenerator nameGenerator) { + this.ownerEClass = Objects.requireNonNull(ownerEClass); + this.nameGenerator = Objects.requireNonNull(nameGenerator); + } + + @Override + public NodeTool create(IViewDiagramElementFinder cache) { + var builder = this.diagramBuilderHelper.newNodeTool(); + + var params = List.of( + AQLUtils.aqlString(this.nameGenerator.getNodeName(StartActionNodeDescriptionProvider.START_ACTION_NAME)), + AQLUtils.aqlString(this.nameGenerator.getNodeName(this.ownerEClass)), + AQLUtils.aqlString(ActionFlowCompartmentNodeDescriptionProvider.COMPARTMENT_LABEL), + "selectedNode", + "diagramContext", + "convertedNodes"); + var creationServiceCall = this.viewBuilderHelper.newChangeContext() + .expression(AQLUtils.getSelfServiceCallExpression("addStartAction")); + + var createViewOperation = this.viewBuilderHelper.newChangeContext() + .expression(AQLUtils.getSelfServiceCallExpression("createViewInFreeFormCompartment", params)) + .build(); + + var revealOperation = this.viewBuilderHelper.newChangeContext() + .expression("aql:selectedNode.revealCompartment(self, diagramContext, editingContext, convertedNodes)") + .build(); + + creationServiceCall.children(createViewOperation, revealOperation); + + return builder.name("Add Start Action") + .iconURLsExpression("/icons/start_action.svg") + .body(creationServiceCall.build()) + .preconditionExpression(AQLUtils.getSelfServiceCallExpression("canAddStartAction")) + .build(); + } +} diff --git a/backend/views/syson-diagram-common-view/src/main/resources/icons/end_action.svg b/backend/views/syson-diagram-common-view/src/main/resources/icons/end_action.svg new file mode 100644 index 000000000..552621349 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/resources/icons/end_action.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/backend/views/syson-diagram-common-view/src/main/resources/icons/start_action.svg b/backend/views/syson-diagram-common-view/src/main/resources/icons/start_action.svg new file mode 100644 index 000000000..2dfb0fe38 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/resources/icons/start_action.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/backend/views/syson-diagram-common-view/src/main/resources/sysonCustomImages/end_action.svg b/backend/views/syson-diagram-common-view/src/main/resources/sysonCustomImages/end_action.svg new file mode 100644 index 000000000..552621349 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/resources/sysonCustomImages/end_action.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/backend/views/syson-diagram-common-view/src/main/resources/sysonCustomImages/start_action.svg b/backend/views/syson-diagram-common-view/src/main/resources/sysonCustomImages/start_action.svg new file mode 100644 index 000000000..2dfb0fe38 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/resources/sysonCustomImages/start_action.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/PortUsageBorderNodeDescriptionProvider.java b/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/PortUsageBorderNodeDescriptionProvider.java index efcbdfeca..65705afce 100644 --- a/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/PortUsageBorderNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/PortUsageBorderNodeDescriptionProvider.java @@ -23,7 +23,6 @@ import org.eclipse.sirius.components.view.diagram.EdgeTool; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.NodePalette; -import org.eclipse.sirius.components.view.diagram.NodeStyleDescription; import org.eclipse.sirius.components.view.diagram.OutsideLabelDescription; import org.eclipse.sirius.components.view.diagram.OutsideLabelPosition; import org.eclipse.sirius.components.view.diagram.OutsideLabelStyle; @@ -100,31 +99,23 @@ private OutsideLabelStyle createOutsideLabelStyle() { } private List createPortUsageConditionalNodeStyles() { + var borderColor = this.colorProvider.getColor(ViewConstants.DEFAULT_BORDER_COLOR); return List.of( this.diagramBuilderHelper.newConditionalNodeStyle() .condition("aql:self.isInPort()") - .style(this.createImageNodeStyleDescription("/images/PortUsage_In.svg")) + .style(this.createImageNodeStyleDescription("/images/PortUsage_In.svg", borderColor)) .build(), this.diagramBuilderHelper.newConditionalNodeStyle() .condition("aql:self.isOutPort()") - .style(this.createImageNodeStyleDescription("/images/PortUsage_Out.svg")) + .style(this.createImageNodeStyleDescription("/images/PortUsage_Out.svg", borderColor)) .build(), this.diagramBuilderHelper.newConditionalNodeStyle() .condition("aql:self.isInOutPort()") - .style(this.createImageNodeStyleDescription("/images/PortUsage_InOut.svg")) + .style(this.createImageNodeStyleDescription("/images/PortUsage_InOut.svg", borderColor)) .build() ); } - private NodeStyleDescription createImageNodeStyleDescription(String imagePath) { - return this.diagramBuilderHelper.newImageNodeStyleDescription() - .borderColor(this.colorProvider.getColor(ViewConstants.DEFAULT_BORDER_COLOR)) - .borderRadius(0) - .positionDependentRotation(true) - .shape(imagePath) - .build(); - } - private NodePalette createNodePalette(IViewDiagramElementFinder cache, NodeDescription nodeDescription) { var changeContext = this.viewBuilderHelper.newChangeContext() .expression("aql:self.deleteFromModel()"); diff --git a/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/RootPortUsageBorderNodeDescriptionProvider.java b/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/RootPortUsageBorderNodeDescriptionProvider.java index fe909a1ff..3b1fa7327 100644 --- a/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/RootPortUsageBorderNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-interconnection-view/src/main/java/org/eclipse/syson/diagram/interconnection/view/nodes/RootPortUsageBorderNodeDescriptionProvider.java @@ -24,7 +24,6 @@ import org.eclipse.sirius.components.view.diagram.EdgeTool; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.NodePalette; -import org.eclipse.sirius.components.view.diagram.NodeStyleDescription; import org.eclipse.sirius.components.view.diagram.OutsideLabelDescription; import org.eclipse.sirius.components.view.diagram.OutsideLabelPosition; import org.eclipse.sirius.components.view.diagram.OutsideLabelStyle; @@ -102,30 +101,22 @@ private OutsideLabelStyle createOutsideLabelStyle() { } private List createPortUsageConditionalNodeStyles() { + var borderColor = this.colorProvider.getColor(ViewConstants.DEFAULT_BORDER_COLOR); return List.of( this.diagramBuilderHelper.newConditionalNodeStyle() .condition("aql:self.isInPort()") - .style(this.createImageNodeStyleDescription("/images/PortUsage_In.svg")) + .style(this.createImageNodeStyleDescription("/images/PortUsage_In.svg", borderColor)) .build(), this.diagramBuilderHelper.newConditionalNodeStyle() .condition("aql:self.isOutPort()") - .style(this.createImageNodeStyleDescription("/images/PortUsage_Out.svg")) + .style(this.createImageNodeStyleDescription("/images/PortUsage_Out.svg", borderColor)) .build(), this.diagramBuilderHelper.newConditionalNodeStyle() .condition("aql:self.isInOutPort()") - .style(this.createImageNodeStyleDescription("/images/PortUsage_InOut.svg")) + .style(this.createImageNodeStyleDescription("/images/PortUsage_InOut.svg", borderColor)) .build()); } - private NodeStyleDescription createImageNodeStyleDescription(String imagePath) { - return this.diagramBuilderHelper.newImageNodeStyleDescription() - .borderColor(this.colorProvider.getColor(ViewConstants.DEFAULT_BORDER_COLOR)) - .borderRadius(0) - .positionDependentRotation(true) - .shape(imagePath) - .build(); - } - private NodePalette createNodePalette(IViewDiagramElementFinder cache, NodeDescription nodeDescription) { var changeContext = this.viewBuilderHelper.newChangeContext() .expression("aql:self.deleteFromModel()");