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
5 changes: 3 additions & 2 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Users should use the dedicated tools to edit expressions instead, as they ensure
It concerns, at least, _actions_ and _items_ compartments list items.
- https://github.com/eclipse-syson/syson/issues/2284[#2284] [diagrams] The expression-related tools in diagram palette are now available in the default _Edit_ section.
- https://github.com/eclipse-syson/syson/issues/2319[#2319] [diagrams] Remove the margins around the `Actor` image.
- https://github.com/eclipse-syson/syson/issues/2325[#2325] [syson] Add checkboxes in the new Expressions popup allowing to set the _isDefault_ and _isInitial_ properties of `FeatureValue` elements when the expression is `FeatureValue` related.

=== New features

Expand All @@ -70,10 +71,10 @@ Disabling the _Hide expression internals_ filter in the _Explorer_ view allows t
- https://github.com/eclipse-syson/syson/issues/2235[#2235] [diagrams] Leverage the selection dialog to improve the graphical node tool creating a _frame_ `ConcernUsage` from `RequirementUsage` and `RequirementDefinition` graphical nodes.
- https://github.com/eclipse-syson/syson/issues/2242[#2242] [explorer] Add two options to the dialog creating a child element of `RequirementUsage` or `RequirementDefinition` tree items.
One creates a `ConcernUsage` and another one creates `FramedConcernMembership`.
- https://github.com/eclipse-syson/syson/issues/2097[#2097] [explorer] Add support for creating and editing exressions through their textual representation.
- https://github.com/eclipse-syson/syson/issues/2097[#2097] [explorer] Add support for creating and editing expressions through their textual representation.
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_.
- https://github.com/eclipse-syson/syson/issues/2270[#2270] [diagram] Add dedicated tools to diagram elements palette to create/edit/delete expressions.
- https://github.com/eclipse-syson/syson/issues/2247[#2247] [diagrams] Add the support for creating _timeslices/snapshots_ from the different kind of `OccurrenceDefiniton` graphical nodes.
- https://github.com/eclipse-syson/syson/issues/2247[#2247] [diagrams] Add the support for creating _timeslices/snapshots_ from the different kind of `OccurrenceDefinition` graphical nodes.
It leverages the selection dialog to either create an _occurrence timeslice/snapshot_, or the _usage timeslice/snapshot_ matching the `OccurrenceDefinition` on which the tool is applied.
- https://github.com/eclipse-syson/syson/issues/2250[#2250] [diagrams] Leverage the selection dialog to improve the graphical node tools creating a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes.
- https://github.com/eclipse-syson/syson/issues/2254[#2254] [diagrams] Add the support for _assume_ and _require_ graphical edges.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@
/**
* Payload for the {@code expressionTextualRepresentation} query field on EditingContext.
*
* @param id
* the query identifier.
* @param textualRepresentation
* the textual representation of the effective expression, or an empty string if none is available.
* @param featureValueProperties
* the current feature value properties when supported.
Comment thread
pcdavid marked this conversation as resolved.
* @author pcdavid
*/
public record ExpressionTextualRepresentationPayload(UUID id, String textualRepresentation) implements IPayload {
public record ExpressionTextualRepresentationPayload(UUID id, String textualRepresentation,
FeatureValueExpressionPropertiesPayload featureValueProperties) implements IPayload {
public ExpressionTextualRepresentationPayload {
Objects.requireNonNull(id);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2026 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.application.expressions.dto;

/**
* Properties exposed by the expression editor for expressions owned by a SysML {@code FeatureValue}.
*
* @param isDefault
* whether the feature value is marked as default.
* @param isInitial
* whether the feature value is marked as initial.
* @author arichard
*/
public record FeatureValueExpressionPropertiesPayload(boolean isDefault, boolean isInitial) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* @author pcdavid
*/
@QueryDataFetcher(type = "EditingContext", field = "expressionTextualRepresentation")
public class EditingContextExpressionTextualRepresentationDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<String>> {
public class EditingContextExpressionTextualRepresentationDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<ExpressionTextualRepresentationPayload>> {

private static final String ELEMENT_ID_ARGUMENT = "elementId";

Expand All @@ -42,15 +42,14 @@ public EditingContextExpressionTextualRepresentationDataFetcher(IEditingContextD
}

@Override
public CompletableFuture<String> get(DataFetchingEnvironment environment) throws Exception {
public CompletableFuture<ExpressionTextualRepresentationPayload> get(DataFetchingEnvironment environment) throws Exception {
String editingContextId = environment.getSource();
String elementId = environment.getArgument(ELEMENT_ID_ARGUMENT);

ExpressionTextualRepresentationInput input = new ExpressionTextualRepresentationInput(UUID.randomUUID(), editingContextId, elementId);
return this.editingContextDispatcher.dispatchQuery(input.editingContextId(), input)
.filter(ExpressionTextualRepresentationPayload.class::isInstance)
.map(ExpressionTextualRepresentationPayload.class::cast)
.map(ExpressionTextualRepresentationPayload::textualRepresentation)
.toFuture();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationInput;
import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationPayload;
import org.eclipse.syson.application.expressions.dto.FeatureValueExpressionPropertiesPayload;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Expression;
import org.eclipse.syson.sysml.Feature;
import org.eclipse.syson.sysml.FeatureValue;
import org.eclipse.syson.sysml.Function;
import org.eclipse.syson.sysml.Succession;
import org.eclipse.syson.sysml.TransitionUsage;
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -55,39 +61,133 @@ public boolean canHandle(IEditingContext editingContext, IInput input) {
@Override
public void handle(Sinks.One<IPayload> payloadSink, Sinks.Many<ChangeDescription> changeDescriptionSink, IEditingContext editingContext, IInput input) {
String textualRepresentation = "";
FeatureValueExpressionPropertiesPayload featureValueProperties = null;
if (input instanceof ExpressionTextualRepresentationInput expressionTextualRepresentationInput) {
String elementId = expressionTextualRepresentationInput.elementId();
Optional<Expression> optionalExpression = this.getExpression(editingContext, elementId);
Optional<Element> optionalElement = this.getElement(editingContext, elementId);
Optional<Expression> optionalExpression = optionalElement.flatMap(this::getExpression);
if (optionalExpression.isPresent()) {
textualRepresentation = this.metamodelQueryElementService.getExpressionTextualRepresentation(optionalExpression.get());
}
featureValueProperties = this.getFeatureValueProperties(optionalElement, optionalExpression);
}
payloadSink.tryEmitValue(new ExpressionTextualRepresentationPayload(input.id(), textualRepresentation));
payloadSink.tryEmitValue(new ExpressionTextualRepresentationPayload(input.id(), textualRepresentation, featureValueProperties));
}

/**
* Finds the {@link Expression} element to consider given the provided {@code elementId}.
* Gets the target {@link Element} designated by the provided identifier.
*
* @param editingContext
* the editing context.
* @param elementId
* either to id of an actual {@link Expression} element, or of the parent {@link Element} of a single
* the identifier of the target element.
* @return the target {@link Element}, if it exists.
*/
private Optional<Element> getElement(IEditingContext editingContext, String elementId) {
return this.objectSearchService.getObject(editingContext, elementId)
.filter(Element.class::isInstance)
.map(Element.class::cast);
}

/**
* Finds the {@link Expression} element to consider given the provided {@code elementId}.
*
* @param element
* either an actual {@link Expression} element, or the parent {@link Element} of a single
* {@code Expression}.
* @return the directly of indirectly designated {@link Expression}.
*/
private Optional<Expression> getExpression(IEditingContext editingContext, String elementId) {
private Optional<Expression> getExpression(Element element) {
Optional<Expression> result = Optional.empty();
Optional<Element> optionalElement = this.objectSearchService.getObject(editingContext, elementId)
.filter(Element.class::isInstance)
.map(Element.class::cast);
if (element instanceof Expression expression && this.metamodelQueryElementService.isTopLevelExpression(expression)) {
result = Optional.of(expression);
} else {
result = this.metamodelQueryElementService.findSingleExpressionDefinition(element);
}
return result;
}

/**
* Gets the {@link FeatureValueExpressionPropertiesPayload} relevant for the expression editor context.
*
* @param optionalElement
* the selected element, if any.
* @param optionalExpression
* the effective expression, if any.
* @return the relevant {@link FeatureValueExpressionPropertiesPayload}, if any.
*/
private FeatureValueExpressionPropertiesPayload getFeatureValueProperties(Optional<Element> optionalElement, Optional<Expression> optionalExpression) {
FeatureValueExpressionPropertiesPayload result = null;
if (optionalElement.isPresent()) {
Element element = optionalElement.get();
if (element instanceof Expression expression && this.metamodelQueryElementService.isTopLevelExpression(expression)) {
result = optionalElement.map(Expression.class::cast);
} else {
result = this.metamodelQueryElementService.findSingleExpressionDefinition(element);
Optional<FeatureValue> optionalFeatureValue = Optional.empty();
if (element instanceof Expression expression) {
optionalFeatureValue = this.getOwningFeatureValue(expression);
} else if (element instanceof FeatureValue featureValue) {
optionalFeatureValue = Optional.of(featureValue);
} else if (this.supportsFeatureValueProperties(element)) {
optionalFeatureValue = this.getOwnedFeatureValue(element, optionalExpression);
}
if (optionalFeatureValue.isPresent()) {
FeatureValue featureValue = optionalFeatureValue.get();
result = new FeatureValueExpressionPropertiesPayload(featureValue.isIsDefault(), featureValue.isIsInitial());
} else if (optionalElement.filter(this::supportsFeatureValueProperties).isPresent()) {
result = new FeatureValueExpressionPropertiesPayload(false, false);
}
}
return result;
}

/**
* Gets the {@link FeatureValue} directly owned by the selected feature-like element or by the resolved expression.
*
* @param element
* the selected element.
* @param optionalExpression
* the effective expression, if any.
* @return the relevant {@link FeatureValue}, if any.
*/
private Optional<FeatureValue> getOwnedFeatureValue(Element element, Optional<Expression> optionalExpression) {
Optional<FeatureValue> result = optionalExpression.flatMap(this::getOwningFeatureValue);
if (result.isEmpty() && element instanceof Feature feature) {
result = feature.getOwnedRelationship().stream()
.filter(FeatureValue.class::isInstance)
.map(FeatureValue.class::cast)
.findFirst();
}
return result;
}

/**
* Gets the owning {@link FeatureValue} of the provided expression, if it is one.
*
* @param expression
* the expression whose owner should be inspected.
* @return the owning {@link FeatureValue}, if any.
*/
private Optional<FeatureValue> getOwningFeatureValue(Expression expression) {
return Optional.ofNullable(expression.getOwningRelationship())
.filter(FeatureValue.class::isInstance)
.map(FeatureValue.class::cast);
}

/**
* Checks whether the selected element would own its expression through a {@link FeatureValue}, which is the only
* case where the expression editor should expose feature value properties in create mode.
*
* @param element
* the selected element.
* @return {@code true} if feature value properties should be exposed for a new expression on this element.
*/
private boolean supportsFeatureValueProperties(Element element) {
boolean result = false;
if (element instanceof TransitionUsage || element instanceof Succession) {
result = false;
} else if (element instanceof Function || element instanceof Expression) {
result = false;
} else {
result = element instanceof Feature;
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
extend type EditingContext {
expressionTextualRepresentation(elementId: ID!): String
expressionTextualRepresentation(elementId: ID!): ExpressionEditorState
}

type ExpressionEditorState {
textualRepresentation: String!
featureValueProperties: FeatureValueExpressionProperties
}

type FeatureValueExpressionProperties {
isDefault: Boolean!
isInitial: Boolean!
}

extend type Mutation {
Expand Down
Loading
Loading