From 31ba02e3a83ed88a1792732125441c79c5030c6d Mon Sep 17 00:00:00 2001 From: Guillaume Coutable Date: Mon, 22 Jun 2026 17:28:37 +0200 Subject: [PATCH] [2303] Add item inheritance in states and exhibit states compartments Bug: https://github.com/eclipse-syson/syson/issues/2303 Signed-off-by: Guillaume Coutable --- CHANGELOG.adoc | 1 + .../GVCompartmentItemInheritanceTests.java | 532 +++++++++++++----- ...eneralViewWithTopNodesTestProjectData.java | 2 + .../syson/tests/GeneralPurposeTests.java | 9 +- ...ompartmentItemNodeDescriptionProvider.java | 44 ++ ...ompartmentItemNodeDescriptionProvider.java | 6 +- ...tesCompartmentNodeDescriptionProvider.java | 2 + .../InheritedCompartmentItemFilterSwitch.java | 63 ++- .../view/SDVDiagramDescriptionProvider.java | 9 + .../pages/release-notes/2026.7.0.adoc | 2 +- 10 files changed, 504 insertions(+), 166 deletions(-) create mode 100644 backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ExhibitStateInheritedCompartmentItemNodeDescriptionProvider.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 814e01561..339fa6de8 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -73,6 +73,7 @@ It leverages the selection dialog to either create an _occurrence timeslice/snap - https://github.com/eclipse-syson/syson/issues/2254[#2254] [diagrams] Add the support for _assume_ and _require_ graphical edges. - https://github.com/eclipse-syson/syson/issues/2260[#2260] [diagrams] Add the _New Assume Constraint_ or _New Require Constraint_ edge tools to create _assume_ and _require_ graphical edges. - https://github.com/eclipse-syson/syson/issues/2113[#2113] [diagrams] Handle start/end/merge/decision... graphical nodes on Action Flow View diagram background +- https://github.com/eclipse-syson/syson/issues/2303[#2303] [diagrams] Add support for list item inheritance in _states_ and _exhibit states_ compartments == v2026.5.0 diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java index 3f86f6cd3..2dbfc7518 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java @@ -18,6 +18,8 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -65,11 +67,10 @@ */ @Transactional @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) -// CHECKSTYLE:OFF +@SuppressWarnings("checkstyle:MultipleStringLiterals") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { SysONTestsProperties.NO_DEFAULT_LIBRARIES_PROPERTY }) public class GVCompartmentItemInheritanceTests extends AbstractIntegrationTests { - private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); @Autowired @@ -152,184 +153,419 @@ public void checkActionDefinitionParameterFilter() { @DisplayName("GIVEN an ActionDefinition with behavior parameter, WHEN subclassing the ActionDefinition with another ActionDefinition, THEN the other ActionDefinition behavior parameters are inherited from the subclassed ActionDefinition") @Test public void checkActionDefinitionParametersInheritanceWithSubclassification() { - this.checkListItemInBaseElementAreInheritedInSpecializingElement( - SysmlPackage.eINSTANCE.getActionDefinition(), - GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID, - GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID, - "New Parameter In", - "in ref parameter1", - true, - "parameters", - "New Action Definition", - SysmlPackage.eINSTANCE.getActionDefinition(), - "New Subclassification" - ); + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getActionDefinition()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID) + .elementToInheritCreationToolName("New Parameter In") + .elementToInheritExpectedListItemLabelText("in ref parameter1") + .alsoRepresentedByBorderNode() + .compartmentName("parameters") + .elementThatInheritFromBaseElementCreationToolName("New Action Definition") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getActionDefinition()) + .specializationToolName("New Subclassification") + .run(); } @DisplayName("GIVEN a base ActionUsage with behavior parameter, WHEN another ActionUsage is subsetting by reference the base ActionUsage, THEN the base ActionUsage behavior parameters are inherited by the other ActionUsage") @Test public void checkActionUsageParametersInheritanceWithReferenceSubsetting() { - this.checkListItemInBaseElementAreInheritedInSpecializingElement( - SysmlPackage.eINSTANCE.getActionUsage(), - GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, - GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, - "New Parameter Out", - "out ref parameter1", - true, - "parameters", - "New Action", - SysmlPackage.eINSTANCE.getActionUsage(), - "New Reference Subsetting" - ); + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID) + .elementToInheritCreationToolName("New Parameter Out") + .elementToInheritExpectedListItemLabelText("out ref parameter1") + .alsoRepresentedByBorderNode() + .compartmentName("parameters") + .elementThatInheritFromBaseElementCreationToolName("New Action") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .specializationToolName("New Reference Subsetting") + .run(); } @DisplayName("GIVEN an ActionDefinition with an action, WHEN using the ActionDefinition as a feature type of an ActionUsage, THEN the ActionUsage actions are inherited from the ActionDefinition") @Test public void checkActionDefinitionActionsInheritanceWithFeatureTyping() { - this.checkListItemInBaseElementAreInheritedInSpecializingElement( - SysmlPackage.eINSTANCE.getActionDefinition(), - GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID, - GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID, - "New Action", - "action1", - false, - "actions", - "New Action", - SysmlPackage.eINSTANCE.getActionUsage(), - "New Feature Typing" - ); + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getActionDefinition()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID) + .elementToInheritCreationToolName("New Action") + .elementToInheritExpectedListItemLabelText("action1") + .compartmentName("actions") + .elementThatInheritFromBaseElementCreationToolName("New Action") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .specializationToolName("New Feature Typing") + .run(); } @DisplayName("GIVEN a base ActionUsage with an action, WHEN subsetting the base ActionUsage with another ActionUsage, THEN the ActionUsage actions are inherited from the base ActionUsage") @Test public void checkActionUsageActionsInheritanceWithSubsetting() { - this.checkListItemInBaseElementAreInheritedInSpecializingElement( - SysmlPackage.eINSTANCE.getActionUsage(), - GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, - GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, - "New Action", - "action1", - false, - "actions", - "New Action", - SysmlPackage.eINSTANCE.getActionUsage(), - "New Subsetting" - ); + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID) + .elementToInheritCreationToolName("New Action") + .elementToInheritExpectedListItemLabelText("action1") + .compartmentName("actions") + .elementThatInheritFromBaseElementCreationToolName("New Action") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .specializationToolName("New Subsetting") + .run(); } @DisplayName("GIVEN a base ActionUsage with an item, WHEN subsetting the base ActionUsage with another ActionUsage, THEN the ActionUsage items are inherited from the base ActionUsage") @Test public void checkActionUsageItemsInheritanceWithSubsetting() { - this.checkListItemInBaseElementAreInheritedInSpecializingElement( - SysmlPackage.eINSTANCE.getActionUsage(), - GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, - GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, - "New Item", - "item1", - false, - "items", - "New Action", - SysmlPackage.eINSTANCE.getActionUsage(), - "New Subsetting" - ); + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID) + .elementToInheritCreationToolName("New Item") + .elementToInheritExpectedListItemLabelText("item1") + .compartmentName("items") + .elementThatInheritFromBaseElementCreationToolName("New Action") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .specializationToolName("New Subsetting") + .run(); } @DisplayName("GIVEN an ActionUsage with an action, WHEN a StateUsage is subsetting by reference the ActionUsage, THEN the ActionUsage actions are inherited by the StateUsage") @Test public void checkActionUsageActionsInheritanceWithReferenceSubsetting() { - this.checkListItemInBaseElementAreInheritedInSpecializingElement( - SysmlPackage.eINSTANCE.getActionUsage(), - GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, - GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, - "New Action", - "action1", - false, - "actions", - "New State", - SysmlPackage.eINSTANCE.getStateUsage(), - "New Reference Subsetting" - ); + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getActionUsage()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID) + .elementToInheritCreationToolName("New Action") + .elementToInheritExpectedListItemLabelText("action1") + .compartmentName("actions") + .elementThatInheritFromBaseElementCreationToolName("New State") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .specializationToolName("New Reference Subsetting") + .run(); } - private void checkListItemInBaseElementAreInheritedInSpecializingElement(EClass baseElementToInheritFrom, String baseElementToInheritFromTargetObjectId, String baseElementToInheritFromNodeId, String elementToInheritCreationToolName, String elementToInheritExpectedListItemLabelText, boolean alsoRepresentedByBorderNode, String compartmentName, String elementThatInheritFromBaseElementCreationToolName, EClass elementThatInheritFromBaseElementEClass, String specializationToolName) { - 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); - - AtomicReference diagram = new AtomicReference<>(); - Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); - - // Create an element in the base element to inherit - String createActionToolId = diagramDescriptionIdProvider.getNodeToolId(this.descriptionNameGenerator.getNodeName(baseElementToInheritFrom), elementToInheritCreationToolName); - Runnable newCreationTool = () -> this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, baseElementToInheritFromTargetObjectId, createActionToolId); - Consumer createdActionDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { - int expectedBorderNodeCount = 0; - if (alsoRepresentedByBorderNode) { - expectedBorderNodeCount = 1; - } - new CheckDiagramElementCount(this.diagramComparator) - .hasNewBorderNodeCount(expectedBorderNodeCount) // The element is created and sometimes can be represented as a border node - .hasNewNodeCount(1) // The element is created and represented as a list item - .hasNewEdgeCount(0) - .check(diagram.get(), newDiagram, true); - - var actionsCompartment = new DiagramNavigator(newDiagram) - .nodeWithTargetObjectId(baseElementToInheritFromTargetObjectId) - .childNodeWithLabel(compartmentName) - .getNode(); - assertThat(actionsCompartment.getChildNodes()).hasSize(1); - assertThat(actionsCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo(elementToInheritExpectedListItemLabelText); // The created element has the expected name - diagram.set(newDiagram); - }); - + @DisplayName("GIVEN a base PartDefinition with a state, WHEN another PartDefinition is subclassing the base PartDefinition, THEN the base PartDefinition states are inherited by the other PartDefinition") + @Test + public void checkParDefinitionStatesInheritanceWithSubclassification() { + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getPartDefinition()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_DEFINITION_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.PART_DEFINITION_ID) + .elementToInheritCreationToolName("New State") + .elementToInheritExpectedListItemLabelText("state1") + .compartmentName("states") + .elementThatInheritFromBaseElementCreationToolName("New Part Definition") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getPartDefinition()) + .specializationToolName("New Subclassification") + .run(); + } - // Create an new element that will inherit from the base element - AtomicReference newActionActionUsageNodeId = new AtomicReference<>(); - String createActionUsageToolId = diagramDescriptionIdProvider.getDiagramCreationToolId(elementThatInheritFromBaseElementCreationToolName); - Runnable createActionDefinitionRunnable = () -> this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, null, createActionUsageToolId); - Consumer createdActionDefinitionDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { - new CheckDiagramElementCount(this.diagramComparator) - .hasNewBorderNodeCount(0) - .hasNewNodeCount(1) // The new element is created - .hasNewEdgeCount(0) - .check(diagram.get(), newDiagram, true); - var newNodes = this.diagramComparator.newNodes(diagram.get(), newDiagram); - newActionActionUsageNodeId.set(newNodes.getFirst().getId()); - diagram.set(newDiagram); - }); + @DisplayName("GIVEN a StateDefinition with a state, WHEN a StateUsage is typed by the StateDefinition, THEN the StateDefinition states are inherited by the StateUsage") + @Test + public void checkStateDefinitionStatesInheritanceWithFeatureTyping() { + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getStateDefinition()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_DEFINITION_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.STATE_DEFINITION_ID) + .elementToInheritCreationToolName("New State") + .elementToInheritExpectedListItemLabelText("state1") + .compartmentName("states") + .elementThatInheritFromBaseElementCreationToolName("New State") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .specializationToolName("New Feature Typing") + .run(); + } + @DisplayName("GIVEN a base StateUsage with a state, WHEN another StateUsage redefines the base StateUsage, THEN the base StateUsage states are inherited by the redefining StateUsage") + @Test + public void checkStateUsageStatesInheritanceWithRedefinition() { + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_USAGE_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.STATE_USAGE_ID) + .elementToInheritCreationToolName("New State") + .elementToInheritExpectedListItemLabelText("state1") + .compartmentName("states") + .elementThatInheritFromBaseElementCreationToolName("New State") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .specializationToolName("New Redefinition") + .run(); + } - // Create the specialization between the newly created element and the base element - String createFeatureTypeToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(this.descriptionNameGenerator.getNodeName(elementThatInheritFromBaseElementEClass), specializationToolName); - Runnable createFeatureTyping = () -> this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, newActionActionUsageNodeId.get(), baseElementToInheritFromNodeId, createFeatureTypeToolId); + @DisplayName("GIVEN a base PartDefinition with an exhibit state, WHEN another PartDefinition is subclassing the base PartDefinition, THEN the base PartDefinition exhibit states are inherited by the other PartDefinition") + @Test + public void checkPartDefinitionExhibitStatesInheritanceWithSubclassification() { + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getPartDefinition()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_DEFINITION_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.PART_DEFINITION_ID) + .elementToInheritCreationToolName("New Exhibit State") + .withSelectedElementId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_USAGE_ID) + .elementToInheritExpectedListItemLabelText("ref ::> state") + .compartmentName("exhibit states") + .elementThatInheritFromBaseElementCreationToolName("New Part Definition") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getPartDefinition()) + .specializationToolName("New Subclassification") + .run(); + } - // Check new created element inherits from the base element, and thus, contains a list item with '^' in its label - Consumer createFeatureTypeDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { - new CheckDiagramElementCount(this.diagramComparator) - .hasNewBorderNodeCount(0) - .hasNewNodeCount(1) // The list item inherited from the base element - .hasNewEdgeCount(1) // The specialization edge - .check(diagram.get(), newDiagram); + @DisplayName("GIVEN a StateDefinition with an exhibit state, WHEN a StateUsage is typed by the StateDefinition, THEN the StateDefinition exhibit states are inherited by the StateUsage") + @Test + public void checkStateDefinitionExhibitStatesInheritanceWithFeatureTyping() { + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getStateDefinition()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_DEFINITION_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.STATE_DEFINITION_ID) + .elementToInheritCreationToolName("New Exhibit State") + .withSelectedElementId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_USAGE_ID) + .elementToInheritExpectedListItemLabelText("ref ::> state") + .compartmentName("exhibit states") + .elementThatInheritFromBaseElementCreationToolName("New State") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .specializationToolName("New Feature Typing") + .run(); + } - var parameterCompartment = new DiagramNavigator(newDiagram) - .nodeWithId(newActionActionUsageNodeId.get()) - .childNodeWithLabel(compartmentName) - .getNode(); - assertThat(parameterCompartment.getChildNodes()).hasSize(1); - assertThat(parameterCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo("^" + elementToInheritExpectedListItemLabelText); // The inheriting element has the same name prefixed with '^' - }); + @DisplayName("GIVEN a base StateUsage with an exhibit state, WHEN another StateUsage redefines the base StateUsage, THEN the base StateUsage exhibit states are inherited by the redefining StateUsage") + @Test + public void checkStateUsageExhibitStatesInheritanceWithRedefinition() { + new ElementSpecializationInheritanceTestRunner() + .baseElementToInheritFromEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .baseElementToInheritFromTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_USAGE_ID) + .baseElementToInheritFromNodeId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.STATE_USAGE_ID) + .elementToInheritCreationToolName("New Exhibit State") + .withSelectedElementId(GeneralViewWithTopNodesTestProjectData.SemanticIds.STATE_USAGE_ID) + .elementToInheritExpectedListItemLabelText("ref ::> state") + .compartmentName("exhibit states") + .elementThatInheritFromBaseElementCreationToolName("New State") + .elementThatInheritFromBaseElementEClass(SysmlPackage.eINSTANCE.getStateUsage()) + .specializationToolName("New Redefinition") + .run(); + } - StepVerifier.create(flux) - .consumeNextWith(initialDiagramContentConsumer) - .then(newCreationTool) - .consumeNextWith(createdActionDiagramConsumer) - .then(createActionDefinitionRunnable) - .consumeNextWith(createdActionDefinitionDiagramConsumer) - .then(createFeatureTyping) - .consumeNextWith(createFeatureTypeDiagramConsumer) - .thenCancel() - .verify(Duration.ofSeconds(10)); + /** + * This test runner verifies that creating a specializing relationship create inherited elements. + * + * It executes the following steps then verifies the inherited elements. + *
    + *
  1. Applies the tool {@code elementToInheritCreationToolName} with the optionally provided {@code selectedElementId} on the base element with the EClass {@code baseElementToInheritFromEClass}, with the target object ID {@code baseElementToInheritFromTargetObjectId}, represented by the graphical node identified by {@code baseElementToInheritFromNodeId}
  2. + *
  3. Applies the node tool {@code elementThatInheritFromBaseElementCreationToolName} to create a node on the diagram background
  4. + *
  5. Applies the tool {@code specializationToolName} to create a specialization relationship between the created node and the base element
  6. + *
+ */ + @SuppressWarnings("checkstyle:HiddenField") + private final class ElementSpecializationInheritanceTestRunner { + + /** + * The EClass of the element on which the {@code elementToInheritCreationToolName} tool will be applied. + */ + private EClass baseElementToInheritFromEClass; + + /** + * The target object ID of the element on which the {@code elementToInheritCreationToolName} tool will be applied. + */ + private String baseElementToInheritFromTargetObjectId; + + /** + * The selected element required for the creation tool applied on the base element. + */ + private Optional selectedElementId = Optional.empty(); + + /** + * The graphical node ID that represents the element on which the {@code elementToInheritCreationToolName} tool will be applied. + */ + private String baseElementToInheritFromNodeId; + + /** + * The tool name to apply on the base element. + */ + private String elementToInheritCreationToolName; + + /** + * The list item label of the element created by the {@code elementToInheritCreationToolName} tool. + */ + private String elementToInheritExpectedListItemLabelText; + + /** + * If element created by the {@code elementToInheritCreationToolName} tool is also represented by a graphical border node. + */ + private boolean alsoRepresentedByBorderNode; + + /** + * The compartment name in which the element created by the {@code elementToInheritCreationToolName} tool is created. + */ + private String compartmentName; + + /** + * The tool name used to create an element that will inherit from the base element. + */ + private String elementThatInheritFromBaseElementCreationToolName; + + /** + * The EClass of the element created by the {@code elementThatInheritFromBaseElementCreationToolName} tool. + */ + private EClass elementThatInheritFromBaseElementEClass; + + /** + * The tool name used to create the specialization relationship between the element created by the {@code elementToInheritCreationToolName} tool, and the base element. + */ + private String specializationToolName; + + public ElementSpecializationInheritanceTestRunner baseElementToInheritFromEClass(EClass baseElementToInheritFromEClass) { + this.baseElementToInheritFromEClass = Objects.requireNonNull(baseElementToInheritFromEClass); + return this; + } + + public ElementSpecializationInheritanceTestRunner baseElementToInheritFromTargetObjectId(String baseElementToInheritFromTargetObjectId) { + this.baseElementToInheritFromTargetObjectId = Objects.requireNonNull(baseElementToInheritFromTargetObjectId); + return this; + } + + public ElementSpecializationInheritanceTestRunner withSelectedElementId(String selectedElementId) { + this.selectedElementId = Optional.of(selectedElementId); + return this; + } + + public ElementSpecializationInheritanceTestRunner baseElementToInheritFromNodeId(String baseElementToInheritFromNodeId) { + this.baseElementToInheritFromNodeId = Objects.requireNonNull(baseElementToInheritFromNodeId); + return this; + } + + public ElementSpecializationInheritanceTestRunner elementToInheritCreationToolName(String elementToInheritCreationToolName) { + this.elementToInheritCreationToolName = Objects.requireNonNull(elementToInheritCreationToolName); + return this; + } + + public ElementSpecializationInheritanceTestRunner elementToInheritExpectedListItemLabelText(String elementToInheritExpectedListItemLabelText) { + this.elementToInheritExpectedListItemLabelText = Objects.requireNonNull(elementToInheritExpectedListItemLabelText); + return this; + } + + public ElementSpecializationInheritanceTestRunner alsoRepresentedByBorderNode() { + this.alsoRepresentedByBorderNode = true; + return this; + } + + public ElementSpecializationInheritanceTestRunner compartmentName(String compartmentName) { + this.compartmentName = Objects.requireNonNull(compartmentName); + return this; + } + + public ElementSpecializationInheritanceTestRunner elementThatInheritFromBaseElementCreationToolName(String elementThatInheritFromBaseElementCreationToolName) { + this.elementThatInheritFromBaseElementCreationToolName = Objects.requireNonNull(elementThatInheritFromBaseElementCreationToolName); + return this; + } + + public ElementSpecializationInheritanceTestRunner elementThatInheritFromBaseElementEClass(EClass elementThatInheritFromBaseElementEClass) { + this.elementThatInheritFromBaseElementEClass = Objects.requireNonNull(elementThatInheritFromBaseElementEClass); + return this; + } + + public ElementSpecializationInheritanceTestRunner specializationToolName(String specializationToolName) { + this.specializationToolName = Objects.requireNonNull(specializationToolName); + return this; + } + + private void isReady() { + Objects.requireNonNull(this.baseElementToInheritFromEClass); + Objects.requireNonNull(this.baseElementToInheritFromTargetObjectId); + Objects.requireNonNull(this.baseElementToInheritFromNodeId); + Objects.requireNonNull(this.elementToInheritCreationToolName); + Objects.requireNonNull(this.elementToInheritExpectedListItemLabelText); + Objects.requireNonNull(this.compartmentName); + Objects.requireNonNull(this.elementThatInheritFromBaseElementCreationToolName); + Objects.requireNonNull(this.elementThatInheritFromBaseElementEClass); + Objects.requireNonNull(this.specializationToolName); + } + + public void run() { + this.isReady(); + + var flux = GVCompartmentItemInheritanceTests.this.givenSubscriptionToDiagram(); + var diagramDescription = GVCompartmentItemInheritanceTests.this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, GVCompartmentItemInheritanceTests.this.diagramIdProvider); + + AtomicReference diagram = new AtomicReference<>(); + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + List toolVariables = new ArrayList<>(); + selectedElementId.ifPresent(s -> toolVariables.add(new ToolVariable("selectedObject", s, ToolVariableType.OBJECT_ID))); + + // Create an element in the base element to inherit + String createActionToolId = diagramDescriptionIdProvider.getNodeToolId(GVCompartmentItemInheritanceTests.this.descriptionNameGenerator.getNodeName(this.baseElementToInheritFromEClass), this.elementToInheritCreationToolName); + Runnable newCreationTool = () -> GVCompartmentItemInheritanceTests.this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, this.baseElementToInheritFromTargetObjectId, createActionToolId, toolVariables); + Consumer createdActionDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + int expectedBorderNodeCount = 0; + if (this.alsoRepresentedByBorderNode) { + expectedBorderNodeCount = 1; + } + new CheckDiagramElementCount(GVCompartmentItemInheritanceTests.this.diagramComparator) + .hasNewBorderNodeCount(expectedBorderNodeCount) // The element is created and sometimes can be represented as a border node + .hasNewNodeCount(1) // The element is created and represented as a list item + .hasNewEdgeCount(0) + .check(diagram.get(), newDiagram, true); + + var actionsCompartment = new DiagramNavigator(newDiagram) + .nodeWithTargetObjectId(this.baseElementToInheritFromTargetObjectId) + .childNodeWithLabel(this.compartmentName) + .getNode(); + assertThat(actionsCompartment.getChildNodes()).hasSize(1); + assertThat(actionsCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo(this.elementToInheritExpectedListItemLabelText); // The created element has the expected name + diagram.set(newDiagram); + }); + + + // Create a new element that will inherit from the base element + AtomicReference newActionActionUsageNodeId = new AtomicReference<>(); + String createActionUsageToolId = diagramDescriptionIdProvider.getDiagramCreationToolId(this.elementThatInheritFromBaseElementCreationToolName); + Runnable createActionDefinitionRunnable = () -> GVCompartmentItemInheritanceTests.this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, null, createActionUsageToolId); + Consumer createdActionDefinitionDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(GVCompartmentItemInheritanceTests.this.diagramComparator) + .hasNewBorderNodeCount(0) + .hasNewNodeCount(1) // The new element is created + .hasNewEdgeCount(0) + .check(diagram.get(), newDiagram, true); + var newNodes = GVCompartmentItemInheritanceTests.this.diagramComparator.newNodes(diagram.get(), newDiagram); + newActionActionUsageNodeId.set(newNodes.getFirst().getId()); + diagram.set(newDiagram); + }); + + + // Create the specialization between the newly created element and the base element + String createFeatureTypeToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(GVCompartmentItemInheritanceTests.this.descriptionNameGenerator.getNodeName(this.elementThatInheritFromBaseElementEClass), this.specializationToolName); + Runnable createFeatureTyping = () -> GVCompartmentItemInheritanceTests.this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, newActionActionUsageNodeId.get(), this.baseElementToInheritFromNodeId, createFeatureTypeToolId); + + // Check new created element inherits from the base element, and thus, contains a list item with '^' in its label + Consumer createFeatureTypeDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(GVCompartmentItemInheritanceTests.this.diagramComparator) + .hasNewBorderNodeCount(0) + .hasNewNodeCount(1) // The list item inherited from the base element + .hasNewEdgeCount(1) // The specialization edge + .check(diagram.get(), newDiagram); + + var parameterCompartment = new DiagramNavigator(newDiagram) + .nodeWithId(newActionActionUsageNodeId.get()) + .childNodeWithLabel(this.compartmentName) + .getNode(); + assertThat(parameterCompartment.getChildNodes()).hasSize(1); + assertThat(parameterCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo("^" + this.elementToInheritExpectedListItemLabelText); // The inheriting element has the same name prefixed with '^' + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(newCreationTool) + .consumeNextWith(createdActionDiagramConsumer) + .then(createActionDefinitionRunnable) + .consumeNextWith(createdActionDefinitionDiagramConsumer) + .then(createFeatureTyping) + .consumeNextWith(createFeatureTypeDiagramConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } } } -// CHECKSTYLE:ON diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java index f6a297cc3..7e9d2e04d 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewWithTopNodesTestProjectData.java @@ -64,6 +64,8 @@ public static class GraphicalIds { public static final String REQUIREMENT_USAGE_ID = "3eea9d01-7033-3f31-b7ef-561b5bc86d10"; + public static final String STATE_DEFINITION_ID = "dbf7eccb-35e6-3ff1-b7b8-fc71f1a4094a"; + public static final String STATE_USAGE_ID = "1541c013-2cc7-3dd7-a39f-6e33d07b411e"; } diff --git a/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java b/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java index 86751463d..4042d31a0 100644 --- a/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java +++ b/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java @@ -117,6 +117,10 @@ public class GeneralPurposeTests { "/org/eclipse/syson/direct/edit/grammars" ); + private static final List CHECKSTYLE_HIDDEN_FIELD_WHITELIST_CLASSNAME = List.of( + "ElementSpecializationInheritanceTestRunner" + ); + /** * Finds the folder containing the Git repository. * @@ -216,7 +220,7 @@ private void testNoSuppressWarnings(int index, String line, Path javaFilePath, L if (line.contains(CHECKSTYLE_HIDDEN_FIELD)) { isValidUsage = true; isValidUsage = isValidUsage && lines.size() > index; - isValidUsage = isValidUsage && lines.get(index + 1).contains(BUILDER); + isValidUsage = isValidUsage && (lines.get(index + 1).contains(BUILDER) || CHECKSTYLE_HIDDEN_FIELD_WHITELIST_CLASSNAME.stream().anyMatch(whiteListClassName -> lines.get(index + 1).contains(whiteListClassName))); } else if (line.contains(CHECKSTYLE_ILLEGAL_CATCH)) { isValidUsage = true; } else if (line.contains(CHECKSTYLE_MULTIPLE_STRING_LITERALS)) { @@ -237,8 +241,7 @@ private void testNoSuppressWarnings(int index, String line, Path javaFilePath, L private void testNoCheckstyleOff(int index, String line, Path javaFilePath) { var whitelist = Stream.of( Path.of("ServiceMethod.java"), - Path.of("GVTopNodeCreationTests.java"), - Path.of("GVCompartmentItemInheritanceTests.java") + Path.of("GVTopNodeCreationTests.java") ); if (whitelist.filter(javaFilePath::endsWith).findFirst().isEmpty()) { if (line.contains(CHECKSTYLE_OFF)) { diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ExhibitStateInheritedCompartmentItemNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ExhibitStateInheritedCompartmentItemNodeDescriptionProvider.java new file mode 100644 index 000000000..04704339e --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ExhibitStateInheritedCompartmentItemNodeDescriptionProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.diagram.common.view.nodes; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.sirius.components.view.builder.providers.IColorProvider; +import org.eclipse.sirius.components.view.diagram.NodeDescription; +import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.SysMLMetamodelHelper; + +/** + * The inherited exhibit state compartment list item node description provider. + * + * @author gcoutable + */ +public class ExhibitStateInheritedCompartmentItemNodeDescriptionProvider extends InheritedCompartmentItemNodeDescriptionProvider { + + public ExhibitStateInheritedCompartmentItemNodeDescriptionProvider(EClass eClass, EReference eReference, IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { + super(eClass, eReference, colorProvider, descriptionNameGenerator); + } + + @Override + public NodeDescription create() { + NodeDescription nd = super.create(); + var qualifiedName = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getExhibitStateUsage()); + nd.setName(this.descriptionNameGenerator.getInheritedCompartmentItemName(this.eClass, this.eReference) + StatesCompartmentNodeDescriptionProvider.EXHIBIT_STATES_NAME); + nd.setDomainType(qualifiedName); + nd.setPreconditionExpression(AQLConstants.AQL_SELF + ".oclIsTypeOf(" + qualifiedName + ")"); + return nd; + } +} diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/InheritedCompartmentItemNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/InheritedCompartmentItemNodeDescriptionProvider.java index f2007edf9..50fcd7665 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/InheritedCompartmentItemNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/InheritedCompartmentItemNodeDescriptionProvider.java @@ -45,11 +45,11 @@ */ public class InheritedCompartmentItemNodeDescriptionProvider extends AbstractNodeDescriptionProvider { - private final EClass eClass; + protected final EClass eClass; - private final EReference eReference; + protected final EReference eReference; - private final IDescriptionNameGenerator descriptionNameGenerator; + protected final IDescriptionNameGenerator descriptionNameGenerator; public InheritedCompartmentItemNodeDescriptionProvider(EClass eClass, EReference eReference, IColorProvider colorProvider, IDescriptionNameGenerator descriptionNameGenerator) { super(colorProvider); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StatesCompartmentNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StatesCompartmentNodeDescriptionProvider.java index b127f556f..3022b5a47 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StatesCompartmentNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/StatesCompartmentNodeDescriptionProvider.java @@ -82,11 +82,13 @@ public void link(DiagramDescription diagramDescription, IViewDiagramElementFinde if (this.showsExhibitOnly) { cache.getNodeDescription(this.getDescriptionNameGenerator().getCompartmentName(this.eClass, this.eReference) + EXHIBIT_STATES_NAME).ifPresent(nd -> { cache.getNodeDescription(this.getDescriptionNameGenerator().getCompartmentItemName(this.eClass, this.eReference) + EXHIBIT_STATES_NAME).ifPresent(nd.getChildrenDescriptions()::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getInheritedCompartmentItemName(this.eClass, eReference) + EXHIBIT_STATES_NAME).ifPresent(nd.getChildrenDescriptions()::add); nd.setPalette(this.createCompartmentPalette(cache)); }); } else { cache.getNodeDescription(this.getDescriptionNameGenerator().getCompartmentName(this.eClass, this.eReference) + STATES_NAME).ifPresent(nd -> { cache.getNodeDescription(this.getDescriptionNameGenerator().getCompartmentItemName(this.eClass, this.eReference) + STATES_NAME).ifPresent(nd.getChildrenDescriptions()::add); + cache.getNodeDescription(this.getDescriptionNameGenerator().getInheritedCompartmentItemName(this.eClass, eReference)).ifPresent(nd.getChildrenDescriptions()::add); nd.setPalette(this.createCompartmentPalette(cache)); }); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java index e539e5dd5..e0cfd3a00 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java @@ -21,15 +21,19 @@ import org.eclipse.emf.ecore.EReference; import org.eclipse.syson.sysml.Behavior; import org.eclipse.syson.sysml.ConstraintUsage; +import org.eclipse.syson.sysml.Definition; +import org.eclipse.syson.sysml.ExhibitStateUsage; import org.eclipse.syson.sysml.Feature; import org.eclipse.syson.sysml.OwningMembership; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.ReferenceUsage; import org.eclipse.syson.sysml.RequirementConstraintKind; import org.eclipse.syson.sysml.RequirementConstraintMembership; +import org.eclipse.syson.sysml.StateUsage; import org.eclipse.syson.sysml.Step; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.sysml.Type; +import org.eclipse.syson.sysml.Usage; import org.eclipse.syson.sysml.util.SysmlSwitch; /** @@ -50,17 +54,6 @@ public Boolean defaultCase(EObject object) { return false; } - @Override - public Boolean caseFeature(Feature object) { - // Add this behavior parameter check for each caseXXXUsage. - // In this case, we want to display inherited parameters (directed feature) but not all features with the same - // type. - if (this.shouldConsiderParameter(object)) { - return this.isInheritedParameter(object); - } - return this.eReference.getEType().equals(object.eClass()); - } - @Override public Boolean caseConstraintUsage(ConstraintUsage object) { boolean keep = true; @@ -80,6 +73,35 @@ public Boolean caseConstraintUsage(ConstraintUsage object) { return keep && this.caseFeature(object); } + @Override + public Boolean caseExhibitStateUsage(ExhibitStateUsage object) { + Boolean shouldKeep = Boolean.FALSE; + // Add this behavior parameter check for each caseXXXUsage. + // In this case, we want to display inherited parameters (directed feature) but not all features with the same + // type. + if (!shouldKeep && this.shouldConsiderParameter(object)) { + shouldKeep = this.isInheritedParameter(object); + } + if (!shouldKeep && this.shouldConsiderExhibitState(object)) { + shouldKeep = this.isInheritedState(object); + } + if (!shouldKeep) { + shouldKeep = super.caseExhibitStateUsage(object); + } + return shouldKeep; + } + + @Override + public Boolean caseFeature(Feature object) { + // Add this behavior parameter check for each caseXXXUsage. + // In this case, we want to display inherited parameters (directed feature) but not all features with the same + // type. + if (this.shouldConsiderParameter(object)) { + return this.isInheritedParameter(object); + } + return this.eReference.getEType().equals(object.eClass()); + } + @Override public Boolean casePartUsage(PartUsage object) { // Add this behavior parameter check for each caseXXXUsage. @@ -121,4 +143,23 @@ private boolean isInheritedParameter(Feature feature) { }; return featureParameter.contains(feature); } + + private boolean shouldConsiderExhibitState(Feature feature) { + if (!(feature instanceof ExhibitStateUsage)) { + return false; + } + + EClassifier eType = this.eReference.getEType(); + EClass eClass = feature.eClass(); + return eType.equals(eClass) || (eType instanceof EClass eTypeEClass && eTypeEClass.isSuperTypeOf(eClass)); + } + + private boolean isInheritedState(Feature feature) { + List featureState = switch (feature.getOwner()) { + case Definition definition -> definition.getOwnedState(); + case Usage usage -> usage.getNestedState(); + default -> List.of(); + }; + return featureState.contains(feature); + } } diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java index c3a2f8ec6..a21b166b9 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java @@ -53,6 +53,7 @@ import org.eclipse.syson.diagram.common.view.nodes.DecisionActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.DoneStateNodeDescriptionProvider; +import org.eclipse.syson.diagram.common.view.nodes.ExhibitStateInheritedCompartmentItemNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ForkActionNodeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.FramedConcernCompartmentItemNodeDescription; import org.eclipse.syson.diagram.common.view.nodes.FramedConcernCompartmentNodeDescription; @@ -617,16 +618,22 @@ private List> createCompartmentsForListIte compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), true)); compartmentNodeDescriptionProviders.add(new StatesCompartmentNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); + compartmentNodeDescriptionProviders.add(new InheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); + compartmentNodeDescriptionProviders.add(new ExhibitStateInheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); } else if (SysmlPackage.eINSTANCE.getStateUsage().equals(eClass) && SysmlPackage.eINSTANCE.getUsage_NestedState().equals(eReference)) { compartmentNodeDescriptionProviders.add(new StatesCompartmentNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), true)); compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), true)); compartmentNodeDescriptionProviders.add(new StatesCompartmentNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); + compartmentNodeDescriptionProviders.add(new InheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); + compartmentNodeDescriptionProviders.add(new ExhibitStateInheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); } else if (SysmlPackage.eINSTANCE.getStateDefinition().equals(eClass) && SysmlPackage.eINSTANCE.getDefinition_OwnedState().equals(eReference)) { compartmentNodeDescriptionProviders.add(new StatesCompartmentNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), true)); compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), true)); compartmentNodeDescriptionProviders.add(new StatesCompartmentNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); + compartmentNodeDescriptionProviders.add(new InheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); + compartmentNodeDescriptionProviders.add(new ExhibitStateInheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); } else if ((SysmlPackage.eINSTANCE.getStateUsage().equals(eClass) && SysmlPackage.eINSTANCE.getUsage_NestedAction().equals(eReference)) || (SysmlPackage.eINSTANCE.getStateDefinition().equals(eClass) && SysmlPackage.eINSTANCE.getDefinition_OwnedAction().equals(eReference))) { compartmentNodeDescriptionProviders.add(new CompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); @@ -647,6 +654,8 @@ private List> createCompartmentsForListIte compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), true)); compartmentNodeDescriptionProviders.add(new StatesCompartmentNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); compartmentNodeDescriptionProviders.add(new StatesCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator(), false)); + compartmentNodeDescriptionProviders.add(new InheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); + compartmentNodeDescriptionProviders.add(new ExhibitStateInheritedCompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); } else if ((SysmlPackage.eINSTANCE.getActionUsage().equals(eClass) && SysmlPackage.eINSTANCE.getUsage_NestedAction().equals(eReference)) || (SysmlPackage.eINSTANCE.getActionDefinition().equals(eClass) && SysmlPackage.eINSTANCE.getDefinition_OwnedAction().equals(eReference))) { compartmentNodeDescriptionProviders.add(new CompartmentItemNodeDescriptionProvider(eClass, eReference, colorProvider, this.getDescriptionNameGenerator())); diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc index 482b1c5d0..6f629058a 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc @@ -45,7 +45,7 @@ image::release-notes-assume-and-require-edge-tools.png[list available edge tools ** When using direct edit on feature elements that have a value expression (e.g., an `Attribute` with a default value defined), the expression part (after the `=` sign) is no longer part of the initial direct edit value and will be ignored if supplied in the new text value. Users should use the dedicated tools to edit expressions instead, as they ensure only valid expressions (with all names resolving) are accepted. - +** Add support for list item inheritance in _states_ and _exhibit states_ compartments. ** Add support for creating _Start_, _Done_, _Decision_, _Fork_, _Join_, and _Merge_ actions directly on the background of an `Action Flow View` diagram if its root element is an `ActionUsage` or `ActionDefinition`. * In the _Explorer_ view: