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
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Both tools optionally allow selecting an existing `StateUsage` to exhibit.
- https://github.com/eclipse-syson/syson/issues/1979[#1979] [diagrams] Add tools to create timeslices and snapshots, available on `OccurrenceUsage`, `ItemUsage`, and `PartUsage` graphical nodes.
These tools leverage a selection dialog to either create a specific timeslice/snapshot graphical node or, if no selection is made, default to a timeslice/snapshot `OccurrenceUsage` graphical node.
- https://github.com/eclipse-syson/syson/issues/2176[#2176] [metamodel] Implements `ownedConcern` and `referencedConcern` of `FramedConcernMembership`.
- 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.

== v2026.3.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*******************************************************************************
* 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.controllers.diagrams.general.view;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat;
import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE;
import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE_STYLE;

import java.time.Duration;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput;
import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload;
import org.eclipse.sirius.components.core.api.IObjectSearchService;
import org.eclipse.sirius.components.diagrams.ArrowStyle;
import org.eclipse.sirius.components.diagrams.Diagram;
import org.eclipse.sirius.components.diagrams.Edge;
import org.eclipse.sirius.components.diagrams.Label;
import org.eclipse.sirius.components.diagrams.ViewModifier;
import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.eclipse.syson.AbstractIntegrationTests;
import org.eclipse.syson.GivenSysONServer;
import org.eclipse.syson.application.controller.editingcontext.checkers.SemanticCheckerService;
import org.eclipse.syson.application.controllers.diagrams.testers.EdgeCreationTester;
import org.eclipse.syson.application.data.GeneralViewWithTopNodesTestProjectData;
import org.eclipse.syson.services.SemanticRunnableFactory;
import org.eclipse.syson.services.diagrams.DiagramComparator;
import org.eclipse.syson.services.diagrams.DiagramDescriptionIdProvider;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramDescription;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator;
import org.eclipse.syson.sysml.FramedConcernMembership;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.helper.LabelConstants;
import org.eclipse.syson.util.IDescriptionNameGenerator;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

/**
* {@link org.eclipse.syson.sysml.FramedConcernMembership} related test in General View.
*
* @author gcoutable
*/
@Transactional
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@SuppressWarnings("checkstyle:MultipleStringLiterals")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GVFramedConcernTests extends AbstractIntegrationTests {

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenDiagramSubscription givenDiagramSubscription;

@Autowired
private IGivenDiagramDescription givenDiagramDescription;

@Autowired
private IDiagramIdProvider diagramIdProvider;

@Autowired
private DiagramComparator diagramComparator;

@Autowired
private SemanticRunnableFactory semanticRunnableFactory;

@Autowired
private IObjectSearchService objectSearchService;

@Autowired
private EdgeCreationTester edgeCreationTester;

private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator();

private SemanticCheckerService semanticCheckerService;

private Flux<DiagramRefreshedEventPayload> givenSubscriptionToDiagram() {
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID);
return this.givenDiagramSubscription.subscribe(diagramEventInput);
}

@BeforeEach
public void setUp() {
this.givenInitialServerState.initialize();
this.semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
}

@Test
public void testCreateFramedConcernWithEdgeBetweenRequirementUsageAndConcernUsage() {
this.createFramedConcernWithEdge(SysmlPackage.eINSTANCE.getRequirementUsage(), GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_USAGE_ID, "requirement");
}

@Test
public void testCreateFramedConcernWithEdgeBetweenRequirementDefinitionAndConcernUsage() {
this.createFramedConcernWithEdge(SysmlPackage.eINSTANCE.getRequirementDefinition(), GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_DEFINITION_ID, "RequirementDefinition");
}

private void createFramedConcernWithEdge(EClass parentEClass, String graphicalSourceNodeId, String parentLabel) {
var flux = this.givenSubscriptionToDiagram();

var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);

var edgeCreationToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(
this.descriptionNameGenerator.getNodeName(parentEClass),
"New framed Concern");

AtomicReference<Diagram> diagram = new AtomicReference<>();
Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set);

Runnable createEdgeRunnable = () -> this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
diagram,
graphicalSourceNodeId,
GeneralViewWithTopNodesTestProjectData.GraphicalIds.CONCERN_USAGE_ID,
edgeCreationToolId);

Consumer<Object> diagramContentConsumerAfterNewEdge = assertRefreshedDiagramThat(newDiagram -> {
var newEdges = this.diagramComparator.newEdges(diagram.get(), newDiagram);
var newVisibleEdges = newEdges.stream().filter(edge -> edge.getState() != ViewModifier.Hidden).toList();

assertThat(newVisibleEdges).hasSize(1).first(EDGE)
.hasSourceId(graphicalSourceNodeId)
.hasTargetId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.CONCERN_USAGE_ID)
.extracting(Edge::getCenterLabel)
.extracting(Label::text)
.hasToString(LabelConstants.OPEN_QUOTE + LabelConstants.FRAME + LabelConstants.CLOSE_QUOTE);

assertThat(newVisibleEdges).hasSize(1).first(EDGE)
.extracting(Edge::getStyle, EDGE_STYLE)
.hasSourceArrow(ArrowStyle.None)
.hasTargetArrow(ArrowStyle.InputArrow);
});

Consumer<Object> additionalCheck = object -> {
assertThat(object).isInstanceOf(List.class)
.asInstanceOf(type(List.class))
.satisfies(framedConcerns -> {
assertThat((List<?>) framedConcerns).size().isEqualTo(1);
assertThat(framedConcerns.getFirst())
.isInstanceOf(FramedConcernMembership.class)
.asInstanceOf(type(FramedConcernMembership.class))
.satisfies(framedConcernMembership -> {
var ownedConcern = framedConcernMembership.getOwnedConcern();
var ownedSubsetting = ownedConcern.getOwnedSubsetting();
assertThat(ownedSubsetting).isNotEmpty();
assertThat(ownedSubsetting.getFirst().getSubsettedFeature()).isEqualTo(framedConcernMembership.getReferencedConcern());
});
});
};

Runnable semanticCheck = this.semanticCheckerService.checkEditingContext(this.semanticCheckerService.getElementInParentSemanticChecker(parentLabel, SysmlPackage.eINSTANCE.getElement_OwnedRelationship(), SysmlPackage.eINSTANCE.getFramedConcernMembership(), additionalCheck));

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(createEdgeRunnable)
.consumeNextWith(diagramContentConsumerAfterNewEdge)
.then(semanticCheck)
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class EdgeCreationTester {
@Autowired
private InvokeSingleClickOnTwoDiagramElementsToolMutationRunner invokeSingleClickOnTwoDiagramElementsToolMutationRunner;

@Deprecated
public void createEdge(String projectId, AtomicReference<Diagram> diagram, String sourceNodeTargetObjectLabel, String targetNodeTargetObjectLabel, String toolId) {
DiagramNavigator diagramNavigator = new DiagramNavigator(diagram.get());
String sourceId = diagramNavigator.nodeWithTargetObjectLabel(sourceNodeTargetObjectLabel).getNode().getId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,27 @@ public class GeneralViewWithTopNodesTestProjectData {
*/
public static class GraphicalIds {

public static final String ACTION_USAGE_ID = "61aaf64a-4fbc-356e-ba73-4bd47b386989";

public static final String ATTRIBUTE_USAGE_ID = "7b8e6835-c563-35cd-8991-e2f894fc2139";

public static final String CONCERN_USAGE_ID = "0999b8c3-d37c-3644-a1d6-b9777a499d11";

public static final String DIAGRAM_ID = "fa8c8a8d-2813-404c-876f-c04e8b297134";

public static final String ITEM_DEFINITION_ID = "df3542d9-6314-3da5-993c-a296f4042674";

public static final String PART_USAGE_ID = "4c4fe0d5-4974-377e-9113-9ab022c75f8c";

public static final String PART_DEFINITION_ID = "fa617798-658e-3812-92f2-52e2fc39f851";

public static final String PART_DEFINITION_TEXTUAL_REP_ID = "3a992e49-95fa-384a-bb54-47284825bf17";

public static final String ACTION_USAGE_ID = "61aaf64a-4fbc-356e-ba73-4bd47b386989";

public static final String STATE_USAGE_ID = "1541c013-2cc7-3dd7-a39f-6e33d07b411e";

public static final String ATTRIBUTE_USAGE_ID = "7b8e6835-c563-35cd-8991-e2f894fc2139";
public static final String REQUIREMENT_USAGE_ID = "3eea9d01-7033-3f31-b7ef-561b5bc86d10";

public static final String ITEM_DEFINITION_ID = "df3542d9-6314-3da5-993c-a296f4042674";
public static final String REQUIREMENT_DEFINITION_ID = "b83f2b54-1602-382d-beb1-c500e08a8684";

public static final String REQUIREMENT_USAGE_ID = "3eea9d01-7033-3f31-b7ef-561b5bc86d10";
public static final String STATE_USAGE_ID = "1541c013-2cc7-3dd7-a39f-6e33d07b411e";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class LabelConstants {

public static final String EQUAL = SysMLv2Keywords.EQUALS;

public static final String FRAME = SysMLv2Keywords.FRAME;

public static final String GREATER_THAN = SysMLv2Keywords.GREATER_THAN;

public static final String IN = SysMLv2Keywords.IN;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public ConcernUsage getReferencedConcern() {
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
* @generated NOT
*/
public ConcernUsage basicGetReferencedConcern() {
var referencedConstraint = super.getReferencedConstraint();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.sirius.components.emf.utils.SiriusEMFCopier;
import org.eclipse.syson.sysml.ConcernUsage;
import org.eclipse.syson.sysml.Definition;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.FeatureTyping;
import org.eclipse.syson.sysml.FramedConcernMembership;
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.PartUsage;
import org.eclipse.syson.sysml.Relationship;
import org.eclipse.syson.sysml.RequirementDefinition;
import org.eclipse.syson.sysml.RequirementUsage;
import org.eclipse.syson.sysml.SatisfyRequirementUsage;
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.Type;
import org.eclipse.syson.sysml.ViewDefinition;
import org.eclipse.syson.sysml.ViewUsage;
import org.eclipse.syson.sysml.helper.EMFUtils;
Expand Down Expand Up @@ -199,6 +203,33 @@ public SatisfyRequirementUsage createSatisfy(Element element, RequirementUsage e
return newSatisfyRequirementUsage;
}

/**
* In a {@link RequirementDefinition}, or a {@link RequirementUsage}, creates {@link FramedConcernMembership} containing a {@link ConcernUsage} subsetted by reference the given concernUsage.
*
* @param type The type that will hold the {@link FramedConcernMembership}, must be a {@link RequirementDefinition}, or a {@link RequirementUsage}
* @param concernUsage The {@link ConcernUsage} subsetted by reference
* @return The created {@link FramedConcernMembership} when type is a {@link RequirementDefinition}, or a {@link RequirementUsage}, {@code null} otherwise
*/
public FramedConcernMembership createFramedConcern(Type type, ConcernUsage concernUsage) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Missing javadoc

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.

Done

if (type instanceof RequirementDefinition || type instanceof RequirementUsage) {
var newFramedConcernMembership = SysmlFactory.eINSTANCE.createFramedConcernMembership();
type.getOwnedRelationship().add(newFramedConcernMembership);

var newConcernUsage = SysmlFactory.eINSTANCE.createConcernUsage();
newFramedConcernMembership.getOwnedRelatedElement().add(newConcernUsage);

var newReferenceSubsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting();
newConcernUsage.getOwnedRelationship().add(newReferenceSubsetting);
newReferenceSubsetting.setReferencedFeature(concernUsage);

this.metamodelMutationElementService.initialize(newFramedConcernMembership);
this.metamodelMutationElementService.initialize(newConcernUsage);
this.metamodelMutationElementService.initialize(newReferenceSubsetting);
return newFramedConcernMembership;
}
return null;
}

/**
* Creates a {@link SatisfyRequirementUsage SatisfyRequirement} on {@code parentElement}.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.syson.model.services.ModelMutationElementService;
import org.eclipse.syson.sysml.ConcernUsage;
import org.eclipse.syson.sysml.Documentation;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Membership;
import org.eclipse.syson.sysml.OccurrenceUsage;
import org.eclipse.syson.sysml.PartUsage;
import org.eclipse.syson.sysml.RequirementUsage;
import org.eclipse.syson.sysml.Type;
import org.eclipse.syson.sysml.ViewUsage;
import org.eclipse.syson.sysml.metamodel.services.MetamodelMutationElementService;

Expand Down Expand Up @@ -79,7 +81,7 @@ public Element createPartUsageAndFlowConnection(PartUsage self) {
}

/**
* {@link ModelMutationElementService#createPartUsageAndFlowConnection(PartUsage)}.
* {@link ModelMutationElementService#createSatisfy(Element, RequirementUsage)}.
*/
public Element createSatisfy(Element element, RequirementUsage existingRequirement) {
return this.modelMutationElementService.createSatisfy(element, existingRequirement);
Expand All @@ -105,4 +107,11 @@ public EObject createOccurrenceInOccurrence(OccurrenceUsage container, EClass eC
public Element setAsView(ViewUsage viewUsage, String newViewDefinition) {
return this.modelMutationElementService.setAsView(viewUsage, newViewDefinition);
}

/**
* {@link ModelMutationElementService#createFramedConcern(Type, ConcernUsage)}.
*/
public Element createFramedConcern(Type type, ConcernUsage concernUsage) {
return this.modelMutationElementService.createFramedConcern(type, concernUsage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@

import java.util.List;

import org.eclipse.syson.sysml.ConcernUsage;
import org.eclipse.syson.sysml.Connector;
import org.eclipse.syson.sysml.ConnectorAsUsage;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Feature;
import org.eclipse.syson.sysml.FramedConcernMembership;
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;

/**
Expand Down Expand Up @@ -68,4 +70,11 @@ public Feature getConnectorSource(Connector connector) {
public List<Feature> getConnectorTarget(ConnectorAsUsage connector) {
return this.metamodelQueryElementService.getConnectorTarget(connector);
}

/**
* {@link MetamodelQueryElementService#getFramedConcernTarget(FramedConcernMembership)}.
*/
public ConcernUsage getFramedConcernTarget(FramedConcernMembership framedConcernMembership) {
return this.metamodelQueryElementService.getFramedConcernTarget(framedConcernMembership);
}
}
Loading
Loading