Skip to content

Commit

Permalink
[961] Add valid default names for Elements
Browse files Browse the repository at this point in the history
Bug: #961
Signed-off-by: Axel RICHARD <axel.richard@obeo.fr>
  • Loading branch information
AxelRICHARD committed Jan 14, 2025
1 parent fd5aa93 commit 3d944c1
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

- https://github.com/eclipse-syson/syson/issues/953[#953] [rest-apis] Fix an issue where the server could crash when successive REST APIs calls are executed.
More precisely, the dates fields were not serialized correctly.
- https://github.com/eclipse-syson/syson/issues/961[#961] [core] Fix an issue where default names of Elements were sometimes invalid because corresponding to SysMLv2 keywords.
New default names now includes a number, this number corresponding to the count of Elements of the same kind in the scope.

=== Improvements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.syson.sysml.FeatureDirectionKind;
import org.eclipse.syson.sysml.FeatureTyping;
import org.eclipse.syson.sysml.FlowConnectionUsage;
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.ObjectiveMembership;
import org.eclipse.syson.sysml.OwningMembership;
import org.eclipse.syson.sysml.Package;
Expand Down Expand Up @@ -102,7 +103,8 @@ public Element caseDependency(Dependency object) {

@Override
public Element caseDefinition(Definition object) {
object.setDeclaredName(object.eClass().getName());
var existingElements = this.existingElementsCount(object);
object.setDeclaredName(object.eClass().getName() + existingElements);
return object;
}

Expand All @@ -120,7 +122,8 @@ public Element caseElement(Element object) {
@Override
public Element caseEnumerationDefinition(EnumerationDefinition object) {
object.setIsVariation(true);
object.setDeclaredName(object.eClass().getName());
var existingElements = this.existingElementsCount(object);
object.setDeclaredName(object.eClass().getName() + existingElements);
return object;
}

Expand All @@ -136,17 +139,32 @@ public Element caseFlowConnectionUsage(FlowConnectionUsage object) {

@Override
public Element casePackage(Package object) {
object.setDeclaredName(object.eClass().getName());
var existingElements = this.existingElementsCount(object);
object.setDeclaredName(object.eClass().getName() + existingElements);
return object;
}

@Override
public Element casePartUsage(PartUsage object) {
this.caseUsage(object);
if (object.getOwningMembership() instanceof ActorMembership) {
object.setDeclaredName("actor");
long existingElements = 0;
Namespace owningNamespace = object.getOwningNamespace();
if (owningNamespace != null) {
existingElements = owningNamespace.getOwnedMember().stream()
.filter(member -> object.eClass().equals(member.eClass()) && object.getOwningMembership() instanceof ActorMembership)
.count();
}
object.setDeclaredName("actor" + existingElements);
} else if (object.getOwningMembership() instanceof StakeholderMembership) {
object.setDeclaredName("stakeholder");
long existingElements = 0;
Namespace owningNamespace = object.getOwningNamespace();
if (owningNamespace != null) {
existingElements = owningNamespace.getOwnedMember().stream()
.filter(member -> object.eClass().equals(member.eClass()) && object.getOwningMembership() instanceof StakeholderMembership)
.count();
}
object.setDeclaredName("stakeholder" + existingElements);
}
return object;
}
Expand All @@ -159,7 +177,8 @@ public Element casePerformActionUsage(PerformActionUsage object) {

@Override
public Element casePortDefinition(PortDefinition object) {
object.setDeclaredName(object.eClass().getName());
var existingElements = this.existingElementsCount(object);
object.setDeclaredName(object.eClass().getName() + existingElements);
OwningMembership owningMembership = SysmlFactory.eINSTANCE.createOwningMembership();
object.getOwnedRelationship().add(owningMembership);
// No need to set the declaredName for the ConjugatedPortDefinition here, it is always the same than its
Expand Down Expand Up @@ -236,11 +255,24 @@ public Element caseUsage(Usage object) {
if (defaultName.endsWith("Usage")) {
defaultName = defaultName.substring(0, defaultName.length() - 5);
}
object.setDeclaredName(defaultName);

var existingElements = this.existingElementsCount(object);

object.setDeclaredName(defaultName + existingElements);
object.setIsComposite(true);
return object;
}

private long existingElementsCount(Element element) {
Namespace owningNamespace = element.getOwningNamespace();
if (owningNamespace != null) {
return owningNamespace.getOwnedMember().stream()
.filter(member -> element.eClass().equals(member.eClass()))
.count();
}
return 0;
}

private ParameterMembership createParameterMembershipWithReferenceUsage(String refName, FeatureDirectionKind direction) {
var reference = SysmlFactory.eINSTANCE.createReferenceUsage();
reference.setDirection(direction);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*******************************************************************************
* Copyright (c) 2025 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.services;

import static org.assertj.core.api.Assertions.assertThat;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.sirius.components.emf.services.EditingContextCrossReferenceAdapter;
import org.eclipse.sirius.emfjson.resource.JsonResourceFactoryImpl;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Membership;
import org.eclipse.syson.sysml.OwningMembership;
import org.eclipse.syson.sysml.Relationship;
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.SysmlPackage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
* ElementInitializerSwitch-related tests.
*
* @author arichard
*/
public class ElementInitializerSwitchTest {

private static final String ROOT = "root";

private ResourceSet rSet;

private Resource resource;

private ElementInitializerSwitch elementInitializerSwitch;

@BeforeEach
public void setUp() {
this.rSet = new ResourceSetImpl();
// Make sure the resources we manipulate use the Sirius Web CrossReferenceAdapter.
// This is particularly important when manipulating memberships, where the cross-referencer may produce
// unexpected behaviors that cannot be observed in a resource without it.
this.rSet.eAdapters().add(new EditingContextCrossReferenceAdapter());
this.resource = new JsonResourceFactoryImpl().createResource(URI.createURI("ElementInitializerSwitchTest"));
this.rSet.getResources().add(this.resource);
this.elementInitializerSwitch = new ElementInitializerSwitch();
}

@DisplayName("Given a PartUsage, when it is initialized, then it's name contains the count of the same kind of elements")
@Test
public void testPartUsageDefaultName() {
var root = SysmlFactory.eINSTANCE.createPackage();
this.resource.getContents().add(root);
root.setDeclaredName(ROOT);
var p1 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(p1, root);
var initializedP1 = this.elementInitializerSwitch.doSwitch(p1);
assertThat(initializedP1.getDeclaredName()).isEqualTo("part1");
var p2 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(p2, root);
var initializedP2 = this.elementInitializerSwitch.doSwitch(p2);
assertThat(initializedP2.getDeclaredName()).isEqualTo("part2");
}

@DisplayName("Given a PartDefinition, when it is initialized, then it's name contains the count of the same kind of elements")
@Test
public void testPartDefinitionDefaultName() {
var root = SysmlFactory.eINSTANCE.createPackage();
this.resource.getContents().add(root);
root.setDeclaredName(ROOT);
var p1 = SysmlFactory.eINSTANCE.createPartDefinition();
this.addInParent(p1, root);
var initializedP1 = this.elementInitializerSwitch.doSwitch(p1);
assertThat(initializedP1.getDeclaredName()).isEqualTo("PartDefinition1");
var p2 = SysmlFactory.eINSTANCE.createPartDefinition();
this.addInParent(p2, root);
var initializedP2 = this.elementInitializerSwitch.doSwitch(p2);
assertThat(initializedP2.getDeclaredName()).isEqualTo("PartDefinition2");
}

@DisplayName("Given a EnumerationDefinition, when it is initialized, then it's name contains the count of the same kind of elements")
@Test
public void testEnumerationDefinitionDefaultName() {
var root = SysmlFactory.eINSTANCE.createPackage();
this.resource.getContents().add(root);
root.setDeclaredName(ROOT);
var ed1 = SysmlFactory.eINSTANCE.createEnumerationDefinition();
this.addInParent(ed1, root);
var initializedED1 = this.elementInitializerSwitch.doSwitch(ed1);
assertThat(initializedED1.getDeclaredName()).isEqualTo("EnumerationDefinition1");
var ed2 = SysmlFactory.eINSTANCE.createEnumerationDefinition();
this.addInParent(ed2, root);
var initializedED2 = this.elementInitializerSwitch.doSwitch(ed2);
assertThat(initializedED2.getDeclaredName()).isEqualTo("EnumerationDefinition2");
}

@DisplayName("Given a Package, when it is initialized, then it's name contains the count of the same kind of elements")
@Test
public void testPackageDefaultName() {
var root = SysmlFactory.eINSTANCE.createPackage();
this.resource.getContents().add(root);
root.setDeclaredName(ROOT);
var p1 = SysmlFactory.eINSTANCE.createPackage();
this.addInParent(p1, root);
var initializedP1 = this.elementInitializerSwitch.doSwitch(p1);
assertThat(initializedP1.getDeclaredName()).isEqualTo("Package1");
var p2 = SysmlFactory.eINSTANCE.createPackage();
this.addInParent(p2, root);
var initializedP2 = this.elementInitializerSwitch.doSwitch(p2);
assertThat(initializedP2.getDeclaredName()).isEqualTo("Package2");
}

@DisplayName("Given an Actor, when it is initialized, then it's name contains the count of the same kind of elements")
@Test
public void testActorDefaultName() {
var root = SysmlFactory.eINSTANCE.createPackage();
this.resource.getContents().add(root);
root.setDeclaredName(ROOT);
var p1 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(p1, root);
var a1 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(a1, p1, SysmlPackage.eINSTANCE.getActorMembership());
var initializedA1 = this.elementInitializerSwitch.doSwitch(a1);
assertThat(initializedA1.getDeclaredName()).isEqualTo("actor1");
var a2 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(a2, p1, SysmlPackage.eINSTANCE.getActorMembership());
var initializedA2 = this.elementInitializerSwitch.doSwitch(a2);
assertThat(initializedA2.getDeclaredName()).isEqualTo("actor2");
}

@DisplayName("Given a Stakeholder, when it is initialized, then it's name contains the count of the same kind of elements")
@Test
public void testStakeholderDefaultName() {
var root = SysmlFactory.eINSTANCE.createPackage();
this.resource.getContents().add(root);
root.setDeclaredName(ROOT);
var p1 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(p1, root);
var s1 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(s1, p1, SysmlPackage.eINSTANCE.getStakeholderMembership());
var initializedS1 = this.elementInitializerSwitch.doSwitch(s1);
assertThat(initializedS1.getDeclaredName()).isEqualTo("stakeholder1");
var s2 = SysmlFactory.eINSTANCE.createPartUsage();
this.addInParent(s2, p1, SysmlPackage.eINSTANCE.getStakeholderMembership());
var initializedS2 = this.elementInitializerSwitch.doSwitch(s2);
assertThat(initializedS2.getDeclaredName()).isEqualTo("stakeholder2");
}

private void addInParent(Element element, Element parent) {
OwningMembership owningMembership = SysmlFactory.eINSTANCE.createOwningMembership();
parent.getOwnedRelationship().add(owningMembership);
owningMembership.getOwnedRelatedElement().add(element);
}

private void addInParent(Element element, Element parent, EClass membershipKind) {
var membership = SysmlFactory.eINSTANCE.create(membershipKind);
assertThat(membership).isInstanceOf(Membership.class);
parent.getOwnedRelationship().add((Relationship) membership);
((Relationship) membership).getOwnedRelatedElement().add(element);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ public InterfaceUsage createInterfaceUsage(PortUsage sourcePort, PortUsage targe
}

InterfaceUsage interfaceUsage = SysmlFactory.eINSTANCE.createInterfaceUsage();
this.addChildInParent(interfaceContainer, interfaceUsage);
this.elementInitializer(interfaceUsage);
// Edges should have an empty default name. This is not the case when using the initializer, because
// InterfaceUsage can be a node, which requires a default name.
interfaceUsage.setDeclaredName("");
this.addChildInParent(interfaceContainer, interfaceUsage);

EndFeatureMembership sourceEndFeatureMembership = SysmlFactory.eINSTANCE.createEndFeatureMembership();
interfaceUsage.getOwnedRelationship().add(sourceEndFeatureMembership);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.OwningMembership;
import org.eclipse.syson.sysml.Package;
import org.eclipse.syson.sysml.PartDefinition;
import org.eclipse.syson.sysml.PartUsage;
import org.eclipse.syson.sysml.PortUsage;
import org.eclipse.syson.sysml.util.ModelBuilder;
Expand All @@ -52,6 +53,8 @@ public class InterconnectionViewCreateServiceTests {

private Package rootPkg;

private PartDefinition partDef1;

private PartUsage part1;

private PartUsage part2;
Expand Down Expand Up @@ -94,7 +97,7 @@ public void testCreateBindingConnectorAsUsage() {
BindingConnectorAsUsage bindingConnectorAsUsage = this.interconnectionViewCreateService.createBindingConnectorAsUsage(this.port2, this.port3);
assertNotNull(bindingConnectorAsUsage);
var owningNamespace = bindingConnectorAsUsage.getOwningNamespace();
assertSame(this.rootPkg, owningNamespace);
assertSame(this.partDef1, owningNamespace);
var owningMembership = bindingConnectorAsUsage.getOwningMembership();
assertInstanceOf(OwningMembership.class, owningMembership);
}
Expand All @@ -105,7 +108,7 @@ public void testCreateInterfaceUsage() {
InterfaceUsage interfaceUsage = this.interconnectionViewCreateService.createInterfaceUsage(this.port2, this.port3);
assertNotNull(interfaceUsage);
var owningNamespace = interfaceUsage.getOwningNamespace();
assertSame(this.rootPkg, owningNamespace);
assertSame(this.partDef1, owningNamespace);
var owningMembership = interfaceUsage.getOwningMembership();
assertInstanceOf(OwningMembership.class, owningMembership);
}
Expand All @@ -116,15 +119,16 @@ public void testCreateFlowConnectionUsage() {
FlowConnectionUsage flowConnectionUsage = this.interconnectionViewCreateService.createFlowConnectionUsage(this.port2, this.port3);
assertNotNull(flowConnectionUsage);
var owningNamespace = flowConnectionUsage.getOwningNamespace();
assertSame(this.rootPkg, owningNamespace);
assertSame(this.partDef1, owningNamespace);
var owningMembership = flowConnectionUsage.getOwningMembership();
assertInstanceOf(OwningMembership.class, owningMembership);
}

private void build() {
Namespace rootNamespace = this.builder.createRootNamespace();
this.rootPkg = this.builder.createInWithName(org.eclipse.syson.sysml.Package.class, rootNamespace, "RootPkg");
this.part1 = this.builder.createInWithName(PartUsage.class, this.rootPkg, "part1");
this.partDef1 = this.builder.createInWithName(PartDefinition.class, this.rootPkg, "PartDefinition1");
this.part1 = this.builder.createInWithName(PartUsage.class, this.partDef1, "part1");
this.part2 = this.builder.createInWithName(PartUsage.class, this.part1, "part2");
this.port2 = this.builder.createInWithName(PortUsage.class, this.part2, "port2");
this.part3 = this.builder.createInWithName(PartUsage.class, this.part1, "part3");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

- Fix an issue where the server could crash when successive REST APIs calls are executed.
More precisely, the dates fields were not serialized correctly.
- Fix an issue where default names of `Elements` were sometimes invalid because corresponding to SysMLv2 keywords.
New default names now includes a number, this number corresponding to the count of `Elements` of the same kind in the scope.
For example, if a `PartDefinition` already define 5 parts, then a new `Part` created under this `PartDefinition` would have _part6_ for name.

== New features

Expand Down

0 comments on commit 3d944c1

Please sign in to comment.