Skip to content
Open
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
6 changes: 3 additions & 3 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Users should use the dedicated tools to edit expressions instead, as they ensure
- https://github.com/eclipse-syson/syson/issues/2292[#2292] [diagrams] Add inheritance of "Behavior" and "Step" graphical elements in list items.
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/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

- https://github.com/eclipse-syson/syson/issues/2174[#2174] [diagrams] Add a new edge tool creating a _frame_ graphical edge between a `RequirementUsage` or a `RequirementDefinition` graphical node, and a `ConcernUsage` graphical node.
Expand All @@ -67,10 +67,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,18 @@
/**
* 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 supportsFeatureValueProperties
* whether the effective expression is owned by a {@code FeatureValue}.
* @param featureValueProperties
* the current feature value properties when supported.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the featureValueProperties is optional/nullable, is there a reason to have an explicit separate flag to indicate if it's supported?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current implementation, I do not see a strong reason to keep the separate flag.

What the code does today in backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/ ExpressionTextualRepresentationEventHandler.java:61 is:

  • unsupported: supportsFeatureValueProperties = false and featureValueProperties = null
  • supported with an owning FeatureValue: supportsFeatureValueProperties = true and featureValueProperties != null
  • supported for AttributeUsage without an owning FeatureValue: supportsFeatureValueProperties = true and featureValueProperties = new ... (false, false)

So with the current backend behavior, featureValueProperties == null already means “not supported”, and featureValueProperties != null already means “supported”. The boolean does not add
information.

The only argument for keeping the flag would be API semantics: if you want to model “capability” separately from “current value presence”. That would matter if you wanted a valid state like:

  • supportsFeatureValueProperties = true
  • featureValueProperties = null

But the handler does not currently emit that state, and the frontend could infer support from nullability alone.

So the precise answer is:

  • conceptually: a separate flag can make sense
  • in this codebase as implemented now: it appears redundant

As a conclusion, I will remove this flag.

* @author pcdavid
*/
public record ExpressionTextualRepresentationPayload(UUID id, String textualRepresentation) implements IPayload {
public record ExpressionTextualRepresentationPayload(UUID id, String textualRepresentation, boolean supportsFeatureValueProperties,
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 @@ -21,10 +21,13 @@
import org.eclipse.sirius.components.core.api.IInput;
import org.eclipse.sirius.components.core.api.IObjectSearchService;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.syson.application.expressions.dto.FeatureValueExpressionPropertiesPayload;
import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationInput;
import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationPayload;
import org.eclipse.syson.sysml.AttributeUsage;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Expression;
import org.eclipse.syson.sysml.FeatureValue;
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -55,39 +58,97 @@ 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 = "";
boolean supportsFeatureValueProperties = false;
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());
}
var optionalFeatureValue = this.getFeatureValueContext(optionalElement, optionalExpression);
supportsFeatureValueProperties = optionalFeatureValue.isPresent();
if (optionalFeatureValue.isPresent()) {
FeatureValue owningFeatureValue = optionalFeatureValue.get();
featureValueProperties = new FeatureValueExpressionPropertiesPayload(owningFeatureValue.isIsDefault(), owningFeatureValue.isIsInitial());
} else if (optionalElement.filter(AttributeUsage.class::isInstance).isPresent()) {
supportsFeatureValueProperties = true;
featureValueProperties = new FeatureValueExpressionPropertiesPayload(false, false);
}
}
payloadSink.tryEmitValue(new ExpressionTextualRepresentationPayload(input.id(), textualRepresentation));
payloadSink.tryEmitValue(new ExpressionTextualRepresentationPayload(input.id(), textualRepresentation, supportsFeatureValueProperties, 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 (optionalElement.isPresent()) {
if (element instanceof Expression expression && this.metamodelQueryElementService.isTopLevelExpression(expression)) {
result = Optional.of(expression);
} else {
result = this.metamodelQueryElementService.findSingleExpressionDefinition(element);
}
return result;
}

/**
* Gets the {@link FeatureValue} relevant for the expression editor context, either from the effective expression or
* directly from the selected element.
*
* @param optionalElement
* the selected element, if any.
* @param optionalExpression
* the effective expression, if any.
* @return the relevant {@link FeatureValue}, if any.
*/
private Optional<FeatureValue> getFeatureValueContext(Optional<Element> optionalElement, Optional<Expression> optionalExpression) {
Optional<FeatureValue> result = optionalExpression.flatMap(this::getOwningFeatureValue);
if (result.isEmpty() && 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);
if (element instanceof FeatureValue featureValue) {
result = Optional.of(featureValue);
} else if (element instanceof AttributeUsage attributeUsage) {
result = attributeUsage.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);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
extend type EditingContext {
expressionTextualRepresentation(elementId: ID!): String
expressionTextualRepresentation(elementId: ID!): ExpressionEditorState
}

type ExpressionEditorState {
textualRepresentation: String!
supportsFeatureValueProperties: Boolean!
featureValueProperties: FeatureValueExpressionProperties
}

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

extend type Mutation {
Expand Down
Loading
Loading