Skip to content

Commit d627e9d

Browse files
committed
[2270] Add dedicated tools to diagram elements palette to create/edit/delete expressions
Bug: #2270 Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
1 parent 2b6f6e0 commit d627e9d

15 files changed

Lines changed: 531 additions & 6 deletions

File tree

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Before, the selection dialog option with selection allowed choosing between all
3939
Now, the choice is restricted to the _timeslice/snapshot_ type that match the graphical node type on which the tool is applied.
4040
- https://github.com/eclipse-syson/syson/issues/2119[#2119] [details] Display expressions values in the _Details_ view and allow to edit them from there.
4141
- https://github.com/eclipse-syson/syson/issues/2251[#2251] [explorer] Allow expression-related operations on their parent element
42+
- https://github.com/eclipse-syson/syson/issues/2270[#2270] [diagram] Add dedicated tools to diagram elements palette to create/edit/delete expressions
4243

4344
=== New features
4445

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Obeo.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Obeo - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.syson.application.expressions.services;
14+
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.Objects;
18+
import java.util.Optional;
19+
20+
import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext;
21+
import org.eclipse.sirius.components.collaborative.diagrams.dto.ITool;
22+
import org.eclipse.sirius.components.collaborative.diagrams.dto.SingleClickOnDiagramElementTool;
23+
import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolSection;
24+
import org.eclipse.sirius.components.core.api.IEditingContext;
25+
import org.eclipse.sirius.components.core.api.IObjectSearchService;
26+
import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicate;
27+
import org.eclipse.sirius.components.diagrams.Edge;
28+
import org.eclipse.sirius.components.diagrams.Node;
29+
import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription;
30+
import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext;
31+
import org.eclipse.sirius.components.view.emf.diagram.api.IPaletteToolsProvider;
32+
import org.eclipse.syson.sysml.Element;
33+
import org.eclipse.syson.sysml.Expression;
34+
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
35+
import org.springframework.stereotype.Service;
36+
37+
/**
38+
* Contribute tools to manipulate {@link Expression} to specific elements's diagram palette:
39+
* <ul>
40+
* <li>"New expression" on diagram elements which represent SysML elements that can contain a new
41+
* {@link Expression}.</li>
42+
* <li>"Edit expression" on diagram elements which represent an {@link Expression} or an element which contains a single
43+
* (non-ambiguous) one.</li>
44+
* <li>"Delete expression" on diagram elements which contain a single (non-ambiguous) expression (on actual expression
45+
* element, the plain Delete is enough).</li>
46+
* </ul>
47+
*
48+
* @author pcdavid
49+
*/
50+
@Service
51+
public class ExpressionsPaletteToolsProvider implements IPaletteToolsProvider {
52+
53+
private static final String NEW_EXPRESSION_TOOL_ID = "tool_new_expression";
54+
55+
private static final String NEW_EXPRESSION_TOOL_LABEL = "New expression";
56+
57+
private static final String EDIT_EXPRESSION_TOOL_ID = "tool_edit_expression";
58+
59+
private static final String EDIT_EXPRESSION_TOOL_LABEL = "Edit expression";
60+
61+
private static final String DELETE_EXPRESSION_TOOL_ID = "tool_delete_expression";
62+
63+
private static final String DELETE_EXPRESSION_TOOL_LABEL = "Delete expression";
64+
65+
private static final String EXPRESSION_TOOL_SECTION_ID = "toolSection_expression";
66+
67+
private static final String EXPRESSION_TOOL_SECTION_LABEL = "Expression";
68+
69+
private final IObjectSearchService objectSearchService;
70+
71+
private final IReadOnlyObjectPredicate readOnlyObjectPredicate;
72+
73+
private final MetamodelQueryElementService metamodelQueryElementService;
74+
75+
public ExpressionsPaletteToolsProvider(IObjectSearchService objectSearchService, IReadOnlyObjectPredicate readOnlyObjectPredicate) {
76+
this.objectSearchService = Objects.requireNonNull(objectSearchService);
77+
this.readOnlyObjectPredicate = Objects.requireNonNull(readOnlyObjectPredicate);
78+
this.metamodelQueryElementService = new MetamodelQueryElementService();
79+
}
80+
81+
@Override
82+
public List<ToolSection> createExtraToolSections(IEditingContext editingContext, DiagramContext diagramContext, Object diagramElementDescription, Object diagramElement) {
83+
var tools = new ArrayList<ITool>();
84+
85+
var optionalTargetObjectId = this.getTargetObjectId(diagramElement);
86+
87+
if (optionalTargetObjectId.isPresent() && diagramElementDescription instanceof IDiagramElementDescription nodeDescription) {
88+
var semanticObject = this.objectSearchService.getObject(editingContext, optionalTargetObjectId.get());
89+
if (semanticObject.isPresent() && semanticObject.get() instanceof Element element) {
90+
if (editingContext instanceof IEMFEditingContext emfEditingContext && this.canHaveNewExpression(emfEditingContext, element)) {
91+
tools.add(new SingleClickOnDiagramElementTool(NEW_EXPRESSION_TOOL_ID, NEW_EXPRESSION_TOOL_LABEL, List.of(), List.of(nodeDescription), "", false, false, List.of()));
92+
}
93+
if (this.canEditExpression(element)) {
94+
tools.add(new SingleClickOnDiagramElementTool(EDIT_EXPRESSION_TOOL_ID, EDIT_EXPRESSION_TOOL_LABEL, List.of(), List.of(nodeDescription), "", false, false, List.of()));
95+
if (!this.metamodelQueryElementService.isTopLevelExpression(element)) {
96+
tools.add(new SingleClickOnDiagramElementTool(DELETE_EXPRESSION_TOOL_ID, DELETE_EXPRESSION_TOOL_LABEL, List.of(), List.of(nodeDescription), "", false, false, List.of()));
97+
}
98+
}
99+
}
100+
}
101+
102+
if (!tools.isEmpty()) {
103+
return List.of(new ToolSection(EXPRESSION_TOOL_SECTION_ID, EXPRESSION_TOOL_SECTION_LABEL, List.of(), tools));
104+
} else {
105+
return List.of();
106+
}
107+
}
108+
109+
private Optional<String> getTargetObjectId(Object diagramElement) {
110+
Optional<String> result = Optional.empty();
111+
if (diagramElement instanceof Node node) {
112+
result = Optional.of(node.getTargetObjectId());
113+
} else if (diagramElement instanceof Edge edge) {
114+
result = Optional.of(edge.getTargetObjectId());
115+
}
116+
return result;
117+
}
118+
119+
private boolean canHaveNewExpression(IEMFEditingContext editingContext, Element element) {
120+
return !this.readOnlyObjectPredicate.test(element) && this.metamodelQueryElementService.canContainExpressionDefinition(element)
121+
&& !this.metamodelQueryElementService.hasSingleExpressionDefinition(element);
122+
}
123+
124+
private boolean canEditExpression(Element element) {
125+
return this.metamodelQueryElementService.isTopLevelExpression(element) || (this.metamodelQueryElementService.hasSingleExpressionDefinition(element)
126+
&& !this.metamodelQueryElementService.hasSingleExpressionDefinition(element.getOwner()));
127+
}
128+
129+
@Override
130+
public List<ITool> createQuickAccessTools(IEditingContext editingContext, DiagramContext diagramContext, Object diagramElementDescription, Object diagramElement) {
131+
return List.of();
132+
}
133+
134+
}

backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/interconnection/view/IVInterconnectionCompartmentToolsTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ public void testQueryPaletteAndExecuteTools() {
113113
assertThat(quickToolsLabels).hasSize(4);
114114
assertThat(quickToolsLabels).containsSequence("Pin", "Adjust size", "Fade", "Hide");
115115
List<String> paletteEntriesLabels = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.paletteEntries[*].label");
116-
assertThat(paletteEntriesLabels).hasSize(5);
117-
assertThat(paletteEntriesLabels).containsSequence("Requirements", "Structure", "Show/Hide", "Related Elements", "Edit");
116+
assertThat(paletteEntriesLabels).hasSize(6);
117+
assertThat(paletteEntriesLabels).containsSequence("Requirements", "Structure", "Show/Hide", "Related Elements", "Edit", "Expression");
118118

119119
List<String> paletteRequirementsSectionToolsLabels = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.paletteEntries[0].tools[*].label");
120120
assertThat(paletteRequirementsSectionToolsLabels).hasSize(1);

backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.eclipse.syson.sysml.Usage;
5959
import org.eclipse.syson.sysml.VariantMembership;
6060
import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
61+
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
6162
import org.eclipse.syson.sysml.metamodel.services.textual.SysMLElementSerializer;
6263
import org.eclipse.syson.sysml.metamodel.services.textual.SysMLSerializingOptions;
6364
import org.eclipse.syson.sysml.metamodel.services.textual.utils.Appender;
@@ -93,6 +94,8 @@ public class DiagramQueryLabelService implements IDiagramLabelService {
9394

9495
private final Logger logger = LoggerFactory.getLogger(DiagramQueryLabelService.class);
9596

97+
private final MetamodelQueryElementService metamodelQueryElementService = new MetamodelQueryElementService();
98+
9699
@Override
97100
public String getIdentificationLabel(Element element) {
98101
StringBuilder label = new StringBuilder();
@@ -508,6 +511,22 @@ private String getCompartmentItemStringRepresentation(Usage usage, boolean direc
508511
return label.toString();
509512
}
510513

514+
/**
515+
* The default begin label for edges.
516+
*
517+
* @param element
518+
* the element to get the edge label from
519+
* @return the begin edge label
520+
*/
521+
public String getBeginEdgeLabel(Element element) {
522+
var optionalExpression = this.metamodelQueryElementService.findSingleExpressionDefinition(element);
523+
if (optionalExpression.isPresent()) {
524+
var expression = optionalExpression.get();
525+
return LabelConstants.OPEN_BRACKET + this.metamodelQueryElementService.getExpressionTextualRepresentation(expression) + LabelConstants.CLOSE_BRACKET;
526+
}
527+
return null;
528+
}
529+
511530
/**
512531
* The default label for edges.
513532
*

backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramQueryAQLService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ public String getDependencyLabel(Dependency dependency) {
137137
return this.diagramQueryLabelService.getDependencyLabel(dependency);
138138
}
139139

140+
/**
141+
* {@link DiagramQueryLabelService#getBeginEdgeLabel(Element)}.
142+
*/
143+
public String getBeginEdgeLabel(Element element) {
144+
return this.diagramQueryLabelService.getBeginEdgeLabel(element);
145+
}
146+
140147
/**
141148
* {@link DiagramQueryLabelService#getEdgeLabel(Element)}.
142149
*/

backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSuccessionEdgeDescriptionProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public EdgeDescription create() {
7878
.domainType(domainType)
7979
.preconditionExpression("aql:graphicalEdgeSource.isInSameGraphicalContainer(graphicalEdgeTarget,cache) and not self.getOwningElement().oclIsKindOf(sysml::TransitionUsage)")
8080
.isDomainBasedEdge(true)
81+
.beginLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getBeginEdgeLabel).aqlSelf())
8182
.centerLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getEdgeLabel).aqlSelf())
8283
.name(this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getSuccession()))
8384
.semanticCandidatesExpression(ServiceMethod.of1(UtilService::getAllReachable).aqlSelf(domainType))

backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private List<ITreeItemContextMenuEntry> getObjectContextMenuEntries(IEMFEditingC
148148
entries.add(new SingleClickTreeItemContextMenuEntry(ExplorerTreeItemContextMenuEntryProvider.NEW_REPRESENTATION, "", List.of(), false, List.of()));
149149
}
150150
entries.add(new SingleClickTreeItemContextMenuEntry(NEW_OBJECTS_FROM_TEXT_MENU_ENTRY_CONTRIBUTION_ID, "", List.of(), false, List.of()));
151-
if (this.canHaveNewExpression(editingContext, treeItem.getId(), object)) {
151+
if (this.canHaveNewExpression(editingContext, object)) {
152152
entries.add(new SingleClickTreeItemContextMenuEntry(CREATE_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID, "", List.of(), false, List.of()));
153153
}
154154

@@ -178,7 +178,7 @@ private boolean canHaveNewRepresentation(IEMFEditingContext editingContext, Stri
178178
return result;
179179
}
180180

181-
private boolean canHaveNewExpression(IEMFEditingContext editingContext, String treeItemId, EObject object) {
181+
private boolean canHaveNewExpression(IEMFEditingContext editingContext, EObject object) {
182182
return !this.readOnlyObjectPredicate.test(object) && object instanceof Element element && this.metamodelQueryElementService.canContainExpressionDefinition(element)
183183
&& !this.metamodelQueryElementService.hasSingleExpressionDefinition(element);
184184
}
71.9 KB
Loading
46.3 KB
Loading

doc/content/modules/user-manual/pages/features/expressions.adoc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ image::expression-details.png[Expression value displayed in the _Details_ veiw,
2525

2626
== Creation
2727

28-
To _create_ an expression, invoke the _New expression_ context menu action on a compatible element (supported elements are `Attributes`, `Constraints` and `Transitions`).
28+
To _create_ an expression, invoke the _New expression_ action on a compatible element (supported elements are `Attributes`, `Constraints` and `Transitions`).
29+
In the {explorer} view, this is available in the element's context menu.
30+
In a _Diagram_, it is avilable in the _Expression_ section of the element's palette (if applicable).
2931

3032
A modal will open where you can enter the textual representation of the expression to create.
3133
Click on the _Update_ button to validate.
@@ -39,7 +41,10 @@ image::edit-expression-modal-error.png[Edit expression modal showing an error me
3941

4042
== Edition
4143

42-
To edit an already existing expression, invoke the _Edit expression_ context menu action directly on the existing expression or on its parent element.
44+
To edit an already existing expression, invoke the _Edit expression_ action directly on the existing expression or on its parent element.
45+
In the {explorer} view, this is available in the element's context menu.
46+
In a _Diagram_, it is avilable in the _Expression_ section of the element's palette (if applicable).
47+
4348
The same modal as for expression creation will open, but with the current textual representation of the expression pre-filled.
4449

4550
Edit the textual representation of the expression as required, and click on the _Update_ button or hit _Ctrl-RET_ to validate.

0 commit comments

Comments
 (0)