Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ This feature can be de-activated by setting the property `org.eclipse.syson.test
The cache holding standard libraries can be invalidated for a specific test method or test class by using the `@InvalidateStandardLibrariesCache` annotation, ensuring the editing contexts are loaded from scratch.
- https://github.com/eclipse-syson/syson/issues/2154[#2154] [diagrams] Improve the _Duplicate Element_ diagram tool to support multi-selection in standard diagrams.
- https://github.com/eclipse-syson/syson/issues/2160[#2160] [diagrams] Improve the _View As_ diagram tool on graphical nodes to support multi-selection in standard diagrams.
- https://github.com/eclipse-syson/syson/issues/2170[#2170] [diagrams] Improve the _Add existing elements_ diagram tool on graphical nodes to support multi-selection in standard diagrams.
- https://github.com/eclipse-syson/syson/issues/2148[#2148] [diagrams] Merge the two perform action creation tools into a single tool, leveraging the updated selection dialog.
- https://github.com/eclipse-syson/syson/issues/2152[#2152] [diagrams] Leverage the latest selection dialog changes to allow creating a sub action with and without associating the sub action with another `ActionUsage`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public void addExistingElementsRecursiveOnDiagram() {
// Objects.equals(imageStyle.getImageURL(), "images/start_action.svg")
// && Objects.equals("start", n.getTargetObjectLabel()));

this.checkAction2(newDiagram);
this.checkAction2(newDiagram, false);

this.checkRequirementUsage(newDiagram);
});
Expand All @@ -207,6 +207,95 @@ public void addExistingElementsRecursiveOnDiagram() {
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a General View diagram with multiple selected nodes, WHEN Add existing elements is invoked, THEN existing elements are added for each selected node")
@GivenSysONServer({ GeneralViewAddExistingElementsTestProjectData.SCRIPT_PATH })
@Test
public void addExistingElementsOnMultiSelection() {
var flux = this.givenSubscriptionToDiagram();

var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID,
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);

AtomicReference<Diagram> diagram = new AtomicReference<>();
AtomicReference<String> part1NodeId = new AtomicReference<>();
AtomicReference<String> action1NodeId = new AtomicReference<>();

Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set);

String addExistingElementsOnDiagramToolId = diagramDescriptionIdProvider.getDiagramCreationToolId("Add existing elements");
Runnable addExistingElementsOnDiagram = () -> this.nodeCreationTester.invokeTool(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram, addExistingElementsOnDiagramToolId);

Consumer<Object> diagramWithTopNodesConsumer = assertRefreshedDiagramThat(newDiagram -> {
diagram.set(newDiagram);
part1NodeId.set(this.getNodeIdWithLabel(newDiagram, PART1));
action1NodeId.set(this.getNodeIdWithLabel(newDiagram, ACTION1));
});

String addExistingElementsGroupToolId = diagramDescriptionIdProvider.getGroupNodeToolId("Add existing elements");
Runnable addExistingElementsOnMultiSelection = () -> this.nodeCreationTester.invokeTool(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram.get().getId(),
List.of(part1NodeId.get(), action1NodeId.get()), addExistingElementsGroupToolId, List.of());

Consumer<Object> updatedDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> {
this.checkPart1(newDiagram);
this.checkAction1(newDiagram, true);
});

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(addExistingElementsOnDiagram)
.consumeNextWith(diagramWithTopNodesConsumer)
.then(addExistingElementsOnMultiSelection)
.consumeNextWith(updatedDiagramConsumer)
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a General View diagram with multiple selected nodes, WHEN Add existing elements recursive is invoked, THEN existing elements are added recursively for each selected node")
@GivenSysONServer({ GeneralViewAddExistingElementsTestProjectData.SCRIPT_PATH })
@Test
public void addExistingElementsRecursiveOnMultiSelection() {
var flux = this.givenSubscriptionToDiagram();

var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID,
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);

AtomicReference<Diagram> diagram = new AtomicReference<>();
AtomicReference<String> part1NodeId = new AtomicReference<>();
AtomicReference<String> action1NodeId = new AtomicReference<>();

Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set);

String addExistingElementsOnDiagramToolId = diagramDescriptionIdProvider.getDiagramCreationToolId("Add existing elements");
Runnable addExistingElementsOnDiagram = () -> this.nodeCreationTester.invokeTool(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram, addExistingElementsOnDiagramToolId);

Consumer<Object> diagramWithTopNodesConsumer = assertRefreshedDiagramThat(newDiagram -> {
diagram.set(newDiagram);
part1NodeId.set(this.getNodeIdWithLabel(newDiagram, PART1));
action1NodeId.set(this.getNodeIdWithLabel(newDiagram, ACTION1));
});

String addExistingElementsRecursiveGroupToolId = diagramDescriptionIdProvider.getGroupNodeToolId("Add existing elements (recursive)");
Runnable addExistingElementsRecursiveOnMultiSelection = () -> this.nodeCreationTester.invokeTool(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram.get().getId(),
List.of(part1NodeId.get(), action1NodeId.get()), addExistingElementsRecursiveGroupToolId, List.of());

Consumer<Object> updatedDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> {
this.checkPart1(newDiagram);
this.checkAction1(newDiagram, true);
this.checkAction2(newDiagram, true);
});

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(addExistingElementsOnDiagram)
.consumeNextWith(diagramWithTopNodesConsumer)
.then(addExistingElementsRecursiveOnMultiSelection)
.consumeNextWith(updatedDiagramConsumer)
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN an ActionUsage with its action flow compartment displayed and a nested ActionUsage in it, WHEN Delete from diagram the nested ActionUsage then use the Add existing element tool on the action flow compartment, THEN the nested ActionUsage should only be displayed in the action flow compartment")
@GivenSysONServer({ GeneralViewAddExistingElementsActionFlowCompartmentTestProjectData.SCRIPT_PATH })
@Test
Expand Down Expand Up @@ -272,8 +361,24 @@ private void checkPackageNode(Diagram newDiagram) {
.anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), ATTRIBUTE_DEFINITION));
}

private void checkAction2(Diagram newDiagram) {
var action1ActionFlowCompartment = this.checkAction1(newDiagram);
private void checkPart1(Diagram newDiagram) {
var optPart1Node = newDiagram.getNodes().stream()
.filter(n -> Objects.equals(n.getTargetObjectLabel(), PART1))
.findFirst();
assertThat(optPart1Node).isPresent();
var part1PartsCompartment = this.getCompartment(optPart1Node.get(), "parts");
assertThat(part1PartsCompartment)
.as(PART1 + " should contain a parts compartment")
.isPresent();
assertThat(part1PartsCompartment.get().getChildNodes())
.as(PART1 + " parts compartment should contain 1 child")
.hasSize(1)
.as(PART1 + " parts compartment should contain " + PART2)
.anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), PART2));
}

private void checkAction2(Diagram newDiagram, boolean expectStartNode) {
var action1ActionFlowCompartment = this.checkAction1(newDiagram, expectStartNode);
var optAction2Node = action1ActionFlowCompartment.getChildNodes().stream()
.filter(n -> Objects.equals(n.getTargetObjectLabel(), ACTION2))
.findFirst();
Expand All @@ -300,7 +405,7 @@ private void checkAction2(Diagram newDiagram) {
.anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), ACTION3));
}

private Node checkAction1(Diagram newDiagram) {
private Node checkAction1(Diagram newDiagram, boolean expectStartNode) {
var optAction1Node = newDiagram.getNodes().stream()
.filter(n -> Objects.equals(n.getTargetObjectLabel(), ACTION1))
.findFirst();
Expand All @@ -320,11 +425,21 @@ private Node checkAction1(Diagram newDiagram) {
assertThat(action1ActionFlowCompartment)
.as(ACTION1 + " should contain an action flow compartment")
.isPresent();
assertThat(action1ActionFlowCompartment.get().getChildNodes())
.as(ACTION1 + " action flow compartment should contain 2 children")
.hasSize(1) // @technical-debt should be 2 when start node will be synchronized
var action1ActionFlowCompartmentChildNodes = action1ActionFlowCompartment.get().getChildNodes();
var expectedChildNodeCount = 1;
if (expectStartNode) {
expectedChildNodeCount = 2;
}
assertThat(action1ActionFlowCompartmentChildNodes)
.as(ACTION1 + " action flow compartment should contain " + expectedChildNodeCount + " child nodes")
.hasSize(expectedChildNodeCount)
.as(ACTION1 + " action flow compartment should contain " + ACTION2)
.anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), ACTION2));
if (expectStartNode) {
assertThat(action1ActionFlowCompartmentChildNodes)
.as(ACTION1 + " action flow compartment should contain a start node")
.anyMatch(n -> Objects.equals(n.getTargetObjectLabel(), "start"));
}
return action1ActionFlowCompartment.get();
}

Expand All @@ -339,6 +454,14 @@ private void checkRequirementUsage(Diagram newDiagram) {
.allMatch(node -> node.getModifiers().contains(ViewModifier.Hidden));
}

private String getNodeIdWithLabel(Diagram diagram, String label) {
return diagram.getNodes().stream()
.filter(node -> Objects.equals(node.getTargetObjectLabel(), label))
.map(Node::getId)
.findFirst()
.orElseThrow();
}

private Optional<Node> getCompartment(Node node, String compartmentName) {
return node.getChildNodes().stream()
.filter(n -> Objects.equals(n.getInsideLabel().getText(), compartmentName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,41 @@ public Element addToExposedElements(Element element, boolean recursive, IEditing
return element;
}

/**
* Adds/displays existing elements from the given element on the matching selected graphical node.
*
* @param element
* the given {@link Element}.
* @param recursive
* if the process should add elements recursively.
* @param editingContext
* the {@link IEditingContext} of the tool.
* @param diagramContext
* the {@link DiagramContext} of the tool.
* @param selectedNodes
* the selected graphical nodes matching the selected semantic elements.
* @param convertedNodes
* the map of all existing node descriptions in the DiagramDescription of this Diagram.
* @return the given {@link Element}.
*/
public Element addToExposedElements(Element element, boolean recursive, IEditingContext editingContext, DiagramContext diagramContext, List<Node> selectedNodes,
Map<org.eclipse.sirius.components.view.diagram.NodeDescription, NodeDescription> convertedNodes) {
var selectedNode = this.findSelectedNode(element, selectedNodes);
this.addToExposedElements(element, recursive, editingContext, diagramContext, selectedNode, convertedNodes);
return element;
}

private Node findSelectedNode(Element element, List<Node> selectedNodes) {
if (selectedNodes == null) {
return null;
}
var elementId = this.siriusWebCoreServices.identityService().getId(element);
return selectedNodes.stream()
.filter(selectedNode -> Objects.equals(selectedNode.getTargetObjectId(), elementId))
.findFirst()
.orElse(null);
}

/**
* Remove the given Element from the exposedElements reference of the {@link ViewUsage} that is the target of the
* given {@link DiagramContext}. Also removes potential children that are sub-nodes of the given selectedNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ public Element addToExposedElements(Element element, boolean recursive, IEditing
return this.diagramMutationExposeService.addToExposedElements(element, recursive, editingContext, diagramContext, selectedNode, convertedNodes);
}

/**
* {@link DiagramMutationExposeService#addToExposedElements(Element, boolean, IEditingContext, DiagramContext, List, Map)}.
*/
public Element addToExposedElements(Element element, boolean recursive, IEditingContext editingContext, DiagramContext diagramContext, List<Node> selectedNodes,
Map<org.eclipse.sirius.components.view.diagram.NodeDescription, NodeDescription> convertedNodes) {
return this.diagramMutationExposeService.addToExposedElements(element, recursive, editingContext, diagramContext, selectedNodes, convertedNodes);
}

/**
* {@link DiagramMutationElementService#createBindingConnectorAsUsage(Feature, Feature, Node, Node, IEditingContext, DiagramContext)}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService;
import org.eclipse.syson.model.services.aql.ModelMutationAQLService;
import org.eclipse.syson.services.UtilService;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Feature;
import org.eclipse.syson.sysml.FeatureDirectionKind;
import org.eclipse.syson.sysml.FeatureMembership;
Expand Down Expand Up @@ -171,6 +172,18 @@ public NodeToolSection relatedElementsNodeToolSection(boolean nested) {
.build();
}

/**
* Create a {@link NodeToolSection} containing the {@code Add Existing Elements} tools for node multi-selection.
*
* @return The created {@link NodeToolSection}
*/
public NodeToolSection relatedElementsGroupToolSection() {
return this.diagramBuilderHelper.newNodeToolSection()
.name("Related Elements")
.nodeTools(this.addExistingElementsGroupTool(false), this.addExistingElementsGroupTool(true))
.build();
}

/**
* Create a {@link NodeTool} adding/displaying existing elements from {@code self} on the context diagram or diagram
* element.
Expand All @@ -187,8 +200,9 @@ public NodeTool addExistingElementsTool(boolean recursive, boolean nested) {

var addToExposedElements = this.viewBuilderHelper.newChangeContext()
.expression(
ServiceMethod.of5(DiagramMutationAQLService::addToExposedElements).aqlSelf("" + recursive, IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT, Node.SELECTED_NODE,
ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE));
ServiceMethod.of5(DiagramMutationAQLService.class, DiagramMutationAQLService::addToExposedElements, Element.class, boolean.class, IEditingContext.class, DiagramContext.class,
Node.class, java.util.Map.class).aqlSelf("" + recursive, IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT, Node.SELECTED_NODE,
ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE));

var changeContextViewUsageOwner = this.viewBuilderHelper.newChangeContext()
.expression(ServiceMethod.of0(UtilService::getViewUsageOwner).aqlSelf())
Expand All @@ -212,6 +226,27 @@ public NodeTool addExistingElementsTool(boolean recursive, boolean nested) {
.build();
}

private NodeTool addExistingElementsGroupTool(boolean recursive) {
String title = "Add existing elements";
String iconURL = "/icons/AddExistingElements.svg";
if (recursive) {
title += " (recursive)";
iconURL = "/icons/AddExistingElementsRecursive.svg";
}

return this.diagramBuilderHelper.newNodeTool()
.name(title)
.iconURLsExpression(iconURL)
.preconditionExpression("aql:selectedNodes->notEmpty() and selectedEdges->isEmpty() and self->forAll(e | e.oclIsKindOf(sysml::Element))")
.body(this.viewBuilderHelper.newChangeContext()
.expression(ServiceMethod.of5(DiagramMutationAQLService.class, DiagramMutationAQLService::addToExposedElements, Element.class, boolean.class, IEditingContext.class, DiagramContext.class,
List.class, java.util.Map.class)
.aqlSelf("" + recursive, IEditingContext.EDITING_CONTEXT, DiagramContext.DIAGRAM_CONTEXT, "selectedNodes",
ViewDiagramDescriptionConverter.CONVERTED_NODES_VARIABLE))
.build())
.build();
}

/**
* Return a tool section used to store Requirements related tools.
*
Expand Down
Loading
Loading