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 @@ -77,6 +77,7 @@ Additional ports can be created using the _New Port as end_ graphical node tool.
- https://github.com/eclipse-syson/syson/issues/1863[#1863] [diagrams] Dropping an elements on a diagram which is already visible gives feedback again.
- https://github.com/eclipse-syson/syson/issues/2004[#2004] [diagrams] The `ports` and `ends` compartments on a newly created `InterfaceDefinition` graphical nodes are not visible by default.
They can be revealed if needed using the _Manage Visbility_ action on the graphical node.
- https://github.com/eclipse-syson/syson/issues/1949[#1949] [diagrams] Allow redefining a `PartUsage` with the same name as the redefined usage.

=== New features

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.eclipse.syson.AbstractIntegrationTests;
import org.eclipse.syson.GivenSysONServer;
import org.eclipse.syson.application.data.GeneralViewDirectEditTestProjectData;
import org.eclipse.syson.application.data.GeneralViewItemAndAttributeProjectData;
import org.eclipse.syson.application.data.GeneralViewPartUsageRedefinitionProjectData;
import org.eclipse.syson.services.SemanticRunnableFactory;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
import org.eclipse.syson.sysml.PartUsage;
Expand Down Expand Up @@ -649,4 +651,70 @@ public void directEditMultiplicityWithOperation() {
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a diagram with a part WHEN creating a redefinition of another part of the same name through direct edit THEN the redefined part is correctly resolved")
@GivenSysONServer({ GeneralViewPartUsageRedefinitionProjectData.SCRIPT_PATH })
@Test
public void directEditRedefinitionWithSameName() {
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(),
GeneralViewPartUsageRedefinitionProjectData.EDITING_CONTEXT_ID,
GeneralViewPartUsageRedefinitionProjectData.GraphicalIds.DIAGRAM_ID);

var flux = this.givenDiagramSubscription.subscribe(diagramEventInput);

var diagramId = new AtomicReference<String>();
var partNodeId = new AtomicReference<String>();
var partNodeLabelId = new AtomicReference<String>();

Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> {
diagramId.set(diagram.getId());
var partNode = new DiagramNavigator(diagram).nodeWithId(GeneralViewPartUsageRedefinitionProjectData.GraphicalIds.Y_X_NODE_ID).getNode();
partNodeId.set(partNode.getId());
partNodeLabelId.set(partNode.getInsideLabel().getId());
});

Runnable editLabelWithRedfinitionOfSameName = () -> {
var input = new EditLabelInput(UUID.randomUUID(), GeneralViewPartUsageRedefinitionProjectData.EDITING_CONTEXT_ID, diagramId.get(), partNodeLabelId.get(), "x :>> x");
var result = this.editLabelMutationRunner.run(input);

String typename = JsonPath.read(result.data(), "$.data.editLabel.__typename");
assertThat(typename).isEqualTo(EditLabelSuccessPayload.class.getSimpleName());
List<String> messages = JsonPath.read(result.data(), "$.data.editLabel.messages[*].body");
assertThat(messages).hasSize(0);
};

Consumer<Object> updatedDiagramContentMatcherBefore = assertRefreshedDiagramThat(diagram -> {
var node = new DiagramNavigator(diagram).nodeWithId(partNodeId.get()).getNode();
DiagramAssertions.assertThat(node.getInsideLabel()).hasText(LabelConstants.OPEN_QUOTE + "part" + LabelConstants.CLOSE_QUOTE + "\nx :>> x");
});

Runnable redefinedElementsChecker = this.semanticRunnableFactory.createRunnable(GeneralViewPartUsageRedefinitionProjectData.EDITING_CONTEXT_ID,
(editingContext, executeEditingContextFunctionInput) -> {
PartUsage yx = this.objectSearchService.getObject(editingContext, GeneralViewPartUsageRedefinitionProjectData.SemanticIds.Y_X_ID)
.filter(PartUsage.class::isInstance)
.map(PartUsage.class::cast)
.orElse(null);
assertThat(yx).isNotNull();

PartUsage bx = this.objectSearchService.getObject(editingContext, GeneralViewPartUsageRedefinitionProjectData.SemanticIds.B_X_ID)
.filter(PartUsage.class::isInstance)
.map(PartUsage.class::cast)
.orElse(null);
assertThat(bx).isNotNull();

assertThat(yx.getOwnedRedefinition()).hasSize(1);
var redefinition = yx.getOwnedRedefinition().get(0);
assertThat(redefinition.getRedefiningFeature()).isSameAs(yx);
assertThat(redefinition.getRedefinedFeature()).isSameAs(bx);
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
});

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(editLabelWithRedfinitionOfSameName)
.consumeNextWith(updatedDiagramContentMatcherBefore)
.then(redefinedElementsChecker)
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*******************************************************************************
* 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.data;

/**
* Ids for project "GeneralView-GeneralView-PartUsage-redefinition".
*
* @author pcdavid
*/
public class GeneralViewPartUsageRedefinitionProjectData {

public static final String EDITING_CONTEXT_ID = "d84a3d30-a8c8-47a2-a278-f6212169b959";

public static final String SCRIPT_PATH = "/scripts/database-content/GeneralView-PartUsage-redefinition.sql";

/**
* Ids of the graphical elements elements.
*/
public static final class GraphicalIds {
public static final String DIAGRAM_ID = "72cccffa-81b6-4ed6-bc45-8b095440f11d";

public static final String B_X_NODE_ID = "efb2c50e-53c9-3f66-97f2-298c13666cb7";

public static final String Y_X_NODE_ID = "a32582de-926a-3f6c-8df3-72908e714121";
}

/**
* Ids of the semantic elements.
*/
public static final class SemanticIds {
public static final String B_X_ID = "d43d7901-779e-4058-951a-0a0ad6e25733";

public static final String Y_X_ID = "aeee4482-d95c-44d8-900c-36463eca802a";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public void unnamedRedefinedReferenceFeature() throws IOException {
part l : Logical {
part :>> component;
}

part c2 = l.component.component2;
}""";
this.checker.checkImportedModel(resource -> {
Expand All @@ -181,6 +181,31 @@ public void unnamedRedefinedReferenceFeature() throws IOException {
}).check(input);
}

@DisplayName("GIVEN a redefinition of a part with the same name as the redefined part, WHEN importing the model, THEN the redefintion's redefined feature is correctly resolved.")
@Test
public void namedRedefinedReferenceFeature() throws IOException {
var input = """
package Package1 {
part def A;
part def B {
part x : A [0..*];
}

part y : B {
x :>> x; // The second x should point B::x, not to y::x
}
}""";
this.checker.checkImportedModel(resource -> {
List<Redefinition> redefinitions = EMFUtils.allContainedObjectOfType(resource, Redefinition.class).toList();
assertThat(redefinitions).hasSize(1);
var xRedefinition = redefinitions.get(0);
// The redefin*ing* feature (the first "x") is in y
assertThat(xRedefinition.getRedefiningFeature().getOwner().getName()).isEqualTo("y");
// The redefin*ed* feature (the second "x") is the one from B
assertThat(xRedefinition.getRedefinedFeature().getOwningType().getName()).isEqualTo("B");
}).check(input);
}

@Test
@DisplayName("GIVEN a set of Usages, WHEN checking if the usage is referential, THEN the computation should be correct whether or not the ref keyword is present.")
public void testReferentialUsages() throws IOException {
Expand Down Expand Up @@ -1177,7 +1202,7 @@ public void checkDoSendPayloadParameterResolution() throws IOException {
package Package1 {
attribute def Sig ;
part p;

state state1 {
state s2;
state s3;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ private void handleRedefintion(RedefinitionExpressionContext ctx, Usage redefini
var identifier = ctx.qualifiedName();
if (identifier != null) {
var usageAsString = this.getFullText(identifier);
var usage = this.utilService.findByNameAndType(redefining, usageAsString, Feature.class);
var usage = this.resolveRedefinedUsage(redefining, usageAsString);
if (usage == null && this.utilService.isQualifiedName(usageAsString)) {
this.feedbackMessageService.addFeedbackMessage(new Message("The qualified name used for the redefinition does not exist", MessageLevel.ERROR));
return;
Expand All @@ -860,6 +860,22 @@ private void handleRedefintion(RedefinitionExpressionContext ctx, Usage redefini
}
}

private Feature resolveRedefinedUsage(Usage redefiningUsage, String redefinedUsageName) {
// When the redefining usage has the same name as the *redefined* one,
// temporarily rename it so that this.utilService.findByNameAndType()
// correctly resolves the redefined one instead of returning redefiningUsage itself.
boolean nameConflict = Objects.equals(redefiningUsage.getName(), redefinedUsageName);
var originalName = redefiningUsage.getDeclaredName();
if (nameConflict) {
redefiningUsage.setDeclaredName("_" + redefinedUsageName);
}
var usage = this.utilService.findByNameAndType(redefiningUsage, redefinedUsageName, Feature.class);
if (nameConflict) {
redefiningUsage.setDeclaredName(originalName);
}
return usage;
}

private void handleTriggerExpressionName(TransitionUsage transition, TriggerExpressionNameContext triggerExpressionName) {
String name = triggerExpressionName.name().getText();
TypingExpressionContext typingExpression = triggerExpressionName.typingExpression();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ This tool allows to create a `SatisfyRequirementUsage` between the `Feature` and
Additional ends can be created using the _New end_ graphical node tool (which replaces the _New Part as end_ previously available only on `AllocationDefinition` graphical nodes).
** Newly created `InterfaceDefinition` graphical nodes now have two initial _ports_ named _source_ and _target_.
Additional ports can be created using the _New Port as end_ graphical node tool.
- The `ports` and `ends` compartments on a newly created `InterfaceDefinition` graphical nodes are not visible by default.
** The `ports` and `ends` compartments on a newly created `InterfaceDefinition` graphical nodes are not visible by default.
They can be revealed if needed using the _Manage Visbility_ action on the graphical node.
** Allow redefining a `PartUsage` with the same name as the redefined `Usage`.


* In textual import/export:
Expand Down
Loading