Skip to content

Commit 8906c7d

Browse files
committed
[2097] Handle expressions in SysON
Bug: #2097 Signed-off-by: Pierre-Charles David <pierre-charles.david@obeo.fr>
1 parent 50ffcb2 commit 8906c7d

57 files changed

Lines changed: 3896 additions & 182 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
- https://github.com/eclipse-syson/syson/issues/2116[#2116] [explorer] In the _Explorer_ view, the items corresponding to the internals of `Expression` elements (syntax tree) are now hidden by default.
2525
Disabling the _Hide expression internals_ filter in the _Explorer_ view allows to display them if needed.
2626
- https://github.com/eclipse-syson/syson/issues/2112[#2112] [diagrams] Add tools to create _Start_ and _Done_ `StateUsages`, available on `StateUsage` and `StateDefinition` graphical nodes.
27+
- https://github.com/eclipse-syson/syson/issues/2097[#2097] [explorer] Add support for creating and editing exressions through their textual representation.
28+
This is currently supported on `Features` (e.g. `Attribute`), `Constraints` and `Transitions` (guard conditions) view new context menu actions (_Create expression_ and _Edit expression_) on the corresponding elements in the _Explorer_.
2729

2830
== v2026.5.0
2931

backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.eclipse.emf.ecore.resource.Resource;
2626
import org.eclipse.emf.ecore.util.EcoreUtil;
2727
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
28-
import org.eclipse.syson.sysml.Element;
2928
import org.eclipse.emf.edit.provider.ComposedAdapterFactory.Descriptor;
3029
import org.eclipse.sirius.components.collaborative.forms.services.api.IPropertiesDescriptionRegistry;
3130
import org.eclipse.sirius.components.collaborative.forms.services.api.IPropertiesDescriptionRegistryConfigurer;
@@ -63,8 +62,9 @@
6362
import org.eclipse.syson.model.services.ModelMutationElementService;
6463
import org.eclipse.syson.model.services.aql.ModelMutationAQLService;
6564
import org.eclipse.syson.model.services.aql.ModelQueryAQLService;
66-
import org.eclipse.syson.services.UtilService;
65+
import org.eclipse.syson.sysml.Element;
6766
import org.eclipse.syson.sysml.SysmlPackage;
67+
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
6868
import org.eclipse.syson.util.AQLConstants;
6969
import org.eclipse.syson.util.AQLUtils;
7070
import org.eclipse.syson.util.ServiceMethod;
@@ -119,10 +119,10 @@ public class SysMLv2PropertiesConfigurer implements IPropertiesDescriptionRegist
119119

120120
private final ILabelService labelService;
121121

122-
private final UtilService utilService;
123-
124122
private final IReadOnlyObjectPredicate readOnlyObjectPredicate;
125123

124+
private final MetamodelQueryElementService metamodelQueryElementService;
125+
126126
private final List<IDetailsViewHelpTextProvider> detailViewHelpTextProviders;
127127

128128
public SysMLv2PropertiesConfigurer(List<Descriptor> composedAdapterFactoryDescriptors, ViewFormDescriptionConverter converter, IFeedbackMessageService feedbackMessageService,
@@ -133,7 +133,7 @@ public SysMLv2PropertiesConfigurer(List<Descriptor> composedAdapterFactoryDescri
133133
this.labelService = Objects.requireNonNull(labelService);
134134
this.readOnlyObjectPredicate = Objects.requireNonNull(readOnlyObjectPredicate);
135135
this.detailViewHelpTextProviders = Objects.requireNonNull(detailViewHelpTextProviders);
136-
this.utilService = new UtilService();
136+
this.metamodelQueryElementService = new MetamodelQueryElementService();
137137
}
138138

139139
@Override
@@ -155,7 +155,8 @@ public void addPropertiesDescriptions(IPropertiesDescriptionRegistry registry) {
155155

156156
// Convert the View-based FormDescription and register the result into the system
157157
AQLInterpreter interpreter = new AQLInterpreter(List.of(),
158-
List.of(new DetailsViewService(this.composedAdapterFactoryDescriptors, this.feedbackMessageService, this.readOnlyObjectPredicate, this.detailViewHelpTextProviders), this.labelService, this.utilService,
158+
List.of(new DetailsViewService(this.composedAdapterFactoryDescriptors, this.feedbackMessageService, this.readOnlyObjectPredicate, this.metamodelQueryElementService,
159+
this.detailViewHelpTextProviders), this.labelService,
159160
new ModelMutationAQLService(new ModelMutationElementService()), new ModelQueryAQLService(), new FormMutationAQLService(), new FormQueryAQLService()),
160161
List.of(SysmlPackage.eINSTANCE));
161162
ViewConverterResult converterResult = this.converter.convert(viewFormDescription, List.of(), interpreter);
@@ -188,6 +189,8 @@ private FormDescription createDetailsViewForElement() {
188189
pageCore.getGroups().add(this.createExtraAcceptActionUsagePropertiesGroup());
189190
pageCore.getGroups().add(this.createExtraTransitionSourceTargetPropertiesGroup());
190191
pageCore.getGroups().add(this.createFeatureValuePropertiesGroup());
192+
pageCore.getGroups().add(this.createResultExpressionPropertiesGroup());
193+
pageCore.getGroups().add(this.createExpressionPropertiesGroup());
191194

192195
PageDescription pageAdvanced = FormFactory.eINSTANCE.createPageDescription();
193196
pageAdvanced.setName("SysON-DetailsView-Advanced");
@@ -217,7 +220,7 @@ private GroupDescription createFeatureValuePropertiesGroup() {
217220

218221
TextAreaDescription expressionWidget = FormFactory.eINSTANCE.createTextAreaDescription();
219222
expressionWidget.setName("ValueExpression");
220-
expressionWidget.setLabelExpression("Value");
223+
expressionWidget.setLabelExpression("custom:Value");
221224
expressionWidget.setValueExpression(ServiceMethod.of0(DetailsViewService::getValueExpressionTextualRepresentation).aqlSelf());
222225
expressionWidget.setIsEnabledExpression(AQLConstants.AQL_FALSE);
223226

@@ -226,6 +229,52 @@ private GroupDescription createFeatureValuePropertiesGroup() {
226229
return group;
227230
}
228231

232+
/**
233+
* Creates a group to display the value of an Expression.
234+
*
235+
* @return a {@link GroupDescription}
236+
*/
237+
private GroupDescription createExpressionPropertiesGroup() {
238+
GroupDescription group = FormFactory.eINSTANCE.createGroupDescription();
239+
group.setDisplayMode(GroupDisplayMode.LIST);
240+
group.setName("Result");
241+
group.setLabelExpression("");
242+
group.setSemanticCandidatesExpression(ServiceMethod.of0(DetailsViewService::getExpression).aqlSelf());
243+
244+
TextAreaDescription expressionWidget = FormFactory.eINSTANCE.createTextAreaDescription();
245+
expressionWidget.setName("Expression");
246+
expressionWidget.setLabelExpression("custom:Expression");
247+
expressionWidget.setValueExpression(ServiceMethod.of0(DetailsViewService::getExpressionTextualRepresentation).aqlSelf());
248+
expressionWidget.setIsEnabledExpression(AQLConstants.AQL_FALSE);
249+
250+
group.getChildren().add(expressionWidget);
251+
252+
return group;
253+
}
254+
255+
/**
256+
* Creates a group to display the value of a ResultExpression.
257+
*
258+
* @return a {@link GroupDescription}
259+
*/
260+
private GroupDescription createResultExpressionPropertiesGroup() {
261+
GroupDescription group = FormFactory.eINSTANCE.createGroupDescription();
262+
group.setDisplayMode(GroupDisplayMode.LIST);
263+
group.setName("Result");
264+
group.setLabelExpression("");
265+
group.setSemanticCandidatesExpression(ServiceMethod.of0(DetailsViewService::getResultExpression).aqlSelf());
266+
267+
TextAreaDescription expressionWidget = FormFactory.eINSTANCE.createTextAreaDescription();
268+
expressionWidget.setName("ResultExpression");
269+
expressionWidget.setLabelExpression("Result");
270+
expressionWidget.setValueExpression(ServiceMethod.of0(DetailsViewService::getResultExpressionTextualRepresentation).aqlSelf());
271+
expressionWidget.setIsEnabledExpression(AQLConstants.AQL_FALSE);
272+
273+
group.getChildren().add(expressionWidget);
274+
275+
return group;
276+
}
277+
229278
private GroupDescription createCorePropertiesGroup() {
230279
GroupDescription group = FormFactory.eINSTANCE.createGroupDescription();
231280
group.setDisplayMode(GroupDisplayMode.LIST);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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.dto;
14+
15+
import java.util.UUID;
16+
17+
import org.eclipse.sirius.components.core.api.IInput;
18+
19+
/**
20+
* The input object for the {@code deleteExpression} mutation.
21+
*
22+
* @author pcdavid
23+
*/
24+
public record DeleteExpressionInput(UUID id, String editingContextId, String parentElementId) implements IInput {
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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.dto;
14+
15+
import java.util.UUID;
16+
17+
import org.eclipse.sirius.components.core.api.IInput;
18+
19+
/**
20+
* Input for the {@code expressionTextualRepresentation} query field on EditingContext.
21+
*
22+
* @author pcdavid
23+
*/
24+
public record ExpressionTextualRepresentationInput(UUID id, String editingContextId, String elementId) implements IInput {
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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.dto;
14+
15+
import java.util.Objects;
16+
import java.util.UUID;
17+
18+
import org.eclipse.sirius.components.core.api.IPayload;
19+
20+
/**
21+
* Payload for the {@code expressionTextualRepresentation} query field on EditingContext.
22+
*
23+
* @author pcdavid
24+
*/
25+
public record ExpressionTextualRepresentationPayload(UUID id, String textualRepresentation) implements IPayload {
26+
public ExpressionTextualRepresentationPayload {
27+
Objects.requireNonNull(id);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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.graphql;
14+
15+
import java.util.Objects;
16+
import java.util.UUID;
17+
import java.util.concurrent.CompletableFuture;
18+
19+
import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher;
20+
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
21+
import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher;
22+
import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationInput;
23+
import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationPayload;
24+
25+
import graphql.schema.DataFetchingEnvironment;
26+
27+
/**
28+
* Data fetcher for the field {@code EditingContext#expressionTextualRepresentation} to fetch the textual representation
29+
* of a SysMLv2 expression.
30+
*
31+
* @author pcdavid
32+
*/
33+
@QueryDataFetcher(type = "EditingContext", field = "expressionTextualRepresentation")
34+
public class EditingContextExpressionTextualRepresentationDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<String>> {
35+
36+
private static final String ELEMENT_ID_ARGUMENT = "elementId";
37+
38+
private final IEditingContextDispatcher editingContextDispatcher;
39+
40+
public EditingContextExpressionTextualRepresentationDataFetcher(IEditingContextDispatcher editingContextDispatcher) {
41+
this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher);
42+
}
43+
44+
@Override
45+
public CompletableFuture<String> get(DataFetchingEnvironment environment) throws Exception {
46+
String editingContextId = environment.getSource();
47+
String elementId = environment.getArgument(ELEMENT_ID_ARGUMENT);
48+
49+
ExpressionTextualRepresentationInput input = new ExpressionTextualRepresentationInput(UUID.randomUUID(), editingContextId, elementId);
50+
return this.editingContextDispatcher.dispatchQuery(input.editingContextId(), input)
51+
.filter(ExpressionTextualRepresentationPayload.class::isInstance)
52+
.map(ExpressionTextualRepresentationPayload.class::cast)
53+
.map(ExpressionTextualRepresentationPayload::textualRepresentation)
54+
.toFuture();
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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.graphql;
14+
15+
import java.util.Objects;
16+
import java.util.concurrent.CompletableFuture;
17+
18+
import org.eclipse.sirius.components.annotations.spring.graphql.MutationDataFetcher;
19+
import org.eclipse.sirius.components.core.api.IPayload;
20+
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
21+
import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher;
22+
import org.eclipse.sirius.components.graphql.api.IExceptionWrapper;
23+
import org.eclipse.syson.application.expressions.dto.DeleteExpressionInput;
24+
25+
import graphql.schema.DataFetchingEnvironment;
26+
import tools.jackson.databind.ObjectMapper;
27+
28+
/**
29+
* The GraphQL data-fetcher for the {@code deleteExpression} mutation to delete the expression associated to a SysMLv2
30+
* element.
31+
*
32+
* @author pcdavid
33+
*/
34+
@MutationDataFetcher(type = "Mutation", field = "deleteExpression")
35+
public class MutationDeleteExpressionDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<IPayload>> {
36+
37+
private static final String INPUT_ARGUMENT = "input";
38+
39+
private final ObjectMapper objectMapper;
40+
41+
private final IExceptionWrapper exceptionWrapper;
42+
43+
private final IEditingContextDispatcher editingContextDispatcher;
44+
45+
public MutationDeleteExpressionDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) {
46+
this.objectMapper = Objects.requireNonNull(objectMapper);
47+
this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper);
48+
this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher);
49+
}
50+
51+
@Override
52+
public CompletableFuture<IPayload> get(DataFetchingEnvironment environment) throws Exception {
53+
Object argument = environment.getArgument(INPUT_ARGUMENT);
54+
var input = this.objectMapper.convertValue(argument, DeleteExpressionInput.class);
55+
return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture();
56+
}
57+
}

0 commit comments

Comments
 (0)